Skip to main content

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:

simple_robot.urdf
Beginner
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>
7
8<link name="arm_link">
9 <!-- ... -->
10</link>
11
12<!-- 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

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:

link_with_visual.urdf
Beginner
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:

link_with_collision.urdf
Beginner
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>
8
9<!-- 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:

link_with_inertial.urdf
Intermediate
1<link name="upper_arm">
2<!-- Visual and collision omitted for brevity -->
3
4<inertial>
5 <origin xyz="0 0 0.1" rpy="0 0 0"/>
6 <mass value="2.5"/> <!-- kg -->
7 <inertia
8 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)

Joints define how links move relative to each other. ROS 2 supports six joint types:

Joint TypeDescriptionExample
revoluteRotates around axis with limitsElbow (0Β° to 150Β°)
continuousRotates 360Β° without limitsWheel axle
prismaticSlides along axis with limitsLinear actuator
fixedNo movement (rigid attachment)Sensor mount
planarMoves in 2D planeOmni-directional base
floating6-DOF free motionFree-falling object

4.3.1 Revolute Joint Example​

revolute_joint.urdf
Intermediate
1<joint name="shoulder_pitch" type="revolute">
2<parent link="torso"/>
3<child link="upper_arm"/>
4
5<!-- Joint attachment point on parent link -->
6<origin xyz="0.15 0.2 0.4" rpy="0 0 0"/>
7
8<!-- Rotation axis (1 0 0 = X-axis) -->
9<axis xyz="0 1 0"/>
10
11<!-- Joint limits -->
12<limit
13 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/>
18
19<!-- 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:

two_dof_arm.urdf
Intermediate⏱ ~30 min
1<?xml version="1.0"?>
2<robot name="two_dof_arm">
3
4<!-- 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>
25
26<!-- 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>
50
51<!-- 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>
75
76<!-- 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>
84
85<!-- 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>
93
94</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​

simple_humanoid.urdf
Advanced⏱ ~60 min
1<?xml version="1.0"?>
2<robot name="simple_humanoid">
3
4<!-- 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>
20
21<!-- 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>
39
40<!-- 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>
56
57<!-- 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>
73
74<!-- 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>
90
91<!-- 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>
107
108<!-- 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>
124
125<!-- JOINTS -->
126
127<!-- 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>
133
134<!-- 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>
142
143<!-- 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>
151
152<!-- 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>
160
161<!-- 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>
169
170<!-- 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>
178
179<!-- TODO: Add right arm and right leg (mirror of left side) -->
180
181</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 package
sudo apt install ros-humble-urdf-tutorial
# Validate URDF file
check_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_gui
ros2 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}'})
humanoid.urdf.xacro
Advanced
1<?xml version="1.0"?>
2<robot name="humanoid" xmlns:xacro="http://www.ros.org/wiki/xacro">
3
4<!-- 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"/>
8
9<!-- 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>
24
25 <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>
33
34<!-- Base and torso links (omitted for brevity) -->
35
36<!-- Instantiate left and right arms -->
37<xacro:arm prefix="left" reflect="1"/>
38<xacro:arm prefix="right" reflect="-1"/>
39
40</robot>

Convert xacro to URDF:

# Process xacro file to generate URDF
ros2 run xacro xacro humanoid.urdf.xacro > humanoid.urdf
# Now use generated URDF file
check_urdf humanoid.urdf

4.8 Hands-On Exercise: Build a Humanoid Torso​

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)