CodeTheRobot

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. 1.

    Source the ROS 2 Lyrical environment in a fresh terminal.

  2. 2.

    Read the tf tree design example and identify what each line does.

  3. 3.

    Build only the affected package, source the workspace overlay, and run the example.

  4. 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. 1.The ros2 commands query the running graph; they do not prove that a node's internal behavior is correct.
  2. 2.Build from the workspace root, then source install/setup.bash so the shell can discover the new package.
  3. 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.

tip x110 y58ROBOT ARM

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_frames

Expected output

frames.pdf shows base_link with children lidar_link and camera_link
Stuck? 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_frames

Expected 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

ros2 cannot find a package or executable.

Source /opt/ros/lyrical/setup.bash, build from the workspace root, then source install/setup.bash in that terminal.

Nodes run but cannot discover each other.

Confirm both terminals use Lyrical, the same ROS_DOMAIN_ID, and compatible network/discovery settings.