Files
2026-08-05 18:17:25 +08:00

20 KiB

60 · URDF 机器人模型(完全指南)

目标:能读懂 URDF,能从零写一个 6-DoF 机械臂 URDF,理解 link / joint / xacro / 物理参数,会校验 + 可视化。


目录


1. URDF 是什么

URDF (Unified Robot Description Format) = 用 XML 描述机器人:

  • 有哪些刚体段(<link>)
  • 刚体之间怎么连(<joint>)
  • 视觉 / 碰撞几何(<visual> / <collision>)
  • 物理参数(质量、惯性,<inertial>)

URDF 通过 robot_state_publisher 翻译成 TF tree,被 MoveIt2 / Gazebo / RViz 读取。


2. URDF 三大元素

子标签 用途 必填
<visual> 视觉几何(RViz / Gazebo 显示) 推荐
<collision> 碰撞几何(物理仿真) 推荐
<inertial> 质量 + 转动惯量(动力学仿真必填) 仿真必填
<material> 视觉颜色
<link name="base_link">
  <visual>
    <origin xyz="0 0 0.05" rpy="0 0 0"/>
    <geometry><box size="0.1 0.1 0.1"/></geometry>
    <material name="gray">
      <color rgba="0.5 0.5 0.5 1.0"/>
    </material>
  </visual>
  <collision>
    <origin xyz="0 0 0.05"/>
    <geometry><box size="0.1 0.1 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>

几何类型

<geometry>
  <box size="w h d"/>           <!-- 长方体 -->
  <sphere radius="r"/>           <!-- 球 -->
  <cylinder radius="r" length="L"/>  <!-- 圆柱 -->
  <mesh filename="package://my_pkg/meshes/arm.stl" scale="0.001 0.001 0.001"/>
</geometry>

颜色 / 材质

<material name="gray">
  <color rgba="0.5 0.5 0.5 1.0"/>     <!-- RGBA,0~1 -->
  <texture filename="..."/>          <!-- 贴图(可选) -->
</material>

2.2 <joint> — 关节

type 含义 DoF
revolute 转动(有限位) 1
continuous 转动(无限位) 1
prismatic 滑动 1
fixed 固定连接 0
floating 6 DoF 6
planar 平面 3 DoF 3
<joint name="joint1" type="revolute">
  <parent link="base_link"/>
  <child  link="link1"/>
  <origin xyz="0 0 0.05" rpy="0 0 0"/>     <!-- 子 link 相对父 link -->
  <axis   xyz="0 0 1"/>                   <!-- 在 parent 系下定义 -->
  <limit  lower="-3.14" upper="3.14"      <!-- 关节限位 -->
          effort="100" velocity="1.0"/>   <!-- 力矩 / 速度上限 -->
  <dynamics damping="0.1" friction="0.0"/> <!-- 仿真摩擦 -->
</joint>

重要约定:

  • axisparent link 系下定义
  • originchild 相对 parent 的偏移
  • 关节运动绕 axis 旋转(或沿 axis 平移)

2.3 <robot>

  • name:机器人名字,在 ROS 工具中显示
  • 必须包含所有 <link><joint>

2.4 最小 URDF 示例

<?xml version="1.0"?>
<robot name="hello_arm">
  <link name="base_link">
    <visual><geometry><box size="0.1 0.1 0.1"/></geometry></visual>
  </link>
  <joint name="joint1" type="revolute">
    <parent link="base_link"/>
    <child  link="link1"/>
    <origin xyz="0 0 0.05"/>
    <axis   xyz="0 0 1"/>
    <limit  lower="-3.14" upper="3.14" effort="1.0" velocity="1.0"/>
  </joint>
  <link name="link1">
    <visual><geometry><box size="0.05 0.05 0.1"/></geometry></visual>
  </link>
</robot>

3. 完整 6-DoF 机械臂示例

模拟 6-DoF 工业机械臂(类似 UR5 / xArm 6):

