In this work we built a differential drive robot with online asynchronous SLAM and autonomous navigation with ROS2 and Gazebo.
The system is split into three independent launch domains that communicate via ROS2 topics, services, and actions:
- Simulation & Bringup — spawns the robot in Gazebo, runs the differential drive controller, bridges Gazebo sensors to ROS topics, and arbitrates velocity commands.
- SLAM — builds an occupancy grid from LiDAR scans and publishes the map.
- Navigation (Nav2) — plans and executes paths based on the map and goal poses.
- ROS2 Jazzy (other versions may require adaptations)
- slam_toolbox
- nav2
- colcon
- Build the workspace:
colcon build --symlink-install- Source the setup files:
source /opt/ros/jazzy/setup.bash
source install/setup.bashLaunch the simulation with a custom world:
ros2 launch my_bot launch_sim.launch.py world:=src/my_bot/worlds/obstacles.worldStart the online asynchronous SLAM node:
ros2 launch slam_toolbox online_async_launch.py \
use_sim_time:=true \
slam_params_file:=./src/my_bot/config/mapper_params_online_async.yamlOpen RViz:
rviz2 -d src/my_bot/config/drive.rvizIn RViz, click "2D Pose Estimate" to provide an initial pose estimate for localization.
Start the Nav2 navigation stack:
ros2 launch nav2_bringup navigation_launch.py use_sim_time:=trueYou can now set navigation goals in RViz.
| Name | Type | Publisher → Subscriber |
|---|---|---|
/scan |
sensor_msgs/LaserScan |
Gazebo GPU LiDAR (via ros_gz_bridge) → SLAM, Nav2 costmaps, RViz |
/camera/image_raw, /camera/camera_info |
sensor_msgs/Image, CameraInfo |
Gazebo camera (via ros_gz_bridge) → RViz display |
/joint_states |
sensor_msgs/JointState |
Joint state broadcaster → Robot State Publisher |
/cmd_vel |
geometry_msgs/Twist |
RViz teleop panel / keyboard / Nav2 → twist_mux → twist_stamper |
/diff_cont/cmd_vel |
geometry_msgs/TwistStamped |
twist_stamper → differential drive controller |
/odom |
nav_msgs/Odometry |
Differential drive controller → SLAM, Nav2, RViz |
/tf, /tf_static |
tf2_msgs/TFMessage |
Diff controller (odom→base_link), RSP (fixed frames), SLAM (map→odom) → all nodes |
/map |
nav_msgs/OccupancyGrid |
SLAM → Nav2, RViz |
/map_updates |
map_msgs/OccupancyGridUpdate |
SLAM → RViz display |
/initialpose |
geometry_msgs/PoseWithCovarianceStamped |
RViz "2D Pose Estimate" → SLAM |
/goal_pose |
geometry_msgs/PoseStamped |
RViz "2D Pose Goal" → Nav2 action server |
navigate_to_pose |
nav2_msgs/NavigateToPose (action) |
RViz goal tool / client → Nav2 bt_navigator |
This project is inspired by the excellent ROS2 robot tutorial series by Josh Newans. See the full playlist for more details.
