ROS 2 Lyrical Luth · Lesson 40 of 86
TF tree design
One parent per frame, avoiding loops, where to attach sensors and naming conventions.
Estimated time · 45–60 min
Learning goals
What you will understand
- Explain tf tree design in your own words
- Use it in a small robotics example
- Verify the result instead of assuming it worked
Understand the idea first
The mental model
TF is a tree: every frame has exactly one parent and there are no loops. Decide the tree before you write code: a fixed mount is a static transform, a moving joint comes from robot_state_publisher, and localization publishes map → odom.
Give frames clear names (base_link, lidar_link, camera_optical_frame), attach each sensor to the link it is rigidly mounted on, and never publish the same transform from two nodes. If two nodes publish the same child frame, TF flickers between values. Inspect with view_frames and tf2_monitor.
Guided lesson
Work through these steps
- 1.
Source the ROS 2 Lyrical environment in a fresh terminal.
- 2.
Read the tf tree design example and identify what each line does.
- 3.
Build only the affected package, source the workspace overlay, and run the example.
- 4.
Inspect the result from a second terminal with the ros2 CLI or RViz2 and compare it with the expected result.
Worked example
Read it before you run it
ros2 run tf2_tools view_frames
# Generate frames.pdf that draws the current TF tree.
ros2 run tf2_ros tf2_monitor
# Show how often each frame is published and its delay.
ros2 run tf2_ros tf2_echo base_link lidar_link
# Print one transform to verify a sensor mount.
ros2 topic echo /tf_static --once
# List the fixed transforms that were published once.
ros2 run tf2_ros static_transform_publisher --x 0.2 --z 0.3 --frame-id base_link --child-frame-id lidar_link
# Add a fixed lidar mount 20 cm forward and 30 cm up.What the example is doing
- 1.The ros2 commands query the running graph; they do not prove that a node's internal behavior is correct.
- 2.Build from the workspace root, then source install/setup.bash so the shell can discover the new package.
- 3.Use a second sourced terminal to inspect nodes and interfaces while the example runs.
Watch it run · Robot arm
See what this code does
The terminal types this lesson's practice solution and runs it. The scene shows the kind of robot behaviour that code drives. It plays by itself and loops.
Demo for TF tree design. Expected result: frames.pdf shows base_link with children lidar_link and camera_link
Practice code
Now type it yourself
Copy the starter into your ROS 2 package, replace every TODO, then build with colcon build and run it from a sourced terminal.
# Two fixed sensor mounts on base_link
ros2 run tf2_ros static_transform_publisher --x 0.2 --z 0.3 --frame-id base_link --child-frame-id lidar_link
# TODO: publish camera_link 0.25 m forward and 0.2 m up from base_link
ros2 run tf2_tools view_framesExpected output
frames.pdf shows base_link with children lidar_link and camera_linkStuck? Show the full solution (try it yourself first)
# Two fixed sensor mounts on base_link
ros2 run tf2_ros static_transform_publisher --x 0.2 --z 0.3 --frame-id base_link --child-frame-id lidar_link
ros2 run tf2_ros static_transform_publisher --x 0.25 --z 0.2 --frame-id base_link --child-frame-id camera_link
ros2 run tf2_tools view_framesExpected result
How you know it worked
- ROS_DISTRO is lyrical in every terminal used for the exercise.
- The complete package from the exercise builds and the expected graph entity appears in a second terminal.
Practical exercise
Apply the lesson
Draw the TF tree for a robot with base_link, two wheels, a lidar, a camera and an IMU on paper, then build it with static publishers and confirm the result with view_frames. Find and fix a frame with two parents.
Before you continue
- ROS_DISTRO reports lyrical in every terminal you used
- The example builds and runs without errors
- You checked the result with a ros2 command, RViz2 or a test, not just by reading the code
If something goes wrong
Source /opt/ros/lyrical/setup.bash, build from the workspace root, then source install/setup.bash in that terminal.
Confirm both terminals use Lyrical, the same ROS_DOMAIN_ID, and compatible network/discovery settings.