第3章 URDF文件结构
3.1 从根开始:<robot> 标签
每个 URDF 文件,说白了就是一个 XML 文档。而 <robot> 就是整个文档的根,所有内容都得包在它里面。我刚开始写 URDF 的时候,经常忘记加这个根标签,结果解析器直接报错——嗯,这种低级错误谁还没犯过呢。
<robot> 标签有两个常用属性:name 和 xmlns。name 就是机器人的名字,最好用英文小写加下划线,别整花里胡哨的。我个人习惯把名字取得简短一点,比如 my_robot,后面调试的时候看着清爽。
<robot name="my_robot" xmlns:xacro="http://www.ros.org/wiki/xacro">
<!-- 所有 link 和 joint 都写在这里 -->
</robot>
你想想看,如果没有这个根标签,ROS 的解析器根本不知道你在描述什么。它就像一栋房子的地基,虽然不起眼,但少了它一切免谈。
3.2 骨架的骨头:<link> 标签
机器人的每个刚体部件,都对应一个 <link>。你可以把 link 想象成骨架里的骨头——它本身是硬的、不形变的。一个机器人可以有多个 link,比如底盘、左轮、右轮、机械臂的每个臂节。
每个 link 必须有一个 name 属性,而且名字在整个文件里不能重复。我在项目中遇到过有人把两个 link 都叫 "base",结果 Gazebo 直接崩溃——这种坑踩过一次就记住了。
一个典型的 link 包含三个子模块:
- visual —— 视觉显示,决定你在 Rviz 里看到的样子
- collision —— 碰撞检测,物理引擎用它算碰撞
- inertial —— 惯性参数,质量、重心、转动惯量
下面是一个最简单的 link 示例:
<link name="base_link">
<visual>
<geometry>
<box size="0.4 0.3 0.1"/>
</geometry>
<origin xyz="0 0 0" rpy="0 0 0"/>
</visual>
</link>
注意,origin 定义的是这个 link 自身的坐标系相对于 link 原点的偏移。很多新手搞混这个,我建议你一开始就把 origin 全设成 0,等熟悉了再调。
3.3 骨架的关节:<joint> 标签
如果说 link 是骨头,那 joint 就是关节。它把两个 link 连接在一起,并且定义了它们之间的相对运动方式。没有 joint,你的机器人就是一盘散沙——每个部件各自为政,根本组不成一个整体。
每个 joint 必须指定 name 和 type。type 决定了关节的运动自由度,常用的有:
| 类型 | 说明 | 典型用途 |
|---|---|---|
| fixed | 固定连接,两个 link 之间没有相对运动 | 底盘与传感器支架 |
| revolute | 旋转关节,绕一个轴转动,有角度限制 | 机械臂关节、舵机 |
| continuous | 连续旋转关节,没有角度限制 | 车轮、转台 |
| prismatic | 平移关节,沿一个轴直线运动 | 升降台、滑轨 |
| floating | 6自由度浮动关节,一般不用在简单模型里 | 特殊场景 |
joint 里最重要的两个子标签是 parent 和 child,它们指定了关节连接哪两个 link。举个例子:
<joint name="left_wheel_joint" type="continuous">
<parent link="base_link"/>
<child link="left_wheel"/>
<origin xyz="0.2 -0.25 0" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
</joint>
这里 parent 是底盘,child 是左轮。origin 定义了轮子相对于底盘的安装位置,axis 指定了轮子绕哪个轴旋转。我曾经犯过一个错:把 axis 写成了 xyz="1 0 0",结果轮子横着转,看起来特别滑稽。
3.4 骨架的组装逻辑
一个完整的 URDF 文件,就是一堆 link 和 joint 的集合。但注意,它们不是随便堆砌的——必须形成一棵树。什么意思呢?就是所有 link 通过 joint 连接,不能出现环路(至少标准 URDF 不支持闭环)。
举个例子,一个两轮差速底盘:
- base_link(底盘)
- left_wheel(左轮)—— 通过 left_wheel_joint 连到 base_link
- right_wheel(右轮)—— 通过 right_wheel_joint 连到 base_link
- caster_wheel(万向轮)—— 通过 caster_joint 连到 base_link
你看,所有轮子都是底盘的孩子,底盘是根。这就是典型的树形结构。我建议你画个草图再写代码,不然很容易搞乱父子关系。
3.5 一个完整的骨架示例
下面是一个极简的 URDF 文件,包含一个底盘和一个轮子。你可以把它当成模板:
<?xml version="1.0"?>
<robot name="simple_car">
<link name="base_link">
<visual>
<geometry>
<box size="0.5 0.3 0.1"/>
</geometry>
</visual>
</link>
<link name="left_wheel">
<visual>
<geometry>
<cylinder radius="0.08" length="0.04"/>
</geometry>
</visual>
</link>
<joint name="left_wheel_joint" type="continuous">
<parent link="base_link"/>
<child link="left_wheel"/>
<origin xyz="0.2 -0.2 0" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
</joint>
</robot>
这个文件虽然简单,但骨架是完整的。你把它保存成 simple_car.urdf,然后用 check_urdf 命令检查,应该能通过。嗯,我第一次跑通的时候还挺有成就感的。