Turn a rosbag into a map you can actually drive on.
ROS 2 LiDAR SLAM that outputs an Autoware-compatible map bundle — pointcloud_map/, map_projector_info.yaml, and auto-generated lanelet2. Frontend is RKO-LIO (MIT), backend is graph_based_slam (BSD-2). No GPL components on the default workflow.
Shinjuku point cloud map built from a demo rosbag with this stack — start at the Quickstart. develop is the default branch; current release candidate notes: v0.9.1. 日本語クイックスタート.
| Goal | Start here | Safety and cost boundary |
|---|---|---|
| See a verified map, no build | Default if unsure: Docker demo | Stable v0.9.0-humble; needs Docker; host writes stay in ./lidarslam_output. |
| Map my own rosbag | Own-bag route: lidarslam-map doctor /path/to/rosbag2 |
Read-only diagnosis first; then lidarslam-map start /path/to/rosbag2 writes a new output. |
| Build the current candidate or contribute | Source quickstart: bash scripts/source_quickstart.sh --dry-run |
Candidate v0.9.1; needs ROS 2, 8 GiB, and roughly 30 minutes. |
docker run --rm -e LIDARSLAM_HOST_UID="$(id -u)" -e LIDARSLAM_HOST_GID="$(id -g)" \
-v "$PWD/lidarslam_output:/lidarslam_ws/output" \
ghcr.io/rsasaki0109/lidar_slam_ros2:v0.9.0-humbleUse the latest published stable image (v0.9.0-humble) for the 517 MB MID-360 demo; it writes lidarslam_output/mid360_demo/ and returns ownership via UID/GID. The v0.9.1 release candidate is not published yet; use the source quickstart for that candidate. See Getting Started for other platforms.
lidarslam-map start /path/to/rosbag2Not sure where to begin? Run lidarslam-map with no arguments in a terminal; its safe home offers an installation check, the demo, your own bag, or previous sessions. Before finding a bag, run lidarslam-map doctor; it uses no network, writes no files, and prints one recovery action for each missing requirement.
start checks sensor setup before writing. See supported inputs and recovery.
mkdir -p ~/ros2_ws/src
cd ~/ros2_ws/src
git clone --recursive https://github.com/rsasaki0109/lidar_slam_ros2.git
cd lidar_slam_ros2
bash scripts/source_quickstart.shThe helper detects Humble/Jazzy, verifies the exact maintained six-package inventory, installs repository-only dependencies, builds only that list, and runs the verified demo. Use --dry-run or --build-only.
Completion prints an absolute lidarslam-map path that auto-activates this build in a fresh terminal—no remembered source install/setup.bash. ROS 2 must be installed. Allow 8 GiB and roughly 30 minutes; see Getting Started and Operator workflows for contracts and contributor tests. Before handoff, run the read-only offline package audit: python3 scripts/check_first_map_verification_package.py --json and continue only on READY.
For an Ouster, Velodyne, RoboSense, simulated, or another compatible PointCloud2 bag, do not edit this package's launch files or YAML first; run lidarslam-map doctor /path/to/rosbag2, then start detects the inputs, builds a verified map, and opens it.
The guided path checks topics, frames, fields, timestamps, a maintained profile, and calibration; unsafe inputs stop with a stable reason code and one next action, while detection alone is not a verified vendor-support or accuracy claim. PointCloud2+Imu, PointCloud2+NavSatFix, and VelodyneScan+Applanix GSOF49 are the maintained input combinations.
lidarslam-map start /path/to/rosbag2With Docker but no ROS installation, run the same high-level workflow from this checkout; the bag is mounted read-only and all output returns to your user:
bash scripts/docker_map_bag.sh /absolute/path/to/rosbag2See Docker Own-Bag Map for dry-run, Jazzy, calibration, immutable-image, and private no-write JSON-plan options.
For RKO-LIO profiles, --editable retains deterministic replay input for later loop fixes. A successful run writes
Autoware artifacts; lidarslam-map view "$PWD/output/my_map" provides offline 3D review and
source-preserving edit plans that lidarslam-map edit applies without extra replay paths.
Reopen runs with lidarslam-map sessions, compare two with lidarslam-map compare day1 day2, create a private-by-default issue ZIP with lidarslam-map support day1, prepare a verified first-map report with lidarslam-map support day1 --first-map, or merge visits with lidarslam-map merge day1 day2 --output-dir site_project.
For fixed Docker/source output, use lidarslam-map report /path/to/output/mid360_demo --json when the reviewed candidate CLI is installed; the stable-image fallback and attachment boundary are in the first-map validation guide.
Automation can use lidarslam-map run; direct launches and filtering are in Operator workflows.
Most LiDAR SLAM stacks stop at a trajectory and a point cloud. This one ships the artifacts you need downstream:
- Autoware-compatible output —
pointcloud_map/+map_projector_info.yamlopen directly in Autoware map loaders;verify_autoware_map.pyprintsmap_verify: PASSon every saved bundle. - lanelet2 auto-generation — drivable lanelets from the SLAM trajectory, validated for multi-segment Autoware routing.
- Surveyed ground truth — releases are gated in CI by per-dataset APE thresholds, including total-station checkpoints on a Livox MID-360 (accuracy).
- Loop closure, GPL-free — opt-in built-in Scan Context, BEV / SOLiD / STD/BTC-style Triangle descriptors, and 3D-BBS verification.
- Tunnel / fog degeneracy presets — opt-in radar fusion and gravity alignment map a ~500 m self-similar tunnel end-to-end (result, guide).
- Deterministic offline mapping — backend and frontend offline runners produce byte-identical trajectories, loop edges, and submaps under the release gate.
- Globally refined, quality-gated maps — clean-room plane bundle adjustment refines submap poses under holdout-validated quality thresholds (evidence).
- GNSS and camera output — optional georeferencing plus calibration-aware, occlusion-resistant camera colouring.
flowchart LR
bag(["rosbag2"]) --> rko["RKO-LIO<br/>LiDAR-inertial odometry"]
rko --> gbs["graph_based_slam<br/>loop closure + graph optimization"]
gbs --> bundle["Autoware map bundle<br/>pointcloud_map · lanelet2 · projector info"]
The pipeline registers LiDAR scans with the corrected trajectory, then projects synchronized camera pixels onto that geometry. This RTK-SLAM Construction Hall 1 result follows the full estimated 60 m walking loop.
K4 has 4.91 M points, 76.66% colour coverage, and 11/11 profile checks passing. Pose-aware dynamic cleaning before K3's camera fusion improves held-out RGB median from 41.17 to 40.54 and planar roughness median from 7.23 to 6.40. See the release-readiness record for paired K3/K4 evidence and limits. The sequence is from RTK-SLAM (CC-BY 4.0); its total-station checkpoints also drive the accuracy gate.
If graph optimization outputs sparse keyframes, the coloured-map pipeline can propagate their corrections onto the dense SLAM pose stream automatically:
python3 tools/colored_map/colored_map_pipeline.py \
<bag> output/<run>/traj_corrected.tum output/<run>/colored_map \
--raw-traj output/<run>/traj_raw.tum \
--extrinsic configs/gaussian_splatting/<lidar_camera_extrinsic>.yamlThe pipeline caches dense_corrected_trajectory.tum and rebuilds stale downstream artifacts; use --force-trajectory for an explicit refresh.
Moving rigs can add --refine-spatiotemporal-calibration; see the held-out-gated design and RTK-SLAM result.
public_suite_v1.yaml connects Localization Zoo trajectories to trajectory,
geometry, real-RGB, runtime, and memory gates, with frozen OFF/ON candidate
promotion across MID-360, HILTI, and RTK-SLAM surveyed references. Commands,
replay, and adoption records:
Benchmarking and release gate.
On the same HILTI 2022 exp04 LiDAR/IMU input and CPU-only host, the competitive
profile recorded 34.7% lower median APE RMSE than GLIM over three completed
historical runs:
| System | Median APE RMSE | Median processing RTF | Maximum peak RSS |
|---|---|---|---|
| lidarslam_ros2 | 0.0565 m | 0.993 | 586.83 MB |
| GLIM CPU | 0.0866 m | 0.244 | 690.88 MB |
This is a scoped HILTI exp04 trajectory-accuracy and peak-memory win; GLIM
wins runtime. It is not an overall-SOTA claim. Normal development needs only
one new run:
python3 scripts/run_ours_competitive_benchmark.py \
--bag <hilti-exp04-ros2-bag> \
--reference-tum <common-six-checkpoints.tum> \
--reference-meta <common-six-checkpoints.json> \
--rko-param configs/hilti2022/rko_lio_hilti2022_pandar_competitive_v2.yaml \
--lidarslam-param configs/hilti2022/lidarslam_competitive_v2.yaml \
--output output/hilti_exp04_ours_n1 --runs 1The GLIM CPU counterpart is:
python3 scripts/run_glim_benchmark.py \
--bag <hilti-exp04-ros2-bag> \
--output output/hilti_exp04_glim_n1 --runs 1Exact scoring rules, revisions, checkpoint policy, and map-quality limitations are in Comparison.
In a separate n=1 development measurement, task-local correspondence
reduction kept the 1,258-pose frontend trajectory byte-identical while mean ICP
time moved from 21.60 ms to 20.92 ms and frontend wall time from 47.51 s to
46.55 s. These timing deltas are directional optimization evidence; see the
SOTA product development plan.
On the ~500 m self-similar Fyllingsdalen tunnel from the NTNU LiDAR degeneracy datasets, the plain frontend covers 98.7 m before along-axis degeneracy freezes it. The opt-in presets (radar ego-velocity fusion + sliding-window gravity alignment) map the whole tunnel — reach 504.5 m, transverse RMS 1.34 m, end-height −4.7 m (−33 m without gravity alignment) — and cut fog clutter-lock drift 35.6 → 9.6 m, with defaults unchanged (a MID-360 driving holdout stays byte-identical). Symptom table: Degeneracy Resilience Guide; evidence: research note.
Current numbers from the release-gate profiles (scripts/release_profiles.yaml).
The pre-release gate rejects missing evidence and regressions in every blocking profile.
| Dataset | Sensor | Reference | APE RMSE | Gate (pass) |
|---|---|---|---|---|
NTU VIRAL tnp_01 (outdoor, ~580 s) |
Ouster OS1-16 + VN-100 | Leica prism ground truth | 0.95 m (best 0.87) | ≤ 1.00 m |
| RTK-SLAM Construction Hall 2 (indoor, ~600 s) | Livox MID-360 | total-station checkpoints¹ | 0.086 m (median 0.064, 16/16) | ≤ 0.30 m |
| RTK-SLAM Construction Hall 1 (indoor, ~741 s) | Livox MID-360 | total-station checkpoints¹ | 0.321 m (median 0.163, 16/16) | ≤ 0.55 m |
| RTK-SLAM Stadtgarten 2 (outdoor park, ~876 s) | Livox MID-360 | total-station checkpoints¹ | 0.426 m (median 0.264, 19/19) | report-only² |
| RTK-SLAM Stadtgarten 1 (outdoor park, ~1 km loop) | Livox MID-360 | total-station checkpoints¹ | 0.838 m (median 0.511, 36/36) | report-only² |
Newer College Maths-Hard (~320 m loop) |
Ouster OS0-128 | ICP registration to survey-grade map | candidate evidence required locally | ≤ 0.10 m |
¹ Surveyed checkpoints from the public RTK-SLAM dataset (CC-BY 4.0), scored like its published baselines (dense odometry trajectory). ² Outdoor profiles soak as report-only before graduating; the former GLIM cross-validation gate is also report-only since v0.5. Methodology and caveats: docs/comparison.md.
Reproduce locally:
python3 scripts/download_rtk_slam_dataset.py --sequence construction_seq2 --eval-assets
python3 scripts/run_rtk_slam_accuracy_suite.pyDetails and optional MID-360 / production-bundle gates: docs/benchmarking.md.
- Getting started: Getting Started · Distribution · Autoware quickstart · Operator workflows · Autoware Foxglove
- Pipelines: Autoware-compatible map authoring
- Benchmarking: Benchmarking and release gate · Comparison
- Project: SOTA product development plan · Product contract · v1.0 readiness · Independent first-map validation · v0.9.1 RC notes · v0.9 roadmap · Contributing · Support · Security · Governance · Changelog · Releasing
Preview the doc site locally: python3 -m mkdocs serve.
| ROS 2 distro | Ubuntu | Scope |
|---|---|---|
| Humble | 22.04 | default workflow build + package tests in CI |
| Jazzy | 24.04 | default workflow build + package tests in CI; Autoware dogfood exercised locally |
graph_based_slam is BSD-2-Clause; RKO-LIO, DLIO, and the optional vendored
3D-BBS are MIT; FAST_GICP is BSD-3-Clause; built-in Scan Context is local. GPL-only
components (Thirdparty/lio-sam, Thirdparty/3d_bbs) are excluded via COLCON_IGNORE.
bash scripts/run_default_ci_checks.sh
bash scripts/run_release_readiness_checks.sh --fail-on-profiles
python3 scripts/check_v1_readiness.py
python3 scripts/check_ndt_omp_release_readiness.py --offlineReference commands and parameter pointers live in docs/workflows.md.
If this project saves you mapping time, a ⭐ helps others find it.