<?xml version="1.0"?>
<robot name="my_6dof_arm">
  <!-- 材料定义 -->
  <material name="white"><color rgba="1 1 1 1"/></material>
  <material name="red"><color rgba="0.8 0.2 0.2 1"/></material>

  <!-- 1) base_link:固定基座 -->
  <link name="base_link">
    <visual>
      <geometry><cylinder radius="0.05" length="0.05"/></geometry>
      <material name="white"/>
    </visual>
    <collision>
      <geometry><cylinder radius="0.05" length="0.05"/></geometry>
    </collision>
    <inertial>
      <mass value="1.0"/>
      <inertia ixx="0.005" ixy="0" ixz="0" iyy="0.005" iyz="0" izz="0.005"/>
    </inertial>
  </link>

  <!-- 2) joint1:底盘转动(绕 z 轴) -->
  <joint name="joint1" type="revolute">
    <parent link="base_link"/>
    <child  link="shoulder_link"/>
    <origin xyz="0 0 0.05"/>
    <axis   xyz="0 0 1"/>
    <limit  lower="-3.14" upper="3.14" effort="100" velocity="2.0"/>
  </joint>
  <link name="shoulder_link">
    <visual>
      <geometry><box size="0.05 0.05 0.05"/></geometry>
      <material name="red"/>
    </visual>
    <collision>
      <geometry><box size="0.05 0.05 0.05"/></geometry>
    </collision>
    <inertial>
      <mass value="0.3"/>
      <inertia ixx="0.001" ixy="0" ixz="0" iyy="0.001" iyz="0" izz="0.001"/>
    </inertial>
  </link>

  <!-- 3) joint2:肩部俯仰(绕 y 轴) -->
  <joint name="joint2" type="revolute">
    <parent link="shoulder_link"/>
    <child  link="upper_arm_link"/>
    <origin xyz="0 0 0.05"/>
    <axis   xyz="0 1 0"/>
    <limit  lower="-1.57" upper="1.57" effort="100" velocity="2.0"/>
  </joint>
  <link name="upper_arm_link">
    <visual>
      <origin xyz="0 0 0.1"/>
      <geometry><cylinder radius="0.025" length="0.2"/></geometry>
      <material name="white"/>
    </visual>
    <collision>
      <origin xyz="0 0 0.1"/>
      <geometry><cylinder radius="0.025" length="0.2"/></geometry>
    </collision>
    <inertial>
      <mass value="0.5"/>
      <inertia ixx="0.002" ixy="0" ixz="0" iyy="0.002" iyz="0" izz="0.0005"/>
    </inertial>
  </link>

  <!-- 4) joint3:肘部俯仰 -->
  <joint name="joint3" type="revolute">
    <parent link="upper_arm_link"/>
    <child  link="forearm_link"/>
    <origin xyz="0 0 0.2"/>
    <axis   xyz="0 1 0"/>
    <limit  lower="-2.0" upper="2.0" effort="50" velocity="2.0"/>
  </joint>
  <link name="forearm_link">
    <visual>
      <origin xyz="0 0 0.075"/>
      <geometry><cylinder radius="0.02" length="0.15"/></geometry>
      <material name="white"/>
    </visual>
    <collision>
      <origin xyz="0 0 0.075"/>
      <geometry><cylinder radius="0.02" length="0.15"/></geometry>
    </collision>
    <inertial>
      <mass value="0.3"/>
      <inertia ixx="0.001" ixy="0" ixz="0" iyy="0.001" iyz="0" izz="0.0003"/>
    </inertial>
  </link>

  <!-- 5) joint4:腕部旋转 -->
  <joint name="joint4" type="revolute">
    <parent link="forearm_link"/>
    <child  link="wrist_1_link"/>
    <origin xyz="0 0 0.15"/>
    <axis   xyz="1 0 0"/>
    <limit  lower="-3.14" upper="3.14" effort="20" velocity="2.0"/>
  </joint>
  <link name="wrist_1_link">
    <visual>
      <geometry><box size="0.04 0.04 0.04"/></geometry>
      <material name="red"/>
    </visual>
    <collision><geometry><box size="0.04 0.04 0.04"/></geometry></collision>
    <inertial>
      <mass value="0.1"/>
      <inertia ixx="0.0003" ixy="0" ixz="0" iyy="0.0003" iyz="0" izz="0.0003"/>
    </inertial>
  </link>

  <!-- 6) joint5:腕部俯仰 -->
  <joint name="joint5" type="revolute">
    <parent link="wrist_1_link"/>
    <child  link="wrist_2_link"/>
    <origin xyz="0 0 0.04"/>
    <axis   xyz="0 0 1"/>
    <limit  lower="-3.14" upper="3.14" effort="20" velocity="2.0"/>
  </joint>
  <link name="wrist_2_link">
    <visual><geometry><box size="0.03 0.03 0.04"/></geometry></visual>
    <collision><geometry><box size="0.03 0.03 0.04"/></geometry></collision>
    <inertial>
      <mass value="0.1"/>
      <inertia ixx="0.0002"/>
    </inertial>
  </link>

  <!-- 7) joint6:末端旋转 -->
  <joint name="joint6" type="revolute">
    <parent link="wrist_2_link"/>
    <child  link="end_effector_link"/>
    <origin xyz="0 0 0.04"/>
    <axis   xyz="1 0 0"/>
    <limit  lower="-3.14" upper="3.14" effort="10" velocity="2.0"/>
  </joint>
  <link name="end_effector_link">
    <visual>
      <geometry><cylinder radius="0.02" length="0.03"/></geometry>
      <material name="red"/>
    </visual>
    <collision>
      <geometry><cylinder radius="0.02" length="0.03"/></geometry>
    </collision>
    <inertial>
      <mass value="0.05"/>
      <inertia ixx="0.0001"/>
    </inertial>
  </link>

  <!-- 8) 末端夹爪(可选 gripper) -->
  <joint name="gripper_joint" type="fixed">
    <parent link="end_effector_link"/>
    <child  link="gripper_link"/>
    <origin xyz="0 0 0.025"/>
  </joint>
  <link name="gripper_link">
    <visual><geometry><box size="0.04 0.06 0.02"/></geometry></visual>
    <collision><geometry><box size="0.04 0.06 0.02"/></geometry></collision>
    <inertial>
      <mass value="0.05"/>
      <inertia ixx="0.0001"/>
    </inertial>
  </link>
