第3章 机器人模型基础:URDF文件结构详解

各位同学,欢迎来到第三章。上一章我们把ROS环境搭好了,但机器人还只是个概念。这一章,我们来给它一副「骨架」——也就是URDF文件。

URDF全称是Unified Robot Description Format,统一机器人描述格式。说白了,它就是用一个XML文件,告诉计算机你的机器人长什么样、关节怎么动。我刚开始学的时候,觉得这玩意儿不就是个配置文件嘛,后来才发现,URDF写得好不好,直接决定了你后面做运动规划、避障、抓取时会不会「翻车」。

核心观点:URDF是机器人的「身份证」。MoveIt、RViz、Gazebo都靠它来理解你的机器人。URDF写错了,后面所有工作都是白费。

3.1 URDF文件结构概览

一个标准的URDF文件,本质上就是一个XML树。根节点是<robot>,下面挂着两种核心元素:<link>和<joint>。你可以把link想象成机器人的骨头(刚体部件),joint就是连接骨头的关节。

我习惯用这样一个比喻:link是「零件」,joint是「关系」。没有joint,link就是一堆散落的零件;没有link,joint就失去了存在的意义。

<robot> <link> (刚体) <joint> (关节) visual (可视化) collision (碰撞) inertial (惯性) origin (原点) parent/child (父子) axis (旋转轴) 图:URDF文件核心结构树

3.2 link的定义与属性

每个<link>代表机器人上的一个刚体部件。比如机械臂的基座、大臂、小臂,或者移动机器人的底盘、激光雷达支架,都是link。

一个完整的link包含三个子模块:

子模块 作用 是否必须
<visual> 定义外观,用于RViz显示 推荐有
<collision> 定义碰撞检测用的形状 强烈建议有
<inertial> 定义质量、惯性张量 仿真必须

来看一个实际的link例子——一个简单的基座:

<link name="base_link">
  <visual>
    <geometry>
      <cylinder length="0.15" radius="0.10"/>
    </geometry>
    <origin xyz="0 0 0" rpy="0 0 0"/>
    <material name="blue">
      <color rgba="0.2 0.4 0.8 1.0"/>
    </material>
  </visual>

  <collision>
    <geometry>
      <cylinder length="0.15" radius="0.10"/>
    </geometry>
    <origin xyz="0 0 0" rpy="0 0 0"/>
  </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不写collision。结果在MoveIt做运动规划时,机器人「穿模」了——明明看起来撞上了,规划器却认为没撞。嗯,这就是collision缺失的后果。我建议你从一开始就把collision加上,哪怕形状和visual一样。

3.3 joint的定义与类型

joint定义了link之间的连接关系。每个joint有且只有一个parent link和一个child link。joint的类型决定了运动自由度:

类型 自由度 典型应用
revolute 1个旋转 机械臂关节(有限角度)
continuous 1个旋转 车轮(无限旋转)
prismatic 1个平移 线性滑轨、升降台
fixed 0 传感器支架、刚性连接
floating 6 移动机器人底盘(很少用)
planar 3 AGV在平面运动(很少用)

来看一个典型的revolute关节定义:

<joint name="shoulder_joint" type="revolute">
  <parent link="base_link"/>
  <child link="shoulder_link"/>
  <origin xyz="0 0 0.15" rpy="0 0 0"/>
  <axis xyz="0 0 1"/>
  <limit lower="-3.14" upper="3.14" effort="10" velocity="1.0"/>
</joint>

这里有几个关键点:

  • origin:child link相对于parent link的偏移。说白了,就是关节装在哪儿。
  • axis:旋转轴的方向。对于revolute关节,这是最重要的参数之一。
  • limit:关节限位。lower/upper是弧度,effort是最大力矩,velocity是最大速度。

我曾经踩过的坑:有一次我写了一个机械臂的URDF,所有关节都设成了revolute,但忘记设置limit。结果在仿真里一运行,关节直接「飞」了——因为没有限位,规划器认为它可以无限旋转。所以请记住:revolute关节必须设置limit,否则MoveIt会报错。

3.4 标准机器人描述格式的约定

写URDF不是乱写的,社区有一些约定俗成的规范。我个人习惯遵循以下几点:

  1. 命名规范:link名用下划线分隔,如base_link、shoulder_link。joint名用对应的动作命名,如shoulder_joint、elbow_joint。
  2. 坐标系约定:一般用右手坐标系。基座的z轴朝上,x轴朝前。这个在移动机器人里特别重要。
  3. 单位统一:长度用米,角度用弧度,质量用千克。ROS里默认就是这套单位,别自己搞特殊。
  4. 树形结构:URDF必须是一棵树,不能有环。也就是说,每个link只能有一个parent。

你想想看,如果团队里每个人命名风格都不一样,有人用arm_base,有人用base_of_arm,那协作起来得多痛苦?所以从一开始就定好规范,后面会省很多事。

3.5 一个完整的URDF示例

下面是一个两连杆机械臂的完整URDF。麻雀虽小,五脏俱全:

<?xml version="1.0"?>
<robot name="simple_arm">

  <!-- 基座 -->
  <link name="base_link">
    <visual>
      <geometry><box size="0.2 0.2 0.1"/></geometry>
      <material name="gray"><color rgba="0.5 0.5 0.5 1"/></material>
    </visual>
  </link>

  <!-- 第一段手臂 -->
  <link name="arm1_link">
    <visual>
      <geometry><box size="0.05 0.05 0.3"/></geometry>
      <origin xyz="0 0 0.15"/>
      <material name="red"><color rgba="0.8 0.2 0.2 1"/></material>
    </visual>
  </link>

  <!-- 第二段手臂 -->
  <link name="arm2_link">
    <visual>
      <geometry><box size="0.05 0.05 0.25"/></geometry>
      <origin xyz="0 0 0.125"/>
      <material name="blue"><color rgba="0.2 0.2 0.8 1"/></material>
    </visual>
  </link>

  <!-- 基座到第一段手臂的关节 -->
  <joint name="joint1" type="revolute">
    <parent link="base_link"/>
    <child link="arm1_link"/>
    <origin xyz="0 0 0.05"/>
    <axis xyz="0 0 1"/>
    <limit lower="-1.57" upper="1.57" effort="5" velocity="0.5"/>
  </joint>

  <!-- 第一段到第二段手臂的关节 -->
  <joint name="joint2" type="revolute">
    <parent link="arm1_link"/>
    <child link="arm2_link"/>
    <origin xyz="0 0 0.3"/>
    <axis xyz="0 1 0"/>
    <limit lower="-2.09" upper="2.09" effort="5" velocity="0.5"/>
  </joint>

</robot>

验证方法:写完URDF后,用check_urdf命令检查语法。然后在RViz里加载看看效果。我每次写完都会跑一遍这两个步骤,能发现90%的低级错误。

3.6 本章小结

URDF是机器人建模的基石。这一章我们讲了:

  • URDF的树形结构:robot → link + joint
  • link的三个子模块:visual、collision、inertial
  • joint的六种类型,重点掌握revolute和fixed
  • 命名规范和坐标系约定

说实话,URDF本身不难,难的是把每个参数都写对。我见过太多人因为origin写错,导致机器人「散架」——link之间要么重叠,要么分离。所以我的建议是:每写一个joint,就在脑子里过一遍它的parent和child在哪里,轴朝哪个方向。这样写出来的URDF,基本不会出大问题。