Skip to main content

Chapter 1.9: URDF for Humanoids

Learning Objectives

By the end of this chapter, students will be able to:

  • Create and structure URDF files for humanoid robots
  • Understand the components of a humanoid robot description
  • Implement kinematic chains for arms and legs
  • Add visual and collision properties to robot models
  • Integrate URDF with ROS 2 for simulation and control

Introduction

URDF (Unified Robot Description Format) is the standard format for describing robot models in ROS. For humanoid robots, URDF becomes particularly important as it defines the complex kinematic structure, joint constraints, and physical properties needed for simulation, visualization, and control. A well-structured URDF file is essential for developing humanoid robots that can operate effectively in both simulated and real environments.

In this chapter, we'll explore how to create comprehensive URDF descriptions for humanoid robots, covering the unique challenges of modeling human-like structures with multiple degrees of freedom, complex kinematic chains, and appropriate physical properties.

URDF Fundamentals

Basic URDF Structure

A URDF file describes a robot using links connected by joints:

<?xml version="1.0"?>
<robot name="simple_robot">
<!-- Links define rigid bodies -->
<link name="base_link">
<visual>
<geometry>
<box size="0.5 0.5 0.2"/>
</geometry>
<material name="blue">
<color rgba="0 0 1 1"/>
</material>
</visual>
<collision>
<geometry>
<box size="0.5 0.5 0.2"/>
</geometry>
</collision>
<inertial>
<mass value="1.0"/>
<inertia ixx="0.1" ixy="0" ixz="0" iyy="0.1" iyz="0" izz="0.1"/>
</inertial>
</link>

<!-- Joints connect links -->
<joint name="base_to_wheel" type="continuous">
<parent link="base_link"/>
<child link="wheel_link"/>
<origin xyz="0 0.3 -0.1" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
</joint>

<link name="wheel_link">
<visual>
<geometry>
<cylinder radius="0.1" length="0.05"/>
</geometry>
</visual>
<collision>
<geometry>
<cylinder radius="0.1" length="0.05"/>
</geometry>
</collision>
<inertial>
<mass value="0.2"/>
<inertia ixx="0.001" ixy="0" ixz="0" iyy="0.001" iyz="0" izz="0.002"/>
</inertial>
</link>
</robot>

Humanoid Robot Structure

Key Components of Humanoid Robots

Humanoid robots have several key structural components:

  1. Torso/Trunk: The central body containing the main computer and power systems
  2. Head: Contains cameras, microphones, and other sensors
  3. Arms: Typically with shoulder, elbow, and wrist joints
  4. Legs: With hip, knee, and ankle joints for locomotion
  5. Hands: For manipulation tasks (optional in basic models)

Humanoid URDF Skeleton

<?xml version="1.0"?>
<robot name="humanoid_robot">
<!-- Torso -->
<link name="base_link">
<visual>
<geometry>
<box size="0.2 0.3 0.6"/>
</geometry>
<material name="grey">
<color rgba="0.5 0.5 0.5 1"/>
</material>
</visual>
<collision>
<geometry>
<box size="0.2 0.3 0.6"/>
</geometry>
</collision>
<inertial>
<mass value="10.0"/>
<inertia ixx="0.5" ixy="0" ixz="0" iyy="0.5" iyz="0" izz="0.5"/>
</inertial>
</link>

<!-- Head -->
<joint name="neck_joint" type="revolute">
<parent link="base_link"/>
<child link="head_link"/>
<origin xyz="0 0 0.35" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
<limit lower="-1.0" upper="1.0" effort="10" velocity="2"/>
</joint>

<link name="head_link">
<visual>
<geometry>
<sphere radius="0.1"/>
</geometry>
</visual>
<collision>
<geometry>
<sphere radius="0.1"/>
</geometry>
</collision>
<inertial>
<mass value="1.0"/>
<inertia ixx="0.01" ixy="0" ixz="0" iyy="0.01" iyz="0" izz="0.01"/>
</inertial>
</link>

