3、机器人模型URDF详解:URDF文件结构、link与joint的定义、坐标系与变换

URDF,全称是 Unified Robot Description Format。说白了,它就是机器人界的「身份证」。

你想想看,要让 MoveIt 知道你的机器人长什么样、胳膊往哪转、轮子在哪,总得有个标准格式吧?URDF 就是干这个的。我刚开始接触 ROS 时,觉得这玩意儿不就是个 XML 嘛,随便写写就行。结果第一次加载模型,机器人直接在地上劈叉——关节方向全反了。嗯,从那以后我再也不敢小看 URDF 了。

3.1 URDF 文件结构:一棵树

URDF 的核心结构其实很简单——一棵树。根节点是 <robot>,下面挂着两种孩子:<link> 和 <joint>。

核心原则:URDF 必须是一棵无环树。不能有闭环,不能有分叉后又合并的结构。如果你做的是四足机器人或并联机械臂,URDF 搞不定——得用 SRDF 或 xacro 配合。

来看一个最简结构:

<robot name="my_robot">
  <link name="base_link">
    <visual>
      <geometry>
        <box size="0.4 0.3 0.1"/>
      </geometry>
    </visual>
  </link>

  <joint name="joint1" type="revolute">
    <parent link="base_link"/>
    <child link="arm_link"/>
    <origin xyz="0 0 0.1" rpy="0 0 0"/>
    <axis xyz="0 0 1"/>
  </joint>

  <link name="arm_link">
    <visual>
      <geometry>
        <cylinder length="0.3" radius="0.05"/>
      </geometry>
    </visual>
  </link>
</robot>

这段代码定义了一个底座加一个旋转关节的简单机械臂。注意看,base_link 是根,joint1 连接它和 arm_link。这就是一棵树——从根出发,一路向下。

3.2 link 的定义:机器人的「骨头」

每个 <link> 代表机器人身上的一个刚体部件。它有三个主要子标签:

子标签 作用 是否必须
<visual> 可视化外观,用于 Rviz 显示 推荐有
<collision> 碰撞检测模型,用于运动规划 强烈推荐
<inertial> 惯性参数,用于动力学仿真 动力学需要

我个人习惯是:visual 和 collision 尽量保持一致。如果 collision 比 visual 简单太多,MoveIt 规划出来的路径可能看着没问题,实际上一碰撞就报错。我在项目里遇到过这种情况——明明 Rviz 里看着没撞,一跑仿真就卡住。查了半天,原来是 collision 模型把一块凸起给简化掉了。

小技巧:collision 可以用更简单的几何体替代 visual,但形状和位置不能差太远。比如 visual 是个复杂的 STL 模型,collision 可以用几个 box 和 cylinder 拼出来。这样既保证碰撞检测效率,又不会漏掉关键干涉区域。

inertial 部分很多人会忽略。如果你只做运动学规划,确实可以跳过。但一旦涉及动力学——比如力控、阻抗控制、拖动示教——惯性参数必须准确。我见过一个案例:某协作机器人 URDF 里所有 link 的 inertia 都填了默认值,结果仿真出来的关节力矩和实际差了 3 倍。嗯,这坑我踩过。

<inertial>
  <mass value="1.5"/>
  <inertia ixx="0.01" ixy="0" ixz="0"
           iyy="0.01" iyz="0"
           izz="0.01"/>
</inertial>

注意惯性张量的单位是 kg·m²,数值别瞎填。如果你不确定,可以用 SolidWorks 或 Fusion 360 导出,或者用 urdf_sim_tutorial 里的工具估算。

3.3 joint 的定义:机器人的「关节」

joint 是连接两个 link 的纽带。它定义了父子关系、运动类型和变换。

joint 的类型有 6 种:

类型 说明 自由度
revolute 旋转关节,有限位 1
continuous 连续旋转关节,无限位 1
prismatic 平移关节 1
fixed 固定连接,无运动 0
floating 6 自由度浮动 6
planar 平面运动(2 平移 + 1 旋转) 3

