3、URDF机器人建模:从零搭建你的第一个机器人

各位同学,今天我们来聊聊URDF。这东西说白了就是机器人的「身份证」——它告诉Gazebo你的机器人长什么样、轮子装在哪、传感器怎么摆。

我记得刚接触ROS那会儿,总觉得URDF就是个简单的XML文件,随便写写就行。结果第一次在Gazebo里加载模型,轮子直接飞到了天上……嗯,从那以后我再也不敢小看它了。

3.1 URDF语法基础——别被XML吓到

URDF全称是Unified Robot Description Format。你把它想象成一份「机器人零件清单+装配图」就行。

核心就两个标签:<link> 和 <joint>。

  • link:描述一个刚体零件。比如底盘、轮子、摄像头支架。
  • joint:描述两个link之间的连接关系。比如轮子怎么装在底盘上。

我习惯把link比作「骨头」,joint比作「关节」。骨头不能动,关节决定怎么动。

重要概念:每个link必须有且只有一个inertial属性(质量+惯性矩阵),否则Gazebo会报错。我在项目里吃过这个亏——模型加载成功但一跑就崩,查了半天发现是惯性参数没写。

来看一个最简单的link示例:

<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" ixy="0" ixz="0"
             iyy="0.01" iyz="0"
             izz="0.01"/>
  </inertial>
</link>

注意看,visual是外观,collision是碰撞体,inertial是物理属性。三者可以不一样——比如你可以用精细的3D模型做外观,用简单的长方体做碰撞检测,这样性能更好。

3.2 创建机器人底盘——从一块平板开始

底盘是机器人的基础。我个人习惯先画一个简单的长方体,把尺寸、颜色、质量定下来。

为什么先定质量?因为Gazebo的物理引擎需要质量来计算惯性。你想想看,一个2kg的底盘和20kg的底盘,运动特性完全不一样。

这里有个小技巧:惯性矩阵可以用公式估算。对于长方体,绕Z轴的惯性矩是 Izz = m * (width² + length²) / 12。我刚开始都是手算,后来发现ROS有个工具叫urdf_to_graphiz,能帮你检查模型结构。

我的经验:底盘的origin坐标尽量放在几何中心。这样后续加轮子、加传感器时,坐标变换会简单很多。我曾经把原点放在角落,结果算坐标算到怀疑人生。

底盘代码示例:

<link name="chassis">
  <visual>
    <geometry>
      <box size="0.5 0.4 0.15"/>
    </geometry>
    <origin xyz="0 0 0" rpy="0 0 0"/>
    <material name="silver">
      <color rgba="0.75 0.75 0.75 1"/>
    </material>
  </visual>
  <collision>
    <geometry>
      <box size="0.5 0.4 0.15"/>
    </geometry>
  </collision>
  <inertial>
    <mass value="3.0"/>
    <inertia ixx="0.025" ixy="0" ixz="0"
             iyy="0.04" iyz="0"
             izz="0.04"/>
  </inertial>
</link>

3.3 添加轮子和传感器——让机器人动起来

底盘有了,接下来装轮子。轮子一般用cylinder几何体,通过continuous类型的joint连接到底盘。

为什么是continuous?因为轮子要无限旋转。如果是舵机关节,就用revolute。

我曾经犯过一个低级错误:轮子的joint坐标写错了,导致轮子悬在半空。后来我学乖了——先在Rviz里可视化检查,再进Gazebo仿真。

<joint name="left_wheel_joint" type="continuous">
  <parent link="chassis"/>
  <child link="left_wheel"/>
  <origin xyz="-0.2 0.25 -0.08" rpy="0 0 0"/>
  <axis xyz="0 1 0"/>
</joint>

<link name="left_wheel">
  <visual>
    <geometry>
      <cylinder radius="0.08" length="0.04"/>
    </geometry>
    <material name="black">
      <color rgba="0.1 0.1 0.1 1"/>
    </material>
  </visual>
  <collision>
    <geometry>
      <cylinder radius="0.08" 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.0002"/>
  </inertial>
</link>

传感器怎么加?比如加一个激光雷达,装在底盘正上方:

<joint name="laser_joint" type="fixed">
  <parent link="chassis"/>
  <child link="laser"/>
  <origin xyz="0.15 0 0.1" rpy="0 0 0"/>
</joint>

<link name="laser">
  <visual>
    <geometry>
      <cylinder radius="0.03" length="0.05"/>
    </geometry>
    <material name="red">
      <color rgba="1 0 0 1"/>
    </material>
  </visual>
</link>

注意:传感器的collision可以省略,因为Gazebo里传感器通常不参与物理碰撞。但如果你省略了inertial,Gazebo会报warning。我建议给传感器也加上质量,哪怕只有0.01kg。

3.4 在Gazebo中加载URDF模型——见证奇迹的时刻

模型写好了,怎么加载到Gazebo?

有两种方式:

  1. 直接加载URDF文件:用spawn_model命令
  2. 通过ROS launch文件:更规范,适合复杂项目

我个人推荐第二种。为什么?因为launch文件可以同时启动Gazebo、加载模型、启动控制器,一键搞定。

来看一个简单的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)/urdf/my_robot.urdf"/>

  <node name="spawn_urdf" 
        pkg="gazebo_ros" 
        type="spawn_model" 
        args="-param robot_description 
              -urdf 
              -model my_robot 
              -x 0 -y 0 -z 0.1"/>
</launch>

这里有个细节:-z 0.1是为了让机器人稍微抬高地面,避免刚加载时陷入地面。我刚开始没加这个参数,结果机器人直接「遁地」了……

3.5 知识体系总览

下面这张图是我自己画的,帮你理清URDF建模的核心脉络:

URDF机器人建模 语法基础 link + joint 核心标签 visual / collision / inertial 底盘与轮子 长方体底盘 + 圆柱轮子 continuous / fixed joint 传感器与加载 激光雷达 / 摄像头 spawn_model + launch 核心原则: 先可视化检查 → 再物理仿真 → 最后调参优化

3.6 避坑指南——我踩过的那些坑

最后分享几个实战中容易翻车的地方:

  • 惯性矩阵不对:Gazebo会直接忽略你的模型,或者运动起来像果冻。建议用urdf_to_graphiz检查。
  • joint坐标写反:parent和child搞混,轮子会跑到底盘里面去。我建议画个草图再写代码。
  • 忘记加<gazebo>标签:有些传感器需要额外的gazebo插件才能工作,比如libgazebo_ros_laser.so。
  • 模型加载后不动:检查一下use_sim_time是否设为true,以及是否有控制器在驱动关节。

我的小习惯:每次修改URDF后,先用check_urdf命令检查语法,再用urdf_to_graphiz生成结构图。这两步能避免80%的低级错误。

好了,URDF建模就聊到这儿。记住一句话:模型是仿真的基础,基础不牢,地动山摇。动手写一个你自己的机器人模型吧,哪怕只是一个带轮子的盒子——相信我,当你看到它在Gazebo里动起来的那一刻,成就感爆棚。

可可精品资料库