<!-- Left Arm -->
<joint name="left_shoulder_pitch" type="revolute">
<parent link="base_link"/>
<child link="left_upper_arm"/>
<origin xyz="0.15 0.15 0.1" rpy="0 0 0"/>
<axis xyz="1 0 0"/>
<limit lower="-1.57" upper="1.57" effort="50" velocity="2"/>
</joint>

<link name="left_upper_arm">
<visual>
<geometry>
<cylinder length="0.3" radius="0.05"/>
</geometry>
<origin xyz="0 0 0.15" rpy="1.57 0 0"/>
</visual>
<collision>
<geometry>
<cylinder length="0.3" radius="0.05"/>
</geometry>
<origin xyz="0 0 0.15" rpy="1.57 0 0"/>
</collision>
<inertial>
<mass value="2.0"/>
<inertia ixx="0.02" ixy="0" ixz="0" iyy="0.02" iyz="0" izz="0.001"/>
</inertial>
</link>

<!-- Continue with more joints and links for complete humanoid -->
</robot>

Complete Humanoid URDF Example

Here's a more complete humanoid robot description:

<?xml version="1.0"?>
<robot name="simple_humanoid" xmlns:xacro="http://www.ros.org/wiki/xacro">
<!-- Include gazebo plugins -->
<gazebo>
<plugin name="gazebo_ros_control" filename="libgazebo_ros_control.so">
<robotNamespace>/simple_humanoid</robotNamespace>
</plugin>
</gazebo>

<!-- Materials -->
<material name="black">
<color rgba="0.0 0.0 0.0 1.0"/>
</material>
<material name="blue">
<color rgba="0.0 0.0 0.8 1.0"/>
</material>
<material name="green">
<color rgba="0.0 0.8 0.0 1.0"/>
</material>
<material name="grey">
<color rgba="0.5 0.5 0.5 1.0"/>
</material>
<material name="orange">
<color rgba="1.0 0.423529411765 0.0392156862745 1.0"/>
</material>
<material name="brown">
<color rgba="0.870588235294 0.811764705882 0.764705882353 1.0"/>
</material>
<material name="red">
<color rgba="0.8 0.0 0.0 1.0"/>
</material>
<material name="white">
<color rgba="1.0 1.0 1.0 1.0"/>
</material>

<!-- Base Link -->
<link name="base_link">
<visual>
<geometry>
<box size="0.1 0.2 0.4"/>
</geometry>
<material name="grey"/>
</visual>
<collision>
<geometry>
<box size="0.1 0.2 0.4"/>
</geometry>
</collision>
<inertial>
<mass value="5.0"/>
<inertia ixx="0.1" ixy="0.0" ixz="0.0" iyy="0.1" iyz="0.0" izz="0.1"/>
</inertial>
</link>

<!-- Torso -->
<joint name="torso_joint" type="fixed">
<parent link="base_link"/>
<child link="torso"/>
<origin xyz="0 0 0.2" rpy="0 0 0"/>
</joint>

<link name="torso">
<visual>
<geometry>
<box size="0.25 0.25 0.5"/>
</geometry>
<material name="white"/>
</visual>
<collision>
<geometry>
<box size="0.25 0.25 0.5"/>
</geometry>
</collision>
<inertial>
<mass value="10.0"/>
<inertia ixx="0.2" ixy="0.0" ixz="0.0" iyy="0.2" iyz="0.0" izz="0.2"/>
</inertial>
</link>

<!-- Head -->
<joint name="neck_joint" type="revolute">
<parent link="torso"/>
<child link="head"/>
<origin xyz="0 0 0.3" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
<limit lower="-1.0" upper="1.0" effort="10" velocity="2"/>
</joint>

<link name="head">
<visual>
<geometry>
<sphere radius="0.1"/>
</geometry>
<material name="skin"/>
</visual>
<collision>
<geometry>
<sphere radius="0.1"/>
</geometry>
</collision>
<inertial>
<mass value="2.0"/>
<inertia ixx="0.02" ixy="0.0" ixz="0.0" iyy="0.02" iyz="0.0" izz="0.02"/>
</inertial>
</link>

