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。
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 时对照着看,不容易漏东西。
4.5 避坑指南
最后,我把自己踩过的坑整理一下,你写 URDF 时多留个心眼。
- 碰撞模型比视觉模型小:这是最常见的错误。机器人看起来没碰到东西,但碰撞检测已经报错了。记住,碰撞模型要「包得住」视觉模型。
- 惯性参数全填 0:Gazebo 会直接报错,或者机器人乱抖。至少给一个很小的正数。
- 重心位置不对:惯性里的 origin 要和实际重心对齐。不对齐的话,仿真时机器人会「头重脚轻」。
- 网格路径写错:用
package://而不是路径。我见过有人把路径写成C:/Users/.../mesh.stl,结果换到 Linux 上直接崩了。 - 忘记加
<inertial>:如果你只做可视化,可以不加。但要做物理仿真,必须加。Gazebo 会直接拒绝加载没有惯性的 link。