</robot>

约定:

  • x = 前进
  • y = 左
  • z = 上
  • joint1 绕 z 轴,旋转"底盘朝向"
  • joint2/joint3 绕 y 轴,"肩膀/肘部俯仰"
  • joint4/joint5/joint6 腕部 3 DoF
  • 总长度约 0.4 m,可伸展约 0.5 m

4. xacro:模板化 URDF

URDF 重复内容多(左右轮、对称连杆)。xacro 提供宏、变量、数学。

4.1 安装

xacro 通常自带 ROS2:

sudo apt install ros-humble-xacro

4.2 xacro 基础语法

<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="arm">
  <!-- 变量 -->
  <xacro:property name="link_len" value="0.1"/>
  <xacro:property name="PI" value="3.14159"/>

  <!-- 宏 -->
  <xacro:macro name="make_link" params="name color *origin">
    <link name="${name}">
      <visual>
        <xacro:insert_block name="origin"/>
        <geometry><box size="0.05 0.05 ${link_len}"/></geometry>
      </visual>
    </link>
  </xacro:macro>

  <link name="base_link"/>
  <xacro:make_link name="link1" color="red">
    <origin xyz="0 0 0.05"/>
  </xacro:make_link>
  <xacro:make_link name="link2" color="green">
    <origin xyz="0 0 0.1"/>
  </xacro:make_link>

  <!-- 数学 -->
  <xacro:property name="mass" value="${1.0 * link_len}"/>
</robot>

4.3 编译 xacro → URDF

xacro arm.xacro > arm.urdf
# 或 ros2 launch 时直接传 xacro 输出:
ros2 run robot_state_publisher robot_state_publisher \
  --ros-args -p robot_description:="$(xacro /path/to/arm.xacro)"

5. 坐标约定 REP-103 / REP-105

ROS2 的标准约定,违反会导致 MoveIt2 / Gazebo 算错:

5.1 REP-103(单位 + 坐标轴方向)

  • 长度单位:(m)
  • 角度单位:弧度(rad)
  • 右手系:
    • x = 前进
    • y = 左
    • z = 上

5.2 REP-105(坐标系语义)

frame 含义
world 全局参考(通常不动)
map SLAM 输出的地图
odom 里程计 / AMCL 定位
base_link 机器人底盘中心(刚体)
base_footprint 底盘在地面的投影(2D 导航)