<!-- Left Arm -->
<joint name="left_shoulder_joint" type="revolute">
<parent link="torso"/>
<child link="left_upper_arm"/>
<origin xyz="0.15 0.1 0.1" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
<limit lower="-1.57" upper="1.57" effort="50" velocity="2"/>
</joint>

<link name="left_upper_arm">
<visual>
<geometry>
<cylinder length="0.3" radius="0.05"/>
</geometry>
<origin xyz="0 0 0.15" rpy="1.57 0 0"/>
</visual>
<collision>
<geometry>
<cylinder length="0.3" radius="0.05"/>
</geometry>
<origin xyz="0 0 0.15" rpy="1.57 0 0"/>
</collision>
<inertial>
<mass value="2.0"/>
<inertia ixx="0.02" ixy="0.0" ixz="0.0" iyy="0.02" iyz="0.0" izz="0.001"/>
</inertial>
</link>

<joint name="left_elbow_joint" type="revolute">
<parent link="left_upper_arm"/>
<child link="left_lower_arm"/>
<origin xyz="0 0 0.3" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
<limit lower="-1.57" upper="1.57" effort="30" velocity="2"/>
</joint>

<link name="left_lower_arm">
<visual>
<geometry>
<cylinder length="0.3" radius="0.04"/>
</geometry>
<origin xyz="0 0 0.15" rpy="1.57 0 0"/>
</visual>
<collision>
<geometry>
<cylinder length="0.3" radius="0.04"/>
</geometry>
<origin xyz="0 0 0.15" rpy="1.57 0 0"/>
</collision>
<inertial>
<mass value="1.5"/>
<inertia ixx="0.01" ixy="0.0" ixz="0.0" iyy="0.01" iyz="0.0" izz="0.001"/>
</inertial>
</link>

<!-- Right Arm (symmetric to left) -->
<joint name="right_shoulder_joint" type="revolute">
<parent link="torso"/>
<child link="right_upper_arm"/>
<origin xyz="0.15 -0.1 0.1" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
<limit lower="-1.57" upper="1.57" effort="50" velocity="2"/>
</joint>

<link name="right_upper_arm">
<visual>
<geometry>
<cylinder length="0.3" radius="0.05"/>
</geometry>
<origin xyz="0 0 0.15" rpy="1.57 0 0"/>
</visual>
<collision>
<geometry>
<cylinder length="0.3" radius="0.05"/>
</geometry>
<origin xyz="0 0 0.15" rpy="1.57 0 0"/>
</collision>
<inertial>
<mass value="2.0"/>
<inertia ixx="0.02" ixy="0.0" ixz="0.0" iyy="0.02" iyz="0.0" izz="0.001"/>
</inertial>
</link>

<joint name="right_elbow_joint" type="revolute">
<parent link="right_upper_arm"/>
<child link="right_lower_arm"/>
<origin xyz="0 0 0.3" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
<limit lower="-1.57" upper="1.57" effort="30" velocity="2"/>
</joint>

<link name="right_lower_arm">
<visual>
<geometry>
<cylinder length="0.3" radius="0.04"/>
</geometry>
<origin xyz="0 0 0.15" rpy="1.57 0 0"/>
</visual>
<collision>
<geometry>
<cylinder length="0.3" radius="0.04"/>
</geometry>
<origin xyz="0 0 0.15" rpy="1.57 0 0"/>
</collision>
<inertial>
<mass value="1.5"/>
<inertia ixx="0.01" ixy="0.0" ixz="0.0" iyy="0.01" iyz="0.0" izz="0.001"/>
</inertial>
</link>

<!-- Left Leg -->
<joint name="left_hip_joint" type="revolute">
<parent link="torso"/>
<child link="left_upper_leg"/>
<origin xyz="-0.05 0.08 -0.25" rpy="0 0 0"/>
<axis xyz="1 0 0"/>
<limit lower="-1.57" upper="1.57" effort="100" velocity="2"/>
</joint>

