3. 机器人模型导入
好,咱们进入第三章。这一章,说白了就是给机器人“造个身体”。
你想想看,没有身体,算法再牛也跑不起来。在仿真世界里,URDF和SDF就是机器人的“骨架说明书”。我最早接触URDF时,觉得不就是个XML嘛,结果一上手就踩了不少坑。今天咱们就把这事彻底聊透。
3.1 URDF vs SDF:到底选哪个?
先解决一个最实际的问题:URDF和SDF,我该用哪个?
我个人习惯是这么分的:
- URDF:Gazebo经典格式,简单直接。适合单机器人、结构不复杂的场景。
- SDF:更现代,支持多机器人、复杂环境、传感器模型。说白了就是URDF的“升级版”。
我在项目中遇到过一件事:用URDF搭了个四足机器人,一切正常。后来想加个激光雷达和地面摩擦参数,URDF就有点力不从心了。换成SDF后,这些配置变得非常直观。
核心区别速览:
| 特性 | URDF | SDF |
|---|---|---|
| 支持多机器人 | ❌ 不直接支持 | ✅ 原生支持 |
| 物理属性 | 基础 | 丰富(摩擦、阻尼等) |
| 传感器模型 | 需额外插件 | 内置支持 |
| 学习曲线 | 平缓 | 稍陡 |
3.2 URDF文件结构:从零搭一个轮式机器人
咱们直接上手。下面这个URDF,描述了一个最简单的两轮差速底盘。嗯,这里要注意,每个标签都有它的“脾气”。
<robot name="my_first_robot">
<!-- 底盘 -->
<link name="base_link">
<visual>
<geometry>
<box size="0.4 0.3 0.1"/>
</geometry>
<material name="blue">
<color rgba="0 0 1 1"/>
</material>
</visual>
<collision>
<geometry>
<box size="0.4 0.3 0.1"/>
</geometry>
</collision>
<inertial>
<mass value="2.0"/>
<inertia ixx="0.01" iyy="0.01" izz="0.01"/>
</inertial>
</link>
<!-- 左轮 -->
<link name="left_wheel">
<visual>
<geometry>
<cylinder radius="0.05" length="0.04"/>
</geometry>
<material name="grey"/>
</visual>
<collision>
<geometry>
<cylinder radius="0.05" length="0.04"/>
</geometry>
</collision>
<inertial>
<mass value="0.2"/>
<inertia ixx="0.0001" iyy="0.0001" 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.2 0" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
</joint>
</robot>
看到没?每个link(刚体)都要定义三件事:长什么样(visual)、碰撞边界在哪(collision)、有多重(inertial)。关节(joint)则决定了它们怎么动。
我的小技巧:visual和collision的几何体可以不一样。比如你为了碰撞检测更保守,可以把collision的box稍微放大一点。我在做仓储机器人时就这么干过,有效避免了“看起来没撞上,实际卡住了”的尴尬。
3.3 SDF文件:更强大的“身体说明书”
如果你觉得URDF有点“简陋”,那SDF就是它的完全体。下面这个SDF,在URDF基础上加了地面摩擦和激光雷达模型。
<sdf version="1.7">
<model name="my_robot">
<link name="base_link">
<visual name="visual">
<geometry>
<box>
<size>0.4 0.3 0.1</size>
</box>
</geometry>
</visual>
<collision name="collision">
<geometry>
<box>
<size>0.4 0.3 0.1</size>
</box>
</geometry>
<surface>
<friction>
<ode>
<mu>0.8</mu>
<mu2>0.8</mu2>
</ode>
</friction>
</surface>
</collision>
</link>
<!-- 激光雷达插件 -->
<plugin name="laser" filename="libLaserPlugin.so">
<topic>/scan</topic>
<frame>base_link</frame>
</plugin>
</model>
</sdf>
你发现没?SDF里可以直接定义表面摩擦系数,还能挂载传感器插件。这在URDF里得靠额外配置,麻烦得很。
3.4 核心知识体系:一张图看懂
下面这张SVG图,是我自己梳理的“机器人模型导入”知识脉络。你照着这个思路走,基本不会迷路。
3.5 避坑指南:那些年我踩过的坑
讲完理论,咱们聊聊实战中容易翻车的地方。
我曾经犯过的错:
- 坐标系搞反了:URDF里joint的origin坐标,是相对于parent link的。我第一次写时把xyz写反了,结果轮子长到了底盘里面。嗯,仿真里看着特别诡异。
- 忘记定义inertial:有些仿真器(比如Gazebo)没有惯性参数会直接报错。我建议你养成习惯,每个link都写上mass和inertia,哪怕是个近似值。
- 碰撞模型太精细:visual可以很漂亮,但collision尽量用box、cylinder这些基本几何体。太复杂的mesh会导致碰撞检测巨慢。我在做机械臂时深有体会,一个stl网格让仿真帧率直接掉到个位数。
3.6 实战:在仿真中加载你的机器人
好了,文件写好了,怎么让它动起来?
如果你用Gazebo,命令很简单:
# 启动Gazebo空世界
gazebo worlds/empty.world
# 插入你的URDF模型
ros2 run gazebo_ros spawn_entity.py -file my_robot.urdf -entity my_robot
或者用SDF:
gazebo --verbose my_robot.sdf
我个人习惯先用check_urdf工具验证一下文件语法:
check_urdf my_robot.urdf
它会告诉你有没有缺失的link、关节类型对不对。这步能省掉你至少一半的调试时间。
一个小建议:第一次加载时,把机器人放在空中(离地面0.5米),让它自由落体。如果它落地后“散架”了,说明关节或碰撞定义有问题。如果稳稳落地,恭喜你,第一步成功了。
你想想看,从一堆XML标签到屏幕上活蹦乱跳的机器人,这个过程其实挺有成就感的。下一章咱们会让这个机器人真正“走”起来——但那是后话了。
可可精品资料库