|
rtabmap_odom package from rtabmap_ros reportabmap_conversions rtabmap_costmap_plugins rtabmap_demos rtabmap_examples rtabmap_launch rtabmap_msgs rtabmap_odom rtabmap_python rtabmap_ros rtabmap_rviz_plugins rtabmap_slam rtabmap_sync rtabmap_util rtabmap_viz |
ROS Distro
|
Package Summary
| Version | 0.23.13 |
| License | BSD |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/introlab/rtabmap_ros.git |
| VCS Type | git |
| VCS Version | humble-devel |
| Last Updated | 2026-10-01 |
| Dev Status | MAINTAINED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- Mathieu Labbe
Authors
- Mathieu Labbe
rtabmap_odom
Odometry for RTAB-Map: where the robot is relative to a local fixed frame, estimated from its own sensors — a pose that moves continuously and never jumps, but drifts over time.
SLAM needs a pose for every measurement it maps. These nodes produce one by registering each new frame against the last — visually from an RGB-D or stereo camera, or geometrically from a lidar — and integrating the result into a nav_msgs/msg/Odometry and a TF.
Contents
Nodes
| Node | Description |
|---|---|
| rgbd_odometry | Visual odometry from a color image and a depth image registered to it. |
| stereo_odometry | Visual odometry from a stereo pair. |
| icp_odometry | Geometric odometry from a 2D or 3D lidar. |
Every node is a composable node as well as a standalone executable.
Choosing a sensor modality for the environment
Which to use is a question about the environment, not about which sensor is better. A camera tracks visual texture; a lidar tracks geometry. Each fails where its own cue is missing, and the two failures do not overlap much.
| Environment | Use | Why |
|---|---|---|
| Visually textured and well lit — offices, cluttered rooms, daylight outdoors | Camera | Plenty of features to match, and appearance gives loop closure for free. |
| Textureless but geometrically rich — bare corridors with doorways and furniture, warehouse aisles | Lidar | Blank walls give a camera nothing; the shape of the space still constrains ICP. |
| Dark, or lighting that changes abruptly | Lidar | A camera is simply blind. Lidar does not care. |
| Geometrically plain but visually rich — a large open hall with a patterned floor, textured flat walls | Camera | Degenerate geometry defeats ICP here, while the texture is exactly what a camera needs. |
| Both plain and textureless — an empty warehouse, a long featureless tunnel | Wheel odometry, with either as a corrector | Neither cue is present. This is the case where wheel odometry carries the pose. |
| Repetitive and self-similar — tiled floors, rows of racking, a long colonnade | Wheel odometry as the guess, with either on top | Both cues are present but ambiguous: a camera matches the wrong copy of a feature, a lidar the wrong bay of shelving. See Repetitive patterns. |
| Outdoors at range | Stereo camera or 3D lidar | RGB-D depth stops working outdoors; both of these keep going. |
Do not underestimate wheel odometry. On a wheeled robot it is locally excellent and only drifts over distance — the opposite failure from both of the above, which are locally noisy but not systematically biased. It is also the only one of the three that keeps working when the environment offers no cue at all — and, because it is indifferent to what the scene looks like, the only one that is not fooled when the scene repeats itself.
Fuse the wheels with an IMU before feeding them in. robot_localization is the standard way: its EKF combines wheel odometry with IMU orientation and angular rates into one filtered odom topic, which is a markedly better guess than the wheels alone. FusionCore is another EKF that does the same job. The IMU fixes exactly what encoders are worst at — yaw through a turn, and wheel slip, which encoders report as motion that never happened. Where the camera or lidar fails outright, that filtered estimate is what carries the robot through, and a pipeline built this way degrades instead of breaking.
Which is why the robust arrangement is rarely one of them alone: feed wheel odometry in as guess_frame_id and the registration starts near the answer every frame. That covers the camera’s fast-motion and blank-wall failures and the lidar’s degenerate-corridor failure, while the camera or lidar in turn corrects the wheels’ drift. See Feeding in an external guess.
For 2D indoor odometry a lidar usually costs less computation. Registering a few hundred scan points is far less CPU than detecting, describing and matching visual features on every frame, and it needs no GPU — which is what decides whether odometry keeps up on the small onboard computers these robots carry.
With both a camera and a lidar, the usual arrangement is icp_odometry for the pose and the camera for appearance — see Combining a camera and a lidar.
Library
The package installs a C++ library, documented in the C++ API reference generated from the headers.
OdometryROS is the base class all three nodes derive from, and it is where most of this package’s behaviour actually lives. It owns the RTAB-Map Odometry object, the pose integration, the TF broadcast, the IMU intake, the services and the diagnostics. Each node subclasses it to do one thing: turn its own topics into a rtabmap::SensorData and hand it over. That is why the three nodes share nearly all of their parameters and publish exactly the same topics.
It also runs the registration on its own thread. A frame arriving while the previous one is still being processed does not block the subscription callback; see Update rates and dropped frames.
Conventions
These apply to all three nodes.
Frames and TF
| Parameter | Type | Default | Description |
|---|---|---|---|
frame_id |
string |
"base_link" |
The robot frame being tracked. The pose published is this frame’s, not the sensor’s — the sensor-to-robot transform is read from TF. |
odom_frame_id |
string |
"odom" |
The fixed frame the pose is expressed in. |
publish_tf |
bool |
true |
Broadcast the pose on TF. Turn this off if something else already publishes that transform, or the two fight and TF alternates between them. What exactly is broadcast depends on guess_frame_id: without it, odom_frame_id → frame_id; with it, a correction odom_frame_id → guess_frame_id (why). |
wait_for_transform |
double |
0.1 |
Seconds to wait for a needed transform before giving up on the frame. |
initial_pose |
string |
"" |
Starting pose, "x y z roll pitch yaw". Also settable at runtime through reset_odom_to_pose. |
ground_truth_frame_id |
string |
"" |
When set, the pose is taken from this TF instead of being computed — for replaying a dataset with a known trajectory. |
ground_truth_base_frame_id |
string |
value of frame_id
|
The robot frame within the ground truth TF tree. |
guess_frame_id |
string |
"" |
A frame carrying another odometry source, used as the initial guess for each registration. Documented with its companions under Feeding in an external guess – the highest-value parameter here for a wheeled robot. |
The sensor must be connected to frame_id in TF before the first frame arrives, or that frame is dropped with a warning. A static publisher is the usual answer.
RTAB-Map’s own parameters
Everything in RTAB-Map’s odometry parameter set is exposed as a ROS parameter under its RTAB-Map name, so tuning is done directly:
ros2 run rtabmap_odom rgbd_odometry --ros-args \
-p "Odom/Strategy:='1'" \
-p "Vis/MinInliers:='15'" \
-p "Odom/ResetCountdown:='1'"
Note the quoting. Every RTAB-Map parameter is declared as a string, whatever it looks like, because that is how RTAB-Map’s own parameter map stores them. Writing -p Odom/Strategy:=1 makes ROS infer an integer, and the node throws on startup rather than starting with the wrong value:
``` parameter ‘Odom/Strategy’ has invalid type: Wrong parameter type,
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged rtabmap_odom at Robotics Stack Exchange
|
rtabmap_odom package from rtabmap_ros reportabmap_conversions rtabmap_costmap_plugins rtabmap_demos rtabmap_examples rtabmap_launch rtabmap_msgs rtabmap_odom rtabmap_python rtabmap_ros rtabmap_rviz_plugins rtabmap_slam rtabmap_sync rtabmap_util rtabmap_viz |
ROS Distro
|
Package Summary
| Version | 0.23.13 |
| License | BSD |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/introlab/rtabmap_ros.git |
| VCS Type | git |
| VCS Version | jazzy-devel |
| Last Updated | 2026-10-01 |
| Dev Status | MAINTAINED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- Mathieu Labbe
Authors
- Mathieu Labbe
rtabmap_odom
Odometry for RTAB-Map: where the robot is relative to a local fixed frame, estimated from its own sensors — a pose that moves continuously and never jumps, but drifts over time.
SLAM needs a pose for every measurement it maps. These nodes produce one by registering each new frame against the last — visually from an RGB-D or stereo camera, or geometrically from a lidar — and integrating the result into a nav_msgs/msg/Odometry and a TF.
Contents
Nodes
| Node | Description |
|---|---|
| rgbd_odometry | Visual odometry from a color image and a depth image registered to it. |
| stereo_odometry | Visual odometry from a stereo pair. |
| icp_odometry | Geometric odometry from a 2D or 3D lidar. |
Every node is a composable node as well as a standalone executable.
Choosing a sensor modality for the environment
Which to use is a question about the environment, not about which sensor is better. A camera tracks visual texture; a lidar tracks geometry. Each fails where its own cue is missing, and the two failures do not overlap much.
| Environment | Use | Why |
|---|---|---|
| Visually textured and well lit — offices, cluttered rooms, daylight outdoors | Camera | Plenty of features to match, and appearance gives loop closure for free. |
| Textureless but geometrically rich — bare corridors with doorways and furniture, warehouse aisles | Lidar | Blank walls give a camera nothing; the shape of the space still constrains ICP. |
| Dark, or lighting that changes abruptly | Lidar | A camera is simply blind. Lidar does not care. |
| Geometrically plain but visually rich — a large open hall with a patterned floor, textured flat walls | Camera | Degenerate geometry defeats ICP here, while the texture is exactly what a camera needs. |
| Both plain and textureless — an empty warehouse, a long featureless tunnel | Wheel odometry, with either as a corrector | Neither cue is present. This is the case where wheel odometry carries the pose. |
| Repetitive and self-similar — tiled floors, rows of racking, a long colonnade | Wheel odometry as the guess, with either on top | Both cues are present but ambiguous: a camera matches the wrong copy of a feature, a lidar the wrong bay of shelving. See Repetitive patterns. |
| Outdoors at range | Stereo camera or 3D lidar | RGB-D depth stops working outdoors; both of these keep going. |
Do not underestimate wheel odometry. On a wheeled robot it is locally excellent and only drifts over distance — the opposite failure from both of the above, which are locally noisy but not systematically biased. It is also the only one of the three that keeps working when the environment offers no cue at all — and, because it is indifferent to what the scene looks like, the only one that is not fooled when the scene repeats itself.
Fuse the wheels with an IMU before feeding them in. robot_localization is the standard way: its EKF combines wheel odometry with IMU orientation and angular rates into one filtered odom topic, which is a markedly better guess than the wheels alone. FusionCore is another EKF that does the same job. The IMU fixes exactly what encoders are worst at — yaw through a turn, and wheel slip, which encoders report as motion that never happened. Where the camera or lidar fails outright, that filtered estimate is what carries the robot through, and a pipeline built this way degrades instead of breaking.
Which is why the robust arrangement is rarely one of them alone: feed wheel odometry in as guess_frame_id and the registration starts near the answer every frame. That covers the camera’s fast-motion and blank-wall failures and the lidar’s degenerate-corridor failure, while the camera or lidar in turn corrects the wheels’ drift. See Feeding in an external guess.
For 2D indoor odometry a lidar usually costs less computation. Registering a few hundred scan points is far less CPU than detecting, describing and matching visual features on every frame, and it needs no GPU — which is what decides whether odometry keeps up on the small onboard computers these robots carry.
With both a camera and a lidar, the usual arrangement is icp_odometry for the pose and the camera for appearance — see Combining a camera and a lidar.
Library
The package installs a C++ library, documented in the C++ API reference generated from the headers.
OdometryROS is the base class all three nodes derive from, and it is where most of this package’s behaviour actually lives. It owns the RTAB-Map Odometry object, the pose integration, the TF broadcast, the IMU intake, the services and the diagnostics. Each node subclasses it to do one thing: turn its own topics into a rtabmap::SensorData and hand it over. That is why the three nodes share nearly all of their parameters and publish exactly the same topics.
It also runs the registration on its own thread. A frame arriving while the previous one is still being processed does not block the subscription callback; see Update rates and dropped frames.
Conventions
These apply to all three nodes.
Frames and TF
| Parameter | Type | Default | Description |
|---|---|---|---|
frame_id |
string |
"base_link" |
The robot frame being tracked. The pose published is this frame’s, not the sensor’s — the sensor-to-robot transform is read from TF. |
odom_frame_id |
string |
"odom" |
The fixed frame the pose is expressed in. |
publish_tf |
bool |
true |
Broadcast the pose on TF. Turn this off if something else already publishes that transform, or the two fight and TF alternates between them. What exactly is broadcast depends on guess_frame_id: without it, odom_frame_id → frame_id; with it, a correction odom_frame_id → guess_frame_id (why). |
wait_for_transform |
double |
0.1 |
Seconds to wait for a needed transform before giving up on the frame. |
initial_pose |
string |
"" |
Starting pose, "x y z roll pitch yaw". Also settable at runtime through reset_odom_to_pose. |
ground_truth_frame_id |
string |
"" |
When set, the pose is taken from this TF instead of being computed — for replaying a dataset with a known trajectory. |
ground_truth_base_frame_id |
string |
value of frame_id
|
The robot frame within the ground truth TF tree. |
guess_frame_id |
string |
"" |
A frame carrying another odometry source, used as the initial guess for each registration. Documented with its companions under Feeding in an external guess – the highest-value parameter here for a wheeled robot. |
The sensor must be connected to frame_id in TF before the first frame arrives, or that frame is dropped with a warning. A static publisher is the usual answer.
RTAB-Map’s own parameters
Everything in RTAB-Map’s odometry parameter set is exposed as a ROS parameter under its RTAB-Map name, so tuning is done directly:
ros2 run rtabmap_odom rgbd_odometry --ros-args \
-p "Odom/Strategy:='1'" \
-p "Vis/MinInliers:='15'" \
-p "Odom/ResetCountdown:='1'"
Note the quoting. Every RTAB-Map parameter is declared as a string, whatever it looks like, because that is how RTAB-Map’s own parameter map stores them. Writing -p Odom/Strategy:=1 makes ROS infer an integer, and the node throws on startup rather than starting with the wrong value:
``` parameter ‘Odom/Strategy’ has invalid type: Wrong parameter type,
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged rtabmap_odom at Robotics Stack Exchange
|
rtabmap_odom package from rtabmap_ros reportabmap_conversions rtabmap_costmap_plugins rtabmap_demos rtabmap_examples rtabmap_launch rtabmap_msgs rtabmap_odom rtabmap_python rtabmap_ros rtabmap_rviz_plugins rtabmap_slam rtabmap_sync rtabmap_util rtabmap_viz |
ROS Distro
|
Package Summary
| Version | 0.23.13 |
| License | BSD |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/introlab/rtabmap_ros.git |
| VCS Type | git |
| VCS Version | kilted-devel |
| Last Updated | 2026-10-01 |
| Dev Status | MAINTAINED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- Mathieu Labbe
Authors
- Mathieu Labbe
rtabmap_odom
Odometry for RTAB-Map: where the robot is relative to a local fixed frame, estimated from its own sensors — a pose that moves continuously and never jumps, but drifts over time.
SLAM needs a pose for every measurement it maps. These nodes produce one by registering each new frame against the last — visually from an RGB-D or stereo camera, or geometrically from a lidar — and integrating the result into a nav_msgs/msg/Odometry and a TF.
Contents
Nodes
| Node | Description |
|---|---|
| rgbd_odometry | Visual odometry from a color image and a depth image registered to it. |
| stereo_odometry | Visual odometry from a stereo pair. |
| icp_odometry | Geometric odometry from a 2D or 3D lidar. |
Every node is a composable node as well as a standalone executable.
Choosing a sensor modality for the environment
Which to use is a question about the environment, not about which sensor is better. A camera tracks visual texture; a lidar tracks geometry. Each fails where its own cue is missing, and the two failures do not overlap much.
| Environment | Use | Why |
|---|---|---|
| Visually textured and well lit — offices, cluttered rooms, daylight outdoors | Camera | Plenty of features to match, and appearance gives loop closure for free. |
| Textureless but geometrically rich — bare corridors with doorways and furniture, warehouse aisles | Lidar | Blank walls give a camera nothing; the shape of the space still constrains ICP. |
| Dark, or lighting that changes abruptly | Lidar | A camera is simply blind. Lidar does not care. |
| Geometrically plain but visually rich — a large open hall with a patterned floor, textured flat walls | Camera | Degenerate geometry defeats ICP here, while the texture is exactly what a camera needs. |
| Both plain and textureless — an empty warehouse, a long featureless tunnel | Wheel odometry, with either as a corrector | Neither cue is present. This is the case where wheel odometry carries the pose. |
| Repetitive and self-similar — tiled floors, rows of racking, a long colonnade | Wheel odometry as the guess, with either on top | Both cues are present but ambiguous: a camera matches the wrong copy of a feature, a lidar the wrong bay of shelving. See Repetitive patterns. |
| Outdoors at range | Stereo camera or 3D lidar | RGB-D depth stops working outdoors; both of these keep going. |
Do not underestimate wheel odometry. On a wheeled robot it is locally excellent and only drifts over distance — the opposite failure from both of the above, which are locally noisy but not systematically biased. It is also the only one of the three that keeps working when the environment offers no cue at all — and, because it is indifferent to what the scene looks like, the only one that is not fooled when the scene repeats itself.
Fuse the wheels with an IMU before feeding them in. robot_localization is the standard way: its EKF combines wheel odometry with IMU orientation and angular rates into one filtered odom topic, which is a markedly better guess than the wheels alone. FusionCore is another EKF that does the same job. The IMU fixes exactly what encoders are worst at — yaw through a turn, and wheel slip, which encoders report as motion that never happened. Where the camera or lidar fails outright, that filtered estimate is what carries the robot through, and a pipeline built this way degrades instead of breaking.
Which is why the robust arrangement is rarely one of them alone: feed wheel odometry in as guess_frame_id and the registration starts near the answer every frame. That covers the camera’s fast-motion and blank-wall failures and the lidar’s degenerate-corridor failure, while the camera or lidar in turn corrects the wheels’ drift. See Feeding in an external guess.
For 2D indoor odometry a lidar usually costs less computation. Registering a few hundred scan points is far less CPU than detecting, describing and matching visual features on every frame, and it needs no GPU — which is what decides whether odometry keeps up on the small onboard computers these robots carry.
With both a camera and a lidar, the usual arrangement is icp_odometry for the pose and the camera for appearance — see Combining a camera and a lidar.
Library
The package installs a C++ library, documented in the C++ API reference generated from the headers.
OdometryROS is the base class all three nodes derive from, and it is where most of this package’s behaviour actually lives. It owns the RTAB-Map Odometry object, the pose integration, the TF broadcast, the IMU intake, the services and the diagnostics. Each node subclasses it to do one thing: turn its own topics into a rtabmap::SensorData and hand it over. That is why the three nodes share nearly all of their parameters and publish exactly the same topics.
It also runs the registration on its own thread. A frame arriving while the previous one is still being processed does not block the subscription callback; see Update rates and dropped frames.
Conventions
These apply to all three nodes.
Frames and TF
| Parameter | Type | Default | Description |
|---|---|---|---|
frame_id |
string |
"base_link" |
The robot frame being tracked. The pose published is this frame’s, not the sensor’s — the sensor-to-robot transform is read from TF. |
odom_frame_id |
string |
"odom" |
The fixed frame the pose is expressed in. |
publish_tf |
bool |
true |
Broadcast the pose on TF. Turn this off if something else already publishes that transform, or the two fight and TF alternates between them. What exactly is broadcast depends on guess_frame_id: without it, odom_frame_id → frame_id; with it, a correction odom_frame_id → guess_frame_id (why). |
wait_for_transform |
double |
0.1 |
Seconds to wait for a needed transform before giving up on the frame. |
initial_pose |
string |
"" |
Starting pose, "x y z roll pitch yaw". Also settable at runtime through reset_odom_to_pose. |
ground_truth_frame_id |
string |
"" |
When set, the pose is taken from this TF instead of being computed — for replaying a dataset with a known trajectory. |
ground_truth_base_frame_id |
string |
value of frame_id
|
The robot frame within the ground truth TF tree. |
guess_frame_id |
string |
"" |
A frame carrying another odometry source, used as the initial guess for each registration. Documented with its companions under Feeding in an external guess – the highest-value parameter here for a wheeled robot. |
The sensor must be connected to frame_id in TF before the first frame arrives, or that frame is dropped with a warning. A static publisher is the usual answer.
RTAB-Map’s own parameters
Everything in RTAB-Map’s odometry parameter set is exposed as a ROS parameter under its RTAB-Map name, so tuning is done directly:
ros2 run rtabmap_odom rgbd_odometry --ros-args \
-p "Odom/Strategy:='1'" \
-p "Vis/MinInliers:='15'" \
-p "Odom/ResetCountdown:='1'"
Note the quoting. Every RTAB-Map parameter is declared as a string, whatever it looks like, because that is how RTAB-Map’s own parameter map stores them. Writing -p Odom/Strategy:=1 makes ROS infer an integer, and the node throws on startup rather than starting with the wrong value:
``` parameter ‘Odom/Strategy’ has invalid type: Wrong parameter type,
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged rtabmap_odom at Robotics Stack Exchange
|
rtabmap_odom package from rtabmap_ros reportabmap_conversions rtabmap_costmap_plugins rtabmap_demos rtabmap_examples rtabmap_launch rtabmap_msgs rtabmap_odom rtabmap_python rtabmap_ros rtabmap_rviz_plugins rtabmap_slam rtabmap_sync rtabmap_util rtabmap_viz |
ROS Distro
|
Package Summary
| Version | 0.23.13 |
| License | BSD |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/introlab/rtabmap_ros.git |
| VCS Type | git |
| VCS Version | lyrical-devel |
| Last Updated | 2026-10-01 |
| Dev Status | MAINTAINED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- Mathieu Labbe
Authors
- Mathieu Labbe
rtabmap_odom
Odometry for RTAB-Map: where the robot is relative to a local fixed frame, estimated from its own sensors — a pose that moves continuously and never jumps, but drifts over time.
SLAM needs a pose for every measurement it maps. These nodes produce one by registering each new frame against the last — visually from an RGB-D or stereo camera, or geometrically from a lidar — and integrating the result into a nav_msgs/msg/Odometry and a TF.
Contents
Nodes
| Node | Description |
|---|---|
| rgbd_odometry | Visual odometry from a color image and a depth image registered to it. |
| stereo_odometry | Visual odometry from a stereo pair. |
| icp_odometry | Geometric odometry from a 2D or 3D lidar. |
Every node is a composable node as well as a standalone executable.
Choosing a sensor modality for the environment
Which to use is a question about the environment, not about which sensor is better. A camera tracks visual texture; a lidar tracks geometry. Each fails where its own cue is missing, and the two failures do not overlap much.
| Environment | Use | Why |
|---|---|---|
| Visually textured and well lit — offices, cluttered rooms, daylight outdoors | Camera | Plenty of features to match, and appearance gives loop closure for free. |
| Textureless but geometrically rich — bare corridors with doorways and furniture, warehouse aisles | Lidar | Blank walls give a camera nothing; the shape of the space still constrains ICP. |
| Dark, or lighting that changes abruptly | Lidar | A camera is simply blind. Lidar does not care. |
| Geometrically plain but visually rich — a large open hall with a patterned floor, textured flat walls | Camera | Degenerate geometry defeats ICP here, while the texture is exactly what a camera needs. |
| Both plain and textureless — an empty warehouse, a long featureless tunnel | Wheel odometry, with either as a corrector | Neither cue is present. This is the case where wheel odometry carries the pose. |
| Repetitive and self-similar — tiled floors, rows of racking, a long colonnade | Wheel odometry as the guess, with either on top | Both cues are present but ambiguous: a camera matches the wrong copy of a feature, a lidar the wrong bay of shelving. See Repetitive patterns. |
| Outdoors at range | Stereo camera or 3D lidar | RGB-D depth stops working outdoors; both of these keep going. |
Do not underestimate wheel odometry. On a wheeled robot it is locally excellent and only drifts over distance — the opposite failure from both of the above, which are locally noisy but not systematically biased. It is also the only one of the three that keeps working when the environment offers no cue at all — and, because it is indifferent to what the scene looks like, the only one that is not fooled when the scene repeats itself.
Fuse the wheels with an IMU before feeding them in. robot_localization is the standard way: its EKF combines wheel odometry with IMU orientation and angular rates into one filtered odom topic, which is a markedly better guess than the wheels alone. FusionCore is another EKF that does the same job. The IMU fixes exactly what encoders are worst at — yaw through a turn, and wheel slip, which encoders report as motion that never happened. Where the camera or lidar fails outright, that filtered estimate is what carries the robot through, and a pipeline built this way degrades instead of breaking.
Which is why the robust arrangement is rarely one of them alone: feed wheel odometry in as guess_frame_id and the registration starts near the answer every frame. That covers the camera’s fast-motion and blank-wall failures and the lidar’s degenerate-corridor failure, while the camera or lidar in turn corrects the wheels’ drift. See Feeding in an external guess.
For 2D indoor odometry a lidar usually costs less computation. Registering a few hundred scan points is far less CPU than detecting, describing and matching visual features on every frame, and it needs no GPU — which is what decides whether odometry keeps up on the small onboard computers these robots carry.
With both a camera and a lidar, the usual arrangement is icp_odometry for the pose and the camera for appearance — see Combining a camera and a lidar.
Library
The package installs a C++ library, documented in the C++ API reference generated from the headers.
OdometryROS is the base class all three nodes derive from, and it is where most of this package’s behaviour actually lives. It owns the RTAB-Map Odometry object, the pose integration, the TF broadcast, the IMU intake, the services and the diagnostics. Each node subclasses it to do one thing: turn its own topics into a rtabmap::SensorData and hand it over. That is why the three nodes share nearly all of their parameters and publish exactly the same topics.
It also runs the registration on its own thread. A frame arriving while the previous one is still being processed does not block the subscription callback; see Update rates and dropped frames.
Conventions
These apply to all three nodes.
Frames and TF
| Parameter | Type | Default | Description |
|---|---|---|---|
frame_id |
string |
"base_link" |
The robot frame being tracked. The pose published is this frame’s, not the sensor’s — the sensor-to-robot transform is read from TF. |
odom_frame_id |
string |
"odom" |
The fixed frame the pose is expressed in. |
publish_tf |
bool |
true |
Broadcast the pose on TF. Turn this off if something else already publishes that transform, or the two fight and TF alternates between them. What exactly is broadcast depends on guess_frame_id: without it, odom_frame_id → frame_id; with it, a correction odom_frame_id → guess_frame_id (why). |
wait_for_transform |
double |
0.1 |
Seconds to wait for a needed transform before giving up on the frame. |
initial_pose |
string |
"" |
Starting pose, "x y z roll pitch yaw". Also settable at runtime through reset_odom_to_pose. |
ground_truth_frame_id |
string |
"" |
When set, the pose is taken from this TF instead of being computed — for replaying a dataset with a known trajectory. |
ground_truth_base_frame_id |
string |
value of frame_id
|
The robot frame within the ground truth TF tree. |
guess_frame_id |
string |
"" |
A frame carrying another odometry source, used as the initial guess for each registration. Documented with its companions under Feeding in an external guess – the highest-value parameter here for a wheeled robot. |
The sensor must be connected to frame_id in TF before the first frame arrives, or that frame is dropped with a warning. A static publisher is the usual answer.
RTAB-Map’s own parameters
Everything in RTAB-Map’s odometry parameter set is exposed as a ROS parameter under its RTAB-Map name, so tuning is done directly:
ros2 run rtabmap_odom rgbd_odometry --ros-args \
-p "Odom/Strategy:='1'" \
-p "Vis/MinInliers:='15'" \
-p "Odom/ResetCountdown:='1'"
Note the quoting. Every RTAB-Map parameter is declared as a string, whatever it looks like, because that is how RTAB-Map’s own parameter map stores them. Writing -p Odom/Strategy:=1 makes ROS infer an integer, and the node throws on startup rather than starting with the wrong value:
``` parameter ‘Odom/Strategy’ has invalid type: Wrong parameter type,
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged rtabmap_odom at Robotics Stack Exchange
|
rtabmap_odom package from rtabmap_ros reportabmap_conversions rtabmap_demos rtabmap_examples rtabmap_launch rtabmap_msgs rtabmap_odom rtabmap_python rtabmap_ros rtabmap_rviz_plugins rtabmap_slam rtabmap_sync rtabmap_util rtabmap_viz |
ROS Distro
|
Package Summary
| Version | 0.23.13 |
| License | BSD |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/introlab/rtabmap_ros.git |
| VCS Type | git |
| VCS Version | rolling-devel |
| Last Updated | 2026-10-02 |
| Dev Status | MAINTAINED |
| Released | UNRELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- Mathieu Labbe
Authors
- Mathieu Labbe
rtabmap_odom
Odometry for RTAB-Map: where the robot is relative to a local fixed frame, estimated from its own sensors — a pose that moves continuously and never jumps, but drifts over time.
SLAM needs a pose for every measurement it maps. These nodes produce one by registering each new frame against the last — visually from an RGB-D or stereo camera, or geometrically from a lidar — and integrating the result into a nav_msgs/msg/Odometry and a TF.
Contents
Nodes
| Node | Description |
|---|---|
| rgbd_odometry | Visual odometry from a color image and a depth image registered to it. |
| stereo_odometry | Visual odometry from a stereo pair. |
| icp_odometry | Geometric odometry from a 2D or 3D lidar. |
Every node is a composable node as well as a standalone executable.
Choosing a sensor modality for the environment
Which to use is a question about the environment, not about which sensor is better. A camera tracks visual texture; a lidar tracks geometry. Each fails where its own cue is missing, and the two failures do not overlap much.
| Environment | Use | Why |
|---|---|---|
| Visually textured and well lit — offices, cluttered rooms, daylight outdoors | Camera | Plenty of features to match, and appearance gives loop closure for free. |
| Textureless but geometrically rich — bare corridors with doorways and furniture, warehouse aisles | Lidar | Blank walls give a camera nothing; the shape of the space still constrains ICP. |
| Dark, or lighting that changes abruptly | Lidar | A camera is simply blind. Lidar does not care. |
| Geometrically plain but visually rich — a large open hall with a patterned floor, textured flat walls | Camera | Degenerate geometry defeats ICP here, while the texture is exactly what a camera needs. |
| Both plain and textureless — an empty warehouse, a long featureless tunnel | Wheel odometry, with either as a corrector | Neither cue is present. This is the case where wheel odometry carries the pose. |
| Repetitive and self-similar — tiled floors, rows of racking, a long colonnade | Wheel odometry as the guess, with either on top | Both cues are present but ambiguous: a camera matches the wrong copy of a feature, a lidar the wrong bay of shelving. See Repetitive patterns. |
| Outdoors at range | Stereo camera or 3D lidar | RGB-D depth stops working outdoors; both of these keep going. |
Do not underestimate wheel odometry. On a wheeled robot it is locally excellent and only drifts over distance — the opposite failure from both of the above, which are locally noisy but not systematically biased. It is also the only one of the three that keeps working when the environment offers no cue at all — and, because it is indifferent to what the scene looks like, the only one that is not fooled when the scene repeats itself.
Fuse the wheels with an IMU before feeding them in. robot_localization is the standard way: its EKF combines wheel odometry with IMU orientation and angular rates into one filtered odom topic, which is a markedly better guess than the wheels alone. FusionCore is another EKF that does the same job. The IMU fixes exactly what encoders are worst at — yaw through a turn, and wheel slip, which encoders report as motion that never happened. Where the camera or lidar fails outright, that filtered estimate is what carries the robot through, and a pipeline built this way degrades instead of breaking.
Which is why the robust arrangement is rarely one of them alone: feed wheel odometry in as guess_frame_id and the registration starts near the answer every frame. That covers the camera’s fast-motion and blank-wall failures and the lidar’s degenerate-corridor failure, while the camera or lidar in turn corrects the wheels’ drift. See Feeding in an external guess.
For 2D indoor odometry a lidar usually costs less computation. Registering a few hundred scan points is far less CPU than detecting, describing and matching visual features on every frame, and it needs no GPU — which is what decides whether odometry keeps up on the small onboard computers these robots carry.
With both a camera and a lidar, the usual arrangement is icp_odometry for the pose and the camera for appearance — see Combining a camera and a lidar.
Library
The package installs a C++ library, documented in the C++ API reference generated from the headers.
OdometryROS is the base class all three nodes derive from, and it is where most of this package’s behaviour actually lives. It owns the RTAB-Map Odometry object, the pose integration, the TF broadcast, the IMU intake, the services and the diagnostics. Each node subclasses it to do one thing: turn its own topics into a rtabmap::SensorData and hand it over. That is why the three nodes share nearly all of their parameters and publish exactly the same topics.
It also runs the registration on its own thread. A frame arriving while the previous one is still being processed does not block the subscription callback; see Update rates and dropped frames.
Conventions
These apply to all three nodes.
Frames and TF
| Parameter | Type | Default | Description |
|---|---|---|---|
frame_id |
string |
"base_link" |
The robot frame being tracked. The pose published is this frame’s, not the sensor’s — the sensor-to-robot transform is read from TF. |
odom_frame_id |
string |
"odom" |
The fixed frame the pose is expressed in. |
publish_tf |
bool |
true |
Broadcast the pose on TF. Turn this off if something else already publishes that transform, or the two fight and TF alternates between them. What exactly is broadcast depends on guess_frame_id: without it, odom_frame_id → frame_id; with it, a correction odom_frame_id → guess_frame_id (why). |
wait_for_transform |
double |
0.1 |
Seconds to wait for a needed transform before giving up on the frame. |
initial_pose |
string |
"" |
Starting pose, "x y z roll pitch yaw". Also settable at runtime through reset_odom_to_pose. |
ground_truth_frame_id |
string |
"" |
When set, the pose is taken from this TF instead of being computed — for replaying a dataset with a known trajectory. |
ground_truth_base_frame_id |
string |
value of frame_id
|
The robot frame within the ground truth TF tree. |
guess_frame_id |
string |
"" |
A frame carrying another odometry source, used as the initial guess for each registration. Documented with its companions under Feeding in an external guess – the highest-value parameter here for a wheeled robot. |
The sensor must be connected to frame_id in TF before the first frame arrives, or that frame is dropped with a warning. A static publisher is the usual answer.
RTAB-Map’s own parameters
Everything in RTAB-Map’s odometry parameter set is exposed as a ROS parameter under its RTAB-Map name, so tuning is done directly:
ros2 run rtabmap_odom rgbd_odometry --ros-args \
-p "Odom/Strategy:='1'" \
-p "Vis/MinInliers:='15'" \
-p "Odom/ResetCountdown:='1'"
Note the quoting. Every RTAB-Map parameter is declared as a string, whatever it looks like, because that is how RTAB-Map’s own parameter map stores them. Writing -p Odom/Strategy:=1 makes ROS infer an integer, and the node throws on startup rather than starting with the wrong value:
``` parameter ‘Odom/Strategy’ has invalid type: Wrong parameter type,
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged rtabmap_odom at Robotics Stack Exchange
|
rtabmap_odom package from rtabmap_ros reportabmap_conversions rtabmap_costmap_plugins rtabmap_demos rtabmap_examples rtabmap_launch rtabmap_msgs rtabmap_odom rtabmap_python rtabmap_ros rtabmap_rviz_plugins rtabmap_slam rtabmap_sync rtabmap_util rtabmap_viz |
ROS Distro
|
Package Summary
| Version | 0.23.13 |
| License | BSD |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/introlab/rtabmap_ros.git |
| VCS Type | git |
| VCS Version | humble-devel |
| Last Updated | 2026-10-01 |
| Dev Status | MAINTAINED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- Mathieu Labbe
Authors
- Mathieu Labbe
rtabmap_odom
Odometry for RTAB-Map: where the robot is relative to a local fixed frame, estimated from its own sensors — a pose that moves continuously and never jumps, but drifts over time.
SLAM needs a pose for every measurement it maps. These nodes produce one by registering each new frame against the last — visually from an RGB-D or stereo camera, or geometrically from a lidar — and integrating the result into a nav_msgs/msg/Odometry and a TF.
Contents
Nodes
| Node | Description |
|---|---|
| rgbd_odometry | Visual odometry from a color image and a depth image registered to it. |
| stereo_odometry | Visual odometry from a stereo pair. |
| icp_odometry | Geometric odometry from a 2D or 3D lidar. |
Every node is a composable node as well as a standalone executable.
Choosing a sensor modality for the environment
Which to use is a question about the environment, not about which sensor is better. A camera tracks visual texture; a lidar tracks geometry. Each fails where its own cue is missing, and the two failures do not overlap much.
| Environment | Use | Why |
|---|---|---|
| Visually textured and well lit — offices, cluttered rooms, daylight outdoors | Camera | Plenty of features to match, and appearance gives loop closure for free. |
| Textureless but geometrically rich — bare corridors with doorways and furniture, warehouse aisles | Lidar | Blank walls give a camera nothing; the shape of the space still constrains ICP. |
| Dark, or lighting that changes abruptly | Lidar | A camera is simply blind. Lidar does not care. |
| Geometrically plain but visually rich — a large open hall with a patterned floor, textured flat walls | Camera | Degenerate geometry defeats ICP here, while the texture is exactly what a camera needs. |
| Both plain and textureless — an empty warehouse, a long featureless tunnel | Wheel odometry, with either as a corrector | Neither cue is present. This is the case where wheel odometry carries the pose. |
| Repetitive and self-similar — tiled floors, rows of racking, a long colonnade | Wheel odometry as the guess, with either on top | Both cues are present but ambiguous: a camera matches the wrong copy of a feature, a lidar the wrong bay of shelving. See Repetitive patterns. |
| Outdoors at range | Stereo camera or 3D lidar | RGB-D depth stops working outdoors; both of these keep going. |
Do not underestimate wheel odometry. On a wheeled robot it is locally excellent and only drifts over distance — the opposite failure from both of the above, which are locally noisy but not systematically biased. It is also the only one of the three that keeps working when the environment offers no cue at all — and, because it is indifferent to what the scene looks like, the only one that is not fooled when the scene repeats itself.
Fuse the wheels with an IMU before feeding them in. robot_localization is the standard way: its EKF combines wheel odometry with IMU orientation and angular rates into one filtered odom topic, which is a markedly better guess than the wheels alone. FusionCore is another EKF that does the same job. The IMU fixes exactly what encoders are worst at — yaw through a turn, and wheel slip, which encoders report as motion that never happened. Where the camera or lidar fails outright, that filtered estimate is what carries the robot through, and a pipeline built this way degrades instead of breaking.
Which is why the robust arrangement is rarely one of them alone: feed wheel odometry in as guess_frame_id and the registration starts near the answer every frame. That covers the camera’s fast-motion and blank-wall failures and the lidar’s degenerate-corridor failure, while the camera or lidar in turn corrects the wheels’ drift. See Feeding in an external guess.
For 2D indoor odometry a lidar usually costs less computation. Registering a few hundred scan points is far less CPU than detecting, describing and matching visual features on every frame, and it needs no GPU — which is what decides whether odometry keeps up on the small onboard computers these robots carry.
With both a camera and a lidar, the usual arrangement is icp_odometry for the pose and the camera for appearance — see Combining a camera and a lidar.
Library
The package installs a C++ library, documented in the C++ API reference generated from the headers.
OdometryROS is the base class all three nodes derive from, and it is where most of this package’s behaviour actually lives. It owns the RTAB-Map Odometry object, the pose integration, the TF broadcast, the IMU intake, the services and the diagnostics. Each node subclasses it to do one thing: turn its own topics into a rtabmap::SensorData and hand it over. That is why the three nodes share nearly all of their parameters and publish exactly the same topics.
It also runs the registration on its own thread. A frame arriving while the previous one is still being processed does not block the subscription callback; see Update rates and dropped frames.
Conventions
These apply to all three nodes.
Frames and TF
| Parameter | Type | Default | Description |
|---|---|---|---|
frame_id |
string |
"base_link" |
The robot frame being tracked. The pose published is this frame’s, not the sensor’s — the sensor-to-robot transform is read from TF. |
odom_frame_id |
string |
"odom" |
The fixed frame the pose is expressed in. |
publish_tf |
bool |
true |
Broadcast the pose on TF. Turn this off if something else already publishes that transform, or the two fight and TF alternates between them. What exactly is broadcast depends on guess_frame_id: without it, odom_frame_id → frame_id; with it, a correction odom_frame_id → guess_frame_id (why). |
wait_for_transform |
double |
0.1 |
Seconds to wait for a needed transform before giving up on the frame. |
initial_pose |
string |
"" |
Starting pose, "x y z roll pitch yaw". Also settable at runtime through reset_odom_to_pose. |
ground_truth_frame_id |
string |
"" |
When set, the pose is taken from this TF instead of being computed — for replaying a dataset with a known trajectory. |
ground_truth_base_frame_id |
string |
value of frame_id
|
The robot frame within the ground truth TF tree. |
guess_frame_id |
string |
"" |
A frame carrying another odometry source, used as the initial guess for each registration. Documented with its companions under Feeding in an external guess – the highest-value parameter here for a wheeled robot. |
The sensor must be connected to frame_id in TF before the first frame arrives, or that frame is dropped with a warning. A static publisher is the usual answer.
RTAB-Map’s own parameters
Everything in RTAB-Map’s odometry parameter set is exposed as a ROS parameter under its RTAB-Map name, so tuning is done directly:
ros2 run rtabmap_odom rgbd_odometry --ros-args \
-p "Odom/Strategy:='1'" \
-p "Vis/MinInliers:='15'" \
-p "Odom/ResetCountdown:='1'"
Note the quoting. Every RTAB-Map parameter is declared as a string, whatever it looks like, because that is how RTAB-Map’s own parameter map stores them. Writing -p Odom/Strategy:=1 makes ROS infer an integer, and the node throws on startup rather than starting with the wrong value:
``` parameter ‘Odom/Strategy’ has invalid type: Wrong parameter type,
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged rtabmap_odom at Robotics Stack Exchange
|
rtabmap_odom package from rtabmap_ros reportabmap_conversions rtabmap_costmap_plugins rtabmap_demos rtabmap_examples rtabmap_launch rtabmap_msgs rtabmap_odom rtabmap_python rtabmap_ros rtabmap_rviz_plugins rtabmap_slam rtabmap_sync rtabmap_util rtabmap_viz |
ROS Distro
|
Package Summary
| Version | 0.23.13 |
| License | BSD |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/introlab/rtabmap_ros.git |
| VCS Type | git |
| VCS Version | humble-devel |
| Last Updated | 2026-10-01 |
| Dev Status | MAINTAINED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- Mathieu Labbe
Authors
- Mathieu Labbe
rtabmap_odom
Odometry for RTAB-Map: where the robot is relative to a local fixed frame, estimated from its own sensors — a pose that moves continuously and never jumps, but drifts over time.
SLAM needs a pose for every measurement it maps. These nodes produce one by registering each new frame against the last — visually from an RGB-D or stereo camera, or geometrically from a lidar — and integrating the result into a nav_msgs/msg/Odometry and a TF.
Contents
Nodes
| Node | Description |
|---|---|
| rgbd_odometry | Visual odometry from a color image and a depth image registered to it. |
| stereo_odometry | Visual odometry from a stereo pair. |
| icp_odometry | Geometric odometry from a 2D or 3D lidar. |
Every node is a composable node as well as a standalone executable.
Choosing a sensor modality for the environment
Which to use is a question about the environment, not about which sensor is better. A camera tracks visual texture; a lidar tracks geometry. Each fails where its own cue is missing, and the two failures do not overlap much.
| Environment | Use | Why |
|---|---|---|
| Visually textured and well lit — offices, cluttered rooms, daylight outdoors | Camera | Plenty of features to match, and appearance gives loop closure for free. |
| Textureless but geometrically rich — bare corridors with doorways and furniture, warehouse aisles | Lidar | Blank walls give a camera nothing; the shape of the space still constrains ICP. |
| Dark, or lighting that changes abruptly | Lidar | A camera is simply blind. Lidar does not care. |
| Geometrically plain but visually rich — a large open hall with a patterned floor, textured flat walls | Camera | Degenerate geometry defeats ICP here, while the texture is exactly what a camera needs. |
| Both plain and textureless — an empty warehouse, a long featureless tunnel | Wheel odometry, with either as a corrector | Neither cue is present. This is the case where wheel odometry carries the pose. |
| Repetitive and self-similar — tiled floors, rows of racking, a long colonnade | Wheel odometry as the guess, with either on top | Both cues are present but ambiguous: a camera matches the wrong copy of a feature, a lidar the wrong bay of shelving. See Repetitive patterns. |
| Outdoors at range | Stereo camera or 3D lidar | RGB-D depth stops working outdoors; both of these keep going. |
Do not underestimate wheel odometry. On a wheeled robot it is locally excellent and only drifts over distance — the opposite failure from both of the above, which are locally noisy but not systematically biased. It is also the only one of the three that keeps working when the environment offers no cue at all — and, because it is indifferent to what the scene looks like, the only one that is not fooled when the scene repeats itself.
Fuse the wheels with an IMU before feeding them in. robot_localization is the standard way: its EKF combines wheel odometry with IMU orientation and angular rates into one filtered odom topic, which is a markedly better guess than the wheels alone. FusionCore is another EKF that does the same job. The IMU fixes exactly what encoders are worst at — yaw through a turn, and wheel slip, which encoders report as motion that never happened. Where the camera or lidar fails outright, that filtered estimate is what carries the robot through, and a pipeline built this way degrades instead of breaking.
Which is why the robust arrangement is rarely one of them alone: feed wheel odometry in as guess_frame_id and the registration starts near the answer every frame. That covers the camera’s fast-motion and blank-wall failures and the lidar’s degenerate-corridor failure, while the camera or lidar in turn corrects the wheels’ drift. See Feeding in an external guess.
For 2D indoor odometry a lidar usually costs less computation. Registering a few hundred scan points is far less CPU than detecting, describing and matching visual features on every frame, and it needs no GPU — which is what decides whether odometry keeps up on the small onboard computers these robots carry.
With both a camera and a lidar, the usual arrangement is icp_odometry for the pose and the camera for appearance — see Combining a camera and a lidar.
Library
The package installs a C++ library, documented in the C++ API reference generated from the headers.
OdometryROS is the base class all three nodes derive from, and it is where most of this package’s behaviour actually lives. It owns the RTAB-Map Odometry object, the pose integration, the TF broadcast, the IMU intake, the services and the diagnostics. Each node subclasses it to do one thing: turn its own topics into a rtabmap::SensorData and hand it over. That is why the three nodes share nearly all of their parameters and publish exactly the same topics.
It also runs the registration on its own thread. A frame arriving while the previous one is still being processed does not block the subscription callback; see Update rates and dropped frames.
Conventions
These apply to all three nodes.
Frames and TF
| Parameter | Type | Default | Description |
|---|---|---|---|
frame_id |
string |
"base_link" |
The robot frame being tracked. The pose published is this frame’s, not the sensor’s — the sensor-to-robot transform is read from TF. |
odom_frame_id |
string |
"odom" |
The fixed frame the pose is expressed in. |
publish_tf |
bool |
true |
Broadcast the pose on TF. Turn this off if something else already publishes that transform, or the two fight and TF alternates between them. What exactly is broadcast depends on guess_frame_id: without it, odom_frame_id → frame_id; with it, a correction odom_frame_id → guess_frame_id (why). |
wait_for_transform |
double |
0.1 |
Seconds to wait for a needed transform before giving up on the frame. |
initial_pose |
string |
"" |
Starting pose, "x y z roll pitch yaw". Also settable at runtime through reset_odom_to_pose. |
ground_truth_frame_id |
string |
"" |
When set, the pose is taken from this TF instead of being computed — for replaying a dataset with a known trajectory. |
ground_truth_base_frame_id |
string |
value of frame_id
|
The robot frame within the ground truth TF tree. |
guess_frame_id |
string |
"" |
A frame carrying another odometry source, used as the initial guess for each registration. Documented with its companions under Feeding in an external guess – the highest-value parameter here for a wheeled robot. |
The sensor must be connected to frame_id in TF before the first frame arrives, or that frame is dropped with a warning. A static publisher is the usual answer.
RTAB-Map’s own parameters
Everything in RTAB-Map’s odometry parameter set is exposed as a ROS parameter under its RTAB-Map name, so tuning is done directly:
ros2 run rtabmap_odom rgbd_odometry --ros-args \
-p "Odom/Strategy:='1'" \
-p "Vis/MinInliers:='15'" \
-p "Odom/ResetCountdown:='1'"
Note the quoting. Every RTAB-Map parameter is declared as a string, whatever it looks like, because that is how RTAB-Map’s own parameter map stores them. Writing -p Odom/Strategy:=1 makes ROS infer an integer, and the node throws on startup rather than starting with the wrong value:
``` parameter ‘Odom/Strategy’ has invalid type: Wrong parameter type,
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged rtabmap_odom at Robotics Stack Exchange
|
rtabmap_odom package from rtabmap_ros reportabmap_conversions rtabmap_costmap_plugins rtabmap_demos rtabmap_examples rtabmap_launch rtabmap_msgs rtabmap_odom rtabmap_python rtabmap_ros rtabmap_rviz_plugins rtabmap_slam rtabmap_sync rtabmap_util rtabmap_viz |
ROS Distro
|
Package Summary
| Version | 0.23.13 |
| License | BSD |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/introlab/rtabmap_ros.git |
| VCS Type | git |
| VCS Version | humble-devel |
| Last Updated | 2026-10-01 |
| Dev Status | MAINTAINED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- Mathieu Labbe
Authors
- Mathieu Labbe
rtabmap_odom
Odometry for RTAB-Map: where the robot is relative to a local fixed frame, estimated from its own sensors — a pose that moves continuously and never jumps, but drifts over time.
SLAM needs a pose for every measurement it maps. These nodes produce one by registering each new frame against the last — visually from an RGB-D or stereo camera, or geometrically from a lidar — and integrating the result into a nav_msgs/msg/Odometry and a TF.
Contents
Nodes
| Node | Description |
|---|---|
| rgbd_odometry | Visual odometry from a color image and a depth image registered to it. |
| stereo_odometry | Visual odometry from a stereo pair. |
| icp_odometry | Geometric odometry from a 2D or 3D lidar. |
Every node is a composable node as well as a standalone executable.
Choosing a sensor modality for the environment
Which to use is a question about the environment, not about which sensor is better. A camera tracks visual texture; a lidar tracks geometry. Each fails where its own cue is missing, and the two failures do not overlap much.
| Environment | Use | Why |
|---|---|---|
| Visually textured and well lit — offices, cluttered rooms, daylight outdoors | Camera | Plenty of features to match, and appearance gives loop closure for free. |
| Textureless but geometrically rich — bare corridors with doorways and furniture, warehouse aisles | Lidar | Blank walls give a camera nothing; the shape of the space still constrains ICP. |
| Dark, or lighting that changes abruptly | Lidar | A camera is simply blind. Lidar does not care. |
| Geometrically plain but visually rich — a large open hall with a patterned floor, textured flat walls | Camera | Degenerate geometry defeats ICP here, while the texture is exactly what a camera needs. |
| Both plain and textureless — an empty warehouse, a long featureless tunnel | Wheel odometry, with either as a corrector | Neither cue is present. This is the case where wheel odometry carries the pose. |
| Repetitive and self-similar — tiled floors, rows of racking, a long colonnade | Wheel odometry as the guess, with either on top | Both cues are present but ambiguous: a camera matches the wrong copy of a feature, a lidar the wrong bay of shelving. See Repetitive patterns. |
| Outdoors at range | Stereo camera or 3D lidar | RGB-D depth stops working outdoors; both of these keep going. |
Do not underestimate wheel odometry. On a wheeled robot it is locally excellent and only drifts over distance — the opposite failure from both of the above, which are locally noisy but not systematically biased. It is also the only one of the three that keeps working when the environment offers no cue at all — and, because it is indifferent to what the scene looks like, the only one that is not fooled when the scene repeats itself.
Fuse the wheels with an IMU before feeding them in. robot_localization is the standard way: its EKF combines wheel odometry with IMU orientation and angular rates into one filtered odom topic, which is a markedly better guess than the wheels alone. FusionCore is another EKF that does the same job. The IMU fixes exactly what encoders are worst at — yaw through a turn, and wheel slip, which encoders report as motion that never happened. Where the camera or lidar fails outright, that filtered estimate is what carries the robot through, and a pipeline built this way degrades instead of breaking.
Which is why the robust arrangement is rarely one of them alone: feed wheel odometry in as guess_frame_id and the registration starts near the answer every frame. That covers the camera’s fast-motion and blank-wall failures and the lidar’s degenerate-corridor failure, while the camera or lidar in turn corrects the wheels’ drift. See Feeding in an external guess.
For 2D indoor odometry a lidar usually costs less computation. Registering a few hundred scan points is far less CPU than detecting, describing and matching visual features on every frame, and it needs no GPU — which is what decides whether odometry keeps up on the small onboard computers these robots carry.
With both a camera and a lidar, the usual arrangement is icp_odometry for the pose and the camera for appearance — see Combining a camera and a lidar.
Library
The package installs a C++ library, documented in the C++ API reference generated from the headers.
OdometryROS is the base class all three nodes derive from, and it is where most of this package’s behaviour actually lives. It owns the RTAB-Map Odometry object, the pose integration, the TF broadcast, the IMU intake, the services and the diagnostics. Each node subclasses it to do one thing: turn its own topics into a rtabmap::SensorData and hand it over. That is why the three nodes share nearly all of their parameters and publish exactly the same topics.
It also runs the registration on its own thread. A frame arriving while the previous one is still being processed does not block the subscription callback; see Update rates and dropped frames.
Conventions
These apply to all three nodes.
Frames and TF
| Parameter | Type | Default | Description |
|---|---|---|---|
frame_id |
string |
"base_link" |
The robot frame being tracked. The pose published is this frame’s, not the sensor’s — the sensor-to-robot transform is read from TF. |
odom_frame_id |
string |
"odom" |
The fixed frame the pose is expressed in. |
publish_tf |
bool |
true |
Broadcast the pose on TF. Turn this off if something else already publishes that transform, or the two fight and TF alternates between them. What exactly is broadcast depends on guess_frame_id: without it, odom_frame_id → frame_id; with it, a correction odom_frame_id → guess_frame_id (why). |
wait_for_transform |
double |
0.1 |
Seconds to wait for a needed transform before giving up on the frame. |
initial_pose |
string |
"" |
Starting pose, "x y z roll pitch yaw". Also settable at runtime through reset_odom_to_pose. |
ground_truth_frame_id |
string |
"" |
When set, the pose is taken from this TF instead of being computed — for replaying a dataset with a known trajectory. |
ground_truth_base_frame_id |
string |
value of frame_id
|
The robot frame within the ground truth TF tree. |
guess_frame_id |
string |
"" |
A frame carrying another odometry source, used as the initial guess for each registration. Documented with its companions under Feeding in an external guess – the highest-value parameter here for a wheeled robot. |
The sensor must be connected to frame_id in TF before the first frame arrives, or that frame is dropped with a warning. A static publisher is the usual answer.
RTAB-Map’s own parameters
Everything in RTAB-Map’s odometry parameter set is exposed as a ROS parameter under its RTAB-Map name, so tuning is done directly:
ros2 run rtabmap_odom rgbd_odometry --ros-args \
-p "Odom/Strategy:='1'" \
-p "Vis/MinInliers:='15'" \
-p "Odom/ResetCountdown:='1'"
Note the quoting. Every RTAB-Map parameter is declared as a string, whatever it looks like, because that is how RTAB-Map’s own parameter map stores them. Writing -p Odom/Strategy:=1 makes ROS infer an integer, and the node throws on startup rather than starting with the wrong value:
``` parameter ‘Odom/Strategy’ has invalid type: Wrong parameter type,
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged rtabmap_odom at Robotics Stack Exchange
|
rtabmap_odom package from rtabmap_ros reportabmap_conversions rtabmap_costmap_plugins rtabmap_demos rtabmap_examples rtabmap_launch rtabmap_msgs rtabmap_odom rtabmap_python rtabmap_ros rtabmap_rviz_plugins rtabmap_slam rtabmap_sync rtabmap_util rtabmap_viz |
ROS Distro
|
Package Summary
| Version | 0.23.13 |
| License | BSD |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/introlab/rtabmap_ros.git |
| VCS Type | git |
| VCS Version | humble-devel |
| Last Updated | 2026-10-01 |
| Dev Status | MAINTAINED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- Mathieu Labbe
Authors
- Mathieu Labbe
rtabmap_odom
Odometry for RTAB-Map: where the robot is relative to a local fixed frame, estimated from its own sensors — a pose that moves continuously and never jumps, but drifts over time.
SLAM needs a pose for every measurement it maps. These nodes produce one by registering each new frame against the last — visually from an RGB-D or stereo camera, or geometrically from a lidar — and integrating the result into a nav_msgs/msg/Odometry and a TF.
Contents
Nodes
| Node | Description |
|---|---|
| rgbd_odometry | Visual odometry from a color image and a depth image registered to it. |
| stereo_odometry | Visual odometry from a stereo pair. |
| icp_odometry | Geometric odometry from a 2D or 3D lidar. |
Every node is a composable node as well as a standalone executable.
Choosing a sensor modality for the environment
Which to use is a question about the environment, not about which sensor is better. A camera tracks visual texture; a lidar tracks geometry. Each fails where its own cue is missing, and the two failures do not overlap much.
| Environment | Use | Why |
|---|---|---|
| Visually textured and well lit — offices, cluttered rooms, daylight outdoors | Camera | Plenty of features to match, and appearance gives loop closure for free. |
| Textureless but geometrically rich — bare corridors with doorways and furniture, warehouse aisles | Lidar | Blank walls give a camera nothing; the shape of the space still constrains ICP. |
| Dark, or lighting that changes abruptly | Lidar | A camera is simply blind. Lidar does not care. |
| Geometrically plain but visually rich — a large open hall with a patterned floor, textured flat walls | Camera | Degenerate geometry defeats ICP here, while the texture is exactly what a camera needs. |
| Both plain and textureless — an empty warehouse, a long featureless tunnel | Wheel odometry, with either as a corrector | Neither cue is present. This is the case where wheel odometry carries the pose. |
| Repetitive and self-similar — tiled floors, rows of racking, a long colonnade | Wheel odometry as the guess, with either on top | Both cues are present but ambiguous: a camera matches the wrong copy of a feature, a lidar the wrong bay of shelving. See Repetitive patterns. |
| Outdoors at range | Stereo camera or 3D lidar | RGB-D depth stops working outdoors; both of these keep going. |
Do not underestimate wheel odometry. On a wheeled robot it is locally excellent and only drifts over distance — the opposite failure from both of the above, which are locally noisy but not systematically biased. It is also the only one of the three that keeps working when the environment offers no cue at all — and, because it is indifferent to what the scene looks like, the only one that is not fooled when the scene repeats itself.
Fuse the wheels with an IMU before feeding them in. robot_localization is the standard way: its EKF combines wheel odometry with IMU orientation and angular rates into one filtered odom topic, which is a markedly better guess than the wheels alone. FusionCore is another EKF that does the same job. The IMU fixes exactly what encoders are worst at — yaw through a turn, and wheel slip, which encoders report as motion that never happened. Where the camera or lidar fails outright, that filtered estimate is what carries the robot through, and a pipeline built this way degrades instead of breaking.
Which is why the robust arrangement is rarely one of them alone: feed wheel odometry in as guess_frame_id and the registration starts near the answer every frame. That covers the camera’s fast-motion and blank-wall failures and the lidar’s degenerate-corridor failure, while the camera or lidar in turn corrects the wheels’ drift. See Feeding in an external guess.
For 2D indoor odometry a lidar usually costs less computation. Registering a few hundred scan points is far less CPU than detecting, describing and matching visual features on every frame, and it needs no GPU — which is what decides whether odometry keeps up on the small onboard computers these robots carry.
With both a camera and a lidar, the usual arrangement is icp_odometry for the pose and the camera for appearance — see Combining a camera and a lidar.
Library
The package installs a C++ library, documented in the C++ API reference generated from the headers.
OdometryROS is the base class all three nodes derive from, and it is where most of this package’s behaviour actually lives. It owns the RTAB-Map Odometry object, the pose integration, the TF broadcast, the IMU intake, the services and the diagnostics. Each node subclasses it to do one thing: turn its own topics into a rtabmap::SensorData and hand it over. That is why the three nodes share nearly all of their parameters and publish exactly the same topics.
It also runs the registration on its own thread. A frame arriving while the previous one is still being processed does not block the subscription callback; see Update rates and dropped frames.
Conventions
These apply to all three nodes.
Frames and TF
| Parameter | Type | Default | Description |
|---|---|---|---|
frame_id |
string |
"base_link" |
The robot frame being tracked. The pose published is this frame’s, not the sensor’s — the sensor-to-robot transform is read from TF. |
odom_frame_id |
string |
"odom" |
The fixed frame the pose is expressed in. |
publish_tf |
bool |
true |
Broadcast the pose on TF. Turn this off if something else already publishes that transform, or the two fight and TF alternates between them. What exactly is broadcast depends on guess_frame_id: without it, odom_frame_id → frame_id; with it, a correction odom_frame_id → guess_frame_id (why). |
wait_for_transform |
double |
0.1 |
Seconds to wait for a needed transform before giving up on the frame. |
initial_pose |
string |
"" |
Starting pose, "x y z roll pitch yaw". Also settable at runtime through reset_odom_to_pose. |
ground_truth_frame_id |
string |
"" |
When set, the pose is taken from this TF instead of being computed — for replaying a dataset with a known trajectory. |
ground_truth_base_frame_id |
string |
value of frame_id
|
The robot frame within the ground truth TF tree. |
guess_frame_id |
string |
"" |
A frame carrying another odometry source, used as the initial guess for each registration. Documented with its companions under Feeding in an external guess – the highest-value parameter here for a wheeled robot. |
The sensor must be connected to frame_id in TF before the first frame arrives, or that frame is dropped with a warning. A static publisher is the usual answer.
RTAB-Map’s own parameters
Everything in RTAB-Map’s odometry parameter set is exposed as a ROS parameter under its RTAB-Map name, so tuning is done directly:
ros2 run rtabmap_odom rgbd_odometry --ros-args \
-p "Odom/Strategy:='1'" \
-p "Vis/MinInliers:='15'" \
-p "Odom/ResetCountdown:='1'"
Note the quoting. Every RTAB-Map parameter is declared as a string, whatever it looks like, because that is how RTAB-Map’s own parameter map stores them. Writing -p Odom/Strategy:=1 makes ROS infer an integer, and the node throws on startup rather than starting with the wrong value:
``` parameter ‘Odom/Strategy’ has invalid type: Wrong parameter type,
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged rtabmap_odom at Robotics Stack Exchange
|
rtabmap_odom package from rtabmap_ros reportabmap_conversions rtabmap_costmap_plugins rtabmap_demos rtabmap_examples rtabmap_launch rtabmap_msgs rtabmap_odom rtabmap_python rtabmap_ros rtabmap_rviz_plugins rtabmap_slam rtabmap_sync rtabmap_util rtabmap_viz |
ROS Distro
|
Package Summary
| Version | 0.23.13 |
| License | BSD |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/introlab/rtabmap_ros.git |
| VCS Type | git |
| VCS Version | humble-devel |
| Last Updated | 2026-10-01 |
| Dev Status | MAINTAINED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- Mathieu Labbe
Authors
- Mathieu Labbe
rtabmap_odom
Odometry for RTAB-Map: where the robot is relative to a local fixed frame, estimated from its own sensors — a pose that moves continuously and never jumps, but drifts over time.
SLAM needs a pose for every measurement it maps. These nodes produce one by registering each new frame against the last — visually from an RGB-D or stereo camera, or geometrically from a lidar — and integrating the result into a nav_msgs/msg/Odometry and a TF.
Contents
Nodes
| Node | Description |
|---|---|
| rgbd_odometry | Visual odometry from a color image and a depth image registered to it. |
| stereo_odometry | Visual odometry from a stereo pair. |
| icp_odometry | Geometric odometry from a 2D or 3D lidar. |
Every node is a composable node as well as a standalone executable.
Choosing a sensor modality for the environment
Which to use is a question about the environment, not about which sensor is better. A camera tracks visual texture; a lidar tracks geometry. Each fails where its own cue is missing, and the two failures do not overlap much.
| Environment | Use | Why |
|---|---|---|
| Visually textured and well lit — offices, cluttered rooms, daylight outdoors | Camera | Plenty of features to match, and appearance gives loop closure for free. |
| Textureless but geometrically rich — bare corridors with doorways and furniture, warehouse aisles | Lidar | Blank walls give a camera nothing; the shape of the space still constrains ICP. |
| Dark, or lighting that changes abruptly | Lidar | A camera is simply blind. Lidar does not care. |
| Geometrically plain but visually rich — a large open hall with a patterned floor, textured flat walls | Camera | Degenerate geometry defeats ICP here, while the texture is exactly what a camera needs. |
| Both plain and textureless — an empty warehouse, a long featureless tunnel | Wheel odometry, with either as a corrector | Neither cue is present. This is the case where wheel odometry carries the pose. |
| Repetitive and self-similar — tiled floors, rows of racking, a long colonnade | Wheel odometry as the guess, with either on top | Both cues are present but ambiguous: a camera matches the wrong copy of a feature, a lidar the wrong bay of shelving. See Repetitive patterns. |
| Outdoors at range | Stereo camera or 3D lidar | RGB-D depth stops working outdoors; both of these keep going. |
Do not underestimate wheel odometry. On a wheeled robot it is locally excellent and only drifts over distance — the opposite failure from both of the above, which are locally noisy but not systematically biased. It is also the only one of the three that keeps working when the environment offers no cue at all — and, because it is indifferent to what the scene looks like, the only one that is not fooled when the scene repeats itself.
Fuse the wheels with an IMU before feeding them in. robot_localization is the standard way: its EKF combines wheel odometry with IMU orientation and angular rates into one filtered odom topic, which is a markedly better guess than the wheels alone. FusionCore is another EKF that does the same job. The IMU fixes exactly what encoders are worst at — yaw through a turn, and wheel slip, which encoders report as motion that never happened. Where the camera or lidar fails outright, that filtered estimate is what carries the robot through, and a pipeline built this way degrades instead of breaking.
Which is why the robust arrangement is rarely one of them alone: feed wheel odometry in as guess_frame_id and the registration starts near the answer every frame. That covers the camera’s fast-motion and blank-wall failures and the lidar’s degenerate-corridor failure, while the camera or lidar in turn corrects the wheels’ drift. See Feeding in an external guess.
For 2D indoor odometry a lidar usually costs less computation. Registering a few hundred scan points is far less CPU than detecting, describing and matching visual features on every frame, and it needs no GPU — which is what decides whether odometry keeps up on the small onboard computers these robots carry.
With both a camera and a lidar, the usual arrangement is icp_odometry for the pose and the camera for appearance — see Combining a camera and a lidar.
Library
The package installs a C++ library, documented in the C++ API reference generated from the headers.
OdometryROS is the base class all three nodes derive from, and it is where most of this package’s behaviour actually lives. It owns the RTAB-Map Odometry object, the pose integration, the TF broadcast, the IMU intake, the services and the diagnostics. Each node subclasses it to do one thing: turn its own topics into a rtabmap::SensorData and hand it over. That is why the three nodes share nearly all of their parameters and publish exactly the same topics.
It also runs the registration on its own thread. A frame arriving while the previous one is still being processed does not block the subscription callback; see Update rates and dropped frames.
Conventions
These apply to all three nodes.
Frames and TF
| Parameter | Type | Default | Description |
|---|---|---|---|
frame_id |
string |
"base_link" |
The robot frame being tracked. The pose published is this frame’s, not the sensor’s — the sensor-to-robot transform is read from TF. |
odom_frame_id |
string |
"odom" |
The fixed frame the pose is expressed in. |
publish_tf |
bool |
true |
Broadcast the pose on TF. Turn this off if something else already publishes that transform, or the two fight and TF alternates between them. What exactly is broadcast depends on guess_frame_id: without it, odom_frame_id → frame_id; with it, a correction odom_frame_id → guess_frame_id (why). |
wait_for_transform |
double |
0.1 |
Seconds to wait for a needed transform before giving up on the frame. |
initial_pose |
string |
"" |
Starting pose, "x y z roll pitch yaw". Also settable at runtime through reset_odom_to_pose. |
ground_truth_frame_id |
string |
"" |
When set, the pose is taken from this TF instead of being computed — for replaying a dataset with a known trajectory. |
ground_truth_base_frame_id |
string |
value of frame_id
|
The robot frame within the ground truth TF tree. |
guess_frame_id |
string |
"" |
A frame carrying another odometry source, used as the initial guess for each registration. Documented with its companions under Feeding in an external guess – the highest-value parameter here for a wheeled robot. |
The sensor must be connected to frame_id in TF before the first frame arrives, or that frame is dropped with a warning. A static publisher is the usual answer.
RTAB-Map’s own parameters
Everything in RTAB-Map’s odometry parameter set is exposed as a ROS parameter under its RTAB-Map name, so tuning is done directly:
ros2 run rtabmap_odom rgbd_odometry --ros-args \
-p "Odom/Strategy:='1'" \
-p "Vis/MinInliers:='15'" \
-p "Odom/ResetCountdown:='1'"
Note the quoting. Every RTAB-Map parameter is declared as a string, whatever it looks like, because that is how RTAB-Map’s own parameter map stores them. Writing -p Odom/Strategy:=1 makes ROS infer an integer, and the node throws on startup rather than starting with the wrong value:
``` parameter ‘Odom/Strategy’ has invalid type: Wrong parameter type,
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged rtabmap_odom at Robotics Stack Exchange
|
rtabmap_odom package from rtabmap_ros reportabmap_conversions rtabmap_costmap_plugins rtabmap_demos rtabmap_examples rtabmap_launch rtabmap_msgs rtabmap_odom rtabmap_python rtabmap_ros rtabmap_rviz_plugins rtabmap_slam rtabmap_sync rtabmap_util rtabmap_viz |
ROS Distro
|
Package Summary
| Version | 0.23.13 |
| License | BSD |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/introlab/rtabmap_ros.git |
| VCS Type | git |
| VCS Version | humble-devel |
| Last Updated | 2026-10-01 |
| Dev Status | MAINTAINED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- Mathieu Labbe
Authors
- Mathieu Labbe
rtabmap_odom
Odometry for RTAB-Map: where the robot is relative to a local fixed frame, estimated from its own sensors — a pose that moves continuously and never jumps, but drifts over time.
SLAM needs a pose for every measurement it maps. These nodes produce one by registering each new frame against the last — visually from an RGB-D or stereo camera, or geometrically from a lidar — and integrating the result into a nav_msgs/msg/Odometry and a TF.
Contents
Nodes
| Node | Description |
|---|---|
| rgbd_odometry | Visual odometry from a color image and a depth image registered to it. |
| stereo_odometry | Visual odometry from a stereo pair. |
| icp_odometry | Geometric odometry from a 2D or 3D lidar. |
Every node is a composable node as well as a standalone executable.
Choosing a sensor modality for the environment
Which to use is a question about the environment, not about which sensor is better. A camera tracks visual texture; a lidar tracks geometry. Each fails where its own cue is missing, and the two failures do not overlap much.
| Environment | Use | Why |
|---|---|---|
| Visually textured and well lit — offices, cluttered rooms, daylight outdoors | Camera | Plenty of features to match, and appearance gives loop closure for free. |
| Textureless but geometrically rich — bare corridors with doorways and furniture, warehouse aisles | Lidar | Blank walls give a camera nothing; the shape of the space still constrains ICP. |
| Dark, or lighting that changes abruptly | Lidar | A camera is simply blind. Lidar does not care. |
| Geometrically plain but visually rich — a large open hall with a patterned floor, textured flat walls | Camera | Degenerate geometry defeats ICP here, while the texture is exactly what a camera needs. |
| Both plain and textureless — an empty warehouse, a long featureless tunnel | Wheel odometry, with either as a corrector | Neither cue is present. This is the case where wheel odometry carries the pose. |
| Repetitive and self-similar — tiled floors, rows of racking, a long colonnade | Wheel odometry as the guess, with either on top | Both cues are present but ambiguous: a camera matches the wrong copy of a feature, a lidar the wrong bay of shelving. See Repetitive patterns. |
| Outdoors at range | Stereo camera or 3D lidar | RGB-D depth stops working outdoors; both of these keep going. |
Do not underestimate wheel odometry. On a wheeled robot it is locally excellent and only drifts over distance — the opposite failure from both of the above, which are locally noisy but not systematically biased. It is also the only one of the three that keeps working when the environment offers no cue at all — and, because it is indifferent to what the scene looks like, the only one that is not fooled when the scene repeats itself.
Fuse the wheels with an IMU before feeding them in. robot_localization is the standard way: its EKF combines wheel odometry with IMU orientation and angular rates into one filtered odom topic, which is a markedly better guess than the wheels alone. FusionCore is another EKF that does the same job. The IMU fixes exactly what encoders are worst at — yaw through a turn, and wheel slip, which encoders report as motion that never happened. Where the camera or lidar fails outright, that filtered estimate is what carries the robot through, and a pipeline built this way degrades instead of breaking.
Which is why the robust arrangement is rarely one of them alone: feed wheel odometry in as guess_frame_id and the registration starts near the answer every frame. That covers the camera’s fast-motion and blank-wall failures and the lidar’s degenerate-corridor failure, while the camera or lidar in turn corrects the wheels’ drift. See Feeding in an external guess.
For 2D indoor odometry a lidar usually costs less computation. Registering a few hundred scan points is far less CPU than detecting, describing and matching visual features on every frame, and it needs no GPU — which is what decides whether odometry keeps up on the small onboard computers these robots carry.
With both a camera and a lidar, the usual arrangement is icp_odometry for the pose and the camera for appearance — see Combining a camera and a lidar.
Library
The package installs a C++ library, documented in the C++ API reference generated from the headers.
OdometryROS is the base class all three nodes derive from, and it is where most of this package’s behaviour actually lives. It owns the RTAB-Map Odometry object, the pose integration, the TF broadcast, the IMU intake, the services and the diagnostics. Each node subclasses it to do one thing: turn its own topics into a rtabmap::SensorData and hand it over. That is why the three nodes share nearly all of their parameters and publish exactly the same topics.
It also runs the registration on its own thread. A frame arriving while the previous one is still being processed does not block the subscription callback; see Update rates and dropped frames.
Conventions
These apply to all three nodes.
Frames and TF
| Parameter | Type | Default | Description |
|---|---|---|---|
frame_id |
string |
"base_link" |
The robot frame being tracked. The pose published is this frame’s, not the sensor’s — the sensor-to-robot transform is read from TF. |
odom_frame_id |
string |
"odom" |
The fixed frame the pose is expressed in. |
publish_tf |
bool |
true |
Broadcast the pose on TF. Turn this off if something else already publishes that transform, or the two fight and TF alternates between them. What exactly is broadcast depends on guess_frame_id: without it, odom_frame_id → frame_id; with it, a correction odom_frame_id → guess_frame_id (why). |
wait_for_transform |
double |
0.1 |
Seconds to wait for a needed transform before giving up on the frame. |
initial_pose |
string |
"" |
Starting pose, "x y z roll pitch yaw". Also settable at runtime through reset_odom_to_pose. |
ground_truth_frame_id |
string |
"" |
When set, the pose is taken from this TF instead of being computed — for replaying a dataset with a known trajectory. |
ground_truth_base_frame_id |
string |
value of frame_id
|
The robot frame within the ground truth TF tree. |
guess_frame_id |
string |
"" |
A frame carrying another odometry source, used as the initial guess for each registration. Documented with its companions under Feeding in an external guess – the highest-value parameter here for a wheeled robot. |
The sensor must be connected to frame_id in TF before the first frame arrives, or that frame is dropped with a warning. A static publisher is the usual answer.
RTAB-Map’s own parameters
Everything in RTAB-Map’s odometry parameter set is exposed as a ROS parameter under its RTAB-Map name, so tuning is done directly:
ros2 run rtabmap_odom rgbd_odometry --ros-args \
-p "Odom/Strategy:='1'" \
-p "Vis/MinInliers:='15'" \
-p "Odom/ResetCountdown:='1'"
Note the quoting. Every RTAB-Map parameter is declared as a string, whatever it looks like, because that is how RTAB-Map’s own parameter map stores them. Writing -p Odom/Strategy:=1 makes ROS infer an integer, and the node throws on startup rather than starting with the wrong value:
``` parameter ‘Odom/Strategy’ has invalid type: Wrong parameter type,
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged rtabmap_odom at Robotics Stack Exchange
|
rtabmap_odom package from rtabmap_ros reportabmap_conversions rtabmap_demos rtabmap_examples rtabmap_launch rtabmap_msgs rtabmap_odom rtabmap_python rtabmap_ros rtabmap_rviz_plugins rtabmap_slam rtabmap_sync rtabmap_util rtabmap_viz |
ROS Distro
|
Package Summary
| Version | 0.21.1 |
| License | BSD |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/introlab/rtabmap_ros.git |
| VCS Type | git |
| VCS Version | foxy-devel |
| Last Updated | 2023-04-09 |
| Dev Status | MAINTAINED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- Mathieu Labbe
Authors
- Mathieu Labbe
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged rtabmap_odom at Robotics Stack Exchange
|
rtabmap_odom package from rtabmap_ros reportabmap_conversions rtabmap_demos rtabmap_examples rtabmap_launch rtabmap_msgs rtabmap_odom rtabmap_python rtabmap_ros rtabmap_rviz_plugins rtabmap_slam rtabmap_sync rtabmap_util rtabmap_viz |
ROS Distro
|
Package Summary
| Version | 0.21.5 |
| License | BSD |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/introlab/rtabmap_ros.git |
| VCS Type | git |
| VCS Version | iron-devel |
| Last Updated | 2024-07-01 |
| Dev Status | MAINTAINED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- Mathieu Labbe
Authors
- Mathieu Labbe
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged rtabmap_odom at Robotics Stack Exchange
|
rtabmap_odom package from rtabmap_ros reportabmap_conversions rtabmap_costmap_plugins rtabmap_demos rtabmap_examples rtabmap_launch rtabmap_msgs rtabmap_odom rtabmap_python rtabmap_ros rtabmap_rviz_plugins rtabmap_slam rtabmap_sync rtabmap_util rtabmap_viz |
ROS Distro
|
Package Summary
| Version | 0.23.13 |
| License | BSD |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/introlab/rtabmap_ros.git |
| VCS Type | git |
| VCS Version | humble-devel |
| Last Updated | 2026-10-01 |
| Dev Status | MAINTAINED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- Mathieu Labbe
Authors
- Mathieu Labbe
rtabmap_odom
Odometry for RTAB-Map: where the robot is relative to a local fixed frame, estimated from its own sensors — a pose that moves continuously and never jumps, but drifts over time.
SLAM needs a pose for every measurement it maps. These nodes produce one by registering each new frame against the last — visually from an RGB-D or stereo camera, or geometrically from a lidar — and integrating the result into a nav_msgs/msg/Odometry and a TF.
Contents
Nodes
| Node | Description |
|---|---|
| rgbd_odometry | Visual odometry from a color image and a depth image registered to it. |
| stereo_odometry | Visual odometry from a stereo pair. |
| icp_odometry | Geometric odometry from a 2D or 3D lidar. |
Every node is a composable node as well as a standalone executable.
Choosing a sensor modality for the environment
Which to use is a question about the environment, not about which sensor is better. A camera tracks visual texture; a lidar tracks geometry. Each fails where its own cue is missing, and the two failures do not overlap much.
| Environment | Use | Why |
|---|---|---|
| Visually textured and well lit — offices, cluttered rooms, daylight outdoors | Camera | Plenty of features to match, and appearance gives loop closure for free. |
| Textureless but geometrically rich — bare corridors with doorways and furniture, warehouse aisles | Lidar | Blank walls give a camera nothing; the shape of the space still constrains ICP. |
| Dark, or lighting that changes abruptly | Lidar | A camera is simply blind. Lidar does not care. |
| Geometrically plain but visually rich — a large open hall with a patterned floor, textured flat walls | Camera | Degenerate geometry defeats ICP here, while the texture is exactly what a camera needs. |
| Both plain and textureless — an empty warehouse, a long featureless tunnel | Wheel odometry, with either as a corrector | Neither cue is present. This is the case where wheel odometry carries the pose. |
| Repetitive and self-similar — tiled floors, rows of racking, a long colonnade | Wheel odometry as the guess, with either on top | Both cues are present but ambiguous: a camera matches the wrong copy of a feature, a lidar the wrong bay of shelving. See Repetitive patterns. |
| Outdoors at range | Stereo camera or 3D lidar | RGB-D depth stops working outdoors; both of these keep going. |
Do not underestimate wheel odometry. On a wheeled robot it is locally excellent and only drifts over distance — the opposite failure from both of the above, which are locally noisy but not systematically biased. It is also the only one of the three that keeps working when the environment offers no cue at all — and, because it is indifferent to what the scene looks like, the only one that is not fooled when the scene repeats itself.
Fuse the wheels with an IMU before feeding them in. robot_localization is the standard way: its EKF combines wheel odometry with IMU orientation and angular rates into one filtered odom topic, which is a markedly better guess than the wheels alone. FusionCore is another EKF that does the same job. The IMU fixes exactly what encoders are worst at — yaw through a turn, and wheel slip, which encoders report as motion that never happened. Where the camera or lidar fails outright, that filtered estimate is what carries the robot through, and a pipeline built this way degrades instead of breaking.
Which is why the robust arrangement is rarely one of them alone: feed wheel odometry in as guess_frame_id and the registration starts near the answer every frame. That covers the camera’s fast-motion and blank-wall failures and the lidar’s degenerate-corridor failure, while the camera or lidar in turn corrects the wheels’ drift. See Feeding in an external guess.
For 2D indoor odometry a lidar usually costs less computation. Registering a few hundred scan points is far less CPU than detecting, describing and matching visual features on every frame, and it needs no GPU — which is what decides whether odometry keeps up on the small onboard computers these robots carry.
With both a camera and a lidar, the usual arrangement is icp_odometry for the pose and the camera for appearance — see Combining a camera and a lidar.
Library
The package installs a C++ library, documented in the C++ API reference generated from the headers.
OdometryROS is the base class all three nodes derive from, and it is where most of this package’s behaviour actually lives. It owns the RTAB-Map Odometry object, the pose integration, the TF broadcast, the IMU intake, the services and the diagnostics. Each node subclasses it to do one thing: turn its own topics into a rtabmap::SensorData and hand it over. That is why the three nodes share nearly all of their parameters and publish exactly the same topics.
It also runs the registration on its own thread. A frame arriving while the previous one is still being processed does not block the subscription callback; see Update rates and dropped frames.
Conventions
These apply to all three nodes.
Frames and TF
| Parameter | Type | Default | Description |
|---|---|---|---|
frame_id |
string |
"base_link" |
The robot frame being tracked. The pose published is this frame’s, not the sensor’s — the sensor-to-robot transform is read from TF. |
odom_frame_id |
string |
"odom" |
The fixed frame the pose is expressed in. |
publish_tf |
bool |
true |
Broadcast the pose on TF. Turn this off if something else already publishes that transform, or the two fight and TF alternates between them. What exactly is broadcast depends on guess_frame_id: without it, odom_frame_id → frame_id; with it, a correction odom_frame_id → guess_frame_id (why). |
wait_for_transform |
double |
0.1 |
Seconds to wait for a needed transform before giving up on the frame. |
initial_pose |
string |
"" |
Starting pose, "x y z roll pitch yaw". Also settable at runtime through reset_odom_to_pose. |
ground_truth_frame_id |
string |
"" |
When set, the pose is taken from this TF instead of being computed — for replaying a dataset with a known trajectory. |
ground_truth_base_frame_id |
string |
value of frame_id
|
The robot frame within the ground truth TF tree. |
guess_frame_id |
string |
"" |
A frame carrying another odometry source, used as the initial guess for each registration. Documented with its companions under Feeding in an external guess – the highest-value parameter here for a wheeled robot. |
The sensor must be connected to frame_id in TF before the first frame arrives, or that frame is dropped with a warning. A static publisher is the usual answer.
RTAB-Map’s own parameters
Everything in RTAB-Map’s odometry parameter set is exposed as a ROS parameter under its RTAB-Map name, so tuning is done directly:
ros2 run rtabmap_odom rgbd_odometry --ros-args \
-p "Odom/Strategy:='1'" \
-p "Vis/MinInliers:='15'" \
-p "Odom/ResetCountdown:='1'"
Note the quoting. Every RTAB-Map parameter is declared as a string, whatever it looks like, because that is how RTAB-Map’s own parameter map stores them. Writing -p Odom/Strategy:=1 makes ROS infer an integer, and the node throws on startup rather than starting with the wrong value:
``` parameter ‘Odom/Strategy’ has invalid type: Wrong parameter type,
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged rtabmap_odom at Robotics Stack Exchange
|
rtabmap_odom package from rtabmap_ros reportabmap_conversions rtabmap_costmap_plugins rtabmap_demos rtabmap_examples rtabmap_launch rtabmap_msgs rtabmap_odom rtabmap_python rtabmap_ros rtabmap_rviz_plugins rtabmap_slam rtabmap_sync rtabmap_util rtabmap_viz |
ROS Distro
|
Package Summary
| Version | 0.23.13 |
| License | BSD |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/introlab/rtabmap_ros.git |
| VCS Type | git |
| VCS Version | humble-devel |
| Last Updated | 2026-10-01 |
| Dev Status | MAINTAINED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- Mathieu Labbe
Authors
- Mathieu Labbe
rtabmap_odom
Odometry for RTAB-Map: where the robot is relative to a local fixed frame, estimated from its own sensors — a pose that moves continuously and never jumps, but drifts over time.
SLAM needs a pose for every measurement it maps. These nodes produce one by registering each new frame against the last — visually from an RGB-D or stereo camera, or geometrically from a lidar — and integrating the result into a nav_msgs/msg/Odometry and a TF.
Contents
Nodes
| Node | Description |
|---|---|
| rgbd_odometry | Visual odometry from a color image and a depth image registered to it. |
| stereo_odometry | Visual odometry from a stereo pair. |
| icp_odometry | Geometric odometry from a 2D or 3D lidar. |
Every node is a composable node as well as a standalone executable.
Choosing a sensor modality for the environment
Which to use is a question about the environment, not about which sensor is better. A camera tracks visual texture; a lidar tracks geometry. Each fails where its own cue is missing, and the two failures do not overlap much.
| Environment | Use | Why |
|---|---|---|
| Visually textured and well lit — offices, cluttered rooms, daylight outdoors | Camera | Plenty of features to match, and appearance gives loop closure for free. |
| Textureless but geometrically rich — bare corridors with doorways and furniture, warehouse aisles | Lidar | Blank walls give a camera nothing; the shape of the space still constrains ICP. |
| Dark, or lighting that changes abruptly | Lidar | A camera is simply blind. Lidar does not care. |
| Geometrically plain but visually rich — a large open hall with a patterned floor, textured flat walls | Camera | Degenerate geometry defeats ICP here, while the texture is exactly what a camera needs. |
| Both plain and textureless — an empty warehouse, a long featureless tunnel | Wheel odometry, with either as a corrector | Neither cue is present. This is the case where wheel odometry carries the pose. |
| Repetitive and self-similar — tiled floors, rows of racking, a long colonnade | Wheel odometry as the guess, with either on top | Both cues are present but ambiguous: a camera matches the wrong copy of a feature, a lidar the wrong bay of shelving. See Repetitive patterns. |
| Outdoors at range | Stereo camera or 3D lidar | RGB-D depth stops working outdoors; both of these keep going. |
Do not underestimate wheel odometry. On a wheeled robot it is locally excellent and only drifts over distance — the opposite failure from both of the above, which are locally noisy but not systematically biased. It is also the only one of the three that keeps working when the environment offers no cue at all — and, because it is indifferent to what the scene looks like, the only one that is not fooled when the scene repeats itself.
Fuse the wheels with an IMU before feeding them in. robot_localization is the standard way: its EKF combines wheel odometry with IMU orientation and angular rates into one filtered odom topic, which is a markedly better guess than the wheels alone. FusionCore is another EKF that does the same job. The IMU fixes exactly what encoders are worst at — yaw through a turn, and wheel slip, which encoders report as motion that never happened. Where the camera or lidar fails outright, that filtered estimate is what carries the robot through, and a pipeline built this way degrades instead of breaking.
Which is why the robust arrangement is rarely one of them alone: feed wheel odometry in as guess_frame_id and the registration starts near the answer every frame. That covers the camera’s fast-motion and blank-wall failures and the lidar’s degenerate-corridor failure, while the camera or lidar in turn corrects the wheels’ drift. See Feeding in an external guess.
For 2D indoor odometry a lidar usually costs less computation. Registering a few hundred scan points is far less CPU than detecting, describing and matching visual features on every frame, and it needs no GPU — which is what decides whether odometry keeps up on the small onboard computers these robots carry.
With both a camera and a lidar, the usual arrangement is icp_odometry for the pose and the camera for appearance — see Combining a camera and a lidar.
Library
The package installs a C++ library, documented in the C++ API reference generated from the headers.
OdometryROS is the base class all three nodes derive from, and it is where most of this package’s behaviour actually lives. It owns the RTAB-Map Odometry object, the pose integration, the TF broadcast, the IMU intake, the services and the diagnostics. Each node subclasses it to do one thing: turn its own topics into a rtabmap::SensorData and hand it over. That is why the three nodes share nearly all of their parameters and publish exactly the same topics.
It also runs the registration on its own thread. A frame arriving while the previous one is still being processed does not block the subscription callback; see Update rates and dropped frames.
Conventions
These apply to all three nodes.
Frames and TF
| Parameter | Type | Default | Description |
|---|---|---|---|
frame_id |
string |
"base_link" |
The robot frame being tracked. The pose published is this frame’s, not the sensor’s — the sensor-to-robot transform is read from TF. |
odom_frame_id |
string |
"odom" |
The fixed frame the pose is expressed in. |
publish_tf |
bool |
true |
Broadcast the pose on TF. Turn this off if something else already publishes that transform, or the two fight and TF alternates between them. What exactly is broadcast depends on guess_frame_id: without it, odom_frame_id → frame_id; with it, a correction odom_frame_id → guess_frame_id (why). |
wait_for_transform |
double |
0.1 |
Seconds to wait for a needed transform before giving up on the frame. |
initial_pose |
string |
"" |
Starting pose, "x y z roll pitch yaw". Also settable at runtime through reset_odom_to_pose. |
ground_truth_frame_id |
string |
"" |
When set, the pose is taken from this TF instead of being computed — for replaying a dataset with a known trajectory. |
ground_truth_base_frame_id |
string |
value of frame_id
|
The robot frame within the ground truth TF tree. |
guess_frame_id |
string |
"" |
A frame carrying another odometry source, used as the initial guess for each registration. Documented with its companions under Feeding in an external guess – the highest-value parameter here for a wheeled robot. |
The sensor must be connected to frame_id in TF before the first frame arrives, or that frame is dropped with a warning. A static publisher is the usual answer.
RTAB-Map’s own parameters
Everything in RTAB-Map’s odometry parameter set is exposed as a ROS parameter under its RTAB-Map name, so tuning is done directly:
ros2 run rtabmap_odom rgbd_odometry --ros-args \
-p "Odom/Strategy:='1'" \
-p "Vis/MinInliers:='15'" \
-p "Odom/ResetCountdown:='1'"
Note the quoting. Every RTAB-Map parameter is declared as a string, whatever it looks like, because that is how RTAB-Map’s own parameter map stores them. Writing -p Odom/Strategy:=1 makes ROS infer an integer, and the node throws on startup rather than starting with the wrong value:
``` parameter ‘Odom/Strategy’ has invalid type: Wrong parameter type,
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged rtabmap_odom at Robotics Stack Exchange
|
rtabmap_odom package from rtabmap_ros reportabmap_conversions rtabmap_costmap_plugins rtabmap_demos rtabmap_examples rtabmap_launch rtabmap_msgs rtabmap_odom rtabmap_python rtabmap_ros rtabmap_rviz_plugins rtabmap_slam rtabmap_sync rtabmap_util rtabmap_viz |
ROS Distro
|
Package Summary
| Version | 0.23.13 |
| License | BSD |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/introlab/rtabmap_ros.git |
| VCS Type | git |
| VCS Version | humble-devel |
| Last Updated | 2026-10-01 |
| Dev Status | MAINTAINED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- Mathieu Labbe
Authors
- Mathieu Labbe
rtabmap_odom
Odometry for RTAB-Map: where the robot is relative to a local fixed frame, estimated from its own sensors — a pose that moves continuously and never jumps, but drifts over time.
SLAM needs a pose for every measurement it maps. These nodes produce one by registering each new frame against the last — visually from an RGB-D or stereo camera, or geometrically from a lidar — and integrating the result into a nav_msgs/msg/Odometry and a TF.
Contents
Nodes
| Node | Description |
|---|---|
| rgbd_odometry | Visual odometry from a color image and a depth image registered to it. |
| stereo_odometry | Visual odometry from a stereo pair. |
| icp_odometry | Geometric odometry from a 2D or 3D lidar. |
Every node is a composable node as well as a standalone executable.
Choosing a sensor modality for the environment
Which to use is a question about the environment, not about which sensor is better. A camera tracks visual texture; a lidar tracks geometry. Each fails where its own cue is missing, and the two failures do not overlap much.
| Environment | Use | Why |
|---|---|---|
| Visually textured and well lit — offices, cluttered rooms, daylight outdoors | Camera | Plenty of features to match, and appearance gives loop closure for free. |
| Textureless but geometrically rich — bare corridors with doorways and furniture, warehouse aisles | Lidar | Blank walls give a camera nothing; the shape of the space still constrains ICP. |
| Dark, or lighting that changes abruptly | Lidar | A camera is simply blind. Lidar does not care. |
| Geometrically plain but visually rich — a large open hall with a patterned floor, textured flat walls | Camera | Degenerate geometry defeats ICP here, while the texture is exactly what a camera needs. |
| Both plain and textureless — an empty warehouse, a long featureless tunnel | Wheel odometry, with either as a corrector | Neither cue is present. This is the case where wheel odometry carries the pose. |
| Repetitive and self-similar — tiled floors, rows of racking, a long colonnade | Wheel odometry as the guess, with either on top | Both cues are present but ambiguous: a camera matches the wrong copy of a feature, a lidar the wrong bay of shelving. See Repetitive patterns. |
| Outdoors at range | Stereo camera or 3D lidar | RGB-D depth stops working outdoors; both of these keep going. |
Do not underestimate wheel odometry. On a wheeled robot it is locally excellent and only drifts over distance — the opposite failure from both of the above, which are locally noisy but not systematically biased. It is also the only one of the three that keeps working when the environment offers no cue at all — and, because it is indifferent to what the scene looks like, the only one that is not fooled when the scene repeats itself.
Fuse the wheels with an IMU before feeding them in. robot_localization is the standard way: its EKF combines wheel odometry with IMU orientation and angular rates into one filtered odom topic, which is a markedly better guess than the wheels alone. FusionCore is another EKF that does the same job. The IMU fixes exactly what encoders are worst at — yaw through a turn, and wheel slip, which encoders report as motion that never happened. Where the camera or lidar fails outright, that filtered estimate is what carries the robot through, and a pipeline built this way degrades instead of breaking.
Which is why the robust arrangement is rarely one of them alone: feed wheel odometry in as guess_frame_id and the registration starts near the answer every frame. That covers the camera’s fast-motion and blank-wall failures and the lidar’s degenerate-corridor failure, while the camera or lidar in turn corrects the wheels’ drift. See Feeding in an external guess.
For 2D indoor odometry a lidar usually costs less computation. Registering a few hundred scan points is far less CPU than detecting, describing and matching visual features on every frame, and it needs no GPU — which is what decides whether odometry keeps up on the small onboard computers these robots carry.
With both a camera and a lidar, the usual arrangement is icp_odometry for the pose and the camera for appearance — see Combining a camera and a lidar.
Library
The package installs a C++ library, documented in the C++ API reference generated from the headers.
OdometryROS is the base class all three nodes derive from, and it is where most of this package’s behaviour actually lives. It owns the RTAB-Map Odometry object, the pose integration, the TF broadcast, the IMU intake, the services and the diagnostics. Each node subclasses it to do one thing: turn its own topics into a rtabmap::SensorData and hand it over. That is why the three nodes share nearly all of their parameters and publish exactly the same topics.
It also runs the registration on its own thread. A frame arriving while the previous one is still being processed does not block the subscription callback; see Update rates and dropped frames.
Conventions
These apply to all three nodes.
Frames and TF
| Parameter | Type | Default | Description |
|---|---|---|---|
frame_id |
string |
"base_link" |
The robot frame being tracked. The pose published is this frame’s, not the sensor’s — the sensor-to-robot transform is read from TF. |
odom_frame_id |
string |
"odom" |
The fixed frame the pose is expressed in. |
publish_tf |
bool |
true |
Broadcast the pose on TF. Turn this off if something else already publishes that transform, or the two fight and TF alternates between them. What exactly is broadcast depends on guess_frame_id: without it, odom_frame_id → frame_id; with it, a correction odom_frame_id → guess_frame_id (why). |
wait_for_transform |
double |
0.1 |
Seconds to wait for a needed transform before giving up on the frame. |
initial_pose |
string |
"" |
Starting pose, "x y z roll pitch yaw". Also settable at runtime through reset_odom_to_pose. |
ground_truth_frame_id |
string |
"" |
When set, the pose is taken from this TF instead of being computed — for replaying a dataset with a known trajectory. |
ground_truth_base_frame_id |
string |
value of frame_id
|
The robot frame within the ground truth TF tree. |
guess_frame_id |
string |
"" |
A frame carrying another odometry source, used as the initial guess for each registration. Documented with its companions under Feeding in an external guess – the highest-value parameter here for a wheeled robot. |
The sensor must be connected to frame_id in TF before the first frame arrives, or that frame is dropped with a warning. A static publisher is the usual answer.
RTAB-Map’s own parameters
Everything in RTAB-Map’s odometry parameter set is exposed as a ROS parameter under its RTAB-Map name, so tuning is done directly:
ros2 run rtabmap_odom rgbd_odometry --ros-args \
-p "Odom/Strategy:='1'" \
-p "Vis/MinInliers:='15'" \
-p "Odom/ResetCountdown:='1'"
Note the quoting. Every RTAB-Map parameter is declared as a string, whatever it looks like, because that is how RTAB-Map’s own parameter map stores them. Writing -p Odom/Strategy:=1 makes ROS infer an integer, and the node throws on startup rather than starting with the wrong value:
``` parameter ‘Odom/Strategy’ has invalid type: Wrong parameter type,
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged rtabmap_odom at Robotics Stack Exchange
|
rtabmap_odom package from rtabmap_ros reportabmap_conversions rtabmap_costmap_plugins rtabmap_demos rtabmap_examples rtabmap_launch rtabmap_msgs rtabmap_odom rtabmap_python rtabmap_ros rtabmap_rviz_plugins rtabmap_slam rtabmap_sync rtabmap_util rtabmap_viz |
ROS Distro
|
Package Summary
| Version | 0.23.13 |
| License | BSD |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/introlab/rtabmap_ros.git |
| VCS Type | git |
| VCS Version | humble-devel |
| Last Updated | 2026-10-01 |
| Dev Status | MAINTAINED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- Mathieu Labbe
Authors
- Mathieu Labbe
rtabmap_odom
Odometry for RTAB-Map: where the robot is relative to a local fixed frame, estimated from its own sensors — a pose that moves continuously and never jumps, but drifts over time.
SLAM needs a pose for every measurement it maps. These nodes produce one by registering each new frame against the last — visually from an RGB-D or stereo camera, or geometrically from a lidar — and integrating the result into a nav_msgs/msg/Odometry and a TF.
Contents
Nodes
| Node | Description |
|---|---|
| rgbd_odometry | Visual odometry from a color image and a depth image registered to it. |
| stereo_odometry | Visual odometry from a stereo pair. |
| icp_odometry | Geometric odometry from a 2D or 3D lidar. |
Every node is a composable node as well as a standalone executable.
Choosing a sensor modality for the environment
Which to use is a question about the environment, not about which sensor is better. A camera tracks visual texture; a lidar tracks geometry. Each fails where its own cue is missing, and the two failures do not overlap much.
| Environment | Use | Why |
|---|---|---|
| Visually textured and well lit — offices, cluttered rooms, daylight outdoors | Camera | Plenty of features to match, and appearance gives loop closure for free. |
| Textureless but geometrically rich — bare corridors with doorways and furniture, warehouse aisles | Lidar | Blank walls give a camera nothing; the shape of the space still constrains ICP. |
| Dark, or lighting that changes abruptly | Lidar | A camera is simply blind. Lidar does not care. |
| Geometrically plain but visually rich — a large open hall with a patterned floor, textured flat walls | Camera | Degenerate geometry defeats ICP here, while the texture is exactly what a camera needs. |
| Both plain and textureless — an empty warehouse, a long featureless tunnel | Wheel odometry, with either as a corrector | Neither cue is present. This is the case where wheel odometry carries the pose. |
| Repetitive and self-similar — tiled floors, rows of racking, a long colonnade | Wheel odometry as the guess, with either on top | Both cues are present but ambiguous: a camera matches the wrong copy of a feature, a lidar the wrong bay of shelving. See Repetitive patterns. |
| Outdoors at range | Stereo camera or 3D lidar | RGB-D depth stops working outdoors; both of these keep going. |
Do not underestimate wheel odometry. On a wheeled robot it is locally excellent and only drifts over distance — the opposite failure from both of the above, which are locally noisy but not systematically biased. It is also the only one of the three that keeps working when the environment offers no cue at all — and, because it is indifferent to what the scene looks like, the only one that is not fooled when the scene repeats itself.
Fuse the wheels with an IMU before feeding them in. robot_localization is the standard way: its EKF combines wheel odometry with IMU orientation and angular rates into one filtered odom topic, which is a markedly better guess than the wheels alone. FusionCore is another EKF that does the same job. The IMU fixes exactly what encoders are worst at — yaw through a turn, and wheel slip, which encoders report as motion that never happened. Where the camera or lidar fails outright, that filtered estimate is what carries the robot through, and a pipeline built this way degrades instead of breaking.
Which is why the robust arrangement is rarely one of them alone: feed wheel odometry in as guess_frame_id and the registration starts near the answer every frame. That covers the camera’s fast-motion and blank-wall failures and the lidar’s degenerate-corridor failure, while the camera or lidar in turn corrects the wheels’ drift. See Feeding in an external guess.
For 2D indoor odometry a lidar usually costs less computation. Registering a few hundred scan points is far less CPU than detecting, describing and matching visual features on every frame, and it needs no GPU — which is what decides whether odometry keeps up on the small onboard computers these robots carry.
With both a camera and a lidar, the usual arrangement is icp_odometry for the pose and the camera for appearance — see Combining a camera and a lidar.
Library
The package installs a C++ library, documented in the C++ API reference generated from the headers.
OdometryROS is the base class all three nodes derive from, and it is where most of this package’s behaviour actually lives. It owns the RTAB-Map Odometry object, the pose integration, the TF broadcast, the IMU intake, the services and the diagnostics. Each node subclasses it to do one thing: turn its own topics into a rtabmap::SensorData and hand it over. That is why the three nodes share nearly all of their parameters and publish exactly the same topics.
It also runs the registration on its own thread. A frame arriving while the previous one is still being processed does not block the subscription callback; see Update rates and dropped frames.
Conventions
These apply to all three nodes.
Frames and TF
| Parameter | Type | Default | Description |
|---|---|---|---|
frame_id |
string |
"base_link" |
The robot frame being tracked. The pose published is this frame’s, not the sensor’s — the sensor-to-robot transform is read from TF. |
odom_frame_id |
string |
"odom" |
The fixed frame the pose is expressed in. |
publish_tf |
bool |
true |
Broadcast the pose on TF. Turn this off if something else already publishes that transform, or the two fight and TF alternates between them. What exactly is broadcast depends on guess_frame_id: without it, odom_frame_id → frame_id; with it, a correction odom_frame_id → guess_frame_id (why). |
wait_for_transform |
double |
0.1 |
Seconds to wait for a needed transform before giving up on the frame. |
initial_pose |
string |
"" |
Starting pose, "x y z roll pitch yaw". Also settable at runtime through reset_odom_to_pose. |
ground_truth_frame_id |
string |
"" |
When set, the pose is taken from this TF instead of being computed — for replaying a dataset with a known trajectory. |
ground_truth_base_frame_id |
string |
value of frame_id
|
The robot frame within the ground truth TF tree. |
guess_frame_id |
string |
"" |
A frame carrying another odometry source, used as the initial guess for each registration. Documented with its companions under Feeding in an external guess – the highest-value parameter here for a wheeled robot. |
The sensor must be connected to frame_id in TF before the first frame arrives, or that frame is dropped with a warning. A static publisher is the usual answer.
RTAB-Map’s own parameters
Everything in RTAB-Map’s odometry parameter set is exposed as a ROS parameter under its RTAB-Map name, so tuning is done directly:
ros2 run rtabmap_odom rgbd_odometry --ros-args \
-p "Odom/Strategy:='1'" \
-p "Vis/MinInliers:='15'" \
-p "Odom/ResetCountdown:='1'"
Note the quoting. Every RTAB-Map parameter is declared as a string, whatever it looks like, because that is how RTAB-Map’s own parameter map stores them. Writing -p Odom/Strategy:=1 makes ROS infer an integer, and the node throws on startup rather than starting with the wrong value:
``` parameter ‘Odom/Strategy’ has invalid type: Wrong parameter type,
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged rtabmap_odom at Robotics Stack Exchange
|
rtabmap_odom package from rtabmap_ros reportabmap_conversions rtabmap_costmap_plugins rtabmap_demos rtabmap_examples rtabmap_launch rtabmap_msgs rtabmap_odom rtabmap_python rtabmap_ros rtabmap_rviz_plugins rtabmap_slam rtabmap_sync rtabmap_util rtabmap_viz |
ROS Distro
|
Package Summary
| Version | 0.23.13 |
| License | BSD |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/introlab/rtabmap_ros.git |
| VCS Type | git |
| VCS Version | humble-devel |
| Last Updated | 2026-10-01 |
| Dev Status | MAINTAINED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- Mathieu Labbe
Authors
- Mathieu Labbe
rtabmap_odom
Odometry for RTAB-Map: where the robot is relative to a local fixed frame, estimated from its own sensors — a pose that moves continuously and never jumps, but drifts over time.
SLAM needs a pose for every measurement it maps. These nodes produce one by registering each new frame against the last — visually from an RGB-D or stereo camera, or geometrically from a lidar — and integrating the result into a nav_msgs/msg/Odometry and a TF.
Contents
Nodes
| Node | Description |
|---|---|
| rgbd_odometry | Visual odometry from a color image and a depth image registered to it. |
| stereo_odometry | Visual odometry from a stereo pair. |
| icp_odometry | Geometric odometry from a 2D or 3D lidar. |
Every node is a composable node as well as a standalone executable.
Choosing a sensor modality for the environment
Which to use is a question about the environment, not about which sensor is better. A camera tracks visual texture; a lidar tracks geometry. Each fails where its own cue is missing, and the two failures do not overlap much.
| Environment | Use | Why |
|---|---|---|
| Visually textured and well lit — offices, cluttered rooms, daylight outdoors | Camera | Plenty of features to match, and appearance gives loop closure for free. |
| Textureless but geometrically rich — bare corridors with doorways and furniture, warehouse aisles | Lidar | Blank walls give a camera nothing; the shape of the space still constrains ICP. |
| Dark, or lighting that changes abruptly | Lidar | A camera is simply blind. Lidar does not care. |
| Geometrically plain but visually rich — a large open hall with a patterned floor, textured flat walls | Camera | Degenerate geometry defeats ICP here, while the texture is exactly what a camera needs. |
| Both plain and textureless — an empty warehouse, a long featureless tunnel | Wheel odometry, with either as a corrector | Neither cue is present. This is the case where wheel odometry carries the pose. |
| Repetitive and self-similar — tiled floors, rows of racking, a long colonnade | Wheel odometry as the guess, with either on top | Both cues are present but ambiguous: a camera matches the wrong copy of a feature, a lidar the wrong bay of shelving. See Repetitive patterns. |
| Outdoors at range | Stereo camera or 3D lidar | RGB-D depth stops working outdoors; both of these keep going. |
Do not underestimate wheel odometry. On a wheeled robot it is locally excellent and only drifts over distance — the opposite failure from both of the above, which are locally noisy but not systematically biased. It is also the only one of the three that keeps working when the environment offers no cue at all — and, because it is indifferent to what the scene looks like, the only one that is not fooled when the scene repeats itself.
Fuse the wheels with an IMU before feeding them in. robot_localization is the standard way: its EKF combines wheel odometry with IMU orientation and angular rates into one filtered odom topic, which is a markedly better guess than the wheels alone. FusionCore is another EKF that does the same job. The IMU fixes exactly what encoders are worst at — yaw through a turn, and wheel slip, which encoders report as motion that never happened. Where the camera or lidar fails outright, that filtered estimate is what carries the robot through, and a pipeline built this way degrades instead of breaking.
Which is why the robust arrangement is rarely one of them alone: feed wheel odometry in as guess_frame_id and the registration starts near the answer every frame. That covers the camera’s fast-motion and blank-wall failures and the lidar’s degenerate-corridor failure, while the camera or lidar in turn corrects the wheels’ drift. See Feeding in an external guess.
For 2D indoor odometry a lidar usually costs less computation. Registering a few hundred scan points is far less CPU than detecting, describing and matching visual features on every frame, and it needs no GPU — which is what decides whether odometry keeps up on the small onboard computers these robots carry.
With both a camera and a lidar, the usual arrangement is icp_odometry for the pose and the camera for appearance — see Combining a camera and a lidar.
Library
The package installs a C++ library, documented in the C++ API reference generated from the headers.
OdometryROS is the base class all three nodes derive from, and it is where most of this package’s behaviour actually lives. It owns the RTAB-Map Odometry object, the pose integration, the TF broadcast, the IMU intake, the services and the diagnostics. Each node subclasses it to do one thing: turn its own topics into a rtabmap::SensorData and hand it over. That is why the three nodes share nearly all of their parameters and publish exactly the same topics.
It also runs the registration on its own thread. A frame arriving while the previous one is still being processed does not block the subscription callback; see Update rates and dropped frames.
Conventions
These apply to all three nodes.
Frames and TF
| Parameter | Type | Default | Description |
|---|---|---|---|
frame_id |
string |
"base_link" |
The robot frame being tracked. The pose published is this frame’s, not the sensor’s — the sensor-to-robot transform is read from TF. |
odom_frame_id |
string |
"odom" |
The fixed frame the pose is expressed in. |
publish_tf |
bool |
true |
Broadcast the pose on TF. Turn this off if something else already publishes that transform, or the two fight and TF alternates between them. What exactly is broadcast depends on guess_frame_id: without it, odom_frame_id → frame_id; with it, a correction odom_frame_id → guess_frame_id (why). |
wait_for_transform |
double |
0.1 |
Seconds to wait for a needed transform before giving up on the frame. |
initial_pose |
string |
"" |
Starting pose, "x y z roll pitch yaw". Also settable at runtime through reset_odom_to_pose. |
ground_truth_frame_id |
string |
"" |
When set, the pose is taken from this TF instead of being computed — for replaying a dataset with a known trajectory. |
ground_truth_base_frame_id |
string |
value of frame_id
|
The robot frame within the ground truth TF tree. |
guess_frame_id |
string |
"" |
A frame carrying another odometry source, used as the initial guess for each registration. Documented with its companions under Feeding in an external guess – the highest-value parameter here for a wheeled robot. |
The sensor must be connected to frame_id in TF before the first frame arrives, or that frame is dropped with a warning. A static publisher is the usual answer.
RTAB-Map’s own parameters
Everything in RTAB-Map’s odometry parameter set is exposed as a ROS parameter under its RTAB-Map name, so tuning is done directly:
ros2 run rtabmap_odom rgbd_odometry --ros-args \
-p "Odom/Strategy:='1'" \
-p "Vis/MinInliers:='15'" \
-p "Odom/ResetCountdown:='1'"
Note the quoting. Every RTAB-Map parameter is declared as a string, whatever it looks like, because that is how RTAB-Map’s own parameter map stores them. Writing -p Odom/Strategy:=1 makes ROS infer an integer, and the node throws on startup rather than starting with the wrong value:
``` parameter ‘Odom/Strategy’ has invalid type: Wrong parameter type,
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged rtabmap_odom at Robotics Stack Exchange
|
rtabmap_odom package from rtabmap_ros reportabmap_conversions rtabmap_costmap_plugins rtabmap_demos rtabmap_examples rtabmap_launch rtabmap_msgs rtabmap_odom rtabmap_python rtabmap_ros rtabmap_rviz_plugins rtabmap_slam rtabmap_sync rtabmap_util rtabmap_viz |
ROS Distro
|
Package Summary
| Version | 0.23.13 |
| License | BSD |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/introlab/rtabmap_ros.git |
| VCS Type | git |
| VCS Version | humble-devel |
| Last Updated | 2026-10-01 |
| Dev Status | MAINTAINED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- Mathieu Labbe
Authors
- Mathieu Labbe
rtabmap_odom
Odometry for RTAB-Map: where the robot is relative to a local fixed frame, estimated from its own sensors — a pose that moves continuously and never jumps, but drifts over time.
SLAM needs a pose for every measurement it maps. These nodes produce one by registering each new frame against the last — visually from an RGB-D or stereo camera, or geometrically from a lidar — and integrating the result into a nav_msgs/msg/Odometry and a TF.
Contents
Nodes
| Node | Description |
|---|---|
| rgbd_odometry | Visual odometry from a color image and a depth image registered to it. |
| stereo_odometry | Visual odometry from a stereo pair. |
| icp_odometry | Geometric odometry from a 2D or 3D lidar. |
Every node is a composable node as well as a standalone executable.
Choosing a sensor modality for the environment
Which to use is a question about the environment, not about which sensor is better. A camera tracks visual texture; a lidar tracks geometry. Each fails where its own cue is missing, and the two failures do not overlap much.
| Environment | Use | Why |
|---|---|---|
| Visually textured and well lit — offices, cluttered rooms, daylight outdoors | Camera | Plenty of features to match, and appearance gives loop closure for free. |
| Textureless but geometrically rich — bare corridors with doorways and furniture, warehouse aisles | Lidar | Blank walls give a camera nothing; the shape of the space still constrains ICP. |
| Dark, or lighting that changes abruptly | Lidar | A camera is simply blind. Lidar does not care. |
| Geometrically plain but visually rich — a large open hall with a patterned floor, textured flat walls | Camera | Degenerate geometry defeats ICP here, while the texture is exactly what a camera needs. |
| Both plain and textureless — an empty warehouse, a long featureless tunnel | Wheel odometry, with either as a corrector | Neither cue is present. This is the case where wheel odometry carries the pose. |
| Repetitive and self-similar — tiled floors, rows of racking, a long colonnade | Wheel odometry as the guess, with either on top | Both cues are present but ambiguous: a camera matches the wrong copy of a feature, a lidar the wrong bay of shelving. See Repetitive patterns. |
| Outdoors at range | Stereo camera or 3D lidar | RGB-D depth stops working outdoors; both of these keep going. |
Do not underestimate wheel odometry. On a wheeled robot it is locally excellent and only drifts over distance — the opposite failure from both of the above, which are locally noisy but not systematically biased. It is also the only one of the three that keeps working when the environment offers no cue at all — and, because it is indifferent to what the scene looks like, the only one that is not fooled when the scene repeats itself.
Fuse the wheels with an IMU before feeding them in. robot_localization is the standard way: its EKF combines wheel odometry with IMU orientation and angular rates into one filtered odom topic, which is a markedly better guess than the wheels alone. FusionCore is another EKF that does the same job. The IMU fixes exactly what encoders are worst at — yaw through a turn, and wheel slip, which encoders report as motion that never happened. Where the camera or lidar fails outright, that filtered estimate is what carries the robot through, and a pipeline built this way degrades instead of breaking.
Which is why the robust arrangement is rarely one of them alone: feed wheel odometry in as guess_frame_id and the registration starts near the answer every frame. That covers the camera’s fast-motion and blank-wall failures and the lidar’s degenerate-corridor failure, while the camera or lidar in turn corrects the wheels’ drift. See Feeding in an external guess.
For 2D indoor odometry a lidar usually costs less computation. Registering a few hundred scan points is far less CPU than detecting, describing and matching visual features on every frame, and it needs no GPU — which is what decides whether odometry keeps up on the small onboard computers these robots carry.
With both a camera and a lidar, the usual arrangement is icp_odometry for the pose and the camera for appearance — see Combining a camera and a lidar.
Library
The package installs a C++ library, documented in the C++ API reference generated from the headers.
OdometryROS is the base class all three nodes derive from, and it is where most of this package’s behaviour actually lives. It owns the RTAB-Map Odometry object, the pose integration, the TF broadcast, the IMU intake, the services and the diagnostics. Each node subclasses it to do one thing: turn its own topics into a rtabmap::SensorData and hand it over. That is why the three nodes share nearly all of their parameters and publish exactly the same topics.
It also runs the registration on its own thread. A frame arriving while the previous one is still being processed does not block the subscription callback; see Update rates and dropped frames.
Conventions
These apply to all three nodes.
Frames and TF
| Parameter | Type | Default | Description |
|---|---|---|---|
frame_id |
string |
"base_link" |
The robot frame being tracked. The pose published is this frame’s, not the sensor’s — the sensor-to-robot transform is read from TF. |
odom_frame_id |
string |
"odom" |
The fixed frame the pose is expressed in. |
publish_tf |
bool |
true |
Broadcast the pose on TF. Turn this off if something else already publishes that transform, or the two fight and TF alternates between them. What exactly is broadcast depends on guess_frame_id: without it, odom_frame_id → frame_id; with it, a correction odom_frame_id → guess_frame_id (why). |
wait_for_transform |
double |
0.1 |
Seconds to wait for a needed transform before giving up on the frame. |
initial_pose |
string |
"" |
Starting pose, "x y z roll pitch yaw". Also settable at runtime through reset_odom_to_pose. |
ground_truth_frame_id |
string |
"" |
When set, the pose is taken from this TF instead of being computed — for replaying a dataset with a known trajectory. |
ground_truth_base_frame_id |
string |
value of frame_id
|
The robot frame within the ground truth TF tree. |
guess_frame_id |
string |
"" |
A frame carrying another odometry source, used as the initial guess for each registration. Documented with its companions under Feeding in an external guess – the highest-value parameter here for a wheeled robot. |
The sensor must be connected to frame_id in TF before the first frame arrives, or that frame is dropped with a warning. A static publisher is the usual answer.
RTAB-Map’s own parameters
Everything in RTAB-Map’s odometry parameter set is exposed as a ROS parameter under its RTAB-Map name, so tuning is done directly:
ros2 run rtabmap_odom rgbd_odometry --ros-args \
-p "Odom/Strategy:='1'" \
-p "Vis/MinInliers:='15'" \
-p "Odom/ResetCountdown:='1'"
Note the quoting. Every RTAB-Map parameter is declared as a string, whatever it looks like, because that is how RTAB-Map’s own parameter map stores them. Writing -p Odom/Strategy:=1 makes ROS infer an integer, and the node throws on startup rather than starting with the wrong value:
``` parameter ‘Odom/Strategy’ has invalid type: Wrong parameter type,
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged rtabmap_odom at Robotics Stack Exchange
|
rtabmap_odom package from rtabmap_ros reportabmap_conversions rtabmap_costmap_plugins rtabmap_demos rtabmap_examples rtabmap_launch rtabmap_legacy rtabmap_msgs rtabmap_odom rtabmap_python rtabmap_ros rtabmap_rviz_plugins rtabmap_slam rtabmap_sync rtabmap_util rtabmap_viz |
ROS Distro
|
Package Summary
| Version | 0.21.13 |
| License | BSD |
| Build type | CATKIN |
| Use | RECOMMENDED |
Repository Summary
| Checkout URI | https://github.com/introlab/rtabmap_ros.git |
| VCS Type | git |
| VCS Version | noetic-devel |
| Last Updated | 2025-04-27 |
| Dev Status | MAINTAINED |
| Released | RELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Additional Links
Maintainers
- Mathieu Labbe
Authors
- Mathieu Labbe