<link name="left_upper_leg">
<visual>
<geometry>
<cylinder length="0.4" radius="0.06"/>
</geometry>
<origin xyz="0 0 -0.2" rpy="1.57 0 0"/>
</visual>
<collision>
<geometry>
<cylinder length="0.4" radius="0.06"/>
</geometry>
<origin xyz="0 0 -0.2" rpy="1.57 0 0"/>
</collision>
<inertial>
<mass value="3.0"/>
<inertia ixx="0.05" ixy="0.0" ixz="0.0" iyy="0.05" iyz="0.0" izz="0.002"/>
</inertial>
</link>

<joint name="left_knee_joint" type="revolute">
<parent link="left_upper_leg"/>
<child link="left_lower_leg"/>
<origin xyz="0 0 -0.4" rpy="0 0 0"/>
<axis xyz="1 0 0"/>
<limit lower="0" upper="1.57" effort="80" velocity="2"/>
</joint>

<link name="left_lower_leg">
<visual>
<geometry>
<cylinder length="0.4" radius="0.05"/>
</geometry>
<origin xyz="0 0 -0.2" rpy="1.57 0 0"/>
</visual>
<collision>
<geometry>
<cylinder length="0.4" radius="0.05"/>
</geometry>
<origin xyz="0 0 -0.2" rpy="1.57 0 0"/>
</collision>
<inertial>
<mass value="2.5"/>
<inertia ixx="0.03" ixy="0.0" ixz="0.0" iyy="0.03" iyz="0.0" izz="0.001"/>
</inertial>
</link>

<joint name="left_ankle_joint" type="revolute">
<parent link="left_lower_leg"/>
<child link="left_foot"/>
<origin xyz="0 0 -0.4" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
<limit lower="-0.5" upper="0.5" effort="50" velocity="2"/>
</joint>

<link name="left_foot">
<visual>
<geometry>
<box size="0.2 0.1 0.05"/>
</geometry>
</visual>
<collision>
<geometry>
<box size="0.2 0.1 0.05"/>
</geometry>
</collision>
<inertial>
<mass value="1.0"/>
<inertia ixx="0.01" ixy="0.0" ixz="0.0" iyy="0.01" iyz="0.0" izz="0.01"/>
</inertial>
</link>

<!-- Right Leg (symmetric to left) -->
<joint name="right_hip_joint" type="revolute">
<parent link="torso"/>
<child link="right_upper_leg"/>
<origin xyz="-0.05 -0.08 -0.25" rpy="0 0 0"/>
<axis xyz="1 0 0"/>
<limit lower="-1.57" upper="1.57" effort="100" velocity="2"/>
</joint>

<link name="right_upper_leg">
<visual>
<geometry>
<cylinder length="0.4" radius="0.06"/>
</geometry>
<origin xyz="0 0 -0.2" rpy="1.57 0 0"/>
</visual>
<collision>
<geometry>
<cylinder length="0.4" radius="0.06"/>
</geometry>
<origin xyz="0 0 -0.2" rpy="1.57 0 0"/>
</collision>
<inertial>
<mass value="3.0"/>
<inertia ixx="0.05" ixy="0.0" ixz="0.0" iyy="0.05" iyz="0.0" izz="0.002"/>
</inertial>
</link>

<joint name="right_knee_joint" type="revolute">
<parent link="right_upper_leg"/>
<child link="right_lower_leg"/>
<origin xyz="0 0 -0.4" rpy="0 0 0"/>
<axis xyz="1 0 0"/>
<limit lower="0" upper="1.57" effort="80" velocity="2"/>
</joint>

<link name="right_lower_leg">
<visual>
<geometry>
<cylinder length="0.4" radius="0.05"/>
</geometry>
<origin xyz="0 0 -0.2" rpy="1.57 0 0"/>
</visual>
<collision>
<geometry>
<cylinder length="0.4" radius="0.05"/>
</geometry>
<origin xyz="0 0 -0.2" rpy="1.57 0 0"/>
</collision>
<inertial>
<mass value="2.5"/>
<inertia ixx="0.03" ixy="0.0" ixz="0.0" iyy="0.03" iyz="0.0" izz="0.001"/>
</inertial>
</link>

