BotanicGarden

BotanicGarden (https://github.com/robot-pesg/BotanicGarden): a ground robot driven through a botanical garden, recorded as one ROS 1 “LIO” bag per sequence.

That bag records two lidars at once, so two pipelines are available:
  (default) the spinning Velodyne VLP-16 -> lidar_odometry_from_botanicgarden.yaml
  --livox   the solid-state Livox AVIA   -> lidar_odometry_from_botanicgarden_livox.yaml
The Livox is non-repetitive (a single scan does not cover the full FOV), so
its pipeline needs different tuning; see this repo's agents.md,
"Non-repetitive (solid-state) LiDARs".

base_link is the Xsens IMU (identity pose); the Velodyne sits at the T_xsens_vlp16 offset below. That is the launch file’s own convention, not the dataset’s top-level README, which names a different base_link.

Wheel odometry (/odom) lives in the SAME bag and is fused when MOLA_ODOMETRY_TOPIC is set. This dataset’s odometry frame is within ~1.5 deg of vehicle-aligned, so unlike CitrusFarm there is no silently-discarded rotation to worry about.

Running it

Online replay with the 3D GUI:

mola-lo-gui-botanicgarden /path/to/dataset_LIO.bag [--livox] [additional flags]

Offline batch run, writing a trajectory file:

mola-lo-cli-botanicgarden /path/to/dataset_LIO.bag [--livox] [additional flags]

What this profile sets, and why

These are defaults, not overrides: exporting any of these variables before running the wrapper takes precedence.

MOLA_DESKEW_METHOD

Default: MotionCompensationMethod::IMU

MOLA_IGNORE_NO_POINT_STAMPS

Default: false

MOLA_LOCALMAP_CLASS

Default: mola::IncrementalPointCloud

No decimation-tuning overrides here on purpose (2026-08-18, lio/03_accuracy_pipeline.md §0.4): the KITTI/Oxford voxel/stride/k_cov bundle regresses badly (+506% mean APE at full tuning, still +62-77% with just k_cov/stride), the coarse voxel alone is catastrophic (+445%), and MOLA_DESKEW_IGNORE_ACCELEROMETER=true – briefly shipped here on a “cuts the band” claim from a different dataset – swung sign between two same-day replicates (5/7 sequences better, then 1/7) once actually measured: this dataset’s run-to-run noise (-56% to +39% on an UNCHANGED config, replicate to replicate) swamps effects that size.

The incremental map class is different: mean APE -26% over 2 replicates x 7 sequences, and unlike the above it clears its own noise floor – 4 of 7 sequences have non-overlapping replicate ranges against the keyframe map (clear wins), 1 is a tie at the accuracy floor, 1 is ambiguous (kf’s own spread already reaches the incremental map’s range), and 1 (1008_03) is a clear, reproducible REGRESSION – the opposite sign from the one older cross-session data point for this exact sequence, which is exactly why this dataset needs replicates rather than single historical runs. Net positive, not a clean sweep. async_rebuild is forced off, not merely the pipeline default: with it on, k-d tree rebuilds run on a background thread nn_* queries never join, so the arm would measure the scheduler as well as the map class (matches the same caution in the corpus’s first ever local-map A/B). CAVEAT worth knowing before using this profile for anything beyond odometry benchmarking: mola::IncrementalPointCloud is documented as odometry-only, not loop-closure-safe (single global k-d tree, no per-keyframe re-mapping) – irrelevant to this LO accuracy comparison, but a real constraint if BotanicGarden is ever run through a loop-closure-capable pipeline.

MOLA_INCREMENTAL_MAP_ASYNC_REBUILD

Default: false

No decimation-tuning overrides here on purpose (2026-08-18, lio/03_accuracy_pipeline.md §0.4): the KITTI/Oxford voxel/stride/k_cov bundle regresses badly (+506% mean APE at full tuning, still +62-77% with just k_cov/stride), the coarse voxel alone is catastrophic (+445%), and MOLA_DESKEW_IGNORE_ACCELEROMETER=true – briefly shipped here on a “cuts the band” claim from a different dataset – swung sign between two same-day replicates (5/7 sequences better, then 1/7) once actually measured: this dataset’s run-to-run noise (-56% to +39% on an UNCHANGED config, replicate to replicate) swamps effects that size.

The incremental map class is different: mean APE -26% over 2 replicates x 7 sequences, and unlike the above it clears its own noise floor – 4 of 7 sequences have non-overlapping replicate ranges against the keyframe map (clear wins), 1 is a tie at the accuracy floor, 1 is ambiguous (kf’s own spread already reaches the incremental map’s range), and 1 (1008_03) is a clear, reproducible REGRESSION – the opposite sign from the one older cross-session data point for this exact sequence, which is exactly why this dataset needs replicates rather than single historical runs. Net positive, not a clean sweep. async_rebuild is forced off, not merely the pipeline default: with it on, k-d tree rebuilds run on a background thread nn_* queries never join, so the arm would measure the scheduler as well as the map class (matches the same caution in the corpus’s first ever local-map A/B). CAVEAT worth knowing before using this profile for anything beyond odometry benchmarking: mola::IncrementalPointCloud is documented as odometry-only, not loop-closure-safe (single global k-d tree, no per-keyframe re-mapping) – irrelevant to this LO accuracy comparison, but a real constraint if BotanicGarden is ever run through a loop-closure-capable pipeline.

MOLA_LIDAR_TOPIC

Default: /livox/lidar

MOLA_MIN_NEARBY_POSES_OCCUPIED

Default: 2

Non-repetitive scan pattern: accumulate 2+ scans per location before moving on, for denser local map / simplemap coverage:

MOLA_SIMPLEMAP_MIN_NEARBY_POSES

Default: 2

Non-repetitive scan pattern: accumulate 2+ scans per location before moving on, for denser local map / simplemap coverage:

MOLA_LIDAR_TOPIC

Default: /velodyne_points

MOLA_IMU_TOPIC

Default: /imu/data

LIDAR_POSE_X

Default: 0.0584868

base_link is the Xsens IMU, at identity; the Velodyne at T_xsens_vlp16. Only consumed by the offline path and the generic launch files: the two dataset-specific launch files above carry the same values themselves.

LIDAR_POSE_Y

Default: 0.0084042

base_link is the Xsens IMU, at identity; the Velodyne at T_xsens_vlp16. Only consumed by the offline path and the generic launch files: the two dataset-specific launch files above carry the same values themselves.

LIDAR_POSE_Z

Default: 0.1689155

base_link is the Xsens IMU, at identity; the Velodyne at T_xsens_vlp16. Only consumed by the offline path and the generic launch files: the two dataset-specific launch files above carry the same values themselves.

LIDAR_POSE_YAW

Default: -1.448155

base_link is the Xsens IMU, at identity; the Velodyne at T_xsens_vlp16. Only consumed by the offline path and the generic launch files: the two dataset-specific launch files above carry the same values themselves.

LIDAR_POSE_PITCH

Default: 0.097462

base_link is the Xsens IMU, at identity; the Velodyne at T_xsens_vlp16. Only consumed by the offline path and the generic launch files: the two dataset-specific launch files above carry the same values themselves.

LIDAR_POSE_ROLL

Default: 0.444791

base_link is the Xsens IMU, at identity; the Velodyne at T_xsens_vlp16. Only consumed by the offline path and the generic launch files: the two dataset-specific launch files above carry the same values themselves.

MOLA_USE_FIXED_LIDAR_POSE

Default: true

base_link is the Xsens IMU, at identity; the Velodyne at T_xsens_vlp16. Only consumed by the offline path and the generic launch files: the two dataset-specific launch files above carry the same values themselves.

IMU_POSE_X

Default: 0}" ; : "${IMU_POSE_Y:=0}" ; : "${IMU_POSE_Z:=0

base_link is the Xsens IMU, at identity; the Velodyne at T_xsens_vlp16. Only consumed by the offline path and the generic launch files: the two dataset-specific launch files above carry the same values themselves.

IMU_POSE_YAW

Default: 0}" ; : "${IMU_POSE_PITCH:=0}" ; : "${IMU_POSE_ROLL:=0

base_link is the Xsens IMU, at identity; the Velodyne at T_xsens_vlp16. Only consumed by the offline path and the generic launch files: the two dataset-specific launch files above carry the same values themselves.

MOLA_USE_FIXED_IMU_POSE

Default: true

base_link is the Xsens IMU, at identity; the Velodyne at T_xsens_vlp16. Only consumed by the offline path and the generic launch files: the two dataset-specific launch files above carry the same values themselves.

Under the hood

Online launch file

lidar_odometry_from_botanicgarden.yaml

Offline CLI input

--input-rosbag1 <MOLA_LO_BAGS_JOINED>

Profile source

scripts/lib/profiles/botanicgarden.sh