All modules
Advanced28 min read

nav2: Autonomous Navigation

nav2 is the ROS2 navigation stack. Configure it, understand its components, and send your robot to goal poses autonomously using a saved SLAM map.

The nav2 architecture

nav2 is a collection of ROS2 nodes that together handle: localization (AMCL), path planning (NavFn or Smac planner), path following (DWB controller), and recovery behaviors (spin, backup, wait). You configure it via a single large YAML params file.

amclAdaptive Monte Carlo Localization — estimates robot pose in the saved map using LIDAR
nav2_plannerGlobal planner — finds a path from current pose to goal pose in the static map
nav2_controllerLocal controller (DWB) — follows the global path while avoiding obstacles in real time
nav2_costmap_2dBuilds layered costmaps: static map layer + obstacle layer from LIDAR
nav2_bt_navigatorBehavior tree navigator — orchestrates the whole pipeline, handles recovery

Minimal nav2_params.yaml

yaml (nav2_params.yaml — key sections)
amcl:
  ros__parameters:
    use_sim_time: false
    min_particles: 500
    max_particles: 2000
    laser_model_type: likelihood_field
    max_beams: 60
    update_min_d: 0.25      # meters
    update_min_a: 0.2       # radians
    resample_interval: 1

controller_server:
  ros__parameters:
    controller_frequency: 20.0
    min_x_velocity_threshold: 0.001
    min_theta_velocity_threshold: 0.001
    FollowPath:
      plugin: "nav2_dwb_controller::DWBLocalPlanner"
      max_vel_x: 0.3          # m/s — match your robot's safe speed
      max_vel_theta: 1.0      # rad/s
      min_speed_xy: 0.0
      acc_lim_x: 1.0
      decel_lim_x: -2.0

local_costmap:
  local_costmap:
    ros__parameters:
      update_frequency: 5.0
      publish_frequency: 2.0
      width: 3
      height: 3
      resolution: 0.05
      robot_radius: 0.175     # meters — half your robot width + margin
      plugins: ["obstacle_layer", "inflation_layer"]
      obstacle_layer:
        plugin: "nav2_costmap_2d::ObstacleLayer"
        observation_sources: scan
        scan:
          topic: /scan
          max_obstacle_height: 2.0
          clearing: true
          marking: true
      inflation_layer:
        plugin: "nav2_costmap_2d::InflationLayer"
        inflation_radius: 0.35   # keep robot away from walls

Launch nav2 with AMCL

bash
# Assumes you have a saved map from slam_toolbox
ros2 launch nav2_bringup bringup_launch.py \
  map:=/home/robot/maps/room.yaml \
  params_file:=/home/robot/config/nav2_params.yaml \
  use_sim_time:=false

# Open RViz2 for visualization
ros2 launch nav2_bringup rviz_launch.py

Set initial pose and navigate

bash
# 1. In RViz2: use "2D Pose Estimate" tool to set initial location
#    Click where the robot is on the map, drag to set heading.
#    Watch AMCL particles converge — when clustered, localization is good.

# 2. Send a navigation goal from command line (for testing):
ros2 action send_goal /navigate_to_pose nav2_msgs/action/NavigateToPose \
  "{pose: {header: {frame_id: map}, pose: {position: {x: 2.0, y: 1.5}, orientation: {w: 1.0}}}}"

# 3. In RViz2: use "Nav2 Goal" button to click-and-go.
# 4. Cancel navigation:
ros2 action send_goal /navigate_to_pose nav2_msgs/action/NavigateToPose \
  "{}" --cancel-after-secs 0

Common nav2 issues

IssueLikely causeFix
Robot stuck at startFootprint too large, hits first obstacleReduce robot_radius in costmap params
Goal rejectedGoal is inside obstacle or too close to wallUse inflation_radius > robot half-width
AMCL not convergingWrong initial pose or sparse mapDrive robot briefly to collect more scans
Oscillating near goalDWB angular velocity too highReduce max_vel_theta to 0.5 rad/s
Path not foundGlobal costmap marks goal as occupiedCheck map resolution matches params resolution
TF error on launchMissing odom → base_link transformEnsure motor driver publishes odometry or add static TF
FULL STACK

You've covered the full pipeline: power → motors → sensors → ROS2 → SLAM → nav2. The Room-Nav kit is the reference build for this exact stack.

Build it: Room-Nav BOM →