<joint name="right_ankle_joint" type="revolute">
<parent link="right_lower_leg"/>
<child link="right_foot"/>
<origin xyz="0 0 -0.4" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
<limit lower="-0.5" upper="0.5" effort="50" velocity="2"/>
</joint>

<link name="right_foot">
<visual>
<geometry>
<box size="0.2 0.1 0.05"/>
</geometry>
</visual>
<collision>
<geometry>
<box size="0.2 0.1 0.05"/>
</geometry>
</collision>
<inertial>
<mass value="1.0"/>
<inertia ixx="0.01" ixy="0.0" ixz="0.0" iyy="0.01" iyz="0.0" izz="0.01"/>
</inertial>
</link>
</robot>

Using Xacro for Complex Humanoid URDFs

Xacro is a macro language for URDF that allows for more complex and reusable robot descriptions:

<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="humanoid_xacro">

<!-- Properties -->
<xacro:property name="M_PI" value="3.1415926535897931" />
<xacro:property name="mass_torso" value="10.0" />
<xacro:property name="mass_head" value="2.0" />
<xacro:property name="mass_arm" value="2.0" />
<xacro:property name="mass_leg" value="3.0" />

<!-- Macro for a simple link -->
<xacro:macro name="simple_link" params="name xyz size mass ixx iyy izz">
<link name="${name}">
<visual>
<origin xyz="${xyz}" rpy="0 0 0" />
<geometry>
<box size="${size}" />
</geometry>
<material name="light_grey">
<color rgba="0.8 0.8 0.8 1.0"/>
</material>
</visual>
<collision>
<origin xyz="${xyz}" rpy="0 0 0" />
<geometry>
<box size="${size}" />
</geometry>
</collision>
<inertial>
<mass value="${mass}" />
<inertia ixx="${ixx}" ixy="0.0" ixz="0.0"
iyy="${iyy}" iyz="0.0" izz="${izz}" />
</inertial>
</link>
</xacro:macro>

<!-- Macro for an arm -->
<xacro:macro name="arm" params="side reflect xyz_origin">
<!-- Shoulder -->
<joint name="${side}_shoulder_pitch" type="revolute">
<parent link="torso"/>
<child link="${side}_shoulder"/>
<origin xyz="${xyz_origin}" rpy="0 0 0"/>
<axis xyz="1 0 0"/>
<limit lower="-1.57" upper="1.57" effort="50" velocity="2"/>
</joint>

<link name="${side}_shoulder">
<visual>
<geometry>
<cylinder length="0.1" radius="0.05"/>
</geometry>
<origin xyz="0 0 0.05" rpy="${M_PI/2} 0 0"/>
</visual>
<collision>
<geometry>
<cylinder length="0.1" radius="0.05"/>
</geometry>
<origin xyz="0 0 0.05" rpy="${M_PI/2} 0 0"/>
</collision>
<inertial>
<mass value="1.0"/>
<inertia ixx="0.01" ixy="0" ixz="0" iyy="0.01" iyz="0" izz="0.001"/>
</inertial>
</link>

<joint name="${side}_shoulder_yaw" type="revolute">
<parent link="${side}_shoulder"/>
<child link="${side}_upper_arm"/>
<origin xyz="0 ${reflect * 0.05} 0.1" rpy="0 0 0"/>
<axis xyz="0 0 1"/>
<limit lower="-1.57" upper="1.57" effort="50" velocity="2"/>
</joint>

<link name="${side}_upper_arm">
<visual>
<geometry>
<cylinder length="0.3" radius="0.04"/>
</geometry>
<origin xyz="0 0 0.15" rpy="${M_PI/2} 0 0"/>
</visual>
<collision>
<geometry>
<cylinder length="0.3" radius="0.04"/>
</geometry>
<origin xyz="0 0 0.15" rpy="${M_PI/2} 0 0"/>
</collision>
<inertial>
<mass value="1.5"/>
<inertia ixx="0.01" ixy="0" ixz="0" iyy="0.01" iyz="0" izz="0.001"/>
</inertial>
</link>

