Calibration tools for Apollo .record data and related image / point-cloud workflows.
| Module | Current status | Practical recommendation |
|---|---|---|
lidar2lidar |
real-bag validated | keep scan2scan as production baseline; use scan2map as conditional refinement |
lidar2imu |
real-bag validated | keep --profile baseline as regression reference; use --profile production as the current map-side production candidate |
camera |
standalone intrinsic tool exists | usable as a local intrinsic calibrator |
camera2lidar |
scripts exist but not yet under repo-wide metrics framework | next target for repo-level cleanup |
The durable project knowledge base lives under context/.
Recommended entry points:
context/index.mdcontext/knowledge_base/calibration_overview.mdcontext/knowledge_base/validated_conclusions.mdcontext/knowledge_base/verification_points.md
Documentation:
- LiDAR-to-LiDAR overview: docs/lidar2lidar.md
- LiDAR-to-LiDAR Quick Start: docs/lidar2lidar_quickstart.md
- LiDAR-to-LiDAR current design: docs/lidar2lidar_design.md
- LiDAR-to-IMU overview: docs/lidar2imu.md
- LiDAR-to-IMU Quick Start: docs/lidar2imu_quickstart.md
- LiDAR-to-IMU current design: docs/lidar2imu_design.md
- Camera intrinsic quick start: camera/README.md
Use a virtual environment:
python3 -m venv .venv
source .venv/bin/activate
pip install -e .Set your Apollo record directory:
RECORD_DIR=../apollo-lite/data/bag/record_data
OUTPUT_DIR=outputs/lidar2lidar/auto_calib_reviewInspect topics:
lidar2lidar-topics "$RECORD_DIR"Bootstrap fallback extrinsics from /tf_static and run automatic calibration:
lidar2lidar-auto \
--record-path "$RECORD_DIR" \
--conf-dir lidar2lidar/conf \
--bootstrap-conf \
--output-dir "$OUTPUT_DIR" \
--sync-threshold-ms 10 \
--min-overlap 0.30 \
--methods 2 \
--max-samples 1 \
--save-merged-pcdResults:
- Final consolidated extrinsics:
$OUTPUT_DIR/calibrated_tf.yaml - Example final extrinsics path:
outputs/lidar2lidar/auto_calib_review/calibrated_tf.yaml - Evaluation metrics:
$OUTPUT_DIR/metrics.yaml - Initial extrinsics snapshot:
$OUTPUT_DIR/initial_guess/*.yaml - Per-edge calibrated extrinsics:
$OUTPUT_DIR/calibrated/*.yaml - Refreshed fallback extrinsics:
lidar2lidar/conf/*.yaml - Detailed diagnostics:
$OUTPUT_DIR/diagnostics/
Build the scan2map dataset artifact on the same bag:
lidar2lidar-scan2map-dataset \
--record-path "$RECORD_DIR" \
--conf-dir lidar2lidar/conf \
--output-dir outputs/lidar2lidar/scan2map_dataset_review \
--pose-topic /apollo/localization/poseThis produces:
diagnostics/scan2map_dataset.yaml: aligned scans, keyframes, holdout split, and submap definitionsdiagnostics/manifest.yaml: extraction artifact manifest
Run the scan2map candidate calibration on that dataset artifact:
lidar2lidar-scan2map \
--record-path "$RECORD_DIR" \
--dataset-yaml outputs/lidar2lidar/scan2map_dataset_review/diagnostics/scan2map_dataset.yaml \
--conf-dir lidar2lidar/conf \
--output-dir outputs/lidar2lidar/scan2map_candidate_review \
--scan2scan-baseline-tf outputs/lidar2lidar/auto_calib_review/calibrated_tf.yamlThis produces:
calibrated_tf.yaml: accepted scan2map candidate extrinsicsmetrics.yaml: coarse and fine scan2map metricsdiagnostics/scan2map_optimization.yaml: per-edge optimization and holdout diagnosticsdiagnostics/evaluation.yaml: summarized comparison outputs
For vehicle-rig verification, you can also lock selected transform components to the scan2scan baseline while testing one edge. This is useful when a scan2map candidate improves holdout fitness mainly through vertical-attitude drift:
lidar2lidar-scan2map --record-path "$RECORD_DIR" --dataset-yaml outputs/lidar2lidar/scan2map_dataset_review/diagnostics/scan2map_dataset.yaml --conf-dir lidar2lidar/conf --output-dir outputs/lidar2lidar/scan2map_candidate_review --source-topics /apollo/sensor/lslidar_right/PointCloud2 --scan2scan-baseline-tf outputs/lidar2lidar/auto_calib_review/calibrated_tf.yaml --constraint-reference scan2scan_baseline --lock-components z pitch rollOptional helpers:
lidar2lidar-extract --input-dir "$RECORD_DIR" --output-dir outputs/lidar2lidar/pcd_export -c /apollo/sensor/lslidar_main/PointCloud2
lidar2lidar-calibrate --source-pcd source.pcd --target-pcd target.pcd --initial-transform lidar2lidar/conf/lslidar_main_lslidar_left_extrinsics.yaml --output-transform result.yaml
lidar2lidar-merge --source-pcd source.pcd --target-pcd target.pcd --transform result.yaml --output-pcd merged_output.pcdlidar2imu-calibrate consumes curated feature samples instead of raw records.
The implementation keeps algorithm stages and evaluation metrics
separate, so the solver can evolve without changing the reporting layout.
lidar2imu-calibrate \
--input lidar2imu_samples.yaml \
--planar-motion-policy auto \
--output-dir outputs/lidar2imu/run01Results:
- Final extrinsics:
outputs/lidar2imu/run01/calibrated/<parent>_<child>_extrinsics.yaml - Consolidated output:
outputs/lidar2imu/run01/calibrated_tf.yaml - Evaluation metrics:
outputs/lidar2imu/run01/metrics.yaml - Detailed diagnostics:
outputs/lidar2imu/run01/diagnostics/
Documentation:
- Overview: docs/lidar2imu.md
- Quick Start: docs/lidar2imu_quickstart.md
- Current design: docs/lidar2imu_design.md
To bootstrap those standardized samples from an Apollo record bag, use:
lidar2imu-convert-record \
--profile production \
--record-path /path/to/record_dir \
--output-dir outputs/lidar2imu/raw_validation \
--calibrateIf the bag does not contain a static LiDAR-to-parent TF, provide an explicit
prior with --initial-transform path/to/extrinsics.yaml, or use
--identity-initial-transform for exploratory-only runs.
This produces:
standardized_samples.yaml: normalized ground and motion samplesconversion_diagnostics.yaml: conversion-layer diagnosticscalibration/: final extrinsics, metrics, and diagnostics from the staged solver, including motion registration quality, turn-balance warnings, and avehicle_motion_assessmentrecommendation
Current lidar2imu operating rule:
- baseline:
lidar2imu-convert-record --profile baseline - production:
lidar2imu-convert-record --profile production - accept production only after checking:
- trusted-reference consistency
- extraction consistency
- planar basin stability
- holdout generalization
For weak-planar bags, --planar-motion-policy auto keeps x/y/yaw near the
initial prior when turn balance or yaw observability is weak, while still letting
the solver refine z/roll/pitch.
- The repo-level conclusion is:
- baseline:
scan2scan - refinement path:
scan2map
- baseline:
- For vehicle rigs, always judge:
- planar:
x/y/yaw - vertical-attitude:
z/pitch/roll
- planar:
- On the current tested bag:
left -> mainscan2map can be accepted as a refinement candidateright -> mainunconstrained scan2map remains diagnostic
- The repo-level conclusion is:
- pose-derived gravity stays the default
- keep
baseline = scan_to_scan - keep
production = submap_to_map + auto reextract + holdout acceptance - weak-planar bags should prefer
--planar-motion-policy auto - acceptance must be driven by tested data, not only by solver convergence
- The current data-layer policy now uses:
- window + gate motion selection
- registration-fitness gating
- weak-planar solver freeze when needed
- trusted-reference / extraction / basin / holdout acceptance gates
The next repo-level cleanup target is lidar2camera / camera2lidar.
The goal is to bring camera-related calibration into the same pattern already used
by lidar2lidar and lidar2imu:
- data layer
- algorithm layer
- stable evaluation layer
That includes:
- explicit dataset artifacts
- window + gate for invalid samples
- stable metrics / diagnostics
- separating validated conclusions from open verification points