[Documentation] [TitleIndex] [WordIndex

Note: This tutorial assumes that you have completed the previous tutorials: RGB-D Hand-Held Mapping With a Kinect.
(!) Please ask about problems and questions regarding this tutorial on answers.ros.org. Don't forget to include in your question the link to this page, the versions of your OS & ROS, and also add appropriate tags.

Setup RTAB-Map on Your Robot!

Description: This tutorial shows multiple RTAB-Map configurations that can be used on your robot.

Tutorial Level: INTERMEDIATE

Next Tutorial: Mapping and Navigation with Turtlebot

Introduction

The robot must be equipped at least with a Kinect-like sensor. I recommend highly to calibrate your Kinect-like sensor following this guide. If you want to use a 2D laser, the Kinect's clouds must be aligned with the laser scans.

I will present in the next sections some possible configurations depending on the robot with example launch files.

Recommended robot configuration:

These examples are based on what I did for AZIMUT3. The robot is equipped with a Kinect, an URG-04LX and odometry is provided using the wheel encoders.

AZIMUT3

Bring-up your robot

Kinect + Odometry + 2D laser

In this configuration, I assume that you have a wheeled robot constrained to the plane XY with only rotation on yaw (around z-axis).

Kinect + Odometry + 2D laser

<launch>
  <group ns="rtabmap">

    <!-- Use RGBD synchronization -->
    <!-- Here is a general example using a standalone nodelet, 
         but it is recommended to attach this nodelet to nodelet 
         manager of the camera to avoid topic serialization -->
    <node pkg="nodelet" type="nodelet" name="rgbd_sync" args="standalone rtabmap_ros/rgbd_sync" output="screen">
      <remap from="rgb/image"       to="/camera/rgb/image_rect_color"/>
      <remap from="depth/image"     to="/camera/depth_registered/image_raw"/>
      <remap from="rgb/camera_info" to="/camera/rgb/camera_info"/>
      <remap from="rgbd_image"      to="rgbd_image"/> <!-- output -->
      
      <!-- Should be true for not synchronized camera topics 
           (e.g., false for kinectv2, zed, realsense, true for xtion, kinect360)-->
      <param name="approx_sync"       value="true"/> 
    </node>

    <node name="rtabmap" pkg="rtabmap_ros" type="rtabmap" output="screen" args="--delete_db_on_start">
          <param name="frame_id" type="string" value="base_link"/>

          <param name="subscribe_depth" type="bool" value="false"/>
          <param name="subscribe_rgbd" type="bool" value="true"/>
          <param name="subscribe_scan" type="bool" value="true"/>

          <remap from="odom" to="/base_controller/odom"/>
          <remap from="scan" to="/base_scan"/>
          <remap from="rgbd_image" to="rgbd_image"/>

          <param name="queue_size" type="int" value="10"/>

          <!-- RTAB-Map's parameters -->
          <param name="RGBD/NeighborLinkRefining" type="string" value="true"/>
          <param name="RGBD/ProximityBySpace"     type="string" value="true"/>
          <param name="RGBD/AngularUpdate"        type="string" value="0.01"/>
          <param name="RGBD/LinearUpdate"         type="string" value="0.01"/>
          <param name="RGBD/OptimizeFromGraphEnd" type="string" value="false"/>
          <param name="Grid/FromDepth"            type="string" value="false"/> <!-- occupancy grid from lidar -->
          <param name="Reg/Force3DoF"             type="string" value="true"/>
          <param name="Reg/Strategy"              type="string" value="1"/> <!-- 1=ICP -->
          
          <!-- ICP parameters -->
          <param name="Icp/VoxelSize"                 type="string" value="0.05"/>
          <param name="Icp/MaxCorrespondenceDistance" type="string" value="0.1"/>
    </node>
  </group>
</launch>

Let's break down this launch file:

 <group ns="rtabmap">

For convenience, we put rtabmap node in rtabmap namespace. rtabmap node provides services which can conflict with other services from other nodes.

<node pkg="nodelet" type="nodelet" name="rgbd_sync" args="standalone rtabmap_ros/rgbd_sync" output="screen">
  <remap from="rgb/image"        to="/camera/rgb/image_rect_color"/>
  <remap from="depth/image"      to="/camera/depth_registered/image_raw"/>
  <remap from="rgb/camera_info"  to="/camera/rgb/camera_info"/>
  <remap from="rgbd_image"       to="rgbd_image"/> 

  <param name="approx_sync"       value="true"/> 
</node>

In this example, because rtabmap node synchronizes topics coming from different sensors, we use rgbd_sync nodelet to make sure that our image topics are correctly synchronized together before feeding them to rtabmap. The output rgbd_image is a rtabmap_ros/RGBDImage message. The parameter approx_sync should be true when camera topics are not already synchronized by the camera node like here for freenect or openni2 drivers for Kinect For Xbox 360. approx_sync should be false with camera drivers for Kinect v2, ZED or realsense as they publish rgb and depth topics already synchronized (same stamp).

<node name="rtabmap" pkg="rtabmap_ros" type="rtabmap" output="screen" args="--delete_db_on_start">

The --delete_db_on_start argument will make rtabmap to delete the database (default located in ~/.ros/rtabmap.db) when starting. If you want the robot to continue mapping from a previous mapping session, you should remove --delete_db_on_start.

<param name="frame_id" type="string" value="base_link"/>

The "frame_id" should be a fixed frame on the robot.

<param name="subscribe_depth" type="bool" value="true"/>
<param name="subscribe_scan" type="bool" value="true"/>

By default, subscribe_depth is true. However, in this setup, we will use RGB-D image input instead, so subscribe_depth is set to false and subscribe_rgbd is set to true. Since we have a 2D lidar, set subscribe_scan to true. If we have a 3D lidar publishing sensor_msgs/PointCloud2 messages, set subscribe_scan_cloud to true instead and remap corresponding scan_cloud topic instead of scan.

<remap from="odom" to="/base_controller/odom"/>
<remap from="scan" to="/base_scan"/>
<remap from="rgbd_image" to="rgbd_image"/>

Set the required input topics. Note that rgbd_image doesn't have leading slash, which means it subscribe to rgbd_image in its namespace, which would be /rtabmap/rgbd_image in this case.

<param name="queue_size" type="int" value="10"/>

Used for synchronization of the input topics above. The rtabmap node synchronizes /base_controller/odom, /base_scan and /rtabmap/rgbd_image in a single callback. Higher the value, more flexible can be the rate used for each topic. On AZIMUT3, /base_controller/odom is published at 50 Hz, /base_scan at 10 Hz, and the images at 30 Hz.

<!-- RTAB-Map's parameters -->
<param name="RGBD/NeighborLinkRefining" type="string" value="true"/>
<param name="RGBD/ProximityBySpace"     type="string" value="true"/>
<param name="RGBD/AngularUpdate"        type="string" value="0.01"/>
<param name="RGBD/LinearUpdate"         type="string" value="0.01"/>
<param name="RGBD/OptimizeFromGraphEnd" type="string" value="false"/>
<param name="Grid/FromDepth"            type="string" value="false"/> 
<param name="Reg/Force3DoF"             type="string" value="true"/>
<param name="Reg/Strategy"              type="string" value="1"/> <!-- 1=ICP -->

<!-- ICP parameters -->
<param name="Icp/VoxelSize"                 type="string" value="0.05"/>
<param name="Icp/MaxCorrespondenceDistance" type="string" value="0.1"/>

This section sets the RTAB-Map's parameters (the same as in Preferences dialog in the standalone version). To know all RTAB-Map's parameters that can be set with some descriptions, execute this command:

Here is a brief overview of the main parameters set here:

  </node>
</group>

Close the group.

Kinect + Odometry + Fake 2D laser from Kinect

Kinect + Odometry + Fake 2D laser from Kinect

If you don't have a laser and you want to create a 2D occupancy grid map (for which laser scans are required), you can simulate a 2D laser with the depth image from the Kinect using depthimage_to_laserscan ros-pkg. This was the configuration used for the IROS 2014 Kinect Contest. This could be also used with a robot like the TurtleBot. Unlike with a real lidar on example above, I don't recommend setting Reg/Strategy to 1 (for ICP) because the field of view of the camera is too small to have good ICP registrations.

<launch>
  <!-- Kinect cloud to laser scan -->
    <node pkg="depthimage_to_laserscan" type="depthimage_to_laserscan" name="depthimage_to_laserscan">
      <remap from="image"       to="/camera/depth_registered/image_raw"/>
      <remap from="camera_info" to="/camera/depth_registered/camera_info"/>
      <remap from="scan"        to="/kinect_scan"/>
      <param name="range_max" type="double" value="4"/>
    </node>

  <!-- SLAM -->
  <group ns="rtabmap">
    <node name="rtabmap" pkg="rtabmap_ros" type="rtabmap" output="screen" args="--delete_db_on_start">
          <param name="frame_id" type="string" value="base_footprint"/>

          <param name="subscribe_depth" type="bool" value="true"/>
          <param name="subscribe_scan" type="bool" value="true"/>

          <remap from="odom" to="/base_controller/odom"/>
          <remap from="scan" to="/kinect_scan"/>

          <remap from="rgb/image"       to="/camera/rgb/image_rect_color"/>
          <remap from="depth/image"     to="/camera/depth_registered/image_raw"/>
          <remap from="rgb/camera_info" to="/camera/rgb/camera_info"/>

          <param name="queue_size" type="int" value="10"/>

          <!-- RTAB-Map's parameters -->
          <param name="RGBD/ProximityBySpace"     type="string" value="false"/>
          <param name="RGBD/AngularUpdate"        type="string" value="0.01"/>
          <param name="RGBD/LinearUpdate"         type="string" value="0.01"/>
          <param name="RGBD/OptimizeFromGraphEnd" type="string" value="false"/>
          <param name="Reg/Force3DoF"             type="string" value="true"/>
          <param name="Vis/MinInliers"            type="string" value="12"/>
    </node>
  </group>
</launch>

Kinect + 2D laser

Kinect + 2D laser

In you don't have odometry, you can create one using the 2D laser scans and ICP. Once you have this new odometry node, you can do the same steps as above (with odometry). To generate odometry from laser scans, take a look at these packages: laser_scan_matcher or hector_slam. NEW RTAB-Map has now its own icp_odometry node.

Here is an example on how hector_slam can be integrated with rtabmap: demo_hector_mapping.launch. you can try it with this bag: demo_mapping.bag (you should remove the "/odom" tf from the bag first) :

$ rosbag filter demo_mapping.bag demo_mapping_no_odom.bag 'topic != "/tf" or topic == "/tf" and m.transforms[0].header.frame_id != "/odom"'
$ roslaunch rtabmap_ros demo_hector_mapping.launch hector:=true
$ rosbag play --clock demo_mapping_no_odom.bag

For an example using rtabmap_ros/icp_odometry, set hector argument to false above.

Kinect + Odometry

Kinect + Odometry

In this configuration, I assume that you have a robot not constrained to a single plane (like UAV), which can move in XYZ and roll, pitch, yaw rotations.

<launch>
  <group ns="rtabmap">

    <node pkg="nodelet" type="nodelet" name="rgbd_sync" args="standalone rtabmap_ros/rgbd_sync" output="screen">
      <remap from="rgb/image"       to="/camera/rgb/image_rect_color"/>
      <remap from="depth/image"     to="/camera/depth_registered/image_raw"/>
      <remap from="rgb/camera_info" to="/camera/rgb/camera_info"/>
      <remap from="rgbd_image"      to="rgbd_image"/> <!-- output -->
      
      <!-- Should be true for not synchronized camera topics 
           (e.g., false for kinectv2, zed, realsense, true for xtion, kinect360)-->
      <param name="approx_sync"       value="true"/> 
    </node>

    <node name="rtabmap" pkg="rtabmap_ros" type="rtabmap" output="screen" args="--delete_db_on_start">
          <param name="frame_id" type="string" value="base_link"/>

          <param name="subscribe_depth" type="bool" value="false"/>
          <param name="subscribe_rgbd" type="bool" value="true"/>

          <remap from="odom" to="/base_controller/odom"/>

          <remap from="rgbd_image" to="rgbd_image"/>

          <param name="queue_size" type="int" value="10"/>

          <!-- RTAB-Map's parameters -->
          <param name="RGBD/AngularUpdate"        type="string" value="0.01"/>
          <param name="RGBD/LinearUpdate"         type="string" value="0.01"/>
          <param name="RGBD/OptimizeFromGraphEnd" type="string" value="false"/>
    </node>
  </group>
</launch>

Kinect

Kinect

If you can't have a reliable odometry, you can map using only RTAB-Map at the cost of "lost odometry" (like the RED screens in the standalone version). The robot may detect this "lost" state when a null odometry message is sent by the rgbd_odometry node.

<launch>
  <group ns="rtabmap">

    <node pkg="nodelet" type="nodelet" name="rgbd_sync" args="standalone rtabmap_ros/rgbd_sync" output="screen">
      <remap from="rgb/image"        to="/camera/rgb/image_rect_color"/>
      <remap from="depth/image"      to="/camera/depth_registered/image_raw"/>
      <remap from="rgb/camera_info"  to="/camera/rgb/camera_info"/>
      <remap from="rgbd_image"       to="rgbd_image"/> <!-- output -->
      
      <!-- Should be true for not synchronized camera topics 
           (e.g., false for kinectv2, zed, realsense, true for xtion, kinect360)-->
      <param name="approx_sync"       value="true"/> 
    </node>

    <!-- Odometry -->
    <node pkg="rtabmap_ros" type="rgbd_odometry" name="rgbd_odometry" output="screen">
      <param name="subscribe_rgbd" type="bool"   value="true"/>
      <param name="frame_id"       type="string" value="base_link"/>
      <remap from="rgbd_image" to="rgbd_image"/>
    </node>

    <node name="rtabmap" pkg="rtabmap_ros" type="rtabmap" output="screen" args="--delete_db_on_start">
          <param name="frame_id"        type="string" value="base_link"/>
          <param name="subscribe_depth" type="bool"   value="false"/>
          <param name="subscribe_rgbd"  type="bool"   value="true"/>

          <remap from="odom"       to="odom"/>
          <remap from="rgbd_image" to="rgbd_image"/>

          <param name="queue_size"  type="int"  value="10"/>
          <param name="approx_sync" type="bool" value="false"/>

          <!-- RTAB-Map's parameters -->
          <param name="RGBD/AngularUpdate"        type="string" value="0.01"/>
          <param name="RGBD/LinearUpdate"         type="string" value="0.01"/>
          <param name="RGBD/OptimizeFromGraphEnd" type="string" value="false"/>
    </node>
  </group>
</launch>
Note that we set `approx_sync` to false for `rtabmap` node to make sure it uses exactly the `odom` computed with the same `rgbd_image` topic.

Stereo A

Stereo A

RTAB-Map can be also used with a stereo camera. As shown in the picture above, you will need to install viso2_ros and the stereo_image_proc nodes.

<launch>
  <!-- Run the ROS package stereo_image_proc for image rectification and disparity computation -->
  <group ns="stereo">
    <node pkg="stereo_image_proc" type="stereo_image_proc" name="stereo_image_proc"/>

    <!-- Disparity to depth -->
    <node pkg="nodelet" type="nodelet" name="disparity2depth" args="standalone rtabmap_ros/disparity_to_depth"/>
  </group>

  <!-- Odometry: Run the viso2_ros package -->
  <node pkg="viso2_ros" type="stereo_odometer" name="stereo_odometer" output="screen">
    <remap from="stereo" to="stereo"/>
    <remap from="image"  to="image_rect"/>
    <param name="base_link_frame_id"      value="/base_link"/>
    <param name="odom_frame_id"           value="/odom"/>
    <param name="ref_frame_change_method" value="1"/>
  </node>

  <group ns="rtabmap">
    <!-- Visual SLAM -->
    <node name="rtabmap" pkg="rtabmap_ros" type="rtabmap" output="screen" args="--delete_db_on_start">
      <param name="subscribe_depth"     type="bool" value="true"/>
      <param name="subscribe_laserScan" type="bool" value="false"/>

      <remap from="rgb/image"       to="/stereo/left/image_rect"/>
      <remap from="rgb/camera_info" to="/stereo/left/camera_info"/>
      <remap from="depth/image"     to="/stereo/depth"/>

      <remap from="odom" to="/stereo_odometer/odometry"/>

      <param name="frame_id"    type="string" value="/base_link"/>
      <param name="queue_size"  type="int"    value="30"/>
      <param name="approx_sync" type="bool"   value="false"/>

      <param name="Vis/MinInliers" type="string" value="12"/>
    </node>
  </group>
</launch>
Note that we set `approx_sync` to false for `rtabmap` node to make sure it uses exactly the `odom` computed with the same image topics.

Stereo B

Stereo B

The page page StereoOutdoorMapping shows a working demonstration that you can try with the provided rosbag. For actual mapping with your stereo camera, you camera driver should publish image messages like these (the camera namespace can be different than stereo_camera):

$ rostopic list
/stereo_camera/left/image_raw
/stereo_camera/left/camera_info
/stereo_camera/right/image_raw
/stereo_camera/right/camera_info

and assuming that the camera driver provides TF stereo_camera and that left and right images are synchronized (note the approx_sync set to false), the corresponding launch file could be like this:

<launch>
<arg name="pi/2" value="1.5707963267948966" />
<arg name="optical_rotate" value="0 0 0 -$(arg pi/2) 0 -$(arg pi/2)" />
<node pkg="tf" type="static_transform_publisher" name="camera_base_link"
     args="$(arg optical_rotate) base_link stereo_camera 100" />  

<!-- Run the ROS package stereo_image_proc -->
<group ns="/stereo_camera" >
    <node pkg="stereo_image_proc" type="stereo_image_proc" name="stereo_image_proc"/>

    <!-- Odometry -->
    <node pkg="rtabmap_ros" type="stereo_odometry" name="stereo_odometry" output="screen">
        <remap from="left/image_rect"       to="left/image_rect"/>
        <remap from="right/image_rect"      to="right/image_rect"/>
        <remap from="left/camera_info"      to="left/camera_info"/>
        <remap from="right/camera_info"     to="right/camera_info"/>

        <param name="frame_id" type="string" value="base_link"/>
        <param name="odom_frame_id" type="string" value="odom"/>
        <param name="approx_sync"   type="bool" value="false"/>
        <param name="queue_size"    type="int" value="5"/>

        <param name="Odom/MinInliers" type="string" value="12"/>
        <param name="Odom/RoiRatios"  type="string" value="0.03 0.03 0.04 0.04"/>
    </node>     
</group>

<group ns="rtabmap">   
  <node name="rtabmap" pkg="rtabmap_ros" type="rtabmap" output="screen" args="--delete_db_on_start">
     <param name="frame_id" type="string" value="base_link"/>
     <param name="subscribe_stereo" type="bool" value="true"/>
     <param name="subscribe_depth"  type="bool" value="false"/>
     <param name="approx_sync"      type="bool" value="false"/>

     <remap from="left/image_rect"   to="/stereo_camera/left/image_rect_color"/>
     <remap from="right/image_rect"  to="/stereo_camera/right/image_rect"/>
     <remap from="left/camera_info"  to="/stereo_camera/left/camera_info"/>
     <remap from="right/camera_info" to="/stereo_camera/right/camera_info"/>

     <remap from="odom" to="/stereo_camera/odom"/>

     <param name="queue_size" type="int" value="30"/>

     <!-- RTAB-Map's parameters -->
     <param name="Vis/MinInliers" type="string" value="12"/>
  </node>
 </group>
</launch>

For visualization, I recommend to try the stereo outdoor mapping tutorial to see what is going on with rviz. To use rtabmapviz, you can add the node under rtabmap namespace above:

<group ns="rtabmap">  
  <!-- Visualisation RTAB-Map -->
  <node pkg="rtabmap_ros" type="rtabmapviz" name="rtabmapviz" args="-d $(find rtabmap_ros)/launch/config/rgbd_gui.ini" output="screen">
     <param name="subscribe_stereo"    type="bool"   value="true"/>
     <param name="subscribe_odom_info" type="bool"   value="true"/>
     <param name="queue_size"          type="int"    value="10"/>
     <param name="frame_id"            type="string" value="base_link"/>
     <remap from="left/image_rect"   to="/stereo_camera/left/image_rect_color"/>
     <remap from="right/image_rect"  to="/stereo_camera/right/image_rect"/>
     <remap from="left/camera_info"  to="/stereo_camera/left/camera_info"/>
     <remap from="right/camera_info" to="/stereo_camera/right/camera_info"/>
     <remap from="odom_info"         to="/stereo_camera/odom_info"/>
     <remap from="odom"              to="/stereo_camera/odom"/>
  </node>
</group>

The rtabmap node uses the laser scans to create a 2D occupancy grid map that can be used by a planner (see grid_map topic). If you don't have laser scans, you can create with rtabmap node with proj_map topic a 2D occupancy grid map from the projection of the Kinect or Stereo point clouds on the ground. Visit the StereoOutdoorNavigation page for an example of creating such maps.

Remote visualization

rtabmapviz

To make rtabmapviz easily communicate with rtabmap node, launch it in the same namespace than rtabmap, so that all topics and services can be directly connected without remappings.

$ export ROS_NAMESPACE=rtabmap
$ rosrun rtabmap_ros rtabmapviz _frame_id:=base_link

rviz

When RTAB-Map's ros-pkg is built, the rtabmap_ros/MapCloud plugin can be selected in RVIZ for visualization of the constructed 3D map cloud.

$ rosrun rviz rviz

Remote visualization: bandwidth efficiency with RVIZ

rviz

rviz

Use the following for rviz:

<launch>
  <node pkg="rviz" type="rviz" name="rviz"/>

  <!-- Construct and voxelize the point cloud (for fast visualization in rviz) -->
  <node pkg="nodelet" type="nodelet" name="points_xyzrgb" args="standalone rtabmap_ros/point_cloud_xyzrgb">
    <remap from="rgbd_image"      to="/rtabmap/rgbd_image/compressed"/>
    <remap from="cloud"           to="voxel_cloud" />

    <param name="queue_size" type="int" value="10"/>
    <param name="voxel_size" type="double" value="0.01"/>
  </node>
</launch>

rtabmap_ros/point_cloud_xyzrgb nodelet creates a point cloud from the RGB and depth images, with optional voxel size (0=disabled).

Remote mapping

Remote mapping

EDIT (February 4 2016): There is now a simple tutorial about remote mapping with a Kinect here: http://wiki.ros.org/rtabmap_ros/Tutorials/RemoteMapping

Switching between Mapping and Localization

It can be convenient after mapping an area to put rtabmap in localization mode to avoid increasing the map size in already mapped areas. If you are using rtabmapviz, there are already buttons on the interface:

mapping_localization_buttons

Otherwise, you can call the set_mode_mapping and set_mode_localization services.

 $ rosservice call rtabmap/set_mode_localization
 $ rosservice call rtabmap/set_mode_mapping

2019-10-19 13:11