<joint name="${side}_elbow" type="revolute">
<parent link="${side}_upper_arm"/>
<child link="${side}_lower_arm"/>
<origin xyz="0 0 0.3" rpy="0 0 0"/>
<axis xyz="1 0 0"/>
<limit lower="-1.57" upper="1.57" effort="30" velocity="2"/>
</joint>

<link name="${side}_lower_arm">
<visual>
<geometry>
<cylinder length="0.3" radius="0.03"/>
</geometry>
<origin xyz="0 0 0.15" rpy="${M_PI/2} 0 0"/>
</visual>
<collision>
<geometry>
<cylinder length="0.3" radius="0.03"/>
</geometry>
<origin xyz="0 0 0.15" rpy="${M_PI/2} 0 0"/>
</collision>
<inertial>
<mass value="1.0"/>
<inertia ixx="0.01" ixy="0" ixz="0" iyy="0.01" iyz="0" izz="0.001"/>
</inertial>
</link>
</xacro:macro>

<!-- Base and torso -->
<link name="base_link">
<inertial>
<mass value="1.0"/>
<inertia ixx="0.1" ixy="0" ixz="0" iyy="0.1" iyz="0" izz="0.1"/>
</inertial>
</link>

<joint name="base_to_torso" type="fixed">
<parent link="base_link"/>
<child link="torso"/>
<origin xyz="0 0 0.3" rpy="0 0 0"/>
</joint>

<link name="torso">
<visual>
<geometry>
<box size="0.3 0.3 0.6"/>
</geometry>
<material name="white">
<color rgba="1 1 1 1"/>
</material>
</visual>
<collision>
<geometry>
<box size="0.3 0.3 0.6"/>
</geometry>
</collision>
<inertial>
<mass value="${mass_torso}"/>
<inertia ixx="0.2" ixy="0" ixz="0" iyy="0.2" iyz="0" izz="0.2"/>
</inertial>
</link>

<!-- Head -->
<joint name="neck" type="revolute">
<parent link="torso"/>
<child link="head"/>
<origin xyz="0 0 0.35" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
<limit lower="-1.0" upper="1.0" effort="10" velocity="2"/>
</joint>

<link name="head">
<visual>
<geometry>
<sphere radius="0.1"/>
</geometry>
</visual>
<collision>
<geometry>
<sphere radius="0.1"/>
</geometry>
</collision>
<inertial>
<mass value="${mass_head}"/>
<inertia ixx="0.02" ixy="0" ixz="0" iyy="0.02" iyz="0" izz="0.02"/>
</inertial>
</link>

<!-- Arms using macro -->
<xacro:arm side="left" reflect="1" xyz_origin="0.15 0.1 0.1"/>
<xacro:arm side="right" reflect="-1" xyz_origin="0.15 -0.1 0.1"/>

</robot>

URDF for Simulation

Gazebo Integration

To use your URDF in Gazebo simulation, add Gazebo-specific tags:

<!-- Add to your URDF file -->
<gazebo reference="base_link">
<material>Gazebo/Grey</material>
</gazebo>

<!-- For joints with actuators -->
<gazebo>
<plugin name="gazebo_ros_control" filename="libgazebo_ros_control.so">
<robotNamespace>/humanoid_robot</robotNamespace>
<controlPeriod>0.001</controlPeriod>
</plugin>
</gazebo>

<!-- For sensors -->
<gazebo reference="head">
<sensor name="camera" type="camera">
<pose>0.05 0 0 0 0 0</pose>
<camera name="head_camera">
<horizontal_fov>1.047</horizontal_fov>
<image>
<width>640</width>
<height>480</height>
<format>R8G8B8</format>
</image>
<clip>
<near>0.1</near>
<far>100</far>
</clip>
</camera>
<always_on>true</always_on>
<update_rate>30</update_rate>
<visualize>true</visualize>
</sensor>
</gazebo>

