Click-to-Navigate Practice: AMCL + Nav2 on a Provided Map

By ihsumlee , 8 March 2026
content

You already have:

  • a saved map: classroom_map.yaml + classroom_map.pgm

  • TF frames (from view_frames):

    robot_0/odom → robot_0/base_link → robot_0/laser

We will do map-based navigation using AMCL + Nav2.


Step 0 — Files & assumptions

Assume your saved map is here:

  • ~/nav_class_assets/maps/classroom_map_simple.yaml

If not, locate it:

ls ~/nav_class_assets/maps
 
  • Make a directory inside nav_claass_assets, called worlds. Move to "worlds" and make a directory "include". 
    • mkdir worlds
    • cd worlds
    • Download robot.inc and classroom.worl and place them in the "worlds" directory. (change robot.inc.txt to robot.inc) 
    • mkdir include directory
    • cd include 
    • download pioneer2dx.inc and place it in the "include" directory. (change pioneer2dx.inc.txt to robot.inc) 

Step 1 — Launch Stage with standard TF topics

Terminal 1:

source /opt/ros/jazzy/setup.bash
source ~/nav_class_ws/install/setup.bash
ros2 launch stage_ros2 stage.launch.py world:=/home/lee/nav_class_assets/worlds/classroom enforce_prefixes:=false

(Optional but recommended for VMs) throttle scan:

Terminal 2:

source /opt/ros/jazzy/setup.bash
ros2 run topic_tools throttle messages /base_scan 10.0 /scan

Step 2 — Prepare a Nav2 params file for robot_0 (one-time)

Install Nav2 if you do not have one. (Link)

1) Make sure the map YAML is valid

Run:

ls -lh /home/lee/nav_class_assets/maps/classroom_map.yaml
sed -n '1,50p' /home/lee/nav_class_assets/maps/classroom_map.yaml

A valid map YAML must look like this (example):

image: classroom_map_simple.pgm
mode: trinary
resolution: 0.05
origin: [-10.0, -7.5, 0.0]
negate: 0
occupied_thresh: 0.65
free_thresh: 0.25

Most common mistake

image: points to a .pgm that doesn’t exist in the same folder.

Check:

grep '^image:' /home/lee/nav_class_assets/maps/classroom_map.yaml
ls -lh /home/lee/nav_class_assets/maps/

If image: classroom_map_simple.pgm, then that file must exist in the same directory:

/home/lee/nav_class_assets/maps/classroom_map.pgm.

âś… If you want zero risk, use the downloaded map you already have (e.g. classroom_map_simple.yaml + .pgm) and set map:=... in the launch command.


2) Make sure Nav2 params file is complete (missing DWB critics)

The error No critics defined for FollowPath happens when your params file does not include the DWB controller configuration block.

✅ Easiest fix: copy Nav2’s default params file and then only modify the few lines you need (frames + scan topic).

Step 2.1 Copy the default Nav2 params

mkdir -p ~/nav_class_assets/nav2
cp /opt/ros/jazzy/share/nav2_bringup/params/nav2_params.yaml \
   ~/nav_class_assets/nav2/nav2_stage_robot0.yaml

This default file includes the DWB critics list, so the controller will configure correctly.

Step 2.2 Edit only the required lines

Open it by vim or gedit:

vim ~/nav_class_assets/nav2/nav2_stage_robot0.yaml

Now change these keys (search with Ctrl+W in nano):

(A) use_sim_time

Set use_sim_time: true for all servers (or at least for amcl/map_server/nav2 stack). In a VM sim with /clock, it should be true.

(B) Map file

Find yaml_filename: under map_server: and set it to your downloaded map:

map_server:
  ros__parameters:
    use_sim_time: true
    yaml_filename: /home/lee/nav_class_assets/maps/classroom_map.yaml

(C) AMCL scan topic + frames (robot_0 namespace)

Find AMCL section and set:

amcl:
  ros__parameters:
    use_sim_time: true
    base_frame_id: base_link
odom_frame_id: odom global_frame_id: map scan_topic: /scan set_initial_pose: true initial_pose: x: 0.0 y: 0.0 yaw: 0.0

(D) Costmap robot_base_frame

Search robot_base_frame: and set to:

robot_base_frame: base_link

