3、URDF模型构建:从零搭建一个6轴机械臂的URDF模型
URDF,全称是 Unified Robot Description Format。说白了,它就是机器人的「身份证」加「骨架说明书」。你要让仿真环境认识你的机械臂,就得用 URDF 把它的关节、连杆、尺寸、颜色,一五一十地告诉系统。
我记得刚入行那会儿,总觉得 URDF 就是个 XML 文件,随便写写就行。结果第一次加载模型,机械臂的关节全拧成了麻花。嗯,从那以后,我再也不敢小看这个文件了。
3.1 URDF 的核心结构
一个完整的 URDF 模型,由两个基本元素组成:link(连杆)和 joint(关节)。
- link:描述机械臂的刚体部分,包括几何形状、尺寸、颜色、惯性参数。
- joint:描述两个 link 之间的连接关系,包括类型、父级、子级、运动范围。
你想想看,6 轴机械臂就是 6 个 joint 加上 7 个 link(基座 + 6 个连杆)。关节把连杆串起来,就成了一整条运动链。
关键原则:URDF 的坐标系是树状结构。基座是根节点,每个 joint 只能有一个父 link 和一个子 link。不能有环,不能有分叉。
3.2 从基座开始:第一个 link
我个人习惯,先从基座写起。基座是整条链的根,它没有父关节,只有子关节。
<robot name="my_6axis_arm">
<link name="base_link">
<visual>
<geometry>
<cylinder length="0.1" radius="0.08"/>
</geometry>
<origin rpy="0 0 0" xyz="0 0 0"/>
<material name="base_color">
<color rgba="0.2 0.5 0.8 1.0"/>
</material>
</visual>
</link>
</robot>
这里我用了圆柱体作为基座。origin 表示几何中心相对于 link 坐标原点的偏移。rpy 是绕 X、Y、Z 轴的旋转角度。嗯,这里要注意:visual 只负责显示,不影响碰撞检测。如果你要做物理仿真,还得加一个 collision 块。
我的经验:visual 和 collision 尽量用相同的几何体。如果不一样,仿真时会出现「看着没撞上,实际撞上了」的诡异情况。我踩过这个坑,调试了整整一个下午。
3.3 添加关节:让机械臂动起来
基座写好了,接下来加第一个关节。6 轴机械臂的关节类型,绝大多数是 revolute(旋转关节)。
<joint name="joint1" type="revolute">
<parent link="base_link"/>
<child link="link1"/>
<origin rpy="0 0 0" xyz="0 0 0.05"/>
<axis xyz="0 0 1"/>
<limit lower="-3.14" upper="3.14" effort="10" velocity="1.0"/>
</joint>
这里 axis 定义了旋转轴。xyz="0 0 1" 表示绕 Z 轴旋转。limit 里的 lower 和 upper 是弧度制的关节限位。effort 是最大力矩,velocity 是最大速度。
为什么会这样设计?因为实际电机就有这些限制。仿真里不设限位,关节就会无限旋转,跟现实完全脱节。
3.4 完整的 6 轴机械臂 URDF 骨架
下面是一个简化版的 6 轴 URDF 骨架。每个关节都绕 Z 轴旋转,连杆长度依次递增。
<robot name="six_axis_arm">
<!-- 基座 -->
<link name="base_link">
<visual>
<geometry><cylinder length="0.1" radius="0.08"/></geometry>
<origin rpy="0 0 0" xyz="0 0 0"/>
<material name="blue"><color rgba="0.2 0.5 0.8 1.0"/></material>
</visual>
</link>
<!-- 关节1 -->
<joint name="joint1" type="revolute">
<parent link="base_link"/>
<child link="link1"/>
<origin rpy="0 0 0" xyz="0 0 0.05"/>
<axis xyz="0 0 1"/>
<limit lower="-3.14" upper="3.14" effort="10" velocity="1.0"/>
</joint>
<link name="link1">
<visual>
<geometry><box size="0.05 0.05 0.3"/></geometry>
<origin rpy="0 0 0" xyz="0 0 0.15"/>
<material name="red"><color rgba="0.8 0.2 0.2 1.0"/></material>
</visual>
</link>
<!-- 关节2 ~ 关节6 结构类似,依次连接 -->
<joint name="joint2" type="revolute">
<parent link="link1"/>
<child link="link2"/>
<origin rpy="0 0 0" xyz="0 0 0.3"/>
<axis xyz="0 0 1"/>
<limit lower="-3.14" upper="3.14" effort="10" velocity="1.0"/>
</joint>
<link name="link2">
<visual>
<geometry><box size="0.04 0.04 0.25"/></geometry>
<origin rpy="0 0 0" xyz="0 0 0.125"/>
<material name="green"><color rgba="0.2 0.8 0.2 1.0"/></material>
</visual>
</link>
<!-- 后续关节和连杆省略,结构完全一致 -->
</robot>
避坑指南:origin 的 xyz 偏移量,是子 link 坐标系原点相对于父 joint 原点的位置。不是子 link 几何中心的位置。我曾经把这两个搞混,结果机械臂的连杆全飞到了天上。
3.5 知识体系总览
下面这张图,把 URDF 构建的核心逻辑串起来了。你可以把它当作写 URDF 时的检查清单。
3.6 参数对比:不同关节类型的适用场景
URDF 支持多种关节类型。6 轴机械臂主要用 revolute,但其他类型也有用武之地。比如夹爪的平移,就用 prismatic。
| 关节类型 | 运动方式 | 典型应用 | 6轴机械臂使用频率 |
|---|---|---|---|
| revolute | 绕轴旋转(有限角度) | 肩关节、肘关节、腕关节 | ★★★★★ |
| continuous | 绕轴旋转(无限角度) | 末端执行器旋转 | ★★☆☆☆ |
| prismatic | 沿轴平移 | 夹爪开合、升降机构 | ★☆☆☆☆ |
| fixed | 无运动 | 传感器支架、固定连接 | ★★★☆☆ |
| floating | 6自由度自由运动 | 移动基座、无人机 | ☆☆☆☆☆ |
我的建议:刚开始写 URDF,先用 revolute 和 fixed 两种类型就够了。等模型能在 Rviz 里正常显示、关节能正常转动,再考虑加其他类型。一口吃不成胖子,URDF 也是。
3.7 验证模型:用 check_urdf 工具
写完了 URDF,别急着加载到仿真里。先用 ROS 自带的工具检查一下语法。
# 安装工具
sudo apt install liburdfdom-tools
# 检查 URDF 文件
check_urdf my_6axis_arm.urdf
如果输出类似下面这样,说明模型结构正确:
robot name is: my_6axis_arm
---------- Successfully Parsed XML ---------------
root link: base_link has 1 child(ren)
child(1): link1
child(1): link2
...
如果有错误,工具会告诉你哪个关节的 parent 或 child 找不到。嗯,我遇到过最蠢的错误——把 link 名字拼错了,大小写不一致。URDF 是大小写敏感的,base_link 和 Base_link 是两个完全不同的连杆。
避坑指南:URDF 文件编码必须用 UTF-8,不要带 BOM。Windows 下用记事本编辑容易踩这个坑。我曾经从 Windows 拷了一个 URDF 到 Ubuntu,加载时一直报 XML 解析错误,折腾了半天才发现是 BOM 的问题。
3.8 本章小结
URDF 模型构建,说白了就是搭积木。link 是积木块,joint 是连接销。你只要把每个积木的位置、大小、颜色定义清楚,再把连接销的类型、范围、轴线设置好,一个完整的机械臂模型就出来了。
我个人习惯,每写完一个关节,就用 check_urdf 验证一次。不要等到全部写完再查错,那样定位问题会非常痛苦。你想想看,6 个关节 7 个连杆,如果最后一个关节的 origin 写错了,你得从头排查到尾巴。