2. URDF基础标签:<robot>、<link>、<joint>

写URDF就像搭乐高。你得先有零件(link),再用关节(joint)把它们拼起来,最后用<robot>把整个模型包住。我刚开始接触ROS时,觉得这玩意儿不就是个XML嘛,后来踩了不少坑才明白——每个标签的细节都藏着设计哲学。

这一章,咱们就把最核心的三个标签掰开揉碎。你想想看,一个机器人模型动起来,全靠它们配合。我会把我在项目里摔过的跟头、总结的窍门都抖出来。

URDF 基础标签结构 <robot> <link> 零件 <joint> 关节 一个 robot 包含多个 link 和 joint,构成运动链

2.1 <robot> —— 模型的根

说白了,<robot>就是整个URDF文件的根元素。所有link和joint都得包在里面。它有两个常用属性:name(必填)和 xmlns(一般写死)。

<robot name="my_robot" xmlns:xacro="http://www.ros.org/wiki/xacro">
    <link name="base_link" />
    <joint name="base_to_wheel" type="continuous"> ... </joint>
</robot>
💡 我的习惯: name 用蛇形命名,比如 two_wheel_robot。别用空格或驼峰,后面写launch文件时容易出幺蛾子。

2.2 <link> —— 机器人的骨头

每个link代表一个刚体零件。你可以给它加视觉、碰撞和惯性属性。我见过新手只写一个空link,结果仿真时物理引擎直接崩了——因为缺少惯性数据。

一个典型的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" />
        <material name="blue">
            <color rgba="0 0.4 0.8 1" />
        </material>
    </visual>
    <collision>
        <geometry>
            <box size="0.4 0.3 0.1" />
        </geometry>
    </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可视化外观,显示在Rviz里推荐
collision碰撞检测模型,通常比visual简单推荐
inertial惯性参数,质量+惯性张量物理仿真必填
⚠️ 我曾经踩过的坑: 把collision做得和visual一样精细,结果Gazebo卡成PPT。后来我学乖了——碰撞模型用基本几何体或者简化网格,能省下大量计算资源。

2.3 <joint> —— 让零件动起来

joint连接两个link,并定义它们的相对运动。URDF支持6种关节类型,但最常用的是 revolute、continuous 和 fixed。

<joint name="left_wheel_joint" type="continuous">
    <parent link="base_link" />
    <child link="left_wheel" />
    <origin xyz="0.2 -0.15 0" rpy="0 0 0" />
    <axis xyz="0 1 0" />
</joint>

关节的核心属性:

  • name —— 唯一标识,别重名。
  • type —— 关节类型:fixed, revolute, continuous, prismatic, floating, planar。
  • parent / child —— 父子link,构成运动树。
  • origin —— 子link相对于父link的位姿。
  • axis —— 旋转或平移轴(对fixed无效)。

🔧 我个人的经验: 写joint时,origin 的 rpy 很容易搞反。记住:rpy 是子坐标系在父坐标系中的旋转,不是关节旋转角度。关节角度由 joint state 发布器控制。

2.4 三个标签的协作关系

一个机器人模型就是一棵树。<robot>是根,link是节点,joint是树枝。我习惯先画草图,标出每个link的坐标系原点,再写代码。你想想看,如果坐标系对不上,TF树就会乱成一团。

下面这个例子展示了一个简单的两轮底盘:

<robot name="mini_dash">
    <link name="chassis">
        <visual>
            <geometry><box size="0.3 0.2 0.05" /></geometry>
        </visual>
    </link>
    <link name="right_wheel">
        <visual>
            <geometry><cylinder radius="0.04" length="0.02" /></geometry>
        </visual>
    </link>
    <joint name="right_joint" type="continuous">
        <parent link="chassis" />
        <child link="right_wheel" />
        <origin xyz="0.1 -0.12 0" />
        <axis xyz="0 1 0" />
    </joint>
</robot>

嗯,这里要注意:joint的 origin 是子link坐标系的原点相对父link的位置。我见过有人把轮子的位置写在link的visual里,结果关节一动,轮子飞出去了——因为joint的origin才是真正的连接点。

📌 避坑指南: 如果你用 fixed 关节连接两个link,记得检查是否真的需要两个link。有时候一个link加两个visual更省事。但为了TF树清晰,我通常还是分成两个link。