4. link 标签详解:几何形状、视觉与碰撞、惯性参数

好,我们继续往下走。上一章我们把 URDF 的骨架搭起来了,robot 和 link 的基本结构也见过了。这一章,咱们得把 link 这个零件彻底吃透。

一个 link 在 URDF 里到底能描述什么?说白了就三件事:长什么样(视觉)、碰撞边界在哪(碰撞)、有多重、重心在哪(惯性)。这三件事分别对应三个子标签:<visual>、<collision>、<inertial>。咱们一个一个拆开讲。

4.1 几何形状:<geometry>

不管你是画视觉还是画碰撞,都得先定义形状。URDF 支持四种基本几何:长方体、圆柱体、球体、网格文件。我个人最常用的是网格文件,因为真实机器人很少有标准的几何体。但调试阶段,用基本几何体跑起来快得多。

形状 标签 参数 示例
长方体 <box> size="长 宽 高" <box size="0.2 0.1 0.05"/>
圆柱体 <cylinder> radius、length <cylinder radius="0.05" length="0.3"/>
球体 <sphere> radius <sphere radius="0.1"/>
网格 <mesh> filename、scale <mesh filename="package://my_robot/meshes/arm.stl" scale="1 1 1"/>
小提示:网格文件的路径用 package:// 开头,别写路径。不然换个电脑就炸了。我早期吃过这个亏,模型在同事电脑上死活加载不出来,查了半天才发现是路径写死了。

4.2 <visual> 与 <collision> 的区别

这两个标签长得几乎一模一样,都有 <geometry>、<origin>、<material>(碰撞没有材质)。但它们的职责完全不同。

  • <visual>:负责显示。你在 RViz 里看到的样子,就是它决定的。可以加材质、颜色、贴图。
  • <collision>:负责物理。碰撞检测、运动规划、抓取判断,全看它。它不参与显示。

为什么要分开?你想想看,一个复杂的机械臂,视觉模型可能有几十万个三角面,如果碰撞也用这么精细的模型,每做一次碰撞检测都要算半天,机器人就别动了。所以碰撞模型通常用简化几何体代替。

核心原则:视觉模型要精细,碰撞模型要保守。碰撞模型可以比视觉模型大一圈,但不能小。小了就会穿模。

我曾经在一个 AGV 项目里,碰撞模型用了和视觉一样的 STL 文件。结果导航规划每走一步都要卡两秒。后来把碰撞模型换成几个长方体拼起来,速度直接快了 20 倍。嗯,这就是教训。

4.2.1 代码示例:一个完整的 link

<link name="base_link">
  <visual>
    <origin xyz="0 0 0" rpy="0 0 0"/>
    <geometry>
      <box size="0.4 0.3 0.1"/>
    </geometry>
    <material name="blue">
      <color rgba="0 0 1 1"/>
    </material>
  </visual>

  <collision>
    <origin xyz="0 0 0" rpy="0 0 0"/>
    <geometry>
      <box size="0.42 0.32 0.12"/>
    </geometry>
  </collision>
</link>

注意看,碰撞模型的长宽高各大了 0.02 米。这就是我说的「保守一圈」。视觉是 0.4,碰撞是 0.42,留了 1 厘米的余量。

4.3 <inertial> 惯性参数

这个标签最容易被忽略,但物理仿真能不能跑起来,全看它。没有惯性参数,Gazebo 直接报错,机器人会像纸片一样飘在空中。

<inertial> 包含三个子项:

  • <mass>:质量,单位千克。
  • <origin>:重心位置,相对于 link 坐标系。
  • <inertia>:惯性张量矩阵,6 个独立分量:ixx、ixy、ixz、iyy、iyz、izz。
注意:惯性张量不能全填 0,也不能填负数。物理引擎会直接崩溃。我见过有人偷懒把惯性全填 0.001,结果机器人一碰就疯狂抖动,根本没法用。

4.3.1 惯性参数怎么算?

对于标准几何体,有公式可以直接算。比如一个均匀密度的长方体,质量 m,尺寸长 a、宽 b、高 c:

  • ixx = (1/12) * m * (b² + c²)
  • iyy = (1/12) * m * (a² + c²)
  • izz = (1/12) * m * (a² + b²)
  • ixy = ixz = iyz = 0(对称时)

如果是圆柱体或球体,也有对应的公式。对于复杂形状,我建议用 SolidWorks 或 Fusion 360 导出 URDF,它们会自动算好惯性参数。手算太容易出错了。

4.3.2 完整的 link 示例(含惯性)

<link name="arm_link">
  <visual>
    <origin xyz="0 0 0.15" rpy="0 0 0"/>
    <geometry>
      <cylinder radius="0.05" length="0.3"/>
    </geometry>
    <material name="red">
      <color rgba="1 0 0 1"/>
    </material>
  </visual>

  <collision>
    <origin xyz="0 0 0.15" rpy="0 0 0"/>
    <geometry>
      <cylinder radius="0.06" length="0.32"/>
    </geometry>
  </collision>

  <inertial>
    <mass value="0.5"/>
    <origin xyz="0 0 0.15" rpy="0 0 0"/>
    <inertia
      ixx="0.001" ixy="0" ixz="0"
      iyy="0.001" iyz="0"
      izz="0.0005"/>
  </inertial>
</link>

这里圆柱体长 0.3 米,重心在中心,所以 origin 的 z 是 0.15。惯性值我用了近似公式算的,质量 0.5kg,半径 0.05m,长度 0.3m,ixx 和 iyy 约 0.001,izz 约 0.0005。

4.4 知识结构图:link 标签全景

下面这张图把 link 的完整结构画出来了。你可以把它当作一个检查清单,写 URDF 时对照着看,不容易漏东西。

link 标签结构图 <link> <visual> <collision> <inertial> <geometry> <origin> <material> <geometry> <origin> (无材质) <mass> <origin> <inertia> <box> <cylinder> <sphere> <mesh> ixx ixy ixz iyy iyz izz 视觉 vs 碰撞:形状可不同 碰撞模型要保守,惯性参数不能省

4.5 避坑指南

最后,我把自己踩过的坑整理一下,你写 URDF 时多留个心眼。

  • 碰撞模型比视觉模型小:这是最常见的错误。机器人看起来没碰到东西,但碰撞检测已经报错了。记住,碰撞模型要「包得住」视觉模型。
  • 惯性参数全填 0:Gazebo 会直接报错,或者机器人乱抖。至少给一个很小的正数。
  • 重心位置不对:惯性里的 origin 要和实际重心对齐。不对齐的话,仿真时机器人会「头重脚轻」。
  • 网格路径写错:用 package:// 而不是路径。我见过有人把路径写成 C:/Users/.../mesh.stl,结果换到 Linux 上直接崩了。
  • 忘记加 <inertial>:如果你只做可视化,可以不加。但要做物理仿真,必须加。Gazebo 会直接拒绝加载没有惯性的 link。
我的习惯:每写完一个 link,先在 RViz 里看视觉和碰撞是否对齐,再在 Gazebo 里测试物理行为。两步都过了,这个 link 才算合格。