4、机器人模型基础:URDF文件格式详解、link与joint的定义、在Gazebo中加载简单机器人模型
好,咱们开始聊机器人模型。说实话,做SLAM也好,搞仿真也罢,第一步永远绕不开URDF。你想想看,连机器人的“身体”都没搭好,后面的建图、导航全是空中楼阁。我个人习惯把URDF比作机器人的“身份证”——它告诉Gazebo:你长什么样,关节在哪,能怎么动。
4.1 URDF到底是什么?
URDF全称是Unified Robot Description Format,统一机器人描述格式。说白了,就是一个XML文件。它用标签来描述机器人的物理结构。我在项目中遇到过不少新手,一上来就急着写代码,结果连URDF的树形结构都没搞懂,后面调试起来特别痛苦。
URDF的核心就两个东西:link(连杆)和joint(关节)。一个机器人就是由若干个link通过joint连接起来的树状结构。嗯,这里要注意:URDF只支持树形结构,不支持闭环。如果你要做四足机器人或者并联机构,那得用别的格式,比如SDF。
核心要点:URDF是树形结构,每个link有且只有一个父joint,但可以有多个子joint。根link没有父joint。
4.2 link的定义——机器人的“骨头”
link就是机器人的一个刚体部件。比如一个轮子、一个底盘、一个机械臂的臂杆。每个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>
<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:视觉部分,就是你在Gazebo里能看到的样子。可以设置几何形状(box、cylinder、sphere、mesh),颜色或者材质。
- collision:碰撞部分,用于物理引擎计算碰撞。我建议你尽量用简单的几何体代替复杂的mesh,否则仿真会卡成PPT。曾经有个项目,我用STL文件做碰撞检测,结果Gazebo直接跑不动,换成box之后流畅多了。
- inertial:惯性部分,包括质量和惯性张量。这个最容易被人忽略,但恰恰是最关键的。惯性参数不对,机器人动起来就跟喝醉了一样。
小技巧:如果你不确定惯性张量怎么算,可以用一个近似公式:对于长方体,Ixx = (1/12)*m*(h²+d²),其中m是质量,h是高度,d是深度。网上也有在线计算器,直接搜“惯性张量计算”就行。
4.3 joint的定义——机器人的“关节”
joint连接两个link,定义了它们之间的相对运动关系。URDF支持六种joint类型:
| 类型 | 说明 | 自由度 |
|---|---|---|
| revolute | 旋转关节,有限位 | 1 |
| continuous | 连续旋转关节,无限位 | 1 |
| prismatic | 平移关节 | 1 |
| fixed | 固定关节,两个link焊死 | 0 |
| floating | 浮动关节,6自由度 | 6 |
| planar | 平面关节,在平面内运动 | 3 |
来看一个joint的例子:
<joint name="left_wheel_joint" type="continuous">
<parent link="base_link"/>
<child link="left_wheel"/>
<origin xyz="-0.2 0.15 0" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
</joint>
这里要注意几个点:
- parent和child:joint的方向是从父link指向子link。这个方向会影响坐标变换的计算。
- origin:joint在父link坐标系下的位置和姿态。rpy是翻滚角、俯仰角、偏航角。
- axis:旋转轴或者平移轴的方向。对于revolute和continuous,这个轴就是旋转轴。
我曾经犯过一个低级错误:把轮子的旋转轴设成了Z轴,结果轮子在地上打转就是不往前走。后来才发现,轮子应该绕着Y轴转才对。你想想看,一个轮子要往前滚,它的旋转轴肯定是水平的,对吧?
4.4 在Gazebo中加载简单机器人模型
好,模型写好了,怎么让它跑起来?Gazebo加载URDF有两种方式:
- 直接加载URDF文件:用
spawn_model命令 - 通过ROS launch文件加载:更推荐的方式
先看第一种,直接命令行操作:
# 启动Gazebo空世界
gazebo worlds/empty.world &
# 加载机器人模型
rosrun gazebo_ros spawn_model -file my_robot.urdf -urdf -model my_robot
第二种,用launch文件,更优雅:
<launch>
<!-- 启动Gazebo -->
<include file="$(find gazebo_ros)/launch/empty_world.launch">
<arg name="paused" value="false"/>
<arg name="use_sim_time" value="true"/>
</include>
<!-- 加载机器人模型到参数服务器 -->
<param name="robot_description"
textfile="$(find my_robot_description)/urdf/my_robot.urdf"/>
<!-- 生成机器人 -->
<node name="spawn_urdf"
pkg="gazebo_ros"
type="spawn_model"
args="-param robot_description -urdf -model my_robot"
output="screen"/>
</launch>
避坑指南:我曾经在加载模型时遇到Gazebo直接闪退,查了半天发现是URDF里忘了加<gazebo>插件标签。如果你想让机器人在Gazebo里动起来,必须添加对应的控制器插件,比如libgazebo_ros_diff_drive.so(差速驱动)或者libgazebo_ros_joint_position_reposition.so(关节位置控制)。
4.5 知识体系总览
下面这张图是我自己整理的URDF知识结构,你可以对照着看:
这张图把URDF的核心脉络理清楚了。你写模型的时候,就照着这个结构来:先定义link,再定义joint,最后用Gazebo加载。每一步都有对应的标签和属性,缺一不可。
4.6 一个完整的简单例子
咱们来写一个两轮差速机器人的URDF。这个模型很简单:一个底盘,两个轮子,一个万向轮。
<?xml version="1.0"?>
<robot name="simple_robot">
<!-- 底盘 -->
<link name="base_link">
<visual>
<geometry><box size="0.4 0.3 0.1"/></geometry>
<material name="blue"/>
</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>
<!-- 左轮 -->
<link name="left_wheel">
<visual>
<geometry><cylinder radius="0.05" length="0.04"/></geometry>
<material name="gray"/>
</visual>
<collision>
<geometry><cylinder radius="0.05" length="0.04"/></geometry>
</collision>
<inertial>
<mass value="0.2"/>
<inertia ixx="0.0001" ixy="0" ixz="0" iyy="0.0001" iyz="0" izz="0.0001"/>
</inertial>
</link>
<!-- 左轮关节 -->
<joint name="left_wheel_joint" type="continuous">
<parent link="base_link"/>
<child link="left_wheel"/>
<origin xyz="-0.15 0.15 0" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
</joint>
<!-- 右轮 -->
<link name="right_wheel">
<visual>
<geometry><cylinder radius="0.05" length="0.04"/></geometry>
<material name="gray"/>
</visual>
<collision>
<geometry><cylinder radius="0.05" length="0.04"/></geometry>
</collision>
<inertial>
<mass value="0.2"/>
<inertia ixx="0.0001" ixy="0" ixz="0" iyy="0.0001" iyz="0" izz="0.0001"/>
</inertial>
</link>
<!-- 右轮关节 -->
<joint name="right_wheel_joint" type="continuous">
<parent link="base_link"/>
<child link="right_wheel"/>
<origin xyz="-0.15 -0.15 0" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
</joint>
</robot>
把这个文件保存为simple_robot.urdf,然后用Gazebo加载。如果一切顺利,你会看到一个蓝色的底盘,两边各有一个灰色的轮子。嗯,虽然它现在还不能动,但至少模型已经站起来了。
调试小技巧:用check_urdf命令可以检查URDF语法是否正确。运行check_urdf simple_robot.urdf,它会告诉你有没有错误。另外,urdf_to_graphiz可以生成模型的结构图,方便你检查父子关系。
好了,URDF的基础就这些。说白了,它就是一套标签语言,把机器人的物理结构描述清楚。你只要记住:link是骨头,joint是关节,两者组合起来就是完整的机器人。下一节咱们会在这个基础上,给机器人加上传感器,让它真正开始感知世界。
可可精品资料库