ROS 2 configurations
This page walks through every supported way of launching MOLA-LO / MOLA-SLAM
in a ROS 2 workflow. Each configuration shows a block diagram, the exact
command line, and the expected /tf tree.
Live parameter editor
The parameter editor lets you set your own robot frame, topic names, bag path, and namespace; the command blocks below will be rewritten automatically and persisted in your browser.
The five axes of configuration
A MOLA ROS 2 deployment is defined by five independent choices:
Axis |
Choices |
What it affects |
|---|---|---|
1. Input |
Offline |
Which module provides sensor data: Rosbag2Dataset vs BridgeROS2. |
2. Namespace |
Plain |
Whether the launch uses |
3. State estimator |
StateEstimationSimple (default) vs StateEstimationSmoother |
Which module publishes the final fused pose. |
4. REP-105 |
Publish |
Whether an external wheel / visual odometry source is expected. REP-105 is only compatible with the Simple estimator. |
5. Extra sensors |
IMU (for LIO / gravity), GNSS (logging, live geo-ref, relocalization), attitude-only IMU (map geo-referencing), multiple odometry sources (Smoother only) |
Topics subscribed, factors added, output TFs. |
Hint
The most common pitfall is REP-105 + Smoother: the smoother
publishes map → base_link directly and cannot split the transform.
Choosing a configuration
Each box names a section below.
Parameter editor
Loading editor… if this message stays, JavaScript is disabled and the commands below show default placeholder values.
Each knob below has two spellings: a ros2 launch argument for the live
node, and an environment variable read by both offline tools
(mola-lo-gui-rosbag2 and mola-lo-cli-rosbag2).
Concept |
Default |
Live: |
Offline: environment variable |
|---|---|---|---|
Robot base frame |
|
|
|
Map frame |
|
|
(no |
Odom frame (REP-105 only) |
|
|
(no |
LiDAR topic |
|
|
|
IMU topic |
|
|
|
GNSS topic |
|
|
|
Attitude-only IMU (AHRS), see Recording a map for later geo-referencing (GNSS + attitude IMU) |
(off) |
|
|
External odom ( |
(off) |
|
(not available) |
External odom (topic) |
(off) |
|
|
Ignore sensor pose from |
|
|
|
|
|
|
|
Save a |
(off) |
|
CLI: |
LiDAR/IMU subscription QoS |
|
|
(not applicable) |
Note
Subscription QoS for sensor topics defaults to REP-2003 (best_effort,
volatile) with a queue depth of 50. For high-rate sensors consumed
together with a heavy SLAM pipeline (e.g. a 640 Hz IMU), set
imu_qos_reliability:=reliable and increase imu_qos_depth (e.g.
1000) so the bridge subscription matches the publisher and can
absorb executor stalls. See the launch arguments documented in
the ROS 2 node reference and the per-topic qos: block accepted by
BridgeROS2’s subscribe list.
Live ROS 2 node
All sections in this group use
ros2 launch mola_lidar_odometry ros2-lidar-odometry.launch.py. See
all launch arguments for the full list.
Simple estimator + REP-105 (external odometry on /tf)
Use this when the robot already publishes a high-frequency
odom → base_link transform from wheel encoders (or any other odometry
source). MOLA-LO only corrects long-term drift by publishing map → odom.
# LiDAR only odometry (LO): No IMU
ros2 launch mola_lidar_odometry ros2-lidar-odometry.launch.py \
lidar_topic_name:=__LIDAR_TOPIC__ \
mola_tf_base_link:=__BASE_LINK__ \
forward_ros_tf_odom_to_mola:=True \
publish_localization_following_rep105:=True
Resulting /tf tree:
map --→ odom --→ base_link --→ (sensor frames)
^ ^
| └── wheel odometry (external)
└── MOLA-LO (corrects drift)
Variant: LIO (LiDAR + IMU), REP-105
Adds IMU-based scan deskew. Gravity alignment is on by default.
# LiDAR inertial odometry (LIO): with IMU
ros2 launch mola_lidar_odometry ros2-lidar-odometry.launch.py \
lidar_topic_name:=__LIDAR_TOPIC__ \
imu_topic_name:=__IMU_TOPIC__ \
mola_tf_base_link:=__BASE_LINK__ \
use_imu_for_lio:=True \
forward_ros_tf_odom_to_mola:=True \
publish_localization_following_rep105:=True
Simple estimator, no external odometry
When no external odometry exists (e.g. handheld mapping, drone), MOLA-LO
publishes map → base_link directly.
# LiDAR only odometry (LO): No IMU
ros2 launch mola_lidar_odometry ros2-lidar-odometry.launch.py \
lidar_topic_name:=__LIDAR_TOPIC__ \
mola_tf_base_link:=__BASE_LINK__ \
publish_localization_following_rep105:=False
Resulting /tf tree:
map --→ base_link --→ (sensor frames)
Variant: LIO (LiDAR + IMU), no REP-105
Adds IMU-based scan deskew. Gravity alignment is on by default.
# LiDAR inertial odometry (LIO): with IMU
ros2 launch mola_lidar_odometry ros2-lidar-odometry.launch.py \
lidar_topic_name:=__LIDAR_TOPIC__ \
imu_topic_name:=__IMU_TOPIC__ \
mola_tf_base_link:=__BASE_LINK__ \
use_imu_for_lio:=True \
publish_localization_following_rep105:=False
Variant: no sensor /tf available
If the driver does not publish base_link → lidar on /tf,
either configure it via a robot_state_publisher or force an
identity sensor pose:
ros2 launch mola_lidar_odometry ros2-lidar-odometry.launch.py \
lidar_topic_name:=__LIDAR_TOPIC__ \
mola_tf_base_link:=__BASE_LINK__ \
ignore_lidar_pose_from_tf:=True \
publish_localization_following_rep105:=False
Namespaced robot (/robot1/tf, /robot1/scan, …)
When your robot publishes everything under a ROS 2 namespace, the launch
file pushes the whole MOLA system into that namespace and remaps /tf /
/tf_static so tf2 inside the namespace is transparent to MOLA.
ros2 launch mola_lidar_odometry ros2-lidar-odometry.launch.py \
lidar_topic_name:=__LIDAR_TOPIC__ \
mola_tf_base_link:=__BASE_LINK__ \
use_namespace:=True \
namespace:=__NS__
Note
The lidar_topic_name should be given relative (no leading /).
It gets prefixed by the namespace automatically. The launch file remaps
/tf ↔ tf internally so tf2 subscribes to
/__NS__/tf without any extra configuration. If only the sensor
topics are namespaced but /tf is global, add
use_namespaced_tf:=False.
Smoother + external odometry fusion (wheels, VIO)
Adds an external odometry source to the smoother. The smoother estimates
a distinct T_map_to_odom per source and fuses their deltas with LO
(and IMU, if enabled).
Important
With the Smoother, external odometry must arrive as a
nav_msgs/Odometry topic (odom_topic_name:=). The Smoother
publishes map → base_link itself, so an external odom → base_link
on /tf would give base_link two parents; the launch file rejects
forward_ros_tf_odom_to_mola:=True together with
use_state_estimator:=True.
The upstream driver publishes a nav_msgs/Odometry message on a topic.
BridgeROS2 subscribes directly, converts each message to a 3D
CObservationRobotPose (6×6 covariance) and forwards it under
odom_sensor_label. This preserves Z / pitch / roll and allows fusing
multiple labeled sources.
ros2 launch mola_lidar_odometry ros2-lidar-odometry.launch.py \
lidar_topic_name:=__LIDAR_TOPIC__ \
imu_topic_name:=__IMU_TOPIC__ \
mola_tf_base_link:=__BASE_LINK__ \
use_state_estimator:=True \
use_imu_for_lio:=True \
odom_topic_name:=__ODOM_TOPIC__ \
odom_sensor_label:=odom_wheels
For multiple external sources (e.g. wheels + VIO), add extra
subscribe entries to lidar_odometry_ros2.yaml with distinct
output_sensor_label values.
Via |
Via |
|
|---|---|---|
Launch arg |
|
|
Upstream must publish |
|
|
Observation type fed to MOLA |
|
|
Sensor label |
Hardcoded |
|
Multiple sources |
No (single TF chain) |
Yes (multiple subscribe entries / labels) |
Rate |
BridgeROS2 |
Publisher rate |
Compatible with |
Simple estimator + REP-105 only (Simple estimator + REP-105 (external odometry on /tf)). Rejected with the Smoother. |
Simple estimator (no REP-105) and Smoother (recommended). |
See the “Fusing two Odometry sources” tutorial for a full worked example with fake publishers.
Smoother + live geo-referencing (GNSS)
The smoother estimates the enu → map transform online from incoming
GNSS fixes. It publishes enu and utm TFs once convergence is
reached.
ros2 launch mola_lidar_odometry ros2-lidar-odometry.launch.py \
lidar_topic_name:=__LIDAR_TOPIC__ \
imu_topic_name:=__IMU_TOPIC__ \
gnss_topic_name:=__GNSS_TOPIC__ \
mola_tf_base_link:=__BASE_LINK__ \
use_state_estimator:=True \
use_imu_for_lio:=True \
gnss_mode:=live_georef
Smoother + relocalization in a geo-referenced map
Load a previously built geo-referenced .mm map. MOLA-LO stays idle
until the smoother has converged on an initial pose from GNSS, then
switches to localization-only mapping.
ros2 launch mola_lidar_odometry ros2-lidar-odometry.launch.py \
lidar_topic_name:=__LIDAR_TOPIC__ \
imu_topic_name:=__IMU_TOPIC__ \
gnss_topic_name:=__GNSS_TOPIC__ \
mola_tf_base_link:=__BASE_LINK__ \
mola_initial_map_mm_file:=__MM_PATH__ \
use_state_estimator:=True \
use_imu_for_lio:=True \
start_mapping_enabled:=False \
gnss_mode:=relocalize
Offline rosbag2
Odometry and mapping from a bag (GUI or batch CLI)
Two wrappers replay a bag, configured through the same environment variables (see the offline column of the parameter table):
mola-lo-gui-rosbag2: replay with the MolaViz GUI, to watch and control the run.mola-lo-cli-rosbag2: no GUI, as fast as the CPU allows. Best for long bags, batch runs and reproducible benchmarks. Extra arguments after the bag path go tomola-lidar-odometry-cli(run it with--helpfor all output options).
MOLA_LIDAR_TOPIC=__LIDAR_TOPIC__ \
MOLA_TF_BASE_LINK=__BASE_LINK__ \
mola-lo-gui-rosbag2 __BAG_PATH__
MOLA_DESKEW_METHOD=MotionCompensationMethod::IMU \
MOLA_LO_INITIAL_LOCALIZATION_METHOD=InitLocalization::PitchAndRollFromIMU \
MOLA_LIDAR_TOPIC=__LIDAR_TOPIC__ \
MOLA_IMU_TOPIC=__IMU_TOPIC__ \
MOLA_TF_BASE_LINK=__BASE_LINK__ \
mola-lo-gui-rosbag2 __BAG_PATH__
MOLA_DESKEW_METHOD=MotionCompensationMethod::IMU \
MOLA_LIDAR_TOPIC=__LIDAR_TOPIC__ \
MOLA_IMU_TOPIC=__IMU_TOPIC__ \
MOLA_TF_BASE_LINK=__BASE_LINK__ \
MOLA_STATE_ESTIMATOR=mola::state_estimation_smoother::StateEstimationSmoother \
MOLA_STATE_ESTIMATOR_YAML="$(ros2 pkg prefix mola_state_estimation_smoother)/share/mola_state_estimation_smoother/params/state-estimation-smoother.yaml" \
mola-lo-gui-rosbag2 __BAG_PATH__
MOLA_LIDAR_TOPIC=__LIDAR_TOPIC__ \
MOLA_TF_BASE_LINK=__BASE_LINK__ \
mola-lo-cli-rosbag2 __BAG_PATH__ \
--output-tum-path trajectory.tum \
--output-simplemap map.simplemap
MOLA_DESKEW_METHOD=MotionCompensationMethod::IMU \
MOLA_LO_INITIAL_LOCALIZATION_METHOD=InitLocalization::PitchAndRollFromIMU \
MOLA_LIDAR_TOPIC=__LIDAR_TOPIC__ \
MOLA_IMU_TOPIC=__IMU_TOPIC__ \
MOLA_TF_BASE_LINK=__BASE_LINK__ \
mola-lo-cli-rosbag2 __BAG_PATH__ \
--output-tum-path trajectory.tum \
--output-simplemap map.simplemap
MOLA_DESKEW_METHOD=MotionCompensationMethod::IMU \
MOLA_LIDAR_TOPIC=__LIDAR_TOPIC__ \
MOLA_IMU_TOPIC=__IMU_TOPIC__ \
MOLA_TF_BASE_LINK=__BASE_LINK__ \
MOLA_STATE_ESTIMATOR=mola::state_estimation_smoother::StateEstimationSmoother \
MOLA_STATE_ESTIMATOR_YAML="$(ros2 pkg prefix mola_state_estimation_smoother)/share/mola_state_estimation_smoother/params/state-estimation-smoother.yaml" \
mola-lo-cli-rosbag2 __BAG_PATH__ \
--output-tum-path trajectory.tum \
--output-simplemap map.simplemap
Variant: bag recorded with a ROS 2 namespace
Rosbag2Dataset looks up topics by their literal name (there is no tf2
remap as in the live node), so both the
sensor topics and the /tf topics must carry the namespace. Enter the
sensor topics with their prefix in the form above (e.g.
/__NS__/ouster/points) and add the two /tf variables to any
command above:
MOLA_TF_TOPIC=/__NS__/tf \
MOLA_TF_STATIC_TOPIC=/__NS__/tf_static \
MOLA_LIDAR_TOPIC=__LIDAR_TOPIC__ \
MOLA_TF_BASE_LINK=__BASE_LINK__ \
mola-lo-gui-rosbag2 __BAG_PATH__
MOLA_TF_BASE_LINK is a frame id inside the TF messages, so it is
not namespaced. Requires mola_input_rosbag2 >= 1.12.
Variant: calling mola-lidar-odometry-cli directly
mola-lo-cli-rosbag2 is a thin wrapper that fills in the pipeline and
state estimator files. The equivalent raw call, with every input given
as a flag, is:
mola-lidar-odometry-cli \
-c $(ros2 pkg prefix mola_lidar_odometry)/share/mola_lidar_odometry/pipelines/lidar3d-default.yaml \
--state-estimator-param-file $(ros2 pkg prefix mola_lidar_odometry)/share/mola_lidar_odometry/state-estimator-params/state-estimation-simple.yaml \
--input-rosbag2 __BAG_PATH__ \
--lidar-sensor-label __LIDAR_TOPIC__ \
--base-link-frame-id __BASE_LINK__ \
--output-tum-path trajectory.tum
Recording a map for later geo-referencing (GNSS + attitude IMU)
To geo-reference a map afterwards with
mola-sm-georeferencing, each .simplemap keyframe must carry the
nearest GNSS fix and IMU reading. The IMU reading gives the azimuth
observation, so it must hold an absolute orientation: the IMU built
into most LiDARs is fine for de-skew but has none.
If the robot has a separate AHRS (or a dual-antenna GNSS publishing a
sensor_msgs/Imu attitude), name it as the attitude IMU topic. Its
readings are then the ones stored into keyframes, while the main IMU keeps
doing de-skew and gravity alignment. Without it, keyframes store readings
from the main IMU, which is the right choice when that IMU already reports
an absolute orientation.
MOLA_DESKEW_METHOD=MotionCompensationMethod::IMU \
MOLA_LIDAR_TOPIC=__LIDAR_TOPIC__ \
MOLA_IMU_TOPIC=__IMU_TOPIC__ \
MOLA_GNSS_TOPIC=__GNSS_TOPIC__ \
MOLA_ATTITUDE_IMU_TOPIC=__ATTITUDE_IMU_TOPIC__ \
MOLA_TF_BASE_LINK=__BASE_LINK__ \
MOLA_GENERATE_SIMPLEMAP=true \
MOLA_SIMPLEMAP_OUTPUT=map.simplemap \
mola-lo-gui-rosbag2 __BAG_PATH__
MOLA_DESKEW_METHOD=MotionCompensationMethod::IMU \
MOLA_LIDAR_TOPIC=__LIDAR_TOPIC__ \
MOLA_IMU_TOPIC=__IMU_TOPIC__ \
MOLA_GNSS_TOPIC=__GNSS_TOPIC__ \
MOLA_ATTITUDE_IMU_TOPIC=__ATTITUDE_IMU_TOPIC__ \
MOLA_TF_BASE_LINK=__BASE_LINK__ \
mola-lo-cli-rosbag2 __BAG_PATH__ \
--output-simplemap map.simplemap
MOLA_SIMPLEMAP_OUTPUT=map.simplemap \
ros2 launch mola_lidar_odometry ros2-lidar-odometry.launch.py \
lidar_topic_name:=__LIDAR_TOPIC__ \
imu_topic_name:=__IMU_TOPIC__ \
gnss_topic_name:=__GNSS_TOPIC__ \
attitude_imu_topic_name:=__ATTITUDE_IMU_TOPIC__ \
mola_tf_base_link:=__BASE_LINK__ \
use_imu_for_lio:=True \
generate_simplemap:=True
The map file is written when the node shuts down.
Note
The attitude IMU also reaches the state estimator. With the Smoother,
exclude its label imu_attitude via do_process_imu_labels_re
unless fusing its absolute attitude is intended.
Troubleshooting
Moved to its own page: Troubleshooting. It covers the frame and topic problems that used to be listed here, plus the failure modes that are not specific to ROS 2.