All modules
Intermediate22 min read

SLAM: Building Maps in Real Time

How slam_toolbox builds an occupancy map from LIDAR scans, how to tune it for your space, and how to hand the map to nav2 for autonomous navigation.

What SLAM actually solves

SLAM stands for Simultaneous Localization and Mapping — the problem of building a map of an unknown environment while at the same time tracking your position within it. The tricky part: you need the map to locate yourself, but you need to know your location to build the map accurately. SLAM algorithms solve this chicken-and-egg problem probabilistically.

For mobile robots, the most practical SLAM approach is 2D LIDAR-based: a spinning LIDAR (RPLIDAR A1, YDLIDAR X4) produces a 360° scan every 100-200ms, and slam_toolbox matches each new scan against previous ones to estimate motion and build a 2D occupancy grid.

The occupancy grid

slam_toolbox outputs a nav_msgs/OccupancyGrid message on the /map topic. Each cell in the grid stores a value: 0 = free space, 100 = obstacle, -1 = unknown. At 5cm resolution (the slam_toolbox default), a typical room produces a grid of ~2000×2000 cells.

0
Free space
100
Obstacle
-1
Unknown

Tuning slam_toolbox for small spaces

Default slam_toolbox params are tuned for large open spaces. For home or office environments (room sizes 3-10m), adjust these:

yaml (slam_toolbox_params.yaml)
slam_toolbox:
  ros__parameters:
    # Resolution: 5cm for indoor, 10cm for large spaces
    resolution: 0.05

    # How far the robot must move before processing a new scan
    # Smaller = more frequent updates, higher CPU
    minimum_travel_distance: 0.05   # meters (default 0.5 — too coarse)
    minimum_travel_heading: 0.05    # radians

    # Loop closure — how aggressively to correct accumulated drift
    # Higher = better maps, more CPU
    link_match_minimum_response_fine: 0.45

    # Scan buffer: hold more scans for better matching
    scan_buffer_size: 20
    scan_buffer_maximum_scan_distance: 5.5  # match RPLIDAR A1 range

    # Pose covariance: trust odometry more if encoders are good
    transform_timeout: 0.2
    tf_buffer_duration: 30.0

The SLAM → nav2 handoff

SLAM builds the map. Once you have a good map, you switch to nav2 + AMCL (Adaptive Monte Carlo Localization) for autonomous navigation. The flow:

1
Map
Drive the robot manually while slam_toolbox builds the map.
2
Save
Call /slam_toolbox/save_map to write .yaml + .pgm files.
3
Localize
Launch nav2 + AMCL with the saved map. AMCL estimates pose from LIDAR.
4
Navigate
Send goal poses via RViz2 or the nav2 action server. Robot plans and drives autonomously.

Minimal nav2 launch with saved map

bash
# After saving map to ~/maps/room.yaml + room.pgm
ros2 launch nav2_bringup bringup_launch.py \
  map:=/home/robot/maps/room.yaml \
  use_sim_time:=false \
  params_file:=./nav2_params.yaml

# In RViz2: set initial pose with "2D Pose Estimate" tool,
# then send navigation goals with "Nav2 Goal" tool
ATTENTION — SLAM quality checklist
  • Drive slowly (≤ 0.3 m/s). Faster driving = missed scan positions = map artifacts.
  • Close loops: return to your starting point before saving. This lets slam_toolbox correct drift.
  • Avoid glass, mirrors, and doors that move during mapping.
  • Wheel odometry is optional but improves map quality significantly — add encoders if you have them.
  • Map symmetrical spaces with care: corridors that look the same from both ends confuse SLAM.
BUILD THIS

The Room-Nav kit includes RPLIDAR A1 + the full ROS2 Humble stack needed to run slam_toolbox and nav2. Everything validated on the Soohoo Labs reference build.

View Room-Nav BOM →