← Back to Blog
navigationros2nav2navigationamr

ROS 2 Nav2: Complete Navigation for AMR

Comprehensive guide to Nav2 stack -- from map building with slam_toolbox, path planning with NavFn and Smac, to behavior trees and launch config.

Nguyen Anh Tuan8 tháng 2, 20267 min read
ROS 2 Nav2: Complete Navigation for AMR

What is Nav2?

Nav2 (Navigation2) is the official navigation stack for ROS 2 -- a complete toolkit for robots to automatically move from point A to point B in known or unknown environments. Nav2 replaces the old ROS 1 navigation stack with entirely new architecture, more flexible and powerful.

If you're building an AMR (Autonomous Mobile Robot) for factories, warehouses, or any application, Nav2 is the best starting point. It provides everything you need: SLAM, localization, path planning, obstacle avoidance, and recovery behaviors.

In this post, I'll guide you through setting up Nav2 from scratch -- from building a map to autonomous navigation.

ROS 2 Nav2 stack controlling robot autonomous movement in environment

Nav2 Stack Architecture

Nav2 is designed with modular architecture -- each component is a separate ROS 2 node, communicating via topics, services, and actions. You can replace any component without affecting the rest.

Main Components

                    ┌─────────────────────┐
                    │   BT Navigator      │  ← Behavior Tree orchestration
                    └────────┬────────────┘
                             │
              ┌──────────────┼──────────────┐
              │              │              │
    ┌─────────▼──────┐ ┌────▼─────┐ ┌─────▼────────┐
    │   Planner      │ │ Controller│ │  Recovery    │
    │   Server       │ │  Server  │ │  Server      │
    └─────────┬──────┘ └────┬─────┘ └─────┬────────┘
              │              │              │
    ┌─────────▼──────┐ ┌────▼─────┐ ┌─────▼────────┐
    │  NavFn/Smac/   │ │ DWB/MPPI/│ │ Spin/Backup/ │
    │  Theta*        │ │ RPP      │ │ Wait         │
    └────────────────┘ └──────────┘ └──────────────┘
              │              │
    ┌─────────▼──────────────▼─────┐
    │     Costmap 2D               │  ← Global + Local costmaps
    │  (static + obstacle layers)  │
    └──────────────────────────────┘
  1. BT Navigator: orchestrates entire navigation process using Behavior Tree
  2. Planner Server: computes global path from current location to goal
  3. Controller Server: follows global path and avoids local obstacles
  4. Recovery Server: handles robot being stuck (spin, back up, wait)
  5. Costmap 2D: cost map for path planning and obstacle avoidance

Step 1: Build Map with slam_toolbox

Before navigating, robot needs a map. slam_toolbox is the main SLAM package for Nav2.

Installation

# ROS 2 Humble
sudo apt install ros-humble-slam-toolbox
sudo apt install ros-humble-navigation2 ros-humble-nav2-bringup

Run SLAM

# Terminal 1: Launch robot (or simulation)
ros2 launch my_robot robot.launch.py

# Terminal 2: Launch slam_toolbox
ros2 launch slam_toolbox online_async_launch.py \
  use_sim_time:=true

# Terminal 3: Launch RViz2 to visualize map
ros2 launch nav2_bringup rviz_launch.py

Drive robot around the environment (with teleop or joystick). slam_toolbox automatically builds an occupancy grid map.

Save Map

# Save map when done scanning
ros2 run nav2_map_server map_saver_cli -f my_warehouse
# Creates 2 files: my_warehouse.pgm (image) and my_warehouse.yaml (metadata)

my_warehouse.yaml:

image: my_warehouse.pgm
resolution: 0.05          # 5cm per pixel
origin: [-10.0, -10.0, 0] # Map origin (x, y, yaw)
occupied_thresh: 0.65
free_thresh: 0.25
negate: 0

Step 2: Localization with AMCL

With map in hand, robot needs to know where it is. AMCL (Adaptive Monte Carlo Localization) uses particle filter to estimate robot pose on map.

