Great — this output means TF is now correct and complete. 🎉
What the
view_frames
result is telling you
From the text in your screenshot we can directly read the key TF chain for each robot:
robot_0/laser → parent: robot_0/base_link
robot_0/base_link → parent: robot_0/odom
(and similarly for robot_1, robot_2)
So for mapping / SLAM on robot_0, the “real TF frame names” are:
base frame: robot_0/base_link
odom frame: robot_0/odom
laser frame: robot_0/laser (but note your LaserScan message’s frame_id may be laser or robot_0/laser depending on configuration)
This is exactly what we needed earlier.
Also, it exported a PDF:
frames_2026-02-28_16.47.01.pdf
You can open it to visually confirm.
What to do next (use these frames in SLAM Toolbox params)
Open your SLAM params file:
nano ~/nav_class_ws/config/mapper_params_stage.yamlSet these (recommended, stable):
slam_toolbox:
ros__parameters:
use_sim_time: true
# Use your throttled scan if you made one
scan_topic: /robot_0/base_scan_throttle # or /robot_0/base_scan
# TF frames from view_frames
base_frame: robot_0/base_link
odom_frame: robot_0/odom
map_frame: map
tf_buffer_duration: 30.0Then restart SLAM toolbox:
ros2 launch slam_toolbox online_async_launch.py \
slam_params_file:=/home/lee/nav_class_ws/config/mapper_params_stage.yaml
Quick verification
ros2 topic echo /map --onceIf it prints, mapping is working.
RViz fixed frame choice (important)
Now that map will exist:
During SLAM: set RViz Fixed Frame = map
If SLAM not started yet: you can use robot_0/odom
Why this matters for your course
This is a perfect teaching point:
“Nav stacks are frame-sensitive. We don’t guess frame names; we inspect TF (view_frames) and then configure SLAM/Nav2 accordingly.”
If you want, paste one LaserScan header (from /robot_0/base_scan) showing the frame_id: line, and I’ll tell you whether SLAM toolbox should use laser or robot_0/laser (sometimes Stage uses the un-namespaced laser string inside the message).