2、机器人模型构建:URDF模型基础、link与joint定义、在Gazebo中显示机器人
好,咱们正式开始动手了。这一章,我们要解决一个最根本的问题——怎么让机器人出现在Gazebo里?
你想想看,没有模型,地图构建就是空中楼阁。我刚开始学的时候,也以为直接跑个SLAM算法就行了,结果Gazebo里空空如也,啥也看不到。后来才明白,得先给机器人“画个像”,告诉仿真环境它长什么样、关节怎么动。
这个“画像”的工具,就是URDF。
2.1 URDF模型基础
URDF,全称是Unified Robot Description Format,统一机器人描述格式。说白了,就是一个XML文件,用标签来描述机器人的身体结构。
我个人习惯把URDF理解成“机器人的身份证”。它记录了:
- 机器人有哪些部件(link)
- 部件之间怎么连接(joint)
- 每个部件长什么样(几何形状、尺寸、颜色)
嗯,这里要注意:URDF只适用于树状结构的机器人。什么意思?就是每个部件只能有一个父节点,不能有闭环。比如四足机器人,如果腿和身体形成了闭环,URDF就搞不定了,得用更高级的SDF格式。不过咱们做轮式移动机器人,URDF完全够用。
核心要点:URDF是ROS和Gazebo之间的桥梁。没有它,Gazebo不知道你的机器人长啥样。
2.2 link与joint定义
这是URDF里最重要的两个概念。我当年花了整整一个下午才搞明白它们的区别,其实很简单:
- link:机器人的“骨头”,也就是一个个刚体部件。比如底盘、轮子、激光雷达支架。
- joint:机器人的“关节”,用来连接两个link,并定义它们之间的相对运动关系。
举个例子,一个两轮差速底盘,至少需要:
- 1个底盘link(base_link)
- 2个轮子link(left_wheel_link、right_wheel_link)
- 2个轮子joint(left_wheel_joint、right_wheel_joint)
2.2.1 link的定义
一个link标签里,主要包含三部分:
- 视觉属性(visual):在Rviz里看到的样子
- 碰撞属性(collision):用于物理碰撞检测的形状
- 惯性属性(inertial):质量、惯性矩阵,影响物理仿真效果
来看一个最简单的底盘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 0.8 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.02"/>
</inertial>
</link>
这里我用了box(长方体)作为底盘形状。尺寸是0.4米长、0.3米宽、0.1米高。颜色设成了蓝色。碰撞模型和视觉模型一样,但实际项目中,为了性能,碰撞模型通常会简化——比如用圆柱体代替复杂的轮子形状。
我的经验:惯性参数别随便填。我曾经偷懒,给轮子设了个很小的质量,结果仿真时机器人一加速就翻车。后来老老实实算了每个部件的质量和惯性矩,才稳定下来。
2.2.2 joint的定义
joint定义了link之间的连接关系。常用的joint类型有:
| 类型 | 说明 | 自由度 |
|---|---|---|
| fixed | 固定连接,两个link之间没有相对运动 | 0 |
| revolute | 旋转关节,绕一个轴转动(有角度限制) | 1 |
| continuous | 连续旋转关节,没有角度限制(比如轮子) | 1 |
| prismatic | 滑动关节,沿一个轴直线运动 | 1 |
轮子一般用continuous类型,因为它要一直转。来看轮子joint的定义:
<joint name="left_wheel_joint" type="continuous">
<parent link="base_link"/>
<child link="left_wheel_link"/>
<origin xyz="-0.15 0.2 -0.05" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
</joint>
这里parent是底盘,child是轮子。origin定义了轮子相对于底盘的位置——在底盘左前方。axis指定了旋转轴是Y轴,这样轮子就能前后转动了。
避坑指南:我曾经把parent和child搞反了,结果轮子带着底盘跑,Gazebo里直接炸了。记住:parent是靠近机器人基座的部件,child是末端部件。
2.3 在Gazebo中显示机器人
模型写好了,怎么让它出现在Gazebo里?这里需要引入一个重要的中间件——robot_state_publisher。
它的作用很简单:读取URDF文件,发布每个link的坐标变换(TF)。Gazebo和Rviz都靠这些TF来知道机器人各部件的位置。
启动流程一般是这样的:
- 写一个launch文件,加载URDF模型
- 启动robot_state_publisher节点
- 启动Gazebo,生成机器人实体
来看一个完整的launch文件示例:
<launch>
<!-- 加载URDF模型 -->
<param name="robot_description"
textfile="$(find my_robot)/urdf/my_robot.urdf"/>
<!-- 启动状态发布器 -->
<node name="robot_state_publisher"
pkg="robot_state_publisher"
type="robot_state_publisher"/>
<!-- 启动Gazebo -->
<include file="$(find gazebo_ros)/launch/empty_world.launch">
<arg name="paused" value="false"/>
<arg name="use_sim_time" value="true"/>
</include>
<!-- 在Gazebo中生成机器人 -->
<node name="spawn_urdf"
pkg="gazebo_ros"
type="spawn_model"
args="-param robot_description -urdf -model my_robot"/>
</launch>
这里最关键的是spawn_model这个节点。它从参数服务器读取robot_description,然后在Gazebo里生成对应的机器人模型。
为什么会这样?因为Gazebo本身不认识URDF文件,它需要spawn_model把URDF转换成Gazebo能理解的SDF格式。
记住这个流程:URDF文件 → robot_state_publisher发布TF → spawn_model生成实体 → Gazebo显示机器人
嗯,这里还要提一句。如果你在Gazebo里看到机器人是灰色的,别慌。那是因为URDF里定义的material颜色,Gazebo默认不识别。需要在URDF里额外添加Gazebo专用的颜色标签,或者用纹理贴图。这个我们后面章节会细讲。
我个人习惯,第一次测试时先用Rviz看看模型对不对。Rviz里显示正常了,再放到Gazebo里跑物理仿真。这样排查问题快很多。
本章知识体系
下面这张图,帮你理清URDF模型构建的核心逻辑:
这张图把整个流程串起来了。你写URDF的时候,心里要时刻想着:这个link的parent是谁?joint的类型对不对?坐标原点有没有偏移?
好了,这一章的内容就到这儿。模型建好了,下一章我们就能让它动起来了。