Lecture#
Prerequisites#
Ensure you have followed the simulation instructions and have everything set up before running any code in this lecture.
Before You Start
Remove the
log/,build/, andinstall/folders.Do a
git pull.Install dependencies:
rosdep install -i --from-path src \
--rosdistro $ROS_DISTRO --ignore-src -y \
--skip-keys "micro_ros_agent python3-ftdi"
Compile lecture 13 packages only:
colcon build --symlink-install \
--cmake-args -DCMAKE_BUILD_TYPE=Release \
--packages-up-to lecture13_meta
Source your workspace.
Verify Gazebo launches cleanly:
ros2 launch rosbot_gazebo husarion_world.launch.py
Mapping#
A map is a spatial model of the environment that a robot uses to plan paths, localize itself, and reason about the world. Before a robot can navigate autonomously, it must either be given a map or build one from sensor data.
Resources
Why Does a Robot Need a Map?#
Without a map, a robot can only react to its immediate sensor readings. A map enables:
Global path planning: computing a route from the current position to a distant goal, even when the goal is not directly visible.
Localization: answering where am I? by matching sensor readings against a known spatial model.
Semantic reasoning: knowing not just that an obstacle exists, but what it is and how to interact with it.
Multi-session operation: a persistent map lets a robot resume work in a familiar environment without re-exploring it from scratch.
Note
The choice of map representation directly determines what the robot can and cannot do. A representation that is efficient for path planning may be useless for object recognition, and vice versa.
Map Representations#
Three broad families of map representation are used in mobile robotics.
Type |
What it stores |
Typical use |
|---|---|---|
Metric |
Geometric structure of the environment at a known scale |
Path planning, obstacle avoidance |
Topological |
Graph of places and the connections between them |
High-level navigation, long-range planning |
Semantic |
Objects, regions, and their meaning |
Task planning, human-robot interaction |
Note
These families are not mutually exclusive. Modern systems often layer them: a metric map for local collision avoidance, a topological graph for room-to-room routing, and semantic labels on top for task-level reasoning.
Fig. 100 Map representations: metric, topological, and semantic.#
Fig. 101 Map representations: metric, topological, and semantic.#
Occupancy Grid Maps#
An occupancy grid map is a metric map that divides the environment into a regular grid of cells. Each cell stores a value representing the probability that the corresponding region of space is occupied by an obstacle.
Grid Parameters
An occupancy grid is defined by a few key parameters:
Resolution: the width (and height) of one cell in metres. A typical value is
0.05 m(5 cm per cell). Finer resolution gives more detail but requires more memory and computation.Width and Height: the number of cells in each dimension. A \(4000 \times 4000\) map at
0.05 m/cellcovers a \(200 \times 200\)-m area.Origin: the pose of the bottom-left corner of the map in the
mapframe, stored as ageometry_msgs/Pose.
Note
The nav_msgs/msg/OccupancyGrid message bundles all of this
together with the cell data array.
ros2 interface show nav_msgs/msg/OccupancyGrid
Cell States
Each cell in the grid can be in one of three states.
State |
Value |
Meaning |
|---|---|---|
Free |
|
The cell is known to be unoccupied. |
Occupied |
|
The cell is known to contain an obstacle. |
Unknown |
|
The cell has not been observed yet. |
Values between 0 and 100 represent intermediate probabilities
produced by probabilistic map-update algorithms such as those used by
slam_toolbox.
Note
When visualized in RViz2: free cells appear white, occupied cells appear black, and unknown cells appear grey.
Fig. 102 Occupancy grid: cell states.#
Fig. 103 Occupancy grid: cell states.#
Why Probabilities? How LiDAR Updates the Map#
Real sensors are noisy: a single reading can be a spurious reflection, a glass surface, or a moving person. A binary free/occupied decision from one scan would be fragile, so probabilistic algorithms accumulate evidence over many scans.
A LiDAR fires many rays per scan. For each ray, slam_toolbox
applies a Bayesian update (typically log-odds based) to every
cell the ray touches:
Cells the ray passes through -> probability decreases (evidence for free).
The cell where the ray terminates -> probability increases (evidence for occupied).
Cells the ray never reaches -> stay at
-1(unknown).
Example
A cell at the edge of a thin obstacle evolves across four scans (hit, hit, pass-through, hit) as \(65 \rightarrow 80 \rightarrow 70 \rightarrow 85\), stabilizing near \(80\): very likely occupied, with some sensor disagreement.
Note
Downstream consumers (e.g., Nav2 costmaps) apply a threshold to convert probabilities into planning decisions, trading off conservative vs. aggressive behavior.
The map Frame#
The map frame is the global reference frame for navigation. REP
105 defines a chain of frames that every mobile robot stack uses.
Transform |
Who publishes it and why |
|---|---|
|
Published by a higher-level mission system when a global reference is needed. Often omitted in single-robot setups. |
|
Published by the localization stack (SLAM or AMCL). Corrects accumulated drift by aligning the robot’s estimated pose with the map. |
|
Published by the robot driver (wheel encoders + IMU). Locally consistent; never jumps but drifts over time. |
|
Published by |
Important
The map \(\to\) odom transform can jump when the
localization estimate is corrected (e.g., loop closure in SLAM,
particle filter convergence in AMCL). This is intentional: the
odom frame absorbs smooth motion; the map frame absorbs
corrections.
Fig. 104 REP 105 frame chain: who publishes each transform and what it represents.#
Fig. 105 REP 105 frame chain: who publishes each transform and what it represents.#
``map`` vs. ``odom``
|
|
|
|---|---|---|
Publisher |
Robot driver |
Localization stack (SLAM/AMCL) |
Can jump? |
No |
Yes (on correction) |
Drifts? |
Yes (over time) |
No (corrected against map) |
Use for |
Short-term, smooth control |
Global planning and goal poses |
Note
Navigation goals sent to Nav2 are expressed in the map frame.
The planner plans in map. The controller executes in odom.
TF2 handles the conversion automatically.
SLAM#
SLAM (Simultaneous Localization and Mapping) solves a chicken-and-egg problem: to build a map you need to know where you are, but to know where you are you need a map. SLAM estimates both simultaneously from sensor data alone.
Note
slam_toolbox is a graph-based 2D SLAM library. It builds a
pose graph from successive LiDAR scans and corrects accumulated
drift through loop closure.
Resources
Scan Matching#
As the robot moves, the LiDAR produces a stream of scans. Each scan is a snapshot of the surrounding obstacles as seen from the robot’s current position.
Scan matching aligns two consecutive scans by finding the rigid transformation (translation + rotation) that best overlaps them.
The result is an estimate of how much the robot moved between the two scans, independently of wheel odometry.
This incremental pose estimate is much more accurate than wheel odometry alone, especially on slippery or uneven surfaces.
Fig. 106 Scan matching: the LiDAR point cloud from pose \(t_1\) is aligned with the cloud from pose \(t_2\) to recover the rigid transform between them, independently of wheel odometry.#
Fig. 107 Scan matching: the LiDAR point cloud from pose \(t_1\) is aligned with the cloud from pose \(t_2\) to recover the rigid transform between them, independently of wheel odometry.#
Pose Graph#
Every time the robot moves far enough (configured by
minimum_travel_distanceandminimum_travel_headingrelative to the last node, measured from the incomingodomestimate),slam_toolboxcreates a new node representing the robot’s pose at that moment.An edge is added between consecutive nodes. Its transform comes from scan matching, not from odometry, so the graph reflects what the LiDAR actually saw.
The pose graph is a sparse representation of the robot’s trajectory. The occupancy grid is rendered from it by stamping each node’s scan into the grid at the node’s optimized pose.
Fig. 108 A pose graph: nodes are estimated robot poses, edges encode the relative transforms between them.#
Fig. 109 A pose graph: nodes are estimated robot poses, edges encode the relative transforms between them.#
Loop Closure#
When the robot returns near a previously visited area (detected by pose proximity in the graph), the current scan is matched against the stored scan from that earlier visit.
If the scan match succeeds with high confidence, a loop-closure edge is added connecting the current node to the earlier node.
The graph is then optimized: all node poses are adjusted to minimize the total inconsistency across odometry edges, scan-match edges, and loop-closure edges. This corrects accumulated drift across the entire trajectory.
Fig. 110 Loop closure corrects accumulated drift: the loop-closure edge lets graph optimization realign the whole trajectory into a globally consistent map.#
Fig. 111 Loop closure corrects accumulated drift: the loop-closure edge lets graph optimization realign the whole trajectory into a globally consistent map.#
Note
Loop closure is why SLAM produces globally consistent maps even after long traversals. Without it, the map would shear and overlap as drift accumulates.
How the Three Pieces Connect#
Scan matching, the pose graph, and loop closure form a pipeline:
Scan matching produces the edges. It aligns consecutive LiDAR scans to estimate the relative motion (translation + rotation) between two robot poses. Each result becomes a constraint.
The covariance (uncertainty) of the scan match result determines the constraint strength. When two scans have many overlapping features and align well, the covariance is small – the optimizer trusts that edge heavily. When scans are sparse or ambiguous (e.g., a featureless corridor), the covariance is large – the optimizer treats that edge as weak and allows other constraints to override it.
The pose graph stores the structure. Each pose is a node; each scan-match result is an edge. The graph grows as the robot drives. Sequential edges accumulate drift over time.
Loop closure adds correction edges. When the robot revisits a previously seen area, scan matching is performed between the current scan and a stored scan from the earlier visit. This creates a new edge connecting two distant nodes. A graph optimizer then adjusts all node poses simultaneously to minimize the total error, correcting the accumulated drift.
Note
In short: scan matching produces local motion estimates, the pose graph accumulates them, and loop closure constrains the graph globally to fix drift.
Launching slam_toolbox#
We launch slam_toolbox alongside Gazebo and RViz2. As the robot
moves, the map grows in real time. slam_toolbox is configured via
a YAML parameter file. The most important parameters for this course:
Parameter |
Default |
Effect |
|---|---|---|
|
|
Map cell size in metres. |
|
|
Ignore LiDAR returns beyond this range. |
|
|
Minimum distance before a new node is added. |
|
|
Minimum rotation (rad) before a new node is added. |
|
|
Enable scan-to-scan matching. |
|
|
Enable loop closure. |
Note
The full parameter list is in the slam_toolbox repository
under config/mapper_params_online_async.yaml. You rarely need
to change parameters beyond resolution and laser range for typical
indoor environments.
Demonstration: Building a Map (teleop)
Command |
|
|---|---|
T1 |
|
T2 |
|
Gazebo |
Drive the robot with teleop |
RViz |
Observe the map display |
Experiment
Drive the robot fast through a narrow corridor. Does the map look accurate? Now drive slowly. What changes? Why does speed affect map quality?
Answer
SLAM quality is bounded by the worst link in the chain of odometry, sensor sweep rate, and scan matching. Speed amplifies all three error sources simultaneously.
The practical rule is: drive at a speed where the displacement between two scans is small compared to the features being mapped, and keep angular velocity especially low since rotational motion distorts LiDAR scans more severely than translation.
Map Saving and Loading#
Once a satisfactory map has been built, it is saved to disk and
reloaded later for localization-only operation. The
nav2_map_server package handles both halves: map_saver_cli
writes the grid to disk, and map_server reads the YAML at startup
and republishes the grid on /map for AMCL and the costmaps to
consume.
Saving the Map
While slam_toolbox is running, save the current map with:
ros2 run nav2_map_server map_saver_cli -f /path/to/husarion_map
This produces two files:
husarion_map.pgm: a grayscale image where each pixel corresponds to one grid cell. White = free, black = occupied, grey = unknown.husarion_map.yaml: metadata file.
image: husarion_map.pgm
mode: trinary
resolution: 0.050
origin: [-11.809, -10.197, 0]
negate: 0
occupied_thresh: 0.65
free_thresh: 0.196