CodeTheRobot

ROS 2 Lyrical Luth · Lesson 20 of 86

robot_state_publisher

Publishing TF from a URDF and joint states, and /robot_description.

Estimated time · 45–60 min

Learning goals

What you will understand

  • Explain robot_state_publisher 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

robot_state_publisher reads the URDF (from the robot_description parameter) and the /joint_states topic, computes the position of every link, and publishes the results on /tf and /tf_static. It also publishes the URDF on the /robot_description topic for other tools.

Fixed joints appear in /tf_static immediately; moving joints appear only once joint values arrive. If a moving joint's link is missing in RViz, check that /joint_states contains its name.

Guided lesson

Work through these steps

  1. 1.

    Source the ROS 2 Lyrical environment in a fresh terminal.

  2. 2.

    Read the robot_state_publisher 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

from launch import LaunchDescription
# Launch container.
from launch_ros.actions import Node
# Node action.
from launch.substitutions import Command
# Lets us run xacro to produce the URDF text.
def generate_launch_description():
# Launch entry point.
    urdf = Command(['xacro ', '/home/user/robot_ws/src/robot_description/urdf/robot.urdf.xacro'])
# Convert the xacro file to URDF at launch time.
    rsp = Node(package='robot_state_publisher', executable='robot_state_publisher', parameters=[{'robot_description': urdf}])
# Give the URDF text to robot_state_publisher as a parameter.
    return LaunchDescription([rsp])
# Start it.

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 robot_state_publisher. Expected result: The robot appears in RViz2 and TF shows base_link and upper_arm

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.

# Terminal A: publish the description (replace the path with your URDF)
ros2 run robot_state_publisher robot_state_publisher --ros-args -p robot_description:="$(cat mini_arm.urdf)"
# Terminal B: publish a joint value
# TODO: publish JointState for joint "shoulder" at position 0.5 rad, 10 times per second

Expected output

The robot appears in RViz2 and TF shows base_link and upper_arm
Stuck? Show the full solution (try it yourself first)
# Terminal A: publish the description (replace the path with your URDF)
ros2 run robot_state_publisher robot_state_publisher --ros-args -p robot_description:="$(cat mini_arm.urdf)"
# Terminal B: publish a joint value
ros2 topic pub -r 10 /joint_states sensor_msgs/msg/JointState "{name: ['shoulder'], position: [0.5]}"

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

Start robot_state_publisher with your two-link arm URDF, publish /joint_states from the CLI at 10 Hz for the shoulder joint, and watch the TF frame move in RViz2.

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.