2 · 连杆与关节:<link> & <joint> 详解
好,咱们直接进入正题。URDF 里最核心的两个东西,就是 <link> 和 <joint>。你可以把 link 想象成机器人的骨头——每一块刚体,而 joint 就是连接骨头的关节。没有 joint,link 就是一堆散落的零件。我个人习惯先把 link 的视觉和碰撞属性搭好,再调 joint,这样思路特别顺。
<link> 标签:骨头的里里外外
每个 link 有三个主要子块:惯性、碰撞、视觉。视觉是给人看的,碰撞是给物理引擎算的,惯性是给动力学用的。嗯,这里要注意:很多人只写视觉,忘了碰撞,结果仿真里两个零件直接穿模——我早期就吃过这个亏。
<geometry> 指定形状(box, cylinder, sphere, mesh),<origin> 定位置,<material> 给颜色。
<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.2 0.4 0.9 1.0"/>
</material>
</visual>
</link>
你看,一个简单的底盘就出来了。但如果你只写到这儿,仿真器里它轻飘飘的,没有重量。所以必须加惯性。
惯性参数:别让机器人飘起来
<inertial> 里放 <mass> 和 <inertia>。惯性矩阵 ixx, ixy, ixz, iyy, iyz, izz 这六个值,很多人直接抄网上的,但那样你的机器人动力学会怪怪的。我记得有一次帮朋友调一个六轴机械臂,就是惯性参数给错了,仿真里手臂像面条一样甩……后来用 SolidWorks 导出了正确的惯性张量,问题才解决。
<inertial>
<mass value="2.0"/>
<inertia ixx="0.01" ixy="0.0" ixz="0.0"
iyy="0.02" iyz="0.0"
izz="0.01"/>
</inertial>
对于简单几何,你可以用公式估算。比如一个实心长方体,质量 m,长宽高 a,b,c,ixx = m*(b²+c²)/12。你想想看,如果随便填个 0.001,那机器人转起来就跟陀螺一样,根本没法控制。
碰撞属性:物理引擎的硬边界
<collision> 的结构和 visual 几乎一样,但不需要 material。它的几何形状决定了物体怎么撞、怎么接触。我一般把碰撞模型做得比视觉稍微大一点点(比如 1-2mm),这样在导航避障时留点余量,不容易卡住。
<collision>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<cylinder radius="0.15" length="0.2"/>
</geometry>
</collision>
说白了,碰撞模型越简单,仿真越快。移动机器人底盘用圆柱或长方体就够了,没必要上高精网格。
<joint> 标签:让骨头动起来
joint 连接两个 link,有父 link 和子 link。URDF 里 joint 的类型决定了运动自由度。我把它分成四大类,也是你最常用的。
| 类型 | 自由度 | 典型用途 | 注意点 |
|---|---|---|---|
| fixed | 0 | 把传感器、支架焊死在底盘上 | 没有运动,但必须显式声明 |
| revolute | 1 (旋转) | 关节臂、舵机轮 | 有角度限位 <limit> |
| continuous | 1 (无限旋转) | 全向轮、万向轮 | 没有限位,但别忘了给摩擦 |
| prismatic | 1 (平移) | 升降台、伸缩臂 | 有位置限位,单位米 |
举个例子,一个旋转关节(revolute)长这样:
<joint name="arm_joint" type="revolute">
<parent link="base_link"/>
<child link="arm_link"/>
<origin xyz="0.2 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>
这里 <axis> 定义旋转轴,xyz="0 0 1" 就是绕 Z 轴转。限位用弧度,effort 是最大力矩,velocity 是最大速度。我习惯把 effort 设得比实际大 20%,留点余量。
<dynamics> 里设置 friction="0.1" 和 damping="0.05",跑起来才真实。
固定关节:最容易被忽略的细节
fixed 关节虽然不动,但你必须写。比如激光雷达固定在底盘上,如果不加 fixed joint,TF 树就断掉了。我之前见过一个新手,把雷达直接挂在 base_link 下没写 joint,结果 rviz 里雷达数据死活不显示——其实就是缺了一个固定关节。
<joint name="laser_joint" type="fixed">
<parent link="base_link"/>
<child link="laser_frame"/>
<origin xyz="0.1 0 0.2" rpy="0 0 0"/>
</joint>
你看,就这么简单。但少了它,整个导航的传感器数据就飘在外太空。
棱柱关节:直线运动
prismatic 关节用于滑动,比如电动推杆。限位用米,轴方向决定滑动方向。我调试过一个升降平台,因为 <limit> 的 effort 设太小,平台升到一半就卡住了。后来把 effort 从 50 提到 80,问题解决。
<joint name="lift_joint" type="prismatic">
<parent link="base_link"/>
<child link="lift_link"/>
<axis xyz="0 0 1"/>
<limit lower="0.0" upper="0.5" effort="80" velocity="0.2"/>
</joint>
<origin> 和 <axis>,这两个最容易出错。
嗯,这一章的内容差不多就是这些。你只要把 link 的三个属性和 joint 的四种类型吃透,URDF 建模就迈过了一大半门槛。下一章我们会聊坐标变换和 TF 树,到时候你会更清楚 joint 的 <origin> 到底影响了什么。