第2章 link标签详解:视觉、碰撞、惯性、几何
link 是 URDF 里最基础的零件。说白了,它就是机器人的一个刚体部件——底盘、轮子、激光雷达支架,全都靠 link 来描述。我刚开始写 URDF 时,以为 link 就是画个形状而已,后来才发现里面藏着三个关键属性:visual(视觉)、collision(碰撞)、inertial(惯性)。再加上 geometry(几何形状)贯穿其中,这四个东西构成了 link 的完整定义。
这一章,我会把每个属性拆开讲清楚。你想想看,一个轮子如果只有好看的模型,没有碰撞体积,仿真时就会直接陷进地面;如果忘了惯性参数,机器人跑起来就像在太空中飘。嗯,咱们一个一个来。
1. visual —— 看得见的外壳
visual 定义的是机器人“长什么样”。在 Rviz 里你看到的那个彩色模型,就是 visual 渲染出来的。它包含三个子元素:geometry(形状)、origin(位置偏移)、material(颜色/材质)。
我个人习惯把 visual 做得精细一些,毕竟调试时看着舒服。但注意,visual 不影响物理仿真——它就是个皮囊。
<link name="base_link">
<visual>
<geometry>
<cylinder length="0.2" radius="0.15"/>
</geometry>
<origin xyz="0 0 0.1" rpy="0 0 0"/>
<material name="blue">
<color rgba="0.2 0.5 0.9 1.0"/>
</material>
</visual>
</link>
2. collision —— 真实的“肉体”
collision 才是仿真引擎用来检测碰撞的体积。我记得有一次做差速底盘,visual 用的复杂轮毂 mesh,但 collision 偷懒直接复制了 visual。结果 Gazebo 里轮子一碰到小石头就疯狂弹跳……后来我把 collision 简化成圆柱体,问题立刻解决。
为什么 collision 要简化? 因为碰撞检测计算量很大。你想想看,如果每个螺丝都用三角网格去算碰撞,仿真帧率直接掉到个位数。所以工业上通常用 box、cylinder、sphere 来近似。
<collision>
<geometry>
<cylinder length="0.2" radius="0.14"/> <!-- 比 visual 半径小0.01,留余量 -->
</geometry>
<origin xyz="0 0 0.1" rpy="0 0 0"/>
</collision>
3. inertial —— 让机器人有“重量”
没有 inertial 的 link,在 Gazebo 里会被当作质量无限大——推不动,也掉不下来。说白了就是“幽灵零件”。inertial 包含三个东西:mass(质量)、inertia(惯性张量)、origin(质心偏移)。
惯性张量是很多人的噩梦。其实对于简单形状,ROS 官方有计算脚本。我一般用 urdf_tutorial 里的 inertia_macro.xacro 自动生成。但如果你手写,记住:单位是 kg·m²,矩阵要对称。
| 形状 | ixx | iyy | izz |
|---|---|---|---|
| 实心圆柱 (半径r, 高h, 质量m) | m(3r²+h²)/12 | m(3r²+h²)/12 | mr²/2 |
| 实心球 (半径r, 质量m) | 2mr²/5 | 2mr²/5 | 2mr²/5 |
| 长方体 (长a, 宽b, 高c, 质量m) | m(b²+c²)/12 | m(a²+c²)/12 | m(a²+b²)/12 |
urdf_to_graphiz 检查,或者直接在 Gazebo 里让机器人自由落体——如果它旋转得不对劲,八成是惯性张量填错了。
<inertial>
<mass value="2.0"/>
<origin xyz="0 0 0.05"/> <!-- 质心略高于几何中心 -->
<inertia ixx="0.012" ixy="0" ixz="0"
iyy="0.012" iyz="0"
izz="0.008"/>
</inertial>
4. geometry —— 形状是万物的基础
visual、collision 甚至某些 sensor 插件都依赖 geometry。URDF 支持四种基本形状:box、cylinder、sphere、mesh。mesh 可以导入 STL/DAE 文件,实现任意复杂外形。
我个人建议:能用基本形状就别用 mesh。因为 mesh 文件一旦丢失,整个模型加载失败。我在项目里吃过这个亏——把 mesh 路径写成了相对路径,换电脑编译就找不到文件。后来统一用 package:// 格式,再也没出过问题。
- box:尺寸用 xyz 表示,适合底盘、箱体。
- cylinder:半径 + 长度,轮子、连杆首选。
- sphere:半径,万向轮、球铰。
- mesh:filename 指定路径,scale 缩放。
<geometry>
<!-- 基本形状示例 -->
<box size="0.4 0.3 0.1"/>
<!-- 或者 -->
<mesh filename="package://my_robot/meshes/arm.stl" scale="1.0 1.0 1.0"/>
</geometry>
5. 组合起来:一个完整的 link
下面是一个真实项目中轮子的 link 定义。你可以看到 visual 用了精细的 mesh,collision 简化为圆柱,inertial 根据实际质量计算。
<link name="left_wheel">
<visual>
<geometry>
<mesh filename="package://bot_description/meshes/wheel.dae"/>
</geometry>
<origin xyz="0 0 0" rpy="1.5708 0 0"/>
<material name="dark_gray"/>
</visual>
<collision>
<geometry>
<cylinder radius="0.08" length="0.04"/>
</geometry>
<origin xyz="0 0 0" rpy="1.5708 0 0"/>
</collision>
<inertial>
<mass value="0.5"/>
<origin xyz="0 0 0"/>
<inertia ixx="0.0003" ixy="0" ixz="0"
iyy="0.0003" iyz="0"
izz="0.0006"/>
</inertial>
</link>
嗯,link 标签的内容差不多就这些。你可能会觉得惯性张量有点烦,但相信我,多写几次就熟练了。下一章我们会讲 joint——把 link 连起来,机器人才能真正动起来。