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.
Source the ROS 2 Lyrical environment in a fresh terminal.
- 2.
Read the robot_state_publisher 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
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.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 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 secondExpected output
The robot appears in RViz2 and TF shows base_link and upper_armStuck? 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
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.