第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就失去了存在的意义。
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不是乱写的,社区有一些约定俗成的规范。我个人习惯遵循以下几点:
- 命名规范:link名用下划线分隔,如
base_link、shoulder_link。joint名用对应的动作命名,如shoulder_joint、elbow_joint。 - 坐标系约定:一般用右手坐标系。基座的z轴朝上,x轴朝前。这个在移动机器人里特别重要。
- 单位统一:长度用米,角度用弧度,质量用千克。ROS里默认就是这套单位,别自己搞特殊。
- 树形结构: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,基本不会出大问题。