(E) Local costmap global frame

Set local costmap global_frame to:

global_frame: odom
robot_base_frame: base_link
width: 3 (but 3.0)
height: 3 (but 3.0)

 

(F) Map server

map_server:
 ros__parameters:
   use_sim_time: true
   yaml_filename: /home/lee/nav_class_assets/maps/classroom_map.yaml

(G)Add lifecycle manager navigation

lifecycle_manager_localization:
 ros__parameters:
   use_sim_time: true
   autostart: true
   node_names: ["map_server", "amcl"]

lifecycle_manager_navigation:
 ros__parameters:
   use_sim_time: true
   autostart: true
   node_names:
     - controller_server
     - planner_server
     - behavior_server
     - bt_navigator
     - waypoint_follower
     - velocity_smoother
     - collision_monitor
     - smoother_server
     - route_server
     - docking_server

That’s it. Do not delete the controller_server section — it contains the DWB critics.

Problem solved: [link]


3) Launch Nav2 with an explicit

map:=..argument (recommended)

This avoids relying only on yaml_filename in the params file and helps debugging. 


Quick verification checklist (after launch)

In a new terminal:

1) Map is published

ros2 topic echo /map --once

2) Controller server is active (no “critics” error)

ros2 node list | grep controlle

3) Nav2 cmd_vel is being published when you send a goal

ros2 topic list | grep cmd_vel

Step 3 — Start Nav2 localization + navigation

Terminal 3:

source /opt/ros/jazzy/setup.bash

ros2 launch nav2_bringup bringup_launch.py   params_file:=/home/lee/nav_class_assets/nav2/nav2_stage_robot0.yaml   use_sim_time:=True   use_composition:=False   autostart:=True 

This brings up

  • map_server

  • AMCL

  • Nav2 stack


Step 4 — Open RViz and load Nav2 config

Terminal 4:

source /opt/ros/jazzy/setup.bash 
rviz2 
or
LIBGL_ALWAYS_SOFTWARE=1 rviz2 --ros-args -p use_sim_time:=true 

In RViz:

  1. Fixed Frame = map
  2. Add displays:
    • Map topic: /map
    • LaserScan topic: /scan
    • TF

Step 5 — Initialize localization (must do)

Before doing initialization, please open a terminal to bridge /cmd_vel_nav to /cmd_vel.

ros2 run topic_tools relay /cmd_vel_nav /cmd_vel

AMCL needs an initial pose.

In RViz:

  1. Click “2D Pose Estimate”

  2. Click on the map near where robot_0 actually is

  3. Drag to set its heading (arrow direction)

Wait a few seconds — you should see the AMCL pose stabilize.


Step 6 — Send a navigation goal

In RViz:

  1. Click “Nav2 Goal” (or “2D Goal Pose” depending on RViz toolbar)

  2. Click a target on the map

  3. Drag to set goal heading

Robot should plan and start moving.


Quick troubleshooting checklist (most common issues)

A) Robot doesn’t move

Check cmd_vel:

ros2 topic echo /robot_0/cmd_vel --once

If nothing is published, Nav2 controller isn’t active (or bringup failed).

B) “No transform” errors

Confirm frames exist:

ros2 run tf2_tools view_frames

Make sure your params match the real frames:

  • robot_0/base_link

  • robot_0/odom

C) AMCL not updating / no laser

ros2 topic echo /robot_0/base_scan_throttle --once

If no message, fix throttle topic or use /robot_0/base_scan.

D) Robot doesn’t move

ros2 topic echo /robot_0/cmd_vel --once

E) Laser not reaching Nav2

ros2 topic echo /robot_0/base_scan_throttle --once

F) TF missing (must use one_tf_tree:=true)

ros2 topic echo /tf --once

F) AMCL not publishing map→robot_0/odom, so map frame missing 

Cannot set Fixed Frame = map [Solution]


Small improvement for class stability

Because your Stage demo runs 3 robots, focus on robot_0 only:

  • turn off robot_1/robot_2 displays in RViz


If you tell me the exact filename of your saved map (and confirm the path), I can give you a fully copy-paste “bringup” command with no hardcoded /home/lee (using $HOME instead).

 

Download maps:

Download them here: (See also the maps in the attachments)

Tags

Teaching Materials