20 KiB
60 · URDF 机器人模型(完全指南)
目标:能读懂 URDF,能从零写一个 6-DoF 机械臂 URDF,理解 link / joint / xacro / 物理参数,会校验 + 可视化。
目录
- 1. URDF 是什么
- 2. URDF 三大元素
- 3. 完整 6-DoF 机械臂示例
- 4. xacro:模板化 URDF
- 5. 坐标约定 REP-103 / REP-105
- 6. ros2_control 集成(加 transmission)
- 7. Gazebo 集成(加 gazebo 标签)
- 8. 校验 + 可视化
- 9. 工具链总结
- 10. MoveIt2 SRDF(规划组)
- 11. 在本仓库里跑
- 12. 进阶:6-DoF 真机械臂 URDF 实战
1. URDF 是什么
URDF (Unified Robot Description Format) = 用 XML 描述机器人:
- 有哪些刚体段(
<link>) - 刚体之间怎么连(
<joint>) - 视觉 / 碰撞几何(
<visual>/<collision>) - 物理参数(质量、惯性,
<inertial>)
URDF 通过 robot_state_publisher 翻译成 TF tree,被 MoveIt2 / Gazebo / RViz 读取。
2. URDF 三大元素
2.1 <link> — 刚体段
| 子标签 | 用途 | 必填 |
|---|---|---|
<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>
重要约定:
axis在 parent link 系下定义origin是 child 相对 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 里:
- Load URDF
- Add Virtual Joint(base_link → world)
- Add Planning Group("arm",含 6 joint)
- Add End Effector(gripper)
- Add Poses(home / ready / up)
- 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 端到端日志
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 篇
- ⏮ 上一篇: 坐标变换
- ⏭ 下一篇: launch 文件系统