3、URDF机器人模型基础:URDF文件结构、link与joint的定义、在Gazebo中加载URDF模型
各位同学,今天我们来聊聊URDF。这东西说白了,就是机器人的「骨架说明书」。你想想看,要让一个机械臂在Gazebo里动起来,总得先告诉仿真器:它长什么样?关节在哪?能怎么转?URDF就是干这个的。
我最早接触URDF的时候,觉得它不就是个XML嘛,写起来应该不难。结果第一次加载到Gazebo里,机械臂直接「躺平」了——关节方向反了。嗯,从那以后我就老老实实把每个坐标轴都画出来确认一遍。今天咱们就把这块彻底讲透。
3.1 URDF文件长什么样?
URDF全称是Unified Robot Description Format,统一机器人描述格式。它用XML树形结构来描述机器人。一个最简单的URDF文件,大概长这样:
<robot name="simple_arm">
<link name="base_link">
<visual>
<geometry>
<box size="0.2 0.2 0.1"/>
</geometry>
</visual>
</link>
<joint name="joint1" type="revolute">
<parent link="base_link"/>
<child link="arm_link"/>
<origin xyz="0 0 0.05" rpy="0 0 0"/>
<axis xyz="0 0 1"/>
</joint>
<link name="arm_link">
<visual>
<geometry>
<cylinder length="0.3" radius="0.02"/>
</geometry>
</visual>
</link>
</robot>
看到没?<robot>是根标签,里面包着<link>和<joint>。每个link就是一个刚体部件,每个joint定义了两个link之间的连接关系。就这么简单。
3.2 link:机器人的「骨头」
link代表机器人的一个刚体部件。比如基座、大臂、小臂、末端执行器,每个都是一个link。一个link的定义包含三大部分:
| 标签 | 作用 | 必填? |
|---|---|---|
<visual> |
可视化外观,Gazebo里能看到的样子 | 推荐填 |
<collision> |
碰撞检测模型,物理引擎用它算碰撞 | 推荐填 |
<inertial> |
惯性参数,质量、质心、转动惯量 | Gazebo仿真必填 |
我见过不少新手只写<visual>,结果模型能显示但一碰就穿模。为什么?因为没有<collision>,物理引擎不知道这玩意儿的边界在哪。
来看一个完整的link示例:
<link name="base_link">
<visual>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<box size="0.3 0.3 0.1"/>
</geometry>
<material name="blue">
<color rgba="0 0 1 1"/>
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<box size="0.3 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.02"/>
</inertial>
</link>
ixx = 0.01 * mass * (y^2 + z^2)这种公式估算一下。
3.3 joint:机器人的「关节」
joint定义了link之间的运动关系。URDF支持6种关节类型:
- revolute:旋转关节,有角度限制。机械臂最常用。
- continuous:连续旋转关节,没有角度限制。比如车轮。
- prismatic:平移关节,直线滑动。比如伸缩臂。
- fixed:固定连接,两个link焊死了。
- floating:6自由度浮动关节。一般不用。
- planar:平面运动关节。也很少用。
joint的核心是定义父link和子link之间的坐标变换。看这个例子:
<joint name="shoulder_joint" type="revolute">
<parent link="base_link"/>
<child link="upper_arm"/>
<origin xyz="0 0 0.15" rpy="0 0 0"/>
<axis xyz="0 0 1"/>
<limit lower="-1.57" upper="1.57" effort="10" velocity="1.0"/>
</joint>
这里<origin>表示子link的坐标系原点相对于父link的位置和姿态。<axis>定义旋转轴。对于revolute关节,<limit>里的lower和upper是弧度制的角度限制。
我个人习惯把<origin>的xyz理解为「关节中心在父link上的位置」。比如基座高0.1米,关节中心在基座顶面,那z就是0.05(基座半高)。
3.4 在Gazebo中加载URDF模型
模型写好了,怎么让它出现在Gazebo里?有两种方式:
方式一:直接加载URDF文件
ros2 launch gazebo_ros gazebo.launch.py
ros2 run gazebo_ros spawn_entity.py -file my_arm.urdf -entity my_arm
方式二:通过launch文件加载
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(
package='gazebo_ros',
executable='spawn_entity.py',
arguments=['-file', 'my_arm.urdf', '-entity', 'my_arm'],
output='screen'
)
])
不过,直接加载URDF到Gazebo有个坑——模型会掉下去。为什么?因为Gazebo默认有重力,而你的模型没有地面接触。解决办法是在URDF里加一个world关节:
<link name="world"/>
<joint name="fixed_base" type="fixed">
<parent link="world"/>
<child link="base_link"/>
</joint>
这样就把基座固定在世界坐标系里了。嗯,这个小细节当年让我折腾了半小时。
- URDF是树形结构,一个父link可以有多个子link,但子link只能有一个父link
- 每个joint必须指定parent和child,且不能形成闭环(URDF不支持闭环运动链)
- Gazebo仿真必须填写
<inertial>和<collision>,否则物理行为会异常 - 关节的
<origin>是子link坐标系相对于父link坐标系的变换
3.5 知识体系总览
下面这张图把URDF的核心结构画出来了,你可以对照着理解:
这张图把URDF的骨架画得很清楚。你写模型的时候,就照着这个结构一层层往下填。先写link,再写joint把它们串起来。记住:joint永远连接两个link,link永远通过joint连接到父link。
好了,URDF的基础就这些。下一节我们会实际动手,从零搭建一个完整的6轴机械臂URDF模型。到时候你会看到,今天讲的这些概念是怎么组合成一个能动的机器人的。
可可精品资料库