3、URDF基础标签:link、joint、geometry、origin等标签详解
好,咱们今天来聊聊URDF。这东西说白了就是机器人的“骨架描述文件”。你想想看,要让一个机器人在Gazebo里动起来,总得告诉它长什么样、关节在哪、每个零件怎么连吧?URDF就是干这个的。
我个人习惯把URDF比作乐高说明书。每个零件是一个link,连接处是joint,零件的位置和朝向由origin决定,而零件的形状就是geometry。嗯,咱们一个一个拆开讲。
3.1 link标签——机器人的“骨头”
link是URDF里最基本的单元。它描述了一个刚体部件,比如底盘、轮子、摄像头支架。每个link必须有名字,而且名字在整个机器人里不能重复。
我在项目中遇到过最坑的事,就是两个link重名了,结果Gazebo直接崩溃,连个像样的报错都没有。所以命名一定要规范,比如base_link、left_wheel_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>
</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(碰撞)、inertial(惯性)。
- visual:就是你能看到的样子。Gazebo里渲染用的。
- collision:物理引擎用来算碰撞的。我建议你尽量用简单的几何体,比如盒子、圆柱,别用太复杂的网格,否则仿真跑起来卡得要命。
- inertial:质量、惯性矩阵。这个容易被忽略,但少了它,机器人就像在太空中飘,一推就飞走了。
ixx = (1/12) * mass * (height^2 + depth^2)。我一般写个Python脚本自动生成,省事。
3.2 joint标签——机器人的“关节”
joint连接两个link,决定了它们之间的相对运动。URDF支持好几种关节类型,我列个表给你看:
| 类型 | 说明 | 自由度 | 典型用途 |
|---|---|---|---|
| fixed | 固定连接,不能动 | 0 | 摄像头支架、外壳 |
| revolute | 旋转关节,有角度限制 | 1 | 机械臂关节、舵机 |
| continuous | 旋转关节,无角度限制 | 1 | 轮子、转台 |
| prismatic | 平移关节,有行程限制 | 1 | 升降台、滑轨 |
| floating | 6自由度浮动关节 | 6 | 无人机、水下机器人 |
| planar | 平面运动关节 | 3 | AGV小车(很少用) |
我曾经犯过一个低级错误:给轮子用了revolute关节,结果轮子转不到一圈就被卡住了。后来才想起来,轮子应该用continuous。你想想看,车轮哪有转半圈就停的?
一个典型的joint示例:
<joint name="left_wheel_joint" type="continuous">
<parent link="base_link"/>
<child link="left_wheel_link"/>
<origin xyz="-0.2 0.15 0" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
<limit effort="10" velocity="5"/>
</joint>
注意axis标签,它定义了旋转轴。对于轮子,一般是绕Y轴转。还有limit里的effort和velocity,分别代表最大力矩和最大速度。Gazebo的物理引擎会拿这两个值来限制关节运动。
joint里忘了写axis,结果轮子原地打转,机器人纹丝不动。Gazebo默认轴是Z轴,但轮子通常绕Y轴转。所以一定要显式指定。
3.3 geometry标签——形状的定义
geometry定义了link的几何形状。支持以下几种:
- box:长方体,用
size指定长宽高。 - cylinder:圆柱体,用
radius和length。 - sphere:球体,用
radius。 - mesh:网格文件,用
filename指定STL或DAE文件。
我个人建议:能用简单几何体就别用mesh。为什么呢?因为mesh文件解析起来慢,而且碰撞检测容易出问题。我在一个项目里用了带花纹的轮胎mesh,结果Gazebo跑起来一卡一卡的,换成圆柱体后瞬间流畅。
举个例子,一个圆柱体轮子:
<geometry>
<cylinder radius="0.1" length="0.05"/>
</geometry>
嗯,就这么简单。但要注意,cylinder的轴线默认是Z轴。如果你想让轮子竖着放,就得配合origin的旋转来调整。
3.4 origin标签——位置和朝向
origin定义了link或joint相对于父级的位置和朝向。它有两个属性:
- xyz:平移量,单位米。
- rpy:旋转量,单位弧度。顺序是绕X轴、Y轴、Z轴。
这里有个容易搞混的地方:origin在link里和joint里的含义不同。
- 在
link的visual里,origin是相对于该link坐标系的偏移。 - 在
joint里,origin是子link相对于父link的变换。
说白了,joint里的origin决定了子零件“长”在父零件的哪个位置。
举个例子,把左轮装在底盘左侧:
<origin xyz="-0.2 0.15 0" rpy="0 0 0"/>
意思是左轮中心在底盘坐标系中,X方向向后0.2米,Y方向向左0.15米。如果你搞反了正负号,轮子就会跑到底盘里面去。我刚开始学的时候经常犯这种错,后来养成了一个习惯:画个草图,标清楚坐标系方向。
3.5 知识体系总览
说了这么多,咱们用一张图来总结一下URDF的核心结构。下面这张SVG图展示了link和joint如何组成一个机器人树:
看到没?整个机器人就是一棵树。根节点是base_link,每个joint连接一个父link和一个子link。这种树状结构的好处是,你修改一个零件的位置,不会影响其他零件。
3.6 实战建议
最后,给你几个我在实际项目中总结出来的建议:
- 先画图再写代码:拿张纸画出机器人的结构树,标清楚每个
link和joint的名字,以及它们之间的相对位置。这能省掉你一半的调试时间。 - 用xacro代替纯URDF:URDF不支持变量和数学运算,写起来很痛苦。xacro可以定义宏、做计算,代码量能减少60%。我后面会专门讲xacro。
- 测试每个关节:写完URDF后,用
check_urdf命令检查语法,再用urdf_to_graphiz生成结构图。确保每个关节都能正确运动。 - 注意单位:URDF里长度单位是米,角度是弧度。我见过有人把毫米当成米,结果机器人缩成蚂蚁大小。
rosrun rqt_tf_tree rqt_tf_tree查看TF树。如果某个link没出现,说明joint配置有问题。这招我用了好几年,屡试不爽。
好了,URDF的基础标签就讲到这里。记住,link是骨头,joint是关节,geometry是形状,origin是位置。把这四个搞明白,你就能搭出任何机器人的骨架了。