ROS2 Navigation Stack (Nav2)
Nav2 architecture, costmaps, planners, AMCL localization, SLAM Toolbox — the production navigation stack used in every modern mobile robot.
What you'll build
A full Nav2 launch file for a differential-drive robot. It brings up: SLAM Toolbox for live mapping (or AMCL for known maps), the global planner, the local controller, both costmaps, behaviour trees, and a recovery server. Send a goal via RViz — the robot plans and drives to it, avoiding obstacles along the way. Production-grade.
Nav2 in one diagram
┌─────────────────────────────┐
/goal_pose ───▶ │ BehaviourTree Server │
│ (NavigateToPose action) │
└─────────┬───────────────────┘
│
┌──────────────┼──────────────┐
▼ ▼ ▼
Planner Controller Recovery
(NavfnPlanner, (DWB / MPPI) (spin, backup,
SmacPlanner) wait, abort)
│ │
▼ ▼
Global costmap Local costmap ◀── /scan, /tf
│ │
▼ ▼
/cmd_vel ──▶ robot
Five servers, one behaviour tree to orchestrate them. Each server is configurable; each can be swapped for an alternative.
The launch file
Save as bringup.launch.py:
import os
from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription, DeclareLaunchArgument
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node
from ament_index_python.packages import get_package_share_directory
def generate_launch_description():
nav2_dir = get_package_share_directory('nav2_bringup')
pkg_dir = get_package_share_directory('r2bot_navigation')
use_sim_time = LaunchConfiguration('use_sim_time', default='true')
params_file = os.path.join(pkg_dir, 'config', 'nav2_params.yaml')
map_file = os.path.join(pkg_dir, 'maps', 'warehouse.yaml')
return LaunchDescription([
DeclareLaunchArgument('use_sim_time', default_value='true'),
# SLAM Toolbox in async mode — build map live
Node(
package='slam_toolbox',
executable='async_slam_toolbox_node',
name='slam_toolbox',
output='screen',
parameters=[
{'use_sim_time': use_sim_time},
{'odom_frame': 'odom'},
{'base_frame': 'base_link'},
{'scan_topic': '/scan'},
{'resolution': 0.05},
{'max_laser_range': 12.0},
],
),
# Full Nav2 stack
IncludeLaunchDescription(
PythonLaunchDescriptionSource(os.path.join(nav2_dir, 'launch', 'bringup_launch.py')),
launch_arguments={
'map': map_file,
'use_sim_time': use_sim_time,
'params_file': params_file,
'slam': 'False', # we run our own SLAM Toolbox above
}.items(),
),
])
nav2_params.yaml — the file that matters
90% of Nav2 tuning happens in this YAML. The fields you'll actually touch:
controller_server:
ros__parameters:
use_sim_time: true
controller_frequency: 20.0
FollowPath:
plugin: "nav2_dwb_controller::DWBLocalPlanner"
max_vel_x: 0.5
max_vel_theta: 1.5
acc_lim_x: 1.0
acc_lim_theta: 2.0
vx_samples: 20
vtheta_samples: 30
sim_time: 1.5
critics: ["RotateToGoal", "Oscillation", "ObstacleFootprint",
"GoalAlign", "PathAlign", "PathDist", "GoalDist"]
planner_server:
ros__parameters:
expected_planner_frequency: 1.0
use_sim_time: true
GridBased:
plugin: "nav2_navfn_planner/NavfnPlanner"
tolerance: 0.25
use_astar: true
local_costmap:
local_costmap:
ros__parameters:
update_frequency: 5.0
publish_frequency: 2.0
width: 3
height: 3
resolution: 0.05
robot_radius: 0.22
plugins: ["voxel_layer", "inflation_layer"]
inflation_layer:
inflation_radius: 0.55
global_costmap:
global_costmap:
ros__parameters:
update_frequency: 1.0
publish_frequency: 1.0
robot_radius: 0.22
resolution: 0.05
track_unknown_space: true
plugins: ["static_layer", "obstacle_layer", "inflation_layer"]
SLAM Toolbox vs AMCL — when to use which
SLAM Toolbox — online mapping. Use when:
- You don't have a map yet
- The environment changes (warehouse layout, retail)
- You're prototyping
AMCL (Adaptive Monte Carlo Localization) — known-map localization. Use when:
- You have a clean static map
- You need lower CPU usage
- The map is stable (hospital, fixed factory)
In production at companies like Ati Motors, the rule of thumb: SLAM Toolbox during commissioning to build the map, then switch to AMCL for daily operation.
Costmaps — the most powerful concept in Nav2
A costmap is a 2D grid where each cell has a cost from 0 (free) to 254 (lethal). The planner avoids high-cost cells.
Inflation layer — the killer feature. Inflates obstacles by your robot's radius, so a point-planner can pretend it's a point. Tune inflation_radius for safety vs. tightness.
Voxel layer — uses 3D obstacle data (multi-LiDAR or depth camera) to mark cells.
Static layer — your pre-built map.
In GreyOrange's warehouse stacks, tuning the inflation layer per-floor is one of the most common field adjustments.
Test Your Understanding
1. Your Nav2 robot keeps stopping 5cm before the goal even though tolerance: 0.25 is set. Walk through three places (config, code, tf) where the bug could be.
2. The planner produces a great path, but the controller hugs walls dangerously close. Which costmap parameter would you tune first and why?
3. You're deploying the same robot at two customer sites — one a 50m warehouse, one a 5m clinic corridor. Which Nav2 parameters change between sites, and which stay the same?
India Opportunity
- Senior Robotics Engineer (Nav2) · Ati Motors, Bangalore — Sherpa autonomous tugs, Nav2 + AMCL, ₹24–40 LPA.
- Robotics Engineer · GreyOrange, Gurgaon — multi-AGV fleet, Nav2 customization, ₹20–35 LPA.
- Field Robotics Engineer · Addverb Technologies, Noida — warehouse robots, deployment + Nav2 tuning, ₹16–28 LPA.
- Autonomy Engineer · Locus Robotics India, Bangalore — warehouse fleet Nav2 + behaviour trees, ₹22–38 LPA.
Next Step
→ Continue to Forge 02 · Robot Manipulation & MoveIt2.
Community discussion
0 questions & insightsLoading discussion…
Spotted something off? Report an error →