Oxford Spires

Oxford Spires Dataset (https://dynamic.robots.ox.ac.uk/datasets/oxford-spires): a hand-held/backpack VILENS payload walked around Oxford colleges.

A sequence is a directory containing raw/ros2bag/, under which the recording may be split across several ros2bag part directories. All parts must be replayed, or the run silently covers only a fraction of the ground truth. Part directories end in “_<n>”; they are ordered by that number, not lexicographically, so a sequence with ten or more parts still replays in order.

There is no /tf or /tf_static in these bags, so fixed sensor poses are mandatory. lidar_odometry_from_oxford_spires.yaml carries the same values as its defaults.

Those poses are in the dataset’s BASE frame, NOT the raw LiDAR frame, and the difference is a 180 deg yaw. Getting that wrong – placing the LiDAR at identity, as if base and lidar were the same frame – leaves every reported pose a body yaw away from the dataset’s own ground truth. APE hides it (a body yaw moves position only by the 0.124 m lever arm, and evo’s –align absorbs the rest); it negates the horizontal part of every RELATIVE motion, so vertical-drift and tilt metrics blow up. Symptom if it ever comes back: relative vertical drift in the tens-to-hundreds of mm/m on a run whose APE looks perfectly good.

T_base_lidar is sensor.yaml’s own value. T_base_imu is the LiDAR-frame IMU pose composed through it (T_base_lidar . T_lidar_imu), which reproduces sensor.yaml’s independently recorded T_base_imu to ~1 mm and 0.6 deg, so the two calibrations agree.

Number of OXFORD_SPIRES_BAG_N slots the launch YAML defines:

Running it

Online replay with the 3D GUI:

mola-lo-gui-oxford-spires /path/to/data/sequences/<sequence-name>/ [--headless] [additional flags]

Offline batch run, writing a trajectory file:

mola-lo-cli-oxford-spires /path/to/data/sequences/<sequence-name>/ [--headless] [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_LIDAR_TOPIC

Default: /hesai/pandar

MOLA_IMU_TOPIC

Default: /alphasense_driver_ros/imu

MOLA_CAMERA_TOPIC

Default: /alphasense_driver_ros/cam0/debayered/image/compressed

LIDAR_POSE_X

Default: 0}" ; : "${LIDAR_POSE_Y:=0}" ; : "${LIDAR_POSE_Z:=0.124

LIDAR_POSE_YAW

Default: 180}" ; : "${LIDAR_POSE_PITCH:=0}" ; : "${LIDAR_POSE_ROLL:=0

MOLA_USE_FIXED_LIDAR_POSE

Default: true

IMU_POSE_X

Default: -0.018771

IMU_POSE_Y

Default: 0.008218

IMU_POSE_Z

Default: 0.053526

IMU_POSE_YAW

Default: 89.3737

IMU_POSE_PITCH

Default: -0.1665

IMU_POSE_ROLL

Default: -0.1287

MOLA_USE_FIXED_IMU_POSE

Default: true

MOLA_LO_INITIAL_LOCALIZATION_METHOD

Default: InitLocalization::PitchAndRollFromIMU

Handheld/backpack platform: lean on the IMU for the initial pitch/roll estimate and for deskewing, same as the ConSLAM wrapper. The Alphasense IMU does not publish an onboard-fused orientation (its Imu messages carry an all-zero quaternion), so the pitch/roll initializer automatically falls back to leveling from raw accelerometer readings.

MOLA_DESKEW_METHOD

Default: MotionCompensationMethod::IMU

Handheld/backpack platform: lean on the IMU for the initial pitch/roll estimate and for deskewing, same as the ConSLAM wrapper. The Alphasense IMU does not publish an onboard-fused orientation (its Imu messages carry an all-zero quaternion), so the pitch/roll initializer automatically falls back to leveling from raw accelerometer readings.

MOLA_DESKEW_IGNORE_ACCELEROMETER

Default: false

Keep the accelerometer in the deskew. This used to be suppressed, on evidence that gyro-only deskew cut path-length error and improved APE. Re-measured on all 13 sequences against today’s pipeline, both halves of that are gone: est/gt is identical to three decimals either way, and suppressing the accelerometer now COSTS about 11% of APE (median 0.886x in its favour, better on 9 of 13). The defect it compensated for has since been fixed elsewhere, so the override no longer earns its keep. Left explicit rather than deleted so the measurement is not lost.

MOLA_CLOUD_DECIMATION_VOXEL_SIZE_MAP

Default: 0.45

GICP-pipeline decimation tuning – per-dataset, not a shipped pipeline default. See the KITTI profile for the shared rationale/evidence; this dataset independently confirmed the same configuration (56% cut in pooled vertical drift at a third of the cost, 12-13/13 sequences).

MOLA_CLOUD_DECIMATION_VOXEL_SIZE_ICP

Default: 0.45

GICP-pipeline decimation tuning – per-dataset, not a shipped pipeline default. See the KITTI profile for the shared rationale/evidence; this dataset independently confirmed the same configuration (56% cut in pooled vertical drift at a third of the cost, 12-13/13 sequences).

MOLA_VOXEL_STRIDE_MAP

Default: 1

GICP-pipeline decimation tuning – per-dataset, not a shipped pipeline default. See the KITTI profile for the shared rationale/evidence; this dataset independently confirmed the same configuration (56% cut in pooled vertical drift at a third of the cost, 12-13/13 sequences).

MOLA_VOXEL_STRIDE_ICP

Default: 2

GICP-pipeline decimation tuning – per-dataset, not a shipped pipeline default. See the KITTI profile for the shared rationale/evidence; this dataset independently confirmed the same configuration (56% cut in pooled vertical drift at a third of the cost, 12-13/13 sequences).

MOLA_LOCALMAP_K_CORRESPONDENCES_FOR_COV

Default: 10

GICP-pipeline decimation tuning – per-dataset, not a shipped pipeline default. See the KITTI profile for the shared rationale/evidence; this dataset independently confirmed the same configuration (56% cut in pooled vertical drift at a third of the cost, 12-13/13 sequences).

MOLA_SMOOTHER_SIGMA_REL_POSE_LIN

Default: 0.1

Smoother-only relative-pose prior floor (no-op for `simple`), same values as KITTI. Measured over all 13 sequences it costs nothing here (0.96x vs the unfloored smoother, 10/13 improving) and beats `simple` overall (1.06x vs 1.11x unfloored): well-conditioned geometry doesn’t need the unfloored prior’s ICP-pinning the way degenerate scenes do, so raising the floor is close to free. See the KITTI profile for the fuller case.

MOLA_SMOOTHER_SIGMA_REL_POSE_ANG

Default: 0.02

Smoother-only relative-pose prior floor (no-op for `simple`), same values as KITTI. Measured over all 13 sequences it costs nothing here (0.96x vs the unfloored smoother, 10/13 improving) and beats `simple` overall (1.06x vs 1.11x unfloored): well-conditioned geometry doesn’t need the unfloored prior’s ICP-pinning the way degenerate scenes do, so raising the floor is close to free. See the KITTI profile for the fuller case.

Under the hood

Online launch file

lidar_odometry_from_oxford_spires.yaml

Offline CLI input

--input-rosbag2 "${parts[*]}"

Profile source

scripts/lib/profiles/oxford-spires.sh