2. URDF基础语法

好,咱们正式开始动手写URDF了。说实话,我第一次接触URDF的时候,觉得这东西不就是个XML嘛,有啥难的?结果真上手写机器人模型时,被各种标签嵌套搞得晕头转向。今天我就把自己踩过的坑、总结的经验,一次性给你讲透。

2.1 URDF的XML骨架——说白了就是个树

URDF全称是Unified Robot Description Format,统一机器人描述格式。它本质上就是一个XML文件。你想想看,机器人不就是一个一个零件拼起来的吗?URDF就是用来描述这些零件长啥样、怎么连接的。

整个URDF文件的结构,其实就三样东西:

  • robot标签:整个文件的根节点,所有东西都包在里面
  • link标签:描述机器人的每一个刚体部件,比如底盘、轮子、手臂
  • joint标签:描述两个link之间怎么连接的,是固定的还是能转动的

嗯,这里要注意:URDF里必须有一个root link,也就是基座。我见过有人把底盘和轮子都写成link,但忘了定义它们之间的joint,结果模型加载出来轮子直接飞出去了……

核心要点:URDF是一棵以robot为根、link为节点、joint为边的树结构。每个joint连接两个link,形成父子关系。

2.2 robot标签——一切从这里开始

robot标签是整个URDF文件的根。所有link、joint、传感器定义都得放在它里面。它的语法极其简单:

<robot name="my_robot">
  <!-- 这里放link和joint -->
</robot>

name属性是必须的,给机器人起个名字。我个人习惯用下划线命名法,比如two_wheel_robot。为什么?因为ROS里很多工具对名字里的空格和特殊字符处理不好,你想想看,要是名字里带个空格,调试的时候多闹心。

小技巧:robot标签里还可以放<gazebo>标签,用来配置仿真参数。不过那是后面章节的内容,咱们先把基础打好。

2.3 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 1 1"/>
    </material>
  </visual>
  <collision>
    <geometry>
      <box size="0.4 0.3 0.1"/>
    </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:定义这个link长什么样,用什么几何体(box、cylinder、sphere、mesh),什么颜色或材质
  • collision:定义碰撞检测用的形状。我建议尽量用简单的几何体,别用复杂的mesh,否则碰撞检测会卡死你
  • inertial:定义质量和惯性张量。这个最容易忘,但物理仿真必须要有

避坑指南:我曾经在项目里把visual和collision的几何体设成不一样的大小,结果机器人看起来正常,但一碰到东西就穿模。后来排查了半天才发现是collision的尺寸比visual小了一圈。记住:visual和collision的尺寸最好保持一致,除非你有特殊需求。

2.4 joint标签——连接link的关节

joint描述了两个link之间的连接关系。它定义了父link和子link,以及它们之间的相对位置和运动方式。joint有六种类型,但常用的就四种:

类型 说明 自由度 典型应用
fixed 固定连接,不能动 0 底盘和支架
revolute 旋转关节,有角度限制 1 机械臂关节
continuous 连续旋转关节,无限制 1 轮子
prismatic 平移关节,直线运动 1 升降台

一个典型的joint定义:

<joint name="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>

这里要注意几个关键点:

  • parent和child:父link和子link。父link是固定的那个,子link是跟着joint动的那个
  • origin:子link相对于父link的初始位置和姿态。这个坐标是相对于父link的坐标系
  • axis:旋转轴或平移轴的方向。对于revolute和continuous,这个轴就是旋转轴

我的经验:定义joint的origin时,我习惯先画个草图,标出每个link的中心位置和joint的位置。否则你很容易搞混xyz的正方向。有一次我写一个六轴机械臂,因为一个joint的z轴方向写反了,整个手臂像麻花一样扭着……

2.5 知识体系总览

下面这张图把URDF的核心结构画出来了,你看一眼就能明白:

robot link (base_link) joint (type) link (wheel) visual / collision / inertial parent / child / origin / axis visual / collision / inertial URDF 树形结构:robot → link + joint → link + joint → ... 根节点 link节点 joint节点 子元素

说白了,URDF就是一棵树。robot是根,link是树枝,joint是连接点。你只要记住这个结构,写再多link和joint都不会乱。

2.6 一个完整的例子

咱们写一个最简单的两轮机器人底盘,包含一个base_link和两个轮子:

<?xml version="1.0"?>
<robot name="simple_robot">
  <!-- 底盘 -->
  <link name="base_link">
    <visual>
      <geometry>
        <box size="0.4 0.3 0.1"/>
      </geometry>
      <material name="blue">
        <color rgba="0 0 1 1"/>
      </material>
    </visual>
    <inertial>
      <mass value="2.0"/>
      <inertia ixx="0.01" ixy="0" ixz="0" iyy="0.01" iyz="0" izz="0.01"/>
    </inertial>
  </link>

  <!-- 左轮 -->
  <link name="left_wheel">
    <visual>
      <geometry>
        <cylinder radius="0.05" length="0.04"/>
      </geometry>
      <origin xyz="0 0 0" rpy="0 1.57 0"/>
      <material name="black">
        <color rgba="0 0 0 1"/>
      </material>
    </visual>
    <inertial>
      <mass value="0.2"/>
      <inertia ixx="0.001" ixy="0" ixz="0" iyy="0.001" iyz="0" izz="0.001"/>
    </inertial>
  </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>

这个例子虽然简单,但包含了URDF最核心的要素。你把这个文件保存成robot.urdf,然后用check_urdf robot.urdf命令检查一下,就能看到模型结构了。

注意:写URDF时,每个link的name必须唯一,joint的name也必须唯一。我见过有人复制粘贴时忘了改名字,结果两个link同名,加载时直接报错。这种低级错误排查起来特别浪费时间。

好了,URDF的基础语法就这些。说白了就是三个标签:robot包一切,link定义零件,joint定义连接。你只要把这个结构刻在脑子里,后面写复杂的机器人模型就不会迷路。