第3章 URDF文件结构

<robot>标签、<link>与<joint>的骨架作用
<robot> 根标签 <link> 刚体部件 视觉 / 碰撞 / 惯性 <joint> 关节连接 类型 / 父子 / 坐标 元数据 / 材质 颜色 / 纹理 / 属性 骨架逻辑: robot 包裹所有 link 和 joint,link 是骨头,joint 是关节 共同构成机器人的运动学树

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,等熟悉了再调。

💡 核心要点: link 是刚体,它自己不会动。想让机器人动起来,得靠 joint 把 link 连起来。

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",结果轮子横着转,看起来特别滑稽。

🔧 我的小技巧: 写 joint 的时候,先确定 parent 和 child 的层级关系。一般把靠近基座的 link 作为 parent,远离的作为 child。这样构建的运动学树才清晰。

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

你看,所有轮子都是底盘的孩子,底盘是根。这就是典型的树形结构。我建议你画个草图再写代码,不然很容易搞乱父子关系。

⚠️ 避坑指南: 我曾经在项目里把两个 joint 的 parent 和 child 写反了,结果 Rviz 里模型完全扭曲。记住:parent 是靠近世界坐标系的那一端,child 是远离的那一端。拿不准的时候,先画个树状图。

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 命令检查,应该能通过。嗯,我第一次跑通的时候还挺有成就感的。

📌 总结一下: robot 是根,link 是骨头,joint 是关节。三者缺一不可。写 URDF 就像搭积木,先把骨架搭好,再慢慢加细节。下一章我们会深入 link 的每个子标签,把骨头做得更真实。