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的核心结构画出来了,你看一眼就能明白:
说白了,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定义连接。你只要把这个结构刻在脑子里,后面写复杂的机器人模型就不会迷路。