# amcl config
amcl:
  ros__parameters:
    use_sim_time: true
    alpha1: 0.2   # Rotation noise from rotation
    alpha2: 0.2   # Rotation noise from translation
    alpha3: 0.2   # Translation noise from translation
    alpha4: 0.2   # Translation noise from rotation
    base_frame_id: "base_footprint"
    global_frame_id: "map"
    max_particles: 2000
    min_particles: 500
    robot_model_type: "nav2_amcl::DifferentialMotionModel"
    tf_broadcast: true
    set_initial_pose: true
    initial_pose:
      x: 0.0
      y: 0.0
      yaw: 0.0

Step 3: Path Planning -- NavFn vs Smac vs Theta*

Nav2 offers multiple planner plugins. Each has its own characteristics.

NavFn (Navigation Function)

planner_server:
  ros__parameters:
    planner_plugins: ["GridBased"]
    GridBased:
      plugin: "nav2_navfn_planner::NavfnPlanner"
      tolerance: 0.5
      use_astar: true
      allow_unknown: true

Smac Planner (State Lattice)

planner_server:
  ros__parameters:
    planner_plugins: ["SmacPlanner"]
    SmacPlanner:
      plugin: "nav2_smac_planner::SmacPlannerHybrid"
      tolerance: 0.25
      motion_model_for_search: "DUBIN"  # DUBIN, REEDS_SHEPP, STATE_LATTICE
      angle_quantization_bins: 72
      minimum_turning_radius: 0.40

Theta* (Any-Angle)

Planner Comparison Table

Planner Path Quality Speed Kinematic Feasible Use Case
NavFn (A*) Average Fastest No Quick prototype
Smac 2D Good Fast No Omnidirectional
Smac Hybrid-A* Very good Medium Yes Diff-drive, Ackermann
Smac Lattice Best Slowest Yes (full) Non-holonomic
Theta* Good Fast No Any-angle paths

Path planning visualization -- global path and local costmap

Step 4: Controller -- Follow Path and Avoid Obstacles

Controller (previously called local planner) is responsible for following global path and avoiding dynamic obstacles.

DWB (Dynamic Window Based)

Inherited from DWA (Dynamic Window Approach) in ROS 1. Samples velocity commands and selects best command based on multiple critics.

MPPI (Model Predictive Path Integral)

Newest, most powerful controller in Nav2. Uses sampling-based MPC -- generates thousands of trajectory candidates on GPU/CPU and selects optimal trajectory.

controller_server:
  ros__parameters:
    controller_plugins: ["FollowPath"]
    FollowPath:
      plugin: "nav2_mppi_controller::MPPIController"
      time_steps: 56
      model_dt: 0.05
      batch_size: 2000
      vx_max: 0.5
      vy_max: 0.0
      wz_max: 1.9
      critics:
        - "GoalCritic"
        - "GoalAngleCritic"
        - "ObstaclesCritic"
        - "PathFollowCritic"
        - "PathAngleCritic"
        - "PreferForwardCritic"

RPP (Regulated Pure Pursuit)

Simple and effective -- follows path using pure pursuit with regulated speed. Good for fast-moving robots in wide corridors.

Step 5: Behavior Trees -- Nav2's Brain

Nav2 uses Behavior Trees (BT) instead of state machines for orchestration. BT is more flexible, easier to debug, and customizable.

Default BT: NavigateToPose

<root BTCPP_format="4">
  <BehaviorTree ID="NavigateWithReplanning">
    <PipelineSequence>
      <!-- Compute path -->
      <RateController hz="1.0">
        <ComputePathToPose goal="{goal}" path="{path}"
                           planner_id="GridBased"/>
      </RateController>
      
      <!-- Follow path -->
      <ReactiveSequence>
        <PathExpiringTimer seconds="5.0" path="{path}"/>
        <FollowPath path="{path}" controller_id="FollowPath"/>
      </ReactiveSequence>
    </PipelineSequence>
  </BehaviorTree>
</root>

Custom BT: Add Recovery Behaviors

