Chapter 4: URDF Robot Modeling
Before you can simulate or control a robot, you need to describe itβits shape, mass, joints, sensors. In ROS 2, this is done with URDF (Unified Robot Description Format), an XML-based language for defining robot structure.
Think of URDF as the "blueprint" for your robot. Just as an architect uses CAD drawings to specify building dimensions, you'll use URDF to specify:
- Links (rigid body parts: torso, arms, legs)
- Joints (connections between links: shoulder, elbow, knee)
- Visual geometry (how the robot looks in RViz2)
- Collision geometry (simplified shapes for physics simulation)
- Inertial properties (mass, center of mass, inertia tensors)
By the end of this chapter, you'll build a URDF model of a simplified humanoid robot and visualize it in RViz2.
4.1 What is URDF?β
4.1.1 URDF File Structureβ
Every URDF file starts with the <robot> root element:
1<?xml version="1.0"?>2<robot name="simple_robot">3<!-- Links define rigid body parts -->4<link name="base_link">5 <!-- Visual, collision, inertial properties here -->6</link>78<link name="arm_link">9 <!-- ... -->10</link>1112<!-- Joints connect links -->13<joint name="base_to_arm" type="revolute">14 <parent link="base_link"/>15 <child link="arm_link"/>16 <!-- Joint properties here -->17</joint>18</robot>
Key Rules:
- Every robot has a root link (typically
base_link) - Links are connected by joints forming a tree structure (no loops)
- Joint names must be unique
- Each joint has exactly one parent and one child link
4.2 Links: Rigid Body Partsβ
A link represents a rigid body part of the robot. Each link can have:
- Visual: How it looks (rendered in RViz2)
- Collision: Simplified shape for collision detection
- Inertial: Mass and inertia tensor for physics
4.2.1 Visual Geometryβ
Visual geometry defines how the link appears in visualization tools:
1<link name="torso">2<visual>3 <origin xyz="0 0 0.25" rpy="0 0 0"/>4 <geometry>5 <box size="0.3 0.2 0.5"/>6 </geometry>7 <material name="blue">8 <color rgba="0.0 0.0 0.8 1.0"/>9 </material>10</visual>11</link>
Visual Elements:
<origin>: Position (xyz in meters) and orientation (rpy in radians, roll-pitch-yaw)<geometry>: Shape definition<box size="x y z"/>: Rectangular box<cylinder radius="r" length="l"/>: Cylinder<sphere radius="r"/>: Sphere<mesh filename="package://my_robot/meshes/torso.stl"/>: 3D mesh file
<material>: Color (rgba: red, green, blue, alpha 0-1)
4.2.2 Collision Geometryβ
Collision geometry is used for physics simulation and path planning. Use simpler shapes than visual geometry for performance:
1<link name="forearm">2<!-- Visual: detailed mesh -->3<visual>4 <geometry>5 <mesh filename="package://my_robot/meshes/forearm_detailed.stl"/>6 </geometry>7</visual>89<!-- Collision: simple cylinder (faster collision detection) -->10<collision>11 <origin xyz="0 0 0.15" rpy="0 0 0"/>12 <geometry>13 <cylinder radius="0.04" length="0.3"/>14 </geometry>15</collision>16</link>
Best Practice: Visual geometry can be detailed meshes, but collision geometry should use primitive shapes (box, cylinder, sphere) for computational efficiency.
4.2.3 Inertial Propertiesβ
Inertial properties define mass distribution for physics simulation:
1<link name="upper_arm">2<!-- Visual and collision omitted for brevity -->34<inertial>5 <origin xyz="0 0 0.1" rpy="0 0 0"/>6 <mass value="2.5"/> <!-- kg -->7 <inertia8 ixx="0.01" ixy="0.0" ixz="0.0"9 iyy="0.01" iyz="0.0"10 izz="0.005"/>11</inertial>12</link>
Inertial Elements:
<mass>: Mass in kilograms<inertia>: 3x3 inertia tensor (kgΒ·mΒ²)- Diagonal elements (ixx, iyy, izz): moments of inertia
- Off-diagonal (ixy, ixz, iyz): products of inertia (often 0 for symmetric objects)
4.3 Joints: Connecting Linksβ
Joints define how links move relative to each other. ROS 2 supports six joint types:
| Joint Type | Description | Example |
|---|---|---|
| revolute | Rotates around axis with limits | Elbow (0Β° to 150Β°) |
| continuous | Rotates 360Β° without limits | Wheel axle |
| prismatic | Slides along axis with limits | Linear actuator |
| fixed | No movement (rigid attachment) | Sensor mount |
| planar | Moves in 2D plane | Omni-directional base |
| floating | 6-DOF free motion | Free-falling object |
4.3.1 Revolute Joint Exampleβ
1<joint name="shoulder_pitch" type="revolute">2<parent link="torso"/>3<child link="upper_arm"/>45<!-- Joint attachment point on parent link -->6<origin xyz="0.15 0.2 0.4" rpy="0 0 0"/>78<!-- Rotation axis (1 0 0 = X-axis) -->9<axis xyz="0 1 0"/>1011<!-- Joint limits -->12<limit13 lower="-1.57" <!-- -90 degrees in radians -->14 upper="3.14" <!-- 180 degrees -->15 effort="50.0" <!-- Maximum torque (Nm) -->16 velocity="2.0" <!-- Maximum angular velocity (rad/s) -->17/>1819<!-- Joint dynamics (optional) -->20<dynamics damping="0.7" friction="0.0"/>21</joint>
Joint Elements:
<parent>and<child>: Links being connected<origin>: Where child link attaches to parent (xyz position, rpy orientation)<axis>: Rotation axis (unit vector in parent link frame)<limit>: Motion constraints (angles in radians for revolute, meters for prismatic)<dynamics>: Damping and friction for simulation
4.4 Example: 2-DOF Robotic Armβ
Let's build a simple 2-DOF arm with a base, upper arm, and forearm:
1<?xml version="1.0"?>2<robot name="two_dof_arm">34<!-- Base Link (fixed to world) -->5<link name="base_link">6 <visual>7 <geometry>8 <cylinder radius="0.1" length="0.05"/>9 </geometry>10 <material name="gray">11 <color rgba="0.5 0.5 0.5 1.0"/>12 </material>13 </visual>14 <collision>15 <geometry>16 <cylinder radius="0.1" length="0.05"/>17 </geometry>18 </collision>19 <inertial>20 <mass value="1.0"/>21 <inertia ixx="0.001" ixy="0.0" ixz="0.0"22 iyy="0.001" iyz="0.0" izz="0.001"/>23 </inertial>24</link>2526<!-- Upper Arm Link -->27<link name="upper_arm">28 <visual>29 <origin xyz="0 0 0.15" rpy="0 0 0"/>30 <geometry>31 <cylinder radius="0.03" length="0.3"/>32 </geometry>33 <material name="blue">34 <color rgba="0.0 0.0 0.8 1.0"/>35 </material>36 </visual>37 <collision>38 <origin xyz="0 0 0.15" rpy="0 0 0"/>39 <geometry>40 <cylinder radius="0.03" length="0.3"/>41 </geometry>42 </collision>43 <inertial>44 <origin xyz="0 0 0.15" rpy="0 0 0"/>45 <mass value="0.5"/>46 <inertia ixx="0.004" ixy="0.0" ixz="0.0"47 iyy="0.004" iyz="0.0" izz="0.0001"/>48 </inertial>49</link>5051<!-- Forearm Link -->52<link name="forearm">53 <visual>54 <origin xyz="0 0 0.125" rpy="0 0 0"/>55 <geometry>56 <cylinder radius="0.025" length="0.25"/>57 </geometry>58 <material name="green">59 <color rgba="0.0 0.8 0.0 1.0"/>60 </material>61 </visual>62 <collision>63 <origin xyz="0 0 0.125" rpy="0 0 0"/>64 <geometry>65 <cylinder radius="0.025" length="0.25"/>66 </geometry>67 </collision>68 <inertial>69 <origin xyz="0 0 0.125" rpy="0 0 0"/>70 <mass value="0.3"/>71 <inertia ixx="0.002" ixy="0.0" ixz="0.0"72 iyy="0.002" iyz="0.0" izz="0.0001"/>73 </inertial>74</link>7576<!-- Joint 1: Base to Upper Arm (Shoulder) -->77<joint name="shoulder_joint" type="revolute">78 <parent link="base_link"/>79 <child link="upper_arm"/>80 <origin xyz="0 0 0.025" rpy="0 0 0"/>81 <axis xyz="0 1 0"/> <!-- Rotate around Y-axis -->82 <limit lower="-1.57" upper="1.57" effort="10.0" velocity="1.0"/>83</joint>8485<!-- Joint 2: Upper Arm to Forearm (Elbow) -->86<joint name="elbow_joint" type="revolute">87 <parent link="upper_arm"/>88 <child link="forearm"/>89 <origin xyz="0 0 0.3" rpy="0 0 0"/>90 <axis xyz="0 1 0"/> <!-- Rotate around Y-axis -->91 <limit lower="0.0" upper="2.8" effort="5.0" velocity="1.0"/>92</joint>9394</robot>
Kinematic Chain: base_link β (shoulder_joint) β upper_arm β (elbow_joint) β forearm
4.5 Humanoid Robot URDFβ
Humanoid robots require more complex kinematic trees with multiple branches for head, arms, and legs.
4.5.1 Humanoid Naming Conventionsβ
4.5.2 Simplified Humanoid URDFβ
1<?xml version="1.0"?>2<robot name="simple_humanoid">34<!-- Base Link (Pelvis) -->5<link name="base_link">6 <visual>7 <geometry>8 <box size="0.2 0.15 0.1"/>9 </geometry>10 <material name="gray">11 <color rgba="0.5 0.5 0.5 1.0"/>12 </material>13 </visual>14 <inertial>15 <mass value="5.0"/>16 <inertia ixx="0.02" ixy="0.0" ixz="0.0"17 iyy="0.03" iyz="0.0" izz="0.02"/>18 </inertial>19</link>2021<!-- Torso Link -->22<link name="torso">23 <visual>24 <origin xyz="0 0 0.25" rpy="0 0 0"/>25 <geometry>26 <box size="0.25 0.2 0.5"/>27 </geometry>28 <material name="blue">29 <color rgba="0.0 0.3 0.8 1.0"/>30 </material>31 </visual>32 <inertial>33 <origin xyz="0 0 0.25" rpy="0 0 0"/>34 <mass value="10.0"/>35 <inertia ixx="0.25" ixy="0.0" ixz="0.0"36 iyy="0.26" iyz="0.0" izz="0.05"/>37 </inertial>38</link>3940<!-- Head Link -->41<link name="head">42 <visual>43 <geometry>44 <sphere radius="0.1"/>45 </geometry>46 <material name="skin">47 <color rgba="0.9 0.7 0.6 1.0"/>48 </material>49 </visual>50 <inertial>51 <mass value="2.0"/>52 <inertia ixx="0.008" ixy="0.0" ixz="0.0"53 iyy="0.008" iyz="0.0" izz="0.008"/>54 </inertial>55</link>5657<!-- Left Upper Arm -->58<link name="left_upper_arm">59 <visual>60 <origin xyz="0 0 -0.15" rpy="0 0 0"/>61 <geometry>62 <cylinder radius="0.03" length="0.3"/>63 </geometry>64 <material name="blue"/>65 </visual>66 <inertial>67 <origin xyz="0 0 -0.15" rpy="0 0 0"/>68 <mass value="1.5"/>69 <inertia ixx="0.01" ixy="0.0" ixz="0.0"70 iyy="0.01" iyz="0.0" izz="0.001"/>71 </inertial>72</link>7374<!-- Left Forearm -->75<link name="left_forearm">76 <visual>77 <origin xyz="0 0 -0.125" rpy="0 0 0"/>78 <geometry>79 <cylinder radius="0.025" length="0.25"/>80 </geometry>81 <material name="skin"/>82 </visual>83 <inertial>84 <origin xyz="0 0 -0.125" rpy="0 0 0"/>85 <mass value="1.0"/>86 <inertia ixx="0.005" ixy="0.0" ixz="0.0"87 iyy="0.005" iyz="0.0" izz="0.001"/>88 </inertial>89</link>9091<!-- Left Thigh -->92<link name="left_thigh">93 <visual>94 <origin xyz="0 0 -0.2" rpy="0 0 0"/>95 <geometry>96 <cylinder radius="0.05" length="0.4"/>97 </geometry>98 <material name="blue"/>99 </visual>100 <inertial>101 <origin xyz="0 0 -0.2" rpy="0 0 0"/>102 <mass value="3.0"/>103 <inertia ixx="0.04" ixy="0.0" ixz="0.0"104 iyy="0.04" iyz="0.0" izz="0.01"/>105 </inertial>106</link>107108<!-- Left Shin -->109<link name="left_shin">110 <visual>111 <origin xyz="0 0 -0.175" rpy="0 0 0"/>112 <geometry>113 <cylinder radius="0.04" length="0.35"/>114 </geometry>115 <material name="gray"/>116 </visual>117 <inertial>118 <origin xyz="0 0 -0.175" rpy="0 0 0"/>119 <mass value="2.0"/>120 <inertia ixx="0.02" ixy="0.0" ixz="0.0"121 iyy="0.02" iyz="0.0" izz="0.005"/>122 </inertial>123</link>124125<!-- JOINTS -->126127<!-- Torso to Base (Hip) -->128<joint name="hip_joint" type="fixed">129 <parent link="base_link"/>130 <child link="torso"/>131 <origin xyz="0 0 0.05" rpy="0 0 0"/>132</joint>133134<!-- Head to Torso (Neck) -->135<joint name="neck_joint" type="revolute">136 <parent link="torso"/>137 <child link="head"/>138 <origin xyz="0 0 0.55" rpy="0 0 0"/>139 <axis xyz="0 0 1"/> <!-- Yaw rotation -->140 <limit lower="-1.57" upper="1.57" effort="5.0" velocity="1.0"/>141</joint>142143<!-- Left Shoulder -->144<joint name="left_shoulder_pitch" type="revolute">145 <parent link="torso"/>146 <child link="left_upper_arm"/>147 <origin xyz="0 0.15 0.45" rpy="0 0 0"/>148 <axis xyz="0 1 0"/> <!-- Pitch rotation -->149 <limit lower="-3.14" upper="3.14" effort="20.0" velocity="2.0"/>150</joint>151152<!-- Left Elbow -->153<joint name="left_elbow" type="revolute">154 <parent link="left_upper_arm"/>155 <child link="left_forearm"/>156 <origin xyz="0 0 -0.3" rpy="0 0 0"/>157 <axis xyz="0 1 0"/>158 <limit lower="0.0" upper="2.8" effort="10.0" velocity="2.0"/>159</joint>160161<!-- Left Hip -->162<joint name="left_hip_pitch" type="revolute">163 <parent link="base_link"/>164 <child link="left_thigh"/>165 <origin xyz="0 0.08 0" rpy="0 0 0"/>166 <axis xyz="0 1 0"/> <!-- Pitch rotation -->167 <limit lower="-1.57" upper="1.57" effort="50.0" velocity="1.5"/>168</joint>169170<!-- Left Knee -->171<joint name="left_knee" type="revolute">172 <parent link="left_thigh"/>173 <child link="left_shin"/>174 <origin xyz="0 0 -0.4" rpy="0 0 0"/>175 <axis xyz="0 1 0"/>176 <limit lower="0.0" upper="2.4" effort="40.0" velocity="1.5"/>177</joint>178179<!-- TODO: Add right arm and right leg (mirror of left side) -->180181</robot>
Kinematic Tree Structure:
base_link (pelvis)
βββ torso
β βββ head
β βββ left_upper_arm β left_forearm
β βββ right_upper_arm β right_forearm (TODO)
βββ left_thigh β left_shin
βββ right_thigh β right_shin (TODO)
4.6 Validating and Visualizing URDFβ
4.6.1 URDF Validationβ
Use check_urdf to validate syntax and structure:
# Install urdf-tutorial packagesudo apt install ros-humble-urdf-tutorial# Validate URDF filecheck_urdf simple_humanoid.urdf# Expected output:# robot name is: simple_humanoid# ---------- Successfully Parsed XML ---------------# root Link: base_link has 2 child(ren)# child(1): torso# child(1): head# child(2): left_upper_arm# child(1): left_forearm# child(2): left_thigh# child(1): left_shin
Common errors:
- Not a tree: Loops in kinematic chain (each link can only have one parent)
- Missing parent/child: Joint references non-existent link
- Invalid inertia: Non-positive-definite inertia tensor
4.6.2 Visualizing in RViz2β
# Terminal 1: Launch robot_state_publisher and joint_state_publisher_guiros2 launch urdf_tutorial display.launch.py model:=simple_humanoid.urdf# This launches:# 1. robot_state_publisher: Publishes TF transforms based on URDF# 2. joint_state_publisher_gui: GUI sliders to control joint positions# 3. RViz2: 3D visualization# In RViz2:# - Add "RobotModel" display to see URDF# - Move sliders in joint_state_publisher_gui to animate joints# - View TF frames to see coordinate transformations
4.7 Xacro: Parameterized URDFβ
For complex robots, writing raw URDF becomes tedious (copy-pasting left/right arms). Xacro (XML Macros) adds:
- Constants: Define reusable values
- Macros: Template functions for repeated structures
- Math: Compute values (e.g.,
${'${pi/2}'})
1<?xml version="1.0"?>2<robot name="humanoid" xmlns:xacro="http://www.ros.org/wiki/xacro">34<!-- Constants -->5<xacro:property name="arm_length" value="0.3"/>6<xacro:property name="arm_radius" value="0.03"/>7<xacro:property name="arm_mass" value="1.5"/>89<!-- Macro for arm (reusable for left/right) -->10<xacro:macro name="arm" params="prefix reflect">11 <link name="${prefix}_upper_arm">12 <visual>13 <origin xyz="0 0 ${-arm_length/2}" rpy="0 0 0"/>14 <geometry>15 <cylinder radius="${arm_radius}" length="${arm_length}"/>16 </geometry>17 </visual>18 <inertial>19 <mass value="${arm_mass}"/>20 <inertia ixx="0.01" ixy="0.0" ixz="0.0"21 iyy="0.01" iyz="0.0" izz="0.001"/>22 </inertial>23 </link>2425 <joint name="${prefix}_shoulder" type="revolute">26 <parent link="torso"/>27 <child link="${prefix}_upper_arm"/>28 <origin xyz="0 ${reflect*0.15} 0.45" rpy="0 0 0"/>29 <axis xyz="0 1 0"/>30 <limit lower="-3.14" upper="3.14" effort="20.0" velocity="2.0"/>31 </joint>32</xacro:macro>3334<!-- Base and torso links (omitted for brevity) -->3536<!-- Instantiate left and right arms -->37<xacro:arm prefix="left" reflect="1"/>38<xacro:arm prefix="right" reflect="-1"/>3940</robot>
Convert xacro to URDF:
# Process xacro file to generate URDFros2 run xacro xacro humanoid.urdf.xacro > humanoid.urdf# Now use generated URDF filecheck_urdf humanoid.urdf
4.8 Hands-On Exercise: Build a Humanoid Torsoβ
Humanoid Upper Body URDF
Learning Objectives
Problem Statementβ
Build a URDF model of a humanoid upper body with:
- Base link (pelvis)
- Torso (fixed to base)
- Head (revolute neck joint, Β±90Β° yaw rotation)
- Left arm: upper arm + forearm (2 links, 2 revolute joints)
- Right arm: upper arm + forearm (2 links, 2 revolute joints)
Requirementsβ
Minimum Links: 6 (base, torso, head, left_upper_arm, left_forearm, right_upper_arm, right_forearm)
Minimum Joints: 5
- hip_joint (fixed): base β torso
- neck_joint (revolute, yaw): torso β head (Β±90Β°)
- left_shoulder (revolute, pitch): torso β left_upper_arm (Β±180Β°)
- left_elbow (revolute, pitch): left_upper_arm β left_forearm (0Β° to 160Β°)
- (Mirror for right arm)
Geometry:
- Use primitive shapes (box, cylinder, sphere)
- Torso: 0.25m Γ 0.2m Γ 0.5m box
- Head: 0.1m radius sphere
- Arms: 0.03m radius cylinders, 0.3m length
Materials:
- Assign colors (torso: blue, head: skin tone, arms: gray)
Testingβ
# Validate URDF
check_urdf humanoid_upper_body.urdf
# Visualize in RViz2
ros2 launch urdf_tutorial display.launch.py model:=humanoid_upper_body.urdf
# Verify:
# 1. All 6 links visible
# 2. Joint sliders control arm movement
# 3. TF frames show proper hierarchy
Deliverableβ
- URDF file:
humanoid_upper_body.urdf - Screenshot from RViz2 showing robot with arms in different positions
- Output from
check_urdfshowing successful validation
Key Takeawaysβ
β Core Concepts Masteredβ
- URDF is an XML-based robot description format defining links (rigid bodies), joints (connections), and properties (visual, collision, inertial)
- Links contain visual geometry for rendering, collision geometry for physics, and inertial properties (mass, inertia tensor) for simulation
- Joints connect links in a tree structure with six types: revolute (limited rotation), continuous (unlimited rotation), prismatic (linear), fixed, planar, floating
- Humanoid robots follow naming conventions (base_link, left_upper_arm, right_thigh) from REP-120 for consistency
- Kinematic chains define motion hierarchies with joints enabling degrees of freedom (DOF) for humanoid movement
- check_urdf validates URDF syntax and structure catching errors like loops, missing links, and invalid inertia tensors
- RViz2 + robot_state_publisher visualize URDF models with joint_state_publisher_gui providing interactive control
- Xacro extends URDF with macros and parameters enabling reusable templates for symmetric structures (left/right arms)
What's Next?β
In Chapter 5: Launch Files and Package Management, you'll learn to:
- Create Python launch files for multi-node systems
- Structure ROS 2 packages with proper dependencies
- Pass parameters and configure nodes via launch files
- Build packages with colcon and distribute them
Prerequisite Check: Before proceeding, ensure you can:
- β Create a URDF file with links, joints, and visual geometry
- β Explain the difference between visual and collision geometry
- β Calculate inertia tensors for primitive shapes (box, cylinder, sphere)
- β Validate URDF with check_urdf and visualize in RViz2
Next: Chapter 5: Launch Files and Package Management (coming soon)