第2章 link与joint详解

这一章我们深入拆解 URDF 里最核心的两个元素——link 和 joint。说白了,link 就是机器人的「骨头」,joint 就是「关节」。骨头长什么样、有多重、能不能撞到东西,关节能怎么动、活动范围多大——这些全写在标签里。我个人习惯先把这两个东西吃透,后面建模才顺手。

URDF 核心 link(刚体) 惯性 碰撞 视觉 joint(关节) fixed revolute prismatic continuous / floating / planar 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,记得用 <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 支持六种关节类型,但常用的其实就三四种。我按使用频率给你排个序。

关节类型自由度典型用途注意点
fixed0固定连接(如相机支架)没有运动,但必须显式声明
revolute1 (旋转)机械臂关节、轮子转向必须设置 limit,否则报错
prismatic1 (平移)伸缩臂、滑轨同样需要 limit
continuous1 (无限旋转)车轮、转台不需要 limit,但小心缠绕
floating6自由漂浮(如无人机)很少用,ROS2 中更推荐 model 方式
planar3 (平面运动)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! 都会顺手很多。下一章我们聊坐标系与变换——那个更绕,但更有意思。