Working with URDF in ROS 2

Loading URDF and Publishing TF

# my_robot_package/nodes/robot_state_publisher.py
import rclpy
from rclpy.node import Node
from sensor_msgs.msg import JointState
from geometry_msgs.msg import TransformStamped
from tf2_ros import TransformBroadcaster
import math
import numpy as np


class RobotStatePublisher(Node):
def __init__(self):
super().__init__('robot_state_publisher')

# Create publisher for joint states
self.joint_pub = self.create_publisher(JointState, 'joint_states', 10)

# Create transform broadcaster
self.tf_broadcaster = TransformBroadcaster(self)

# Create timer to publish state
self.timer = self.create_timer(0.1, self.publish_joint_states)

# Initialize joint names and positions
self.joint_names = [
'neck_joint',
'left_shoulder_pitch', 'left_elbow_joint',
'right_shoulder_pitch', 'right_elbow_joint',
'left_hip_joint', 'left_knee_joint', 'left_ankle_joint',
'right_hip_joint', 'right_knee_joint', 'right_ankle_joint'
]

self.joint_positions = [0.0] * len(self.joint_names)
self.joint_velocities = [0.0] * len(self.joint_names)
self.joint_efforts = [0.0] * len(self.joint_names)

self.get_logger().info('Robot state publisher initialized')

def publish_joint_states(self):
"""Publish joint states and transforms."""
# Create joint state message
msg = JointState()
msg.name = self.joint_names
msg.position = self.joint_positions
msg.velocity = self.joint_velocities
msg.effort = self.joint_efforts
msg.header.stamp = self.get_clock().now().to_msg()
msg.header.frame_id = 'base_link'

# Publish joint states
self.joint_pub.publish(msg)

# Publish transforms (simplified - in practice, use forward kinematics)
self.publish_transforms()

def publish_transforms(self):
"""Publish transforms for the robot."""
# Example: Publish a simple transform
t = TransformStamped()

t.header.stamp = self.get_clock().now().to_msg()
t.header.frame_id = 'base_link'
t.child_frame_id = 'torso'

# Set transform (example values)
t.transform.translation.x = 0.0
t.transform.translation.y = 0.0
t.transform.translation.z = 0.3
t.transform.rotation.x = 0.0
t.transform.rotation.y = 0.0
t.transform.rotation.z = 0.0
t.transform.rotation.w = 1.0

self.tf_broadcaster.sendTransform(t)


def main(args=None):
rclpy.init(args=args)
node = RobotStatePublisher()

try:
rclpy.spin(node)
except KeyboardInterrupt:
pass
finally:
node.destroy_node()
rclpy.shutdown()

if __name__ == '__main__':
main()

URDF Validation and Visualization

# Validate URDF syntax
check_urdf /path/to/robot.urdf

# Visualize URDF in RViz
ros2 run rviz2 rviz2

# Publish URDF to parameter server
ros2 param set /robot_state_publisher robot_description "$(cat robot.urdf)"

# Or use a launch file to load URDF
ros2 launch my_robot_package robot.launch.py

Best Practices for Humanoid URDF

1. Proper Inertial Properties

<!-- Always include realistic inertial properties -->
<link name="upper_arm">
<visual>
<geometry>
<cylinder length="0.3" radius="0.05"/>
</geometry>
</visual>
<collision>
<geometry>
<cylinder length="0.3" radius="0.05"/>
</geometry>
</collision>
<!-- Proper inertial calculation -->
<inertial>
<mass value="2.0"/>
<!-- Calculate moments of inertia for cylinder -->
<inertia ixx="0.02" ixy="0" ixz="0"
iyy="0.02" iyz="0" izz="0.001"/>
</inertial>
</link>

2. Appropriate Joint Limits

