In the previous chapter, ARCHO learned how to build a map of the warehouse. Now the next question is: given that map, how does ARCHO decide on its own where to go, how to avoid obstacles, and how to reach its destination? This is exactly what Navigation2 (Nav2) does.
In earlier chapters we saw how ros2_control sends raw commands to the motors. Nav2 sits one layer above that: instead of saying "turn the left motor by 1.2 radians," it says "you need to go to shelf 24." This is the technology behind nearly every AMR, warehouse robot, hospital robot, and delivery robot today.
Before we dive into the details, we need to separate four questions โ each belongs to a different part of Nav2:
| Question | Answered by |
|---|---|
| Where am I right now? | Localization (AMCL) |
| What does the world look like? | Map (Map Server) |
| Where is it better and safer to go? | Costmap |
| How do I get from here to my destination? | Global Planner + Local Controller |
On day one, ARCHO built a map using SLAM Toolbox. From day two onward, there's no need to rebuild the map:
Adaptive Monte Carlo Localization โ using live LiDAR data and comparing it against the saved map, it answers the question: "Where exactly on this map is the robot standing?" Unlike SLAM Toolbox, AMCL does not build a map โ it only finds the position on a map you already have.
Full Nav2 installation:
sudo apt install \
ros-jazzy-navigation2 \
ros-jazzy-nav2-bringup
Navigation2 needs four things to be correctly set up for a custom robot like ARCHO: a valid Map, TF, Odometry, and LaserScan, along with an accurate Footprint. Nav2 cannot magically fix errors from lower layers (chapters 5, 8, and 9) โ it works on top of whatever foundation you have built.
Nav2 needs to know ARCHO's exact shape and size so it can predict collisions.
# Rectangular model โ more precise view
footprint: "[[0.25, 0.18], [0.25, -0.18], [-0.25, -0.18], [-0.25, 0.18]]"
# or a simple circular model
robot_radius: 0.30
| Error | Consequence |
|---|---|
| Footprint smaller than the real robot | Actual collision with a shelf or wall |
| Footprint larger than the real robot | ARCHO cannot pass through doors and narrow aisles |
An ordinary map only says where a wall is. A Costmap is a smarter layer on top of that map, saying where it is "expensive" or dangerous to go โ even in places where there is no wall yet but it is close to one.
Nav2 has two separate Costmaps, each for a different purpose:
| Global Costmap | Local Costmap | |
|---|---|---|
| Reference frame | map | odom |
| Coverage | The entire map | A small window around the robot (e.g. 4ร4 meters) |
| Update rate | Slow (1-2 Hz) | Fast (5-10 Hz) |
| Use | Overall path planning | Instant reaction to nearby obstacles |
The Local Controller needs smooth, continuous motion. The map frame may jump slightly every time
AMCL performs a correction; but the odom frame is always continuous and jump-free (remember,
chapter 5). For moment-to-moment control, continuity matters more than absolute accuracy.
# global_costmap.yaml
global_costmap:
global_costmap:
ros__parameters:
global_frame: map
robot_base_frame: base_link
update_frequency: 2.0
publish_frequency: 1.0
resolution: 0.05
track_unknown_space: true
plugins:
- static_layer
- obstacle_layer
- inflation_layer
# local_costmap.yaml
local_costmap:
local_costmap:
ros__parameters:
global_frame: odom
robot_base_frame: base_link
rolling_window: true
width: 4.0
height: 4.0
resolution: 0.05
update_frequency: 10.0
publish_frequency: 5.0
# Obstacle Layer โ adds obstacles from LiDAR
obstacle_layer:
plugin: nav2_costmap_2d::ObstacleLayer
observation_sources: scan
scan:
topic: /scan
data_type: LaserScan
clearing: true
marking: true
max_obstacle_height: 2.0
obstacle_max_range: 3.5
raytrace_max_range: 4.0
# Inflation Layer โ draws a safety margin around obstacles
inflation_layer:
plugin: nav2_costmap_2d::InflationLayer
inflation_radius: 0.45
cost_scaling_factor: 4.0
| Concept | Meaning |
|---|---|
marking | When LiDAR sees an obstacle, record it on the Costmap |
clearing | If a space that was previously occupied is now seen as free, clear it |
inflation_radius | The radius of the safety margin around each obstacle |
If inflation_radius is too small, ARCHO hugs walls and shelves and the collision risk goes up.
If it's too large, narrow warehouse aisles appear entirely "closed" and the Planner can't find a path
through them โ even if passing through them is practically possible.
A real-life example: when you go to the airport, you make two completely different kinds of decisions.
| Overall Trip Plan | Moment-to-Moment Driving Decision | |
|---|---|---|
| Example | Home โ highway โ exit 5 โ airport | A slight turn of the wheel, braking, going around a bicycle |
| In Nav2 | Global Planner | Local Controller |
Its job is to find the best overall path from the starting point to the destination โ regardless of how many degrees the steering needs to turn. Its input is Map + Costmap + Goal, and its output is just a Path.
| Planner Plugin | Feature |
|---|---|
| NavFn | Simple and fast, based on basic graph search |
| Smac Planner | Supports A* and Hybrid A*, better suited for non-circular robots |
| Theta* | Smoother paths with fewer turns |
The Planner only gives an overall path; it doesn't say how many radians per second the left motor should turn. That's the job of the Local Controller โ which makes a decision every few milliseconds (e.g. 20-50 times per second).
| Controller | Core Idea |
|---|---|
| Pure Pursuit | Instead of the whole path, it only looks at a point a few meters ahead โ just like driving, where you don't stare at your front bumper |
| DWB (Dynamic Window Approach) | Tries dozens of combinations of linear and angular velocity and picks the best one |
| MPPI (Model Predictive Path Integral) | Simulates the future: "If I give this command, where will I be in 2 seconds?" and picks the best option among hundreds of possibilities |
If the Planner recomputed a 200-meter path every 20 milliseconds, the robot's processor would be crippled. So the Planner only runs when needed: a new destination, a blocked path, or a large obstacle. But the Controller must be constantly active because the robot is moving at every moment โ a worker walking past ARCHO doesn't require recomputing the overall path, just a small instant correction. But if a forklift blocks the entire aisle, the Controller can no longer do anything on its own and asks the Planner for help.
The warehouse management system tells ARCHO "go to shelf 24." The Planner designs the overall path and ARCHO starts moving. A worker walks past in front of it โ the Local Controller slows down and slightly adjusts the path, without the Planner even being notified. Moments later a forklift blocks the entire aisle โ this time a minor correction isn't enough, so the Controller asks the Planner for a completely new path through a different aisle.
# planner_server.yaml
planner_server:
ros__parameters:
planner_plugins:
- GridBased
GridBased:
plugin: nav2_navfn_planner::NavfnPlanner
tolerance: 0.5
use_astar: false
allow_unknown: true
# controller_server.yaml
controller_server:
ros__parameters:
controller_frequency: 20.0
controller_plugins:
- FollowPath
FollowPath:
plugin: nav2_regulated_pure_pursuit_controller::RegulatedPurePursuitController
desired_linear_vel: 0.4
lookahead_dist: 0.6
min_lookahead_dist: 0.3
max_lookahead_dist: 0.9
use_velocity_scaled_lookahead_dist: true
use_collision_detection: true
The startup order matters the first time:
| Order | What Gets Turned On |
|---|---|
| 1 | Gazebo (Chapter 7) |
| 2 | Robot + Controllers (Chapter 8) |
| 3 | Map Server |
| 4 | AMCL |
| 5 | Nav2 (Planner + Controller + Behavior Tree) |
| 6 | RViz |
In RViz:
For a full system check:
ros2 topic list
ros2 node list
ros2 lifecycle nodes
ros2 action list
ros2 control list_controllers
Navigation2 works well when the robot's TF, Odometry, Footprint, and Sensor Data have already been correctly engineered; Nav2 cannot magically fix errors in the lower layers.
ARCHO can now, in a warehouse it has already mapped, locate itself, plan an overall path, and deal with moving obstacles moment by moment. But what we haven't seen yet is how these decisions โ when to recompute the path, when to wait, when to perform recovery โ are actually coordinated. That's the topic of the next chapter.
ARCHO Project now has the archo_navigation package fully configured with Map Server, AMCL, dual Costmaps, Planner Server, and Controller Server โ and for the first time, ARCHO can navigate the warehouse completely autonomously.
In Chapter Twelve we go into the heart of Nav2's decision-making: the Behavior Tree and Lifecycle Nodes โ where you'll see that Nav2 isn't a linear program at all, but thinks more like the brain of a real robot.