关系:

  • world → map → odom → base_link 是固定链
  • world → map 由 SLAM 提供(变化)
  • map → odom 由 AMCL / 里程计提供
  • odom → base_link 由编码器 / IMU 提供

6. ros2_control 集成(加 transmission)

ros2_control 需要每个关节加 <transmission> 标签:

<joint name="joint1" type="revolute">
  <parent link="base_link"/>
  <child  link="shoulder_link"/>
  <axis   xyz="0 0 1"/>
  <limit  effort="100" velocity="2.0" lower="-3.14" upper="3.14"/>

  <!-- ros2_control 必填 -->
  <transmission name="trans_joint1">
    <type>transmission_interface/SimpleTransmission</type>
    <joint name="joint1">
      <hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
    </joint>
    <actuator name="motor_joint1">
      <hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
      <mechanicalReduction>1</mechanicalReduction>
    </actuator>
  </transmission>
</joint>

<transmission> 描述:

  • 哪个 joint
  • 用哪个 hardware interface(Position / Velocity / Effort)
  • 减速比(mechanicalReduction)

7. Gazebo 集成(加 gazebo 标签)

Gazebo Sim / ros_gz 加载 URDF 时需要 <gazebo> 标签 + <transmission>(已经加)。

<gazebo>
  <plugin name="gazebo_ros2_control" filename="libgazebo_ros2_control.so">
    <parameters>$(find my_arm)/config/controllers.yaml</parameters>
  </plugin>
</gazebo>

<!-- 每个 link 加颜色(可选) -->
<gazebo reference="shoulder_link">
  <material>Gazebo/Red</material>
</gazebo>

8. 校验 + 可视化

8.1 校验

sudo apt install liburdfdom-tools

# 检查 URDF 合法性
check_urdf my_arm.urdf

# 输出 OK 即合法,会打印 link / joint 树结构

8.2 生成 PDF/PNG 图

sudo apt install liburdfdom-tools python3-urdfdom-py

urdf_to_graphiz my_arm.urdf    # 生成 .pdf
# 或
urdf_to_graphiz my_arm.urdf -o my_arm.pdf

8.3 RViz 可视化

ros2 launch urdf_tutorial display.launch.py
# 或自己写 launch:
ros2 run rviz2 rviz2
# 在 RViz:
#   Fixed Frame → base_link
#   Add → RobotModel    ← 显示 URDF
#   Add → TF           ← 显示 TF tree

8.4 joint_state_publisher_gui(手动调关节)

ros2 run joint_state_publisher_gui joint_state_publisher_gui
# 弹出 GUI,拖滑块调关节角,看机械臂在 RViz 里动

9. 工具链总结

xacro .xacro ───> .urdf (用 xacro 编译)
                │
                ├──> check_urdf (校验)
                ├──> urdf_to_graphiz (可视化)
                │
                ├──> robot_state_publisher
                │       │
                │       │ 读 URDF + /joint_states → 算 TF → /tf
                │       │
                │       └──> tf2::Buffer (其他节点订阅)
                │
                ├──> MoveIt2 (规划)
                │       │
                │       │ 读 URDF + SRDF → 算 IK / path
                │       │
                │       └──> trajectory_msgs/JointTrajectory
                │
                └──> Gazebo Sim (仿真)
                        │
                        │ 读 URDF + gazebo 标签 → 物理仿真

10. MoveIt2 SRDF(规划组)

URDF 描述"机器人长啥样",SRDF 加 MoveIt2 需要的规划信息:

  • 虚拟关节:base_link → world(让 MoveIt2 把臂放在世界系)
  • 规划组(Planning Group):哪些关节一起动
  • 末端执行器:夹爪
  • 预设位姿(Pose):home / ready / extended

10.1 用 MoveIt Setup Assistant 生成

ros2 launch moveit_setup_assistant setup_assistant.launch.py

GUI 里:

  1. Load URDF
  2. Add Virtual Joint(base_link → world)
  3. Add Planning Group("arm",含 6 joint)
  4. Add End Effector(gripper)
  5. Add Poses(home / ready / up)
  6. Generate SRDF + MoveIt config

10.2 SRDF 简例