<!-- Set realistic joint limits based on human anatomy -->
<joint name="elbow_joint" type="revolute">
<parent link="upper_arm"/>
<child link="lower_arm"/>
<axis xyz="1 0 0"/>
<!-- Human elbow can't rotate fully - set appropriate limits -->
<limit lower="0" upper="2.356" effort="30" velocity="2"/>
<!-- 2.356 radians = 135 degrees -->
</joint>

3. Use Xacro for Complex Models

Xacro helps manage complex humanoid models by allowing parameterization and macros:

<xacro:macro name="humanoid_limb" params="name parent_link xyz_origin joint_limits">
<!-- Define a parameterized limb -->
<joint name="${name}_joint" type="revolute">
<parent link="${parent_link}"/>
<child link="${name}_link"/>
<origin xyz="${xyz_origin}" rpy="0 0 0"/>
<axis xyz="1 0 0"/>
<limit lower="${joint_limits[0]}" upper="${joint_limits[1]}"
effort="${joint_limits[2]}" velocity="${joint_limits[3]}"/>
</joint>

<link name="${name}_link">
<visual>
<geometry>
<cylinder length="0.4" radius="0.05"/>
</geometry>
<origin xyz="0 0 -0.2" rpy="${M_PI/2} 0 0"/>
</visual>
<collision>
<geometry>
<cylinder length="0.4" radius="0.05"/>
</geometry>
<origin xyz="0 0 -0.2" rpy="${M_PI/2} 0 0"/>
</collision>
<inertial>
<mass value="3.0"/>
<inertia ixx="0.05" ixy="0" ixz="0" iyy="0.05" iyz="0" izz="0.002"/>
</inertial>
</link>
</xacro:macro>

Hands-On Exercise: Create a Humanoid URDF

Objective

Create a complete humanoid robot URDF with proper kinematic structure.

Prerequisites

  • ROS 2 Humble installed
  • Understanding of URDF basics
  • Text editor or XML editor

Steps

  1. Create a new ROS 2 package called humanoid_description
  2. Create a URDF file for a simple humanoid robot with:
    • Torso, head, 2 arms, 2 legs
    • Appropriate joints with realistic limits
    • Visual and collision properties
    • Inertial properties
  3. Validate the URDF file
  4. Visualize the robot in RViz
  5. Create a launch file to load and display the robot
  6. Add Gazebo integration tags

Expected Result

Students will have a complete, valid humanoid URDF that can be visualized and used in simulation.

Assessment Questions

Multiple Choice

Q1: What does URDF stand for in ROS?

  • a) Unified Robot Definition Format
  • b) Universal Robot Description Format
  • c) Unified Robot Description Format
  • d) Universal Robot Definition Format
Details

Click to reveal answer Answer: c
Explanation: URDF stands for Unified Robot Description Format, which is the standard format for describing robot models in ROS.

Short Answer

Q2: Explain the difference between visual, collision, and inertial properties in URDF, and why all three are important for humanoid robots.

Practical Exercise

Q3: Create a URDF for a humanoid robot with at least 10 joints, including arms, legs, and a head. Ensure the model has appropriate joint limits based on human anatomy, realistic inertial properties, and proper visual/collision geometries. Validate the URDF and visualize it in RViz.

Further Reading

  1. "URDF Guide" - Official ROS guide to URDF
  2. "Xacro Tutorial" - Advanced URDF with macros
  3. "Robot Modeling for Simulation" - Best practices for simulation-ready models

Summary

In this chapter, we've explored URDF (Unified Robot Description Format) for humanoid robots, learning how to create comprehensive robot descriptions that include proper kinematic structures, visual and collision properties, and inertial parameters. We've covered the fundamentals of URDF, advanced techniques using Xacro, and integration with simulation environments.

Creating accurate URDF models is essential for humanoid robotics as they form the basis for simulation, visualization, motion planning, and control. Properly defined inertial properties, joint limits, and kinematic chains are crucial for realistic simulation and effective robot control.

With the completion of this chapter, we've covered all the fundamental ROS 2 concepts needed for physical AI and humanoid robotics. In the next module, we'll explore simulation environments where these concepts come together in practice.