第1章 · URDF基础入门

URDF,全称是 Unified Robot Description Format,统一机器人描述格式。说白了,它就是 ROS 世界里用来描述机器人长什么样、关节怎么连的一套 XML 语言。我刚开始接触 ROS 的时候,觉得这玩意儿不就是个配置文件嘛,后来才发现——URDF 是机器人建模的基石,导航、控制、仿真全都得靠它。

这一章我们不讲虚的,直接拆解 URDF 的核心概念。你想想看,一个机器人再怎么复杂,拆到底也就是「刚体」加「关节」。URDF 里用 <link> 表示刚体,用 <joint> 表示关节。就这么简单。

URDF 模型 <link> 刚体 视觉 / 碰撞 / 惯性 <joint> 关节 连续 / 旋转 / 棱柱 / 固定 机器人本体描述 运动学 / 动力学 link + joint = 完整的机器人描述

什么是 URDF?

URDF 是一种基于 XML 的格式,专门用来描述机器人的运动学和动力学属性。你可以把它想象成机器人的「身份证」—— 上面写了你有几个胳膊几条腿,每个关节能怎么动。ROS 里的 robot_state_publisher 就是靠 URDF 来发布 tf 变换的。

核心要点: URDF 只描述树状结构,不能有闭环。如果你的机器人有并联机构(比如闭环连杆),那就得用 xacro 或者其它方式处理。我在项目里遇到过有人试图用 URDF 描述四足机器人的闭环腿,结果 tf 树直接崩溃……后来改用 xacro 加仿真约束才搞定。

URDF 文件结构

一个标准的 URDF 文件长这样:

<robot name="my_robot">
    <link name="base_link">
        <visual>
            <geometry>
                <box size="0.4 0.3 0.1"/>
            </geometry>
        </visual>
    </link>

    <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>
</robot>

最外层是 <robot> 标签,里面包含若干 <link> 和 <joint>。我个人习惯把 base_link 放在最前面,它是整个机器人的根坐标系。

link 与 joint 的概念

link 就是刚体,比如底盘、轮子、机械臂的连杆。每个 link 可以定义视觉外观、碰撞模型和惯性参数。我记得有一次调试导航,机器人一直原地打转,最后发现是 base_link 的惯性矩阵写错了,导致里程计计算异常。嗯,惯性参数千万别乱填。

joint 连接两个 link,并定义它们之间的相对运动。joint 有几种常用类型:

类型说明自由度
fixed固定连接,不能运动0
revolute旋转关节,有角度限制1
continuous连续旋转关节,无限制(如轮子)1
prismatic平移关节,有位移限制1
floating6自由度浮动关节(很少用)6
选 joint 类型时,轮子用 continuous,机械臂关节用 revolute,摄像头支架用 fixed。我曾经见过有人把轮子关节设成 revolute 还加了限位,结果小车只能转半圈…… 别犯这种低级错误。

第一个 URDF 模型

我们来写一个最简单的两轮差速底盘。只有三个 link:一个底盘和两个轮子,外加两个 continuous joint。

<robot name="simple_diff_drive">
    <link name="base_link">
        <visual>
            <geometry>
                <box size="0.4 0.3 0.08"/>
            </geometry>
            <material name="blue">
                <color rgba="0 0.5 1 1"/>
            </material>
        </visual>
    </link>

    <link name="left_wheel">
        <visual>
            <geometry>
                <cylinder radius="0.05" length="0.04"/>
            </geometry>
            <material name="gray">
                <color rgba="0.3 0.3 0.3 1"/>
            </material>
        </visual>
    </link>

    <link name="right_wheel">
        <visual>
            <geometry>
                <cylinder radius="0.05" length="0.04"/>
            </geometry>
            <material name="gray">
                <color rgba="0.3 0.3 0.3 1"/>
            </material>
        </visual>
    </link>

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

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

把这个文件保存为 diff_drive.urdf,然后用 check_urdf diff_drive.urdf 验证一下。如果输出 Robot successfully validated,恭喜你,第一个 URDF 模型就搞定了。

常见坑: 轮子的 origin 坐标一定要相对于 base_link。我刚开始写 URDF 时,把轮子原点写成了相对于世界坐标系,结果轮子飞到了天上去…… 记住,origin 是相对于父 link 的。

你可以在 Rviz 里直接加载这个 URDF,用 robot_state_publisher 发布 tf,就能看到小车模型了。嗯,看到自己写的第一个机器人动起来,还是挺有成就感的。