<robot name="my_arm">
  <!-- virtual joint: world → base_link -->
  <virtual_joint name="world_to_base" type="fixed"
                parent_frame="world" child_link="base_link"/>

  <!-- planning group -->
  <group name="arm">
    <chain base_link="base_link" tip_link="gripper_link"/>
    <joint name="joint1"/>
    <joint name="joint2"/>
    ...
  </group>

  <!-- end effector -->
  <end_effector name="gripper" parent_link="end_effector_link"
               group="gripper"/>

  <!-- pose -->
  <group_state name="home" group="arm">
    <joint name="joint1" value="0"/>
    <joint name="joint2" value="0"/>
    ...
  </group_state>
</robot>

11. 在本仓库里跑

11.1 启 3 关节机械臂 demo

docker exec ros2_dev bash -lc "source /root/ros2_ws/install/setup.bash && ros2 launch bringup robot_launch.py"

节点(launch 重命名后是 joint_state_publisher / tf2_listener,executable 名带 _cpp):

  • joint_state_publisher_cpp (本包,executable 名)
  • robot_state_publisher (系统包)
  • tf2_listener_cpp (本包,executable 名)

11.2 校验本仓库 URDF

docker exec ros2_dev bash -lc "check_urdf /root/ros2_ws/install/cpp_robot_tf2/share/cpp_robot_tf2/urdf/simple_arm.urdf"

11.3 源码

11.4 端到端日志

docker/robot_e2e.log


12. 进阶:6-DoF 真机械臂 URDF 实战

12.1 找开源 URDF

机械臂 URDF 来源
Franka Panda ros-planning/moveit_resources/panda
UR5 / UR10 ros-industrial/universal_robot
Ufactory xArm 6 xArm-Robotics/xarm_ros2
Aloha tonyzhaozh/aerial_manipulation

12.2 URDF 适配本仓库

把开源 URDF 拷到 src/<your_arm>/urdf/:

cp -r <upstream>/franka_description/urdf src/my_arm/urdf/

# 加 transmission(ros2_control 必填)
# 已经在开源版本里有,但要确认 hardwareInterface 是 PositionJointInterface

12.3 加 ros2_control 配置

src/my_arm/config/controllers.yaml:

controller_manager:
  ros__parameters:
    update_rate: 100
    joint_state_broadcaster:
      type: joint_state_broadcaster/JointStateController
    arm_controller:
      type: position_controllers/JointGroupPositionController

arm_controller:
  ros__parameters:
    joints:
      - joint1
      - joint2
      - joint3
      - joint4
      - joint5
      - joint6

12.4 加 launch

src/my_arm/launch/arm_bringup.launch.py:

from launch import LaunchDescription
from launch_ros.actions import Node
from ament_index_python.packages import get_package_share_directory
import os

def generate_launch_description():
    pkg = get_package_share_directory('my_arm')
    urdf = os.path.join(pkg, 'urdf', 'arm.urdf')
    controllers = os.path.join(pkg, 'config', 'controllers.yaml')

    with open(urdf, 'r') as f: urdf_content = f.read()

    return LaunchDescription([
        # 1) robot_state_publisher
        Node(package='robot_state_publisher', executable='robot_state_publisher',
             parameters=[{'robot_description': urdf_content}]),
        # 2) ros2_control_node
        Node(package='controller_manager', executable='ros2_control_node',
             parameters=[{'robot_description': urdf_content,
                          'update_rate': 100}],
             output='screen'),
        # 3) spawn joint_state_broadcaster
        Node(package='controller_manager', executable='spawner',
             arguments=['joint_state_broadcaster', '-c', '/controller_manager']),
        # 4) spawn arm_controller
        Node(package='controller_manager', executable='spawner',
             arguments=['arm_controller', '-c', '/controller_manager']),
    ])

12.5 加 MoveIt2

sudo apt install ros-humble-moveit
# 用 MoveIt Setup Assistant 加载你的 URDF 生成 SRDF
ros2 launch moveit_setup_assistant setup_assistant.launch.py

详见 99-embodied-ai.md 阶段 2。


接下来读

主题 文档
TF 坐标变换 50-tf2.md
ros2_control + MoveIt2 99-embodied-ai.md 阶段 2
launch 文件 70-launch.md
三机部署 100-embedded-deployment.md


📖 阅读路径导航

💡 这是仓库 doc/ 下所有文档的推荐阅读顺序。返回 README 总导航

本文预计阅读时间: 60 分钟 📍 当前位置: 第 17 / 24 篇