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图,是我自己梳理的“机器人模型导入”知识脉络。你照着这个思路走,基本不会迷路。

机器人模型导入知识体系 机器人模型 URDF 格式 SDF 格式 link(刚体) joint(关节) 模型+环境 传感器插件 核心三要素:几何(visual) + 碰撞(collision) + 惯性(inertial) 关节决定运动方式,传感器让机器人“感知”世界

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标签到屏幕上活蹦乱跳的机器人,这个过程其实挺有成就感的。下一章咱们会让这个机器人真正“走”起来——但那是后话了。

可可精品资料库