4. URDF模型解析:从零搭建你的机械臂“骨架”
好,咱们进入第四讲。前面几章我们把ROS2的基础环境、通信机制都捋了一遍,现在终于要动手做点“看得见摸得着”的东西了——写一个URDF文件,让机械臂在Rviz2里站起来。
URDF,全称是Unified Robot Description Format,说白了就是机器人的“骨架说明书”。它用XML格式告诉ROS2:你的机器人长什么样、关节在哪、能怎么动。我个人习惯把URDF看作机器人的“身份证”,没有它,后续的规划、控制、仿真全都无从谈起。
核心要点:URDF只描述机器人的运动学与外观,不涉及动力学(质量、惯性等)。如果你要做动力学仿真,需要额外添加<inertial>标签。
4.1 编写一个简单的URDF文件
先别想太复杂。咱们从最简单的两连杆机械臂开始。你想想看,一个机械臂无非就是:几个刚体(link)通过关节(joint)串起来。
下面这个例子,我定义了一个基座(base_link)和一个连杆(arm_link),中间用一个旋转关节连着。
<?xml version="1.0"?>
<robot name="simple_arm">
<!-- 基座 -->
<link name="base_link">
<visual>
<geometry>
<box size="0.2 0.2 0.1"/>
</geometry>
<origin xyz="0 0 0" rpy="0 0 0"/>
<material name="blue">
<color rgba="0 0 1 1"/>
</material>
</visual>
</link>
<!-- 关节 -->
<joint name="arm_joint" type="revolute">
<parent link="base_link"/>
<child link="arm_link"/>
<origin xyz="0 0 0.1" rpy="0 0 0"/>
<axis xyz="0 0 1"/>
<limit lower="-1.57" upper="1.57" effort="10" velocity="1"/>
</joint>
<!-- 连杆 -->
<link name="arm_link">
<visual>
<geometry>
<cylinder length="0.4" radius="0.02"/>
</geometry>
<origin xyz="0 0 0.2" rpy="0 0 0"/>
<material name="red">
<color rgba="1 0 0 1"/>
</material>
</visual>
</link>
</robot>
小技巧:写URDF时,<origin>的xyz和rpy最容易搞混。我的经验是:先画个草图,标清楚每个link的原点位置,再动手写代码。曾经有一次我写反了z轴方向,结果机械臂“钻到地底下”去了,排查了半天才发现。
4.2 link与joint的定义——骨架与关节
咱们拆开来看。link和joint是URDF的两大核心,缺一不可。
link(刚体部件)
每个link代表一个刚体。你可以给它定义:
- visual:视觉外观,用于Rviz2显示。说白了就是“长什么样”。
- collision:碰撞模型,用于物理仿真。可以比visual更简化,提高计算效率。
- inertial:惯性参数(质量、惯性张量),用于动力学计算。
嗯,这里要注意:如果你只是做运动学展示,只写visual就够了。但如果你要做抓取仿真,collision和inertial必须补上,否则物理引擎会报错。
joint(关节连接)
joint定义了父子link之间的运动关系。常用的关节类型有:
| 类型 | 说明 | 自由度 |
|---|---|---|
| revolute | 旋转关节,有角度限制 | 1 |
| continuous | 连续旋转关节,无限制(如车轮) | 1 |
| prismatic | 平移关节,直线运动 | 1 |
| fixed | 固定关节,两个link刚性连接 | 0 |
我个人习惯把joint想象成“铰链”。你想想看,人的手臂能弯曲,靠的就是肘关节这个revolute joint。机械臂也一样,每个旋转关节对应一个电机。
避坑指南:我曾经犯过一个低级错误——joint的<origin>写错了位置,导致子link飞到了天边。记住:joint的origin是相对于parent link的坐标系。子link的origin是相对于joint的坐标系。这两个别搞混了。
4.3 使用robot_state_publisher发布TF变换
URDF文件写好了,怎么让ROS2知道它?这时候就需要robot_state_publisher登场了。
这个节点会读取你的URDF,然后根据joint的状态(角度/位置),自动计算出每个link之间的TF变换。说白了,它负责回答“我的机械臂现在是什么姿势”。
启动方式很简单,在launch文件里加上:
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(
package='robot_state_publisher',
executable='robot_state_publisher',
parameters=[{'robot_description': open('simple_arm.urdf').read()}]
)
])
嗯,这里有个细节:robot_description参数必须是一个字符串,所以要用open().read()把文件内容读进来。我刚开始用的时候,直接传了文件路径,结果节点一直报错找不到模型。
启动之后,你可以用ros2 topic echo /tf_static看看发布的TF变换。你会看到类似这样的信息:
transforms:
- header:
frame_id: base_link
child_frame_id: arm_link
transform:
translation:
x: 0.0
y: 0.0
z: 0.1
rotation:
x: 0.0
y: 0.0
z: 0.0
w: 1.0
这就是robot_state_publisher根据你的URDF和joint状态,自动算出来的坐标变换。
4.4 在Rviz2中显示模型
最后一步,也是最激动人心的一步——在Rviz2里看到你的机械臂。
启动Rviz2:
ros2 run rviz2 rviz2
然后在Rviz2界面中:
- 将Fixed Frame设置为
base_link(你的机器人基座坐标系)。 - 点击左下角的Add按钮,选择RobotModel。
- 在RobotModel的属性中,将Robot Description设置为
robot_description。
如果一切顺利,你应该能看到一个蓝色的方块(基座)上面连着一根红色的圆柱(连杆)。
常见问题:如果Rviz2里显示“No transform from [base_link] to [arm_link]”,说明robot_state_publisher没有正确运行。检查一下:
- URDF文件是否有语法错误?用
check_urdf命令检查。 - launch文件中的
robot_description参数是否正确加载? - joint的
<origin>是否合理?
我记得第一次在Rviz2里看到自己写的机械臂动起来时,那种成就感真的难以形容。虽然只是一个简单的两连杆,但那是你亲手“造”出来的机器人。
进阶玩法:你可以用ros2 topic pub /joint_states sensor_msgs/msg/JointState手动发布关节角度,看看机械臂在Rviz2里实时运动。这就是后面章节做运动控制的基础。
好了,这一章的内容就到这里。URDF是ROS2机器人开发的基石,花点时间把link和joint的关系理清楚,后面做运动规划、抓取控制都会顺畅很多。