2 · Link与Joint详解

这一章我们深入拆解 URDF 里最核心的两个零件——Link 和 Joint。说白了,机器人模型就是一堆 Link 通过 Joint 串起来的骨架。你想想看,没有 Joint 的 Link 就是一堆孤立的铁块,没有 Link 的 Joint 就是空气。我刚开始学 URDF 的时候,总把视觉和碰撞搞混,后来踩了几个坑才摸清楚门道。

Link visual · collision · inertial Joint 类型 · 属性 · 父子坐标系 父子坐标系树 → TF 变换 连接 构成

Link 标签属性

每个 Link 就是一个刚体。URDF 里 Link 必须包含三个子模块:visual(长什么样)、collision(碰撞箱)、inertial(质量/惯量)。我个人习惯先把 visual 搭好,再复制一份改成 collision,省得手写两遍几何体。

核心结构:
<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 1 1"/>
        </material>
    </visual>
    <collision>
        <geometry>
            <box size="0.4 0.3 0.1"/>
        </geometry>
        <origin xyz="0 0 0" rpy="0 0 0"/>
    </collision>
    <inertial>
        <mass value="2.0"/>
        <inertia ixx="0.01" ixy="0" ixz="0" iyy="0.01" iyz="0" izz="0.01"/>
    </inertial>
</link>

visual / collision / inertial 细节

visual 只影响显示,不影响物理。你可以把模型做得花里胡哨,但 collision 一定要简单——用 box、cylinder、sphere 就够了。我在项目里见过有人把 collision 直接复制成带倒角的 mesh,结果仿真卡成 PPT……

inertial 是很多新手忽略的坑。如果你不写 inertial,ROS 会默认给一个极小的惯量,机械臂动力学直接崩掉。我曾经调试一个六轴臂,关节抖得像帕金森,最后发现是惯性张量少了个零。

惯量矩阵用平行轴定理算。实在懒的话,用 ixx = 0.001 * mass 先顶着,但别用在真实机器人上。

Joint 类型与属性

Joint 决定了两个 Link 怎么动。URDF 支持六种类型,我按常用度排个序:

类型自由度典型用途
revolute1 (旋转)关节电机、轮子转向
continuous1 (无限旋转)车轮、转台
prismatic1 (平移)伸缩臂、滑轨
fixed0焊接件、传感器支架
floating6自由物体(很少用)
planar3 (平面运动)AGV 全向轮简化

每个 Joint 必须指定 parent 和 child,以及 origin(子坐标系相对于父坐标系的偏移)。你想想看,这个 origin 就是 TF 变换的源头。

revolute 和 prismatic 必须设置 limit 子标签,否则 MoveIt 会报错。我当年忘了加 limit,结果规划器一直说“joint out of range”,排查了半小时。

父子坐标系关系

这是整个 URDF 的命根子。每个 Joint 定义了一个坐标系变换:child Link 的坐标系原点在 parent Link 坐标系中的位置。说白了,你写 <origin xyz="0.2 0 0.1" rpy="0 0 1.57"/>,就是告诉 TF:子 Link 的原点相对于父 Link 移动了 (0.2,0,0.1) 米,再绕 Z 轴转 90°。

我习惯在纸上先画坐标系树,再写 URDF。举个例子:

<joint name="shoulder_pan" type="revolute">
    <parent link="base_link"/>
    <child link="shoulder_link"/>
    <origin xyz="0 0 0.15" rpy="0 0 0"/>
    <axis xyz="0 0 1"/>
    <limit lower="-3.14" upper="3.14" effort="100" velocity="1.0"/>
</joint>

这里 shoulder_link 的原点就在 base_link 上方 0.15 米处,旋转轴朝 Z。嗯,要注意 axis 是在 父坐标系 下定义的,别搞反了。

避坑指南: 我曾经把 child link 的 origin 写成了相对于世界坐标系,结果机器人一加载直接飞到了外太空。记住:origin 永远是相对于 parent link 的。

实战小贴士

  • 用 check_urdf 工具验证文件,它会告诉你哪个 joint 缺了 parent。
  • visual 和 collision 的 origin 可以不同,但建议保持一致,否则显示和碰撞箱错位。
  • inertial 的 origin 默认在 link 原点,如果你的重心不在几何中心,记得加 <origin/> 偏移。

说实话,Link 和 Joint 的标签就那么几个,但组合起来能搭出任何机器人。你只要把父子关系理清楚,后面的 TF 和运动学就是水到渠成的事。