最常用的是 revolute 和 fixed。连续旋转关节(比如 AGV 的轮子)用 continuous。浮动关节一般只用于移动机器人的 base 与世界的连接。

joint 里最重要的三个子标签:

  • <origin>:子 link 相对于父 link 的变换。包含 xyz(平移)和 rpy(旋转,单位弧度)。
  • <axis>:关节的运动轴。对于 revolute,就是旋转轴;对于 prismatic,就是平移方向。
  • <limit>:关节限位。包含 lower、upper、effort、velocity。

注意:axis 的默认值是 (1, 0, 0),也就是 X 轴。很多人忘记写 axis,结果关节绕着 X 轴转,跟预期完全不一样。我曾经调试一个六轴机械臂,第五关节怎么转都不对,最后发现 axis 没写,默认绕 X 轴,而实际应该绕 Y 轴。改完就好了。

3.4 坐标系与变换:TF 树的基石

URDF 里的坐标系关系,直接决定了 TF 树的结构。每个 joint 的 <origin> 定义的就是子 link 坐标系相对于父 link 坐标系的变换。

举个例子:

<joint name="shoulder_pan" type="revolute">
  <parent link="base_link"/>
  <child link="shoulder_link"/>
  <origin xyz="0 0 0.2" rpy="0 0 0"/>
  <axis xyz="0 0 1"/>
</joint>

这表示 shoulder_link 的坐标系原点在 base_link 坐标系下 (0, 0, 0.2) 的位置,姿态与 base_link 一致。关节绕 Z 轴旋转。

你想想看,如果这里 xyz 写错了,整个机械臂的位置就全偏了。我见过有人把 xyz 和 rpy 写反——把旋转填到平移里,结果模型飞到天上去。

URDF 的坐标系遵循右手定则。X 向前,Y 向左,Z 向上——这是 ROS 的默认约定。但有些机器人厂商有自己的习惯,比如 Z 向上但 X 向右。这时候你需要在 URDF 里做一次坐标系转换,或者在 launch 文件中用 static_transform_publisher 发布一个额外的变换。

关键点:URDF 定义的 TF 树是静态的。关节运动后的变换由 robot_state_publisher 根据关节角度实时计算并发布。所以 URDF 里只需要定义「零位」时的坐标系关系。

3.5 知识体系总览

下面这张图帮你理清 URDF 的核心逻辑:

URDF 知识体系结构图 <robot> 根节点 <link> 刚体部件 visual 可视化 collision 碰撞 inertial 惯性 <joint> 关节连接 origin 变换 axis 运动轴 limit 限位 输出:TF 树 + 运动学模型 ⚠ 注意 URDF 必须是一棵无环树 闭环结构(如并联机构)需要 SRDF 或 xacro 扩展

3.6 常见错误与避坑指南

写 URDF 时最容易犯的几个错误,我帮你列出来:

  • 坐标系方向搞反:rpy 的顺序是 Z-Y-X(绕固定轴),不是 X-Y-Z。很多人写成 rpy="0 0 1.57" 想绕 Z 转 90 度,结果绕 X 转了。正确写法是 rpy="0 0 1.57" 没错——但前提是你理解这是绕固定轴 Z 转 1.57。
  • 忘记写 collision:MoveIt 的规划场景需要 collision 模型。没有 collision,碰撞检测会报错或直接跳过。我见过有人只写了 visual,结果规划出来的路径看着没问题,实际上一执行就撞。
  • joint 的 parent 和 child 写反:这会导致 TF 树方向反了。检查方法很简单——用 check_urdf 工具跑一遍,它会告诉你树结构是否合理。
  • limit 里的 effort 和 velocity 填 0:这会导致关节无法运动。effort 是最大力矩,velocity 是最大速度。填 0 等于告诉 MoveIt「这个关节动不了」。

调试工具推荐:

  • check_urdf my_robot.urdf — 检查 URDF 语法和树结构
  • urdf_to_graphiz my_robot.urdf — 生成树结构 PDF 图
  • roslaunch urdf_tutorial display.launch model:=my_robot.urdf — 在 Rviz 中可视化