<root BTCPP_format="4">
  <BehaviorTree ID="NavigateWithRecovery">
    <RecoveryNode number_of_retries="3">
      <PipelineSequence>
        <ComputePathToPose goal="{goal}" path="{path}"/>
        <FollowPath path="{path}"/>
      </PipelineSequence>
      
      <!-- Recovery actions on failure -->
      <SequenceStar>
        <ClearEntireCostmap name="ClearGlobalCostmap"
                            service_name="/global_costmap/clear_entirely_global_costmap"/>
        <ClearEntireCostmap name="ClearLocalCostmap"
                            service_name="/local_costmap/clear_entirely_local_costmap"/>
        <Spin spin_dist="1.57"/>  <!-- Spin 90 degrees -->
        <Wait wait_duration="3"/>
        <BackUp backup_dist="0.3" backup_speed="0.1"/>
      </SequenceStar>
    </RecoveryNode>
  </BehaviorTree>
</root>

Step 6: Complete Launch Config

# nav2_launch.py
from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from ament_index_python.packages import get_package_share_directory
import os

def generate_launch_description():
    nav2_dir = get_package_share_directory('nav2_bringup')
    
    return LaunchDescription([
        IncludeLaunchDescription(
            PythonLaunchDescriptionSource(
                os.path.join(nav2_dir, 'launch', 'bringup_launch.py')
            ),
            launch_arguments={
                'map': '/path/to/my_warehouse.yaml',
                'params_file': '/path/to/nav2_params.yaml',
                'use_sim_time': 'true',
                'autostart': 'true',
            }.items(),
        ),
    ])

Send Navigation Goal from Code

# Python client to send goal
from geometry_msgs.msg import PoseStamped
from nav2_simple_commander.robot_navigator import BasicNavigator

navigator = BasicNavigator()
navigator.waitUntilNav2Active()

# Create goal pose
goal = PoseStamped()
goal.header.frame_id = 'map'
goal.header.stamp = navigator.get_clock().now().to_msg()
goal.pose.position.x = 5.0
goal.pose.position.y = 3.0
goal.pose.orientation.w = 1.0

# Navigate!
navigator.goToPose(goal)

while not navigator.isTaskComplete():
    feedback = navigator.getFeedback()
    print(f"Distance remaining: {feedback.distance_remaining:.2f}m")

result = navigator.getResult()
print(f"Navigation result: {result}")

Tips and Best Practices

1. Costmap Tuning

Costmap is the most critical factor affecting navigation quality. Some tips:

2. Recovery Behavior

Always configure recovery behaviors. Robot will get stuck -- question is when, not if.

3. Simulate First, Real Robot Later

Test every config in Gazebo before deploying to real robot. See Simulation for Robotics to choose appropriate simulator.

4. Monitor and Logging

# Check Nav2 status
ros2 topic echo /bt_navigator/transition_event

# View costmap
ros2 topic echo /global_costmap/costmap

# Record rosbag for debugging
ros2 bag record -a -o nav2_debug

Robot AMR navigating warehouse with obstacle avoidance

Up Next in Series

This is Part 2 of Modern Navigation series. In upcoming posts:


Related Posts

Related Posts

Deep DiveDigital Twins và ROS 2: Simulation trong sản xuất
simulationros2digital-twinPart 6

Digital Twins và ROS 2: Simulation trong sản xuất

Ứng dụng simulation trong công nghiệp — digital twins, ROS 2 + Gazebo/Isaac integration cho nhà máy thông minh.

3/4/202611 min read
ROS 2 từ A đến Z (Phần 4): ros2_control và Hardware
ros2tutorialrobot-armPart 4

ROS 2 từ A đến Z (Phần 4): ros2_control và Hardware

Kết nối ROS 2 với phần cứng thực — viết hardware interface cho motor driver và đọc encoder với ros2_control framework.

26/3/202611 min read
Multi-robot Coordination: Thuật toán phân công task
fleetamrprogramming

Multi-robot Coordination: Thuật toán phân công task

Các thuật toán phân công nhiệm vụ cho đội robot — từ Hungarian algorithm, auction-based đến RL-based task allocation.

20/3/202612 min read