Operator Workflows
This page keeps the procedural details that do not need to stay in the top-level README.
Build Prerequisites
scanmatcherdepends onndt_omp_ros2- clone with submodules:
cd ~/ros2_ws/src
git clone --recursive https://github.com/rsasaki0109/lidarslam_ros2
cd ..
rosdep install --from-paths src --ignore-src -r -y
- build and run the default checks:
colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=Release
bash scripts/run_default_ci_checks.sh
Main Entry Points
| Goal | Entrypoint |
|---|---|
| Autoware pointcloud-map quickstart | bash scripts/run_autoware_quickstart.sh |
| Full dogfood flow | bash scripts/run_rko_lio_graph_autoware_dogfood.sh --auto-exit-secs 20 |
| Standard NTU VIRAL benchmark | bash scripts/run_rko_lio_graph_benchmark.sh |
| MID360 cross-validation benchmark | bash scripts/run_rko_lio_mid360_crossval_benchmark.sh |
| Mixed-quality open-data GNSS smoke | bash scripts/run_open_data_applanix_velodyne_gnss_smoke.sh --bag /path/to/rosbag2 --applanix-msg-dir /tmp/applanix/applanix_msgs/msg --verify-map |
| Mixed-quality open-data GNSS benchmark | bash scripts/run_open_data_applanix_velodyne_gnss_benchmark.sh --bag /path/to/rosbag2 --applanix-msg-dir /tmp/applanix/applanix_msgs/msg --verify-map |
| Leo Drive classic-path suite | bash scripts/run_open_data_classic_path_benchmark_suite.sh --applanix-msg-dir /tmp/applanix/applanix_msgs/msg --verify-map |
| Packet IMU deskew validation matrix | bash scripts/run_open_data_packet_imu_deskew_validation_matrix.sh --applanix-msg-dir /tmp/applanix/applanix_msgs/msg |
| Dynamic-object-filter save-map benchmark | bash scripts/run_dynamic_object_filter_benchmark.sh |
| MID360 place-recognition comparison | bash scripts/run_place_recognition_benchmark.sh |
| Release/readiness gate | bash scripts/run_release_readiness_checks.sh --ape-threshold 0.10 |
Required Input Topics
Public default path: RKO-LIO + graph_based_slam
Launch:
ros2 launch lidarslam rko_lio_slam.launch.py \
bag_path:=/path/to/rosbag2 \
lidar_topic:=/os_cloud_node/points \
imu_topic:=/os_cloud_node/imu
Required inputs:
lidar_topic:sensor_msgs/msg/PointCloud2imu_topic:sensor_msgs/msg/Imu
Optional inputs:
/gnss/fix:sensor_msgs/msg/NavSatFixwhengraph_based_slam use_gnss:=true
Internal wiring in this launch:
RKO-LIOpublishes odometry on/rko_lio/odometryRKO-LIOpublishes submap source clouds on/rko_lio/framegraph_based_slamconsumes those viaodom_inputandcloud_input
Not currently supported in the public path:
- wheel odometry / vehicle speed topic fusion
GNSS note:
- GNSS is added as translation-only pose-graph constraints in the backend
- when covariance is present, edge weight is scaled from
position_covariance NavSatFixdoes not standardize RTK fix status, sograph_based_slamtreats low horizontal covariance asRTK-like- default threshold:
gnss_rtk_fix_max_horizontal_stddev_m = 0.3
Classic path: scanmatcher + graph_based_slam
Launch:
ros2 launch lidarslam lidarslam.launch.py \
input_cloud:=/points_raw \
imu_topic:=/imu
Required inputs:
input_cloud:sensor_msgs/msg/PointCloud2- TF from
robot_frame_idto the LiDAR frame
Optional inputs:
imu_topic:sensor_msgs/msg/Imuwhenscanmatcher use_imu:=true- odom TF into
odom_frame_idwhenscanmatcher use_odom:=true /gnss/fix:sensor_msgs/msg/NavSatFixwhen backenduse_gnss:=true
Internal wiring in this launch:
scanmatcherpublisheslidarslam_msgs/msg/MapArrayonmap_arraygraph_based_slamsubscribes tomap_array
Backend only: graph_based_slam
Launch:
ros2 launch graph_based_slam graphbasedslam.launch.py
Default required input:
map_array:lidarslam_msgs/msg/MapArray
Optional backend aids:
/imu:sensor_msgs/msg/Imuwhenuse_imu_preintegration:=true/gnss/fix:sensor_msgs/msg/NavSatFixwhenuse_gnss:=true
Alternative direct-input mode used by the RKO-LIO launch:
odom_input:nav_msgs/msg/Odometrycloud_input:sensor_msgs/msg/PointCloud2
Useful GNSS weighting parameters:
gnss_topicgnss_info_weightgnss_use_covariance_weightinggnss_covariance_min_variance_m2gnss_covariance_max_variance_m2gnss_rtk_fix_max_horizontal_stddev_mgnss_rtk_fix_weight_scalegnss_non_rtk_weight_scale
Optional save-time dynamic-object filter:
- affects only the map written by
/map_save - does not change live odometry, loop closure, or the published working map
- useful when repeated passes observe parked/static structure consistently but transient objects appear only once
Parameters:
use_dynamic_object_filterdynamic_object_filter_voxel_sizedynamic_object_filter_min_observationsdynamic_object_filter_temporal_windowdynamic_object_filter_max_range_from_sensor_m
Inspect a bag before enabling GNSS weighting:
python3 scripts/inspect_navsatfix_covariance.py /path/to/rosbag2 --topic /gnss/fix
For Leo Drive open-data driving bags that only expose Applanix raw GNSS status,
inspect GSOF50 instead:
git clone --depth=1 https://github.com/autowarefoundation/applanix.git /tmp/applanix
python3 scripts/inspect_applanix_gsof50_quality.py /path/to/rosbag2 \
--topic /lvx_client/gsof/ins_solution_rms_50 \
--applanix-msg-dir /tmp/applanix/applanix_msgs/msg
If the bag has GSOF49/50 but no /gnss/fix, generate a sidecar rosbag2 that
publishes only sensor_msgs/msg/NavSatFix:
git clone --depth=1 https://github.com/autowarefoundation/applanix.git /tmp/applanix
python3 scripts/convert_applanix_gsof_to_navsatfix_bag.py \
--input /path/to/rosbag2 \
--output /tmp/applanix_navsatfix_bag \
--gsof49-topic /lvx_client/gsof/ins_solution_49 \
--gsof50-topic /lvx_client/gsof/ins_solution_rms_50 \
--applanix-msg-dir /tmp/applanix/applanix_msgs/msg \
--force
If you want to test the same Applanix raw messages as sensor_msgs/msg/Imu,
generate an IMU sidecar too:
git clone --depth=1 https://github.com/autowarefoundation/applanix.git /tmp/applanix
python3 scripts/convert_applanix_gsof_to_imu_bag.py \
--input /path/to/rosbag2 \
--output /tmp/applanix_imu_bag \
--gsof49-topic /lvx_client/gsof/ins_solution_49 \
--gsof50-topic /lvx_client/gsof/ins_solution_rms_50 \
--output-topic /imu \
--frame-id base_link \
--applanix-msg-dir /tmp/applanix/applanix_msgs/msg \
--force
Then play the original bag together with the generated sidecar bag:
ros2 bag play /path/to/rosbag2 --clock
ros2 bag play /tmp/applanix_navsatfix_bag
For an end-to-end open-data GNSS smoke with automatic /map_save, use:
bash scripts/run_open_data_gnss_smoke.sh \
--bag /path/to/rosbag2 \
--verify-map
run_open_data_gnss_smoke.sh auto-detects the NavSatFix topic from
--gnss-bag when provided, otherwise from --bag.
For Leo Drive driving bags that expose LiDAR as
velodyne_msgs/msg/VelodyneScan and GNSS quality as Applanix GSOF49/50,
use the packet-to-PointCloud2 wrapper instead:
git clone --depth=1 https://github.com/autowarefoundation/applanix.git /tmp/applanix
bash scripts/run_open_data_applanix_velodyne_gnss_smoke.sh \
--bag demo_data/autoware_leo_drive_isuzu/driving_30_kmh_2022_06_10-15_47_42_compressed \
--applanix-msg-dir /tmp/applanix/applanix_msgs/msg \
--verify-map
That wrapper will:
- prefer same-bag native
sensor_msgs/msg/NavSatFix/sensor_msgs/msg/Imutopics when they exist - otherwise generate a
NavSatFixsidecar bag fromGSOF49/50 - optionally generate an
Imusidecar bag fromGSOF49/50 - extract a local
TUMreference fromGSOF49withextract_applanix_gsof49_reference.py - build a minimal
velodyne_pointcloudoverlay on demand withbash scripts/prepare_velodyne_pointcloud_overlay.sh - convert
VelodyneScanpackets intosensor_msgs/msg/PointCloud2 - run
lidarslam.launch.py, call/map_save, and optionally verify the output
To benchmark the same driving_30_kmh bag as a four-way classic-path
comparison, use:
git clone --depth=1 https://github.com/autowarefoundation/applanix.git /tmp/applanix
bash scripts/run_open_data_classic_path_benchmark_suite.sh \
--applanix-msg-dir /tmp/applanix/applanix_msgs/msg \
--verify-map
That suite writes:
classic_path_report.mdclassic_path_report.jsonclassic_path_report.svg
To rerun the current MID360 place-recognition comparison entrypoint, use:
bash scripts/run_place_recognition_benchmark.sh
That wrapper reruns the distance-only baseline and a Scan Context candidate,
then emits:
place_recognition_report.mdplace_recognition_report.json
For packet IMU deskew, the important caveat is runtime sensitivity. On the real
Leo Drive all-sensors-bag1 and all-sensors-bag6 front-lidar cases, native
/sensing/imu/imu_data works when the packet benchmark runs at rate=1.0.
Current reference numbers are:
bag1_front,no_imu:APE RMSE 0.248 mbag1_front,imu:APE RMSE 0.251 mbag6_front,no_imu:APE RMSE 0.422 mbag6_front,imu:APE RMSE 0.365 m
The benchmark wrapper therefore auto-selects rate=1.0 when --use-imu=true
and --rate is omitted. To validate the same A/B automatically on the default
front-lidar cases, run:
git clone --depth=1 https://github.com/autowarefoundation/applanix.git /tmp/applanix
bash scripts/run_open_data_packet_imu_deskew_validation_matrix.sh \
--applanix-msg-dir /tmp/applanix/applanix_msgs/msg
That matrix runs both no_imu and imu at rate=1.0 for determinism and
writes per-case outputs plus:
packet_imu_deskew_validation.mdpacket_imu_deskew_validation.json
The default acceptance criteria are:
no_imupath coverage >=0.95imupath coverage >=0.95imu_rmse / no_imu_rmse <= 1.10imu_matched_poses / no_imu_matched_poses >= 0.80deskew and34.089 mwith--imu-rotation-use-orientation false. That is why the public packet path still keeps--use-imu falseby default.
If a bag carries NavSatFix messages whose header stamps do not track ROS time,
the backend now falls back to receive time when the skew exceeds
gnss_header_stamp_max_skew_sec (default 30 s). That makes all-sensors-bag6
attach GNSS edges again, but its native /gnss/fix still disagrees with the
GSOF49 reference enough that it is better suited to georeferenced smoke tests
than to clean GNSS cross-validation.
If you still want to test packet-based IMU deskew with a real static TF, use:
git clone --depth=1 https://github.com/autowarefoundation/applanix.git /tmp/applanix
bash scripts/run_open_data_applanix_velodyne_gnss_benchmark.sh \
--bag demo_data/autoware_leo_drive_isuzu/driving_30_kmh_2022_06_10-15_47_42_compressed \
--applanix-msg-dir /tmp/applanix/applanix_msgs/msg \
--use-imu true \
--tf-bag demo_data/autoware_leo_drive_isuzu/all-sensors-bag6_compressed \
--robot-frame-id base_link \
--imu-frame-id base_link \
--verify-map
That path uses:
convert_applanix_gsof_to_imu_bag.pyextract_static_transform_from_bag.pyPointCloud2.time-based deskew inscanmatcher--imu-rotation-use-orientation falsefor the gyro-only rotation variant
To turn the same real open-data path into a benchmark artifact with
traj_raw.tum, traj_corrected.tum, and metrics.json, use:
git clone --depth=1 https://github.com/autowarefoundation/applanix.git /tmp/applanix
bash scripts/run_open_data_applanix_velodyne_gnss_benchmark.sh \
--bag demo_data/autoware_leo_drive_isuzu/driving_30_kmh_2022_06_10-15_47_42_compressed \
--applanix-msg-dir /tmp/applanix/applanix_msgs/msg \
--verify-map
Run RKO-LIO + graph_based_slam
The main launch entrypoint is:
ros2 launch lidarslam rko_lio_slam.launch.py \
bag_path:=/path/to/rosbag2 \
lidar_topic:=/os_cloud_node/points \
imu_topic:=/os_cloud_node/imu
Useful parameter files:
- default graph backend:
graph_based_slam/param/graphbasedslam.yaml - default scanmatcher frontend:
lidarslam/param/lidarslam.yaml - NTU VIRAL RKO-LIO profile:
lidarslam/param/rko_lio_ntu_viral.yaml - MID360 tuned profile:
lidarslam/param/lidarslam_mid360_rko_graph.yaml
Save Maps
Save the current map at any time with:
ros2 service call /map_save std_srvs/srv/Empty
Typical outputs:
map.pcdpose_graph.g2opointcloud_map/pointcloud_map_metadata.yamlpointcloud_map/*.pcdmap_projector_info.yaml
Autoware Map Output Notes
graph_based_slam always writes map_projector_info.yaml.
- without GNSS:
projector_type: Local - with GNSS and a stable origin:
projector_type: LocalCartesianplusmap_origin
To stage an existing run into an Autoware map bundle:
bash scripts/prepare_autoware_map_from_graph_slam.sh \
output/bench_rko_lio_ntu_viral_loopgate_20260324 \
/tmp/autoware_maps/ntu_viral_loopgate
To open the staged map through Autoware's map loaders:
bash scripts/run_autoware_pointcloud_map_viewer_docker.sh \
/tmp/autoware_maps/ntu_viral_loopgate \
/tmp/autoware_core \
/tmp/autoware_map_runtime_ws
For the short supported path, use
bash scripts/run_autoware_quickstart.sh instead.
Loop Closure Notes
graph_based_slam supports two loop-candidate sources:
- distance-based revisit search
- built-in GPL-free Scan Context place recognition
The backend validates candidates geometrically before adding a loop edge and keeps only the best local edge inside the configured dedup window.
To regenerate the README loop-area zoom figure used for visual inspection of closing-segment duplication:
python3 scripts/generate_readme_loop_zoom_figure.py
Benchmark And Dataset Pointers
Recommended public benchmark:
bash scripts/download_ntu_viral_tnp01.sh
bash scripts/run_rko_lio_graph_benchmark.sh
Current MID360 cross-validation path:
bash scripts/run_rko_lio_mid360_crossval_benchmark.sh
The public benchmark and release-report flow is documented in benchmarking.md.