Detailed content
🔦 What a LiDAR sees (LaserScan)
A 2D LiDAR It is a rotating distance sensor. It emits a beam of infrared light, measures how long it takes for the light to return (or the angle at which it returns), and records the distance. Because the head rotates several times per second, each rotation becomes a “ring” of hundreds of measurements around the robot, all at the same height. It’s like an ultrasonic sensor from module 1.4, except it points in every direction at once and measures with centimeter-level precision.
🌱 New here?
LiDAR comes from “Light Detection and Ranging”: measuring distance with light. In ROS 2, each LiDAR rotation arrives as a message of type sensor_msgs/msg/LaserScan, published on a topic that is conventionally called /scan. The message and topic are the same as in module 4.1: one node publishes, others listen.
How to read this map: each amber line is a beam, and each bright dot is a returned measurement. Notice the cyan shadow behind the box: the LiDAR only sees the first surface the beam hits. To “see” behind it, the robot has to move.
The LaserScan message doesn’t store points (x, y). It stores a list of distances and the rules for finding the angle of each one: where the scan starts, where it ends, and how many degrees separate one measurement from the next. A measurement that didn’t return appears as inf (nothing within range) or falls outside the limits range_min/range_max.
How to read this map: the row of boxes is the ranges. To draw the point, ROS 2 takes the index, calculates the angle using the cyan formula, and converts (angle, distance) into (x, y). RViz2 and slam_toolbox do this for you.
✓ A 2D LiDAR sees well
- ✓Walls, doors, tall furniture, and table legs at the height of the beam.
- ✓Corridors and corners: great for matching one scan to the next.
- ✓In the dark: it brings its own light, so it doesn’t depend on the room lighting.
✗ A 2D LiDAR can’t see
- ✗Glass and mirrors: the light passes through or reflects away.
- ✗Tabletops above the beam and objects below it (a toy, a step).
- ✗Stairs going down: the floor disappears, but the horizontal beam can’t detect that.
Why learn this
LiDAR is the most widely used sensor on indoor mobile robots, from vacuum cleaners to warehouse robots. Understanding what it measures and what it doesn’t determines whether your robot will avoid the chair or hit the glass door.
Key concepts
🔌 Connecting the LiDAR to ROS 2
The most common low-cost LiDARs for makers are the RPLIDAR C1 or A1 (Slamtec) and the LD19 (LDRobot). They all connect to a PC or Raspberry Pi 5 through a USB-to-serial adapter. The component that turns serial bytes into /scan messages is a driver: a ROS 2 package from the manufacturer that you build in your workspace, as you did with colcon in module 4.2.
How to read this map: your job is just the amber part: connect the USB and start the driver node. From /scan (cyan) onward, any ROS 2 node can use the data. The note below is the detail that most often causes problems: without the base_link → laser transform, RViz2 doesn’t know where to draw it.
Step by step with a real LiDAR
Find the port
Connect the LiDAR and run ls /dev/ttyUSB*. Usually, you’ll see /dev/ttyUSB0. Give your user account permission by adding it to the dialout group (see the block below), then log out and back into your session.
Download and build the driver
RPLIDAR: the rplidar_ros repository from Slamtec (ROS 2 branch). LD19: ldlidar_stl_ros2 from LDRobot. Clone it into ~/ros2_ws/src and build it with colcon.
Start the launch file for your model
Each driver includes a launch file for each model. The name varies between versions: list the contents of the package’s launch/ folder and use the one for your sensor.
View it in RViz2
In RViz2, set Fixed Frame = laser (or the frame used by the driver), click Add → LaserScan, and select the topic /scan.
Goal: see the LiDAR publishing on /scan. The example uses the RPLIDAR; for the LD19, change the repository and launch file. Run this in a terminal with ROS 2 sourced (<distro> = jazzy or lyrical).
# 1) permission to use the serial port (log out and back into your session afterward)
sudo usermod -aG dialout $USER
# 2) driver in the workspace (check the repository README for the ROS 2 branch)
source /opt/ros/<distro>/setup.bash
mkdir -p ~/ros2_ws/src && cd ~/ros2_ws/src
git clone https://github.com/Slamtec/rplidar_ros.git -b ros2
cd ~/ros2_ws && colcon build --symlink-install
source install/setup.bash
# 3) see which launch files the package has and start the one for your model
ls $(ros2 pkg prefix rplidar_ros)/share/rplidar_ros/launch
ros2 launch rplidar_ros rplidar_c1_launch.py serial_port:=/dev/ttyUSB0
# 4) in ANOTHER terminal: check the topic
ros2 topic hz /scan
ros2 topic echo /scan --once --field ranges
How to check
- ✓The LiDAR starts rotating and ros2 topic hz /scan shows a steady rate (close to 10 Hz for the models mentioned; check your sensor’s specifications).
- ✓O echo prints a list of numbers; place your hand near the sensor and the values on that side will drop.
- ✗“Permission denied” on the port: the dialout group membership hasn’t taken effect yet; log out of your session. Launch file not found: use the name shown by ls .
🌱 New here?
A frame (coordinate frame) is a “point of view” with its own origin and axes: the center of the robot (base_link), the LiDAR itself (laser). TF2, covered in module 4.2, stores how one frame is positioned relative to another. If your URDF already includes the LiDAR link, robot_state_publisher publishes that relationship automatically.
Why learn this
Adding a new sensor in ROS 2 always follows the same steps: port, permissions, driver, launch, topic, frame. After doing it with the LiDAR, you can connect a depth camera or IMU the same way.
Key concepts
🗺️ SLAM explained: mapping and localization at the same time
To draw the map, the robot needs to know where it was when it took each measurement. To know where it is, it needs a map. This "chicken-and-egg" problem has a name: SLAM, simultaneous localization and mapping. The solution is to estimate both together, correcting one with the other each time the LiDAR makes a new pass.
🌱 New here?
Odometry is the position calculated from the wheels’ rotation (module 2.2). It has drift: small errors add up, and after a few meters the robot "thinks" it is far from where it actually is. Scan matching is fitting a new LiDAR pass over previous ones, like puzzle pieces, to correct this drift. Loop closure is recognizing "I've been here before" and spreading the accumulated error across the entire path.
How to read this map: The amber loop (predict, fit, correct) runs all the time. The dashed cyan box only appears occasionally, when the robot returns to a known place; it is what keeps the corridor from "bending" in the final map.
In ROS 2, the standard package for SLAM with a 2D LiDAR is slam_toolbox. It stores the robot's positions as nodes in a graph, with scans attached to them; when it closes a loop, it adjusts the entire graph. The result is published on the topic /map and in the transform map → odom, which is exactly the drift correction. Older tutorials mention ROS 1 packages; use slam_toolbox with ROS 2. Cartographer is also available, but is less actively maintained.
| Font | Good at | Weak at |
|---|---|---|
| Odometry (wheels) | short term, smooth, fast | drifts; slips on carpet |
| LiDAR (matching) | corrects drift using walls | featureless, uniform corridors; glass |
| Loop closure | straightens the entire map | only if you return to a place you've seen |
Why learn this
When the map comes out crooked or "duplicated," you'll know where to look: poor odometry, sparse scans, or a loop that didn't close. Without this mental model, your only option is to change parameters in the dark.
Key concepts
🖥️ slam_toolbox in simulation
Before taking the robot into the room, map a simulated world. You have two options: use your robot from module 4.2 in Gazebo (Harmonic with Jazzy, Jetty with Lyrical), as long as it has a simulated LiDAR publishing /scan; or use the simulated TurtleBot that comes with the Nav2 examples, without setting anything up. slam_toolbox can't tell the difference: it only needs /scan, odometry, and TF.
Goal: install the packages, open TurtleBot in a simulated world with SLAM enabled, and drive with the keyboard until the map appears. Simulation launch file names vary between distros: check the "Getting Started" page at docs.nav2.org.
# install SLAM, Nav2, and the example robots (replace <distro> with jazzy or lyrical)
sudo apt update
sudo apt install ros-<distro>-slam-toolbox ros-<distro>-navigation2 ros-<distro>-nav2-bringup
sudo apt install ros-<distro>-nav2-minimal-tb* # simulated TurtleBot (check the name in the docs)
# terminal 1: TurtleBot in Gazebo + RViz2 with slam_toolbox already enabled
source /opt/ros/<distro>/setup.bash
ros2 launch nav2_bringup tb4_simulation_launch.py slam:=True headless:=False
# terminal 2: drive with the keyboard (i, j, l, comma keys)
source /opt/ros/<distro>/setup.bash
ros2 run teleop_twist_keyboard teleop_twist_keyboard
How to check
- ✓Gazebo opens with the robot; RViz2 shows the LiDAR points and a gray map that grows as you drive.
- ✓ros2 topic list terminal /map, /scan e /odom.
- ✗"Package not found" when launching? Your Nav2 may use tb3_simulation_launch.py; list $(ros2 pkg prefix nav2_bringup)/share/nav2_bringup/launch.
With your own simulated (or real) robot, slam_toolbox starts on its own. The online_async mode processes scans "as fast as it can," skipping some if the computer is busy; it's recommended for getting started and for the Raspberry Pi 5.
# with your robot already publishing /scan, /odom, and TF (for simulation: use_sim_time:=true)
source /opt/ros/<distro>/setup.bash
ros2 launch slam_toolbox online_async_launch.py use_sim_time:=true
# check the frame tree: map -> odom -> base_link -> laser
ros2 run tf2_tools view_frames
How to check: o view_frames generates a PDF with the frame tree; it needs to have map at the top, followed by odom, base_link and the LiDAR frame. On the real robot, switch to use_sim_time:=false.
💡 use_sim_time: the #1 pitfall
In simulation, the clock comes from Gazebo (topic /clock). If one node uses the PC clock and another uses the simulator clock, messages appear to be "from the future" or "too old," and TF complains. Rule: in simulation, use use_sim_time:=true on every node; on the real robot, use false on every node.
Why learn this
In simulation, you can make mistakes for free: hit a wall, redo the map, change a parameter, all without draining the battery or scratching furniture. When you switch to the real robot, the only things that change are the sources of /scan e /odom.
Key concepts
🏠 Map your home or classroom
Mapping a real place is more about "how you drive" than code. slam_toolbox does the math; you choose the path, speed, and whether to return to places you've already seen. A good map comes from a slow drive, with smooth turns, passing the same landmarks twice. At this stage, you still drive the robot yourself (teleop); autonomous navigation comes in module 4.5.
Mapping steps
Prepare the environment
Doors in their intended positions, wires and toys off the floor, people and pets informed. Close off or block access to stairs.
Start in a corner with walls
The starting point becomes the map’s origin. A place with corners gives slam_toolbox solid reference points right from the first pass.
Drive slowly and turn slowly
In teleop, lower the speed (z key) to about 0.1–0.2 m/s. Fast turns blur the map the most: the wheels slip and the odometry becomes inaccurate.
Close loops
Go around a room and finish where you started; pass through the hallway again. Watch the map “straighten out” in RViz2 when the loop closes.
Save before shutting down
With the map looking good on screen, save it (topic 6). If you shut down slam_toolbox first, the map will be lost.
✓ Do
- ✓Mount the LiDAR at the highest, clearest point on the robot, with no wires crossing its beam.
- ✓Cover glass doors or mirrors with paper while mapping.
- ✓Stay within reach of the robot and the motor power switch.
✗ Don't
- ✗Mapping while people walk around: their legs show up as “walls” on the map.
- ✗Leaving the robot near a staircase going down: the 2D LiDAR can’t see the step.
- ✗Mapping a huge, empty hall: with no walls within range, there’s nothing to match against.
🌱 New here?
Don’t have the real robot yet? You can do this whole walkthrough in the simulation from topic 4, including in a classroom with a projector: one student drives, while the class watches the map and points out where the loop closed. Gazebo also lets you build a world similar to your classroom, with boxes in place of furniture.
Why learn this
The map is the foundation for everything that comes next: if it has duplicate walls or gaps, Nav2 will plan paths through walls or reject paths that are clear. Ten minutes of careful mapping can save hours of navigation tuning.
Key concepts
💾 Save and load maps
The map published by slam_toolbox is an occupancy map: the floor divided into small squares (cells) a few centimeters across, each marked as free, occupied, or unknown. Saving this map creates two files: an image .pgm (the “picture” of the cells) and a .yaml that specifies the size of each cell and the location of the origin.
How to read this map: Each small square is a cell on the map. Gray shows where the LiDAR has never reached; white is clear floor; black is a wall or piece of furniture. The black square in the middle is an isolated obstacle, such as a cabinet. With 5 cm cells, a 5 m × 4 m room becomes a 100 × 80 pixel image.
Goal: save the current map to ~/mapas, check the configuration file, and open the saved map again to use it with Nav2.
# with slam_toolbox STILL running and the map on screen
source /opt/ros/<distro>/setup.bash
mkdir -p ~/mapas && cd ~/mapas
ros2 run nav2_map_server map_saver_cli -f mapa
# check the two files that were created
ls -l mapa.pgm mapa.yaml
cat mapa.yaml
# later, to navigate using the saved map (module 4.5)
ros2 launch nav2_bringup bringup_launch.py map:=$HOME/mapas/mapa.yaml
How to check
- ✓The terminal ends with a message saying the map was saved, and you’ll see mapa.pgm e mapa.yaml.
- ✓O mapa.yaml has lines like image: mapa.pgm, resolution: 0.05 e origin: [x, y, angle].
- ✓Opening the .pgm in an image editor lets you see the floor plan in white, black, and gray.
- ✗“Failed to receive map”? slam_toolbox was shut down, or the topic /map doesn’t exist; check with ros2 topic list.
💡 Two ways to save
O map_saver_cli saves the image Nav2 uses to navigate. slam_toolbox can also save its own graph (the “Serialize Map” button in the RViz2 SlamToolboxPlugin panel): with it, you can continue mapping another day or use slam_toolbox’s own localization mode. Keep both files; you can even touch up the PGM in an image editor (paint a restricted area black).
Why learn this
Mapping from scratch every time you turn on the robot is slow and unstable. With the map saved, the robot starts up, localizes itself, and is ready to navigate—that’s exactly what Nav2 expects in the next module.
Key concepts
✅ Module summary
To go further
- slam_toolbox (GitHub) — modes, parameters, and the RViz2 plugin
- Nav2: Getting Started — installation and TurtleBot simulation for your distro
- Nav2: navigating while mapping — SLAM with Nav2 running
- ROS 2 documentation — replace lyrical with jazzy in the URL if that’s your distro
- rplidar_ros e ldlidar_stl_ros2 — drivers for the LiDARs mentioned
Next module
4.5 · Navigation with Nav2: the robot localizes itself on the saved map and drives to wherever you tell it to go.