2. 机器人建模基础
这一章我们深入拆解 URDF 里最核心的两个元素——link 和 joint。说白了,link 就是机器人的「骨头」,joint 就是「关节」。骨头长什么样、有多重、能不能撞到东西,关节能怎么动、活动范围多大——这些全写在标签里。我个人习惯先把这两个东西吃透,后面建模才顺手。
2.1 link 标签:机器人的每一块骨头
每个 <link> 描述一个刚体。它有三个子标签:inertial(惯性)、collision(碰撞)、visual(视觉)。我刚开始学的时候总以为 visual 最重要,后来才发现——物理引擎根本不看 visual,它只认 collision 和 inertial。你想想看,如果碰撞模型没做好,机器人仿真里直接穿模,那画面……
2.1.1 视觉模型(visual)
视觉模型就是「给人看的」。你可以用 <geometry> 定义形状:box、cylinder、sphere、mesh。我个人习惯先用简单几何体快速验证,最后再替换成精细的 mesh 文件。
<link name="base_link">
<visual>
<geometry>
<box size="0.4 0.3 0.1"/>
</geometry>
<origin xyz="0 0 0" rpy="0 0 0"/>
<material name="blue">
<color rgba="0 0.4 0.8 1"/>
</material>
</visual>
</link>
2.1.2 碰撞模型(collision)
碰撞模型是物理引擎用来算「有没有撞到」的。这里有个坑——碰撞模型越简单,仿真越快。我曾经把一个机械臂的 mesh 直接拿来当碰撞模型,结果仿真卡成幻灯片。后来我改成用圆柱体和长方体组合,速度直接翻倍。
<mesh filename="..." scale="..."/> 并降低面数。
2.1.3 惯性参数(inertial)
惯性参数决定了机器人「有多重」「转动惯量多大」。很多初学者直接抄别人的数值,结果机器人一跑就翻。我建议你至少算一下质量分布。
<inertial>
<mass value="2.5"/>
<inertia ixx="0.01" ixy="0" ixz="0"
iyy="0.01" iyz="0"
izz="0.01"/>
</inertial>
urdf_sim_tutorial 里的工具自动估算。
2.2 joint 标签:关节决定运动
joint 把两个 link 连起来,并定义它们之间的运动关系。URDF 支持六种关节类型,但常用的其实就三四种。我按使用频率给你排个序。
| 关节类型 | 自由度 | 典型用途 | 注意点 |
|---|---|---|---|
| fixed | 0 | 固定连接(如相机支架) | 没有运动,但必须显式声明 |
| revolute | 1 (旋转) | 机械臂关节、轮子转向 | 必须设置 limit,否则报错 |
| prismatic | 1 (平移) | 伸缩臂、滑轨 | 同样需要 limit |
| continuous | 1 (无限旋转) | 车轮、转台 | 不需要 limit,但小心缠绕 |
| floating | 6 | 自由漂浮(如无人机) | 很少用,ROS2 中更推荐 model 方式 |
| planar | 3 (平面运动) | AGV 全向轮 | 实际项目中常被 prismatic+revolute 替代 |
2.2.1 fixed 关节——最简单也最容易忘
fixed 关节就是「焊死」。比如你把激光雷达固定在底盘上,就用 fixed。我见过有人把 fixed 关节写成 revolute 然后 limit 设成 0,也能跑,但语义不对,后期维护很坑。
<joint name="lidar_joint" type="fixed">
<parent link="base_link"/>
<child link="lidar_link"/>
<origin xyz="0.1 0 0.2" rpy="0 0 0"/>
</joint>
2.2.2 revolute 关节——机械臂的灵魂
revolute 关节绕一个轴旋转,有角度限制。配置时除了 axis 和 origin,必须写 <limit>。我曾经忘了写 lower 和 upper,Gazebo 直接罢工。
<joint name="shoulder_joint" type="revolute">
<parent link="base_link"/>
<child link="upper_arm"/>
<origin xyz="0 0 0.1" rpy="0 0 0"/>
<axis xyz="0 0 1"/>
<limit lower="-1.57" upper="1.57" effort="10" velocity="1.0"/>
</joint>
2.2.3 prismatic 关节——直线运动
prismatic 关节沿一个轴平移。配置和 revolute 几乎一样,只是运动方式不同。我做过一个伸缩货叉,用的就是 prismatic,limit 里的 effort 一定要给够,不然推不动重物。
2.2.4 continuous 关节——转圈圈
continuous 关节没有角度限制,适合车轮。但要注意:没有 limit 不代表没有物理约束。仿真里如果力矩太大,车轮会飞出去。我一般会在 <dynamics> 里加一点阻尼。
2.3 避坑指南:我踩过的几个坑
- 坐标系搞反: joint 的
origin是子 link 相对于父 link 的变换。我一开始总把 xyz 写成父相对于子,结果机器人拧成麻花。 - 惯性矩阵全零: 物理引擎要求惯性矩阵正定。如果你偷懒全写 0,Gazebo 会直接崩溃。至少给对角线填上小值。
- 碰撞模型比视觉模型还大: 这会导致机器人「还没碰到就弹开」。记得让碰撞模型略小于视觉模型,或者至少一致。
- joint 的 axis 写错: 比如 revolute 关节的轴应该是
0 0 1(绕 Z 转),你写成0 1 0,关节就横着扭了。检查三遍。
核心总结:
- link 三件套:visual(给人看)、collision(给物理引擎看)、inertial(给动力学看)
- joint 六种类型,记住 fixed、revolute、prismatic、continuous 就够用
- 每个 joint 必须指定 parent 和 child,顺序不能反
- limit 标签在 revolute 和 prismatic 中是强制项
嗯,这一章的内容就这些。link 和 joint 是 URDF 的基石,你花时间把它们搞透,后面写 xacro、调 MoveIt! 都会顺手很多。下一章我们聊坐标系与变换——那个更绕,但更有意思。