2. 机器人模型URDF入门

各位同学,欢迎来到实战训练的第二课。今天咱们要亲手搭建一个机器人模型——用URDF语言。说白了,URDF就是机器人的“身份证”,告诉Gazebo你长什么样、胳膊腿儿怎么连。

我记得刚入行那会儿,第一次写URDF,把轮子和底盘的坐标搞反了,结果机器人一加载就在地上“劈叉”。嗯,这种坑咱们今天必须绕过去。

2.1 URDF到底是什么?

URDF全称是Unified Robot Description Format,统一机器人描述格式。它用XML语法描述机器人的运动学和动力学属性。你想想看,一个机器人无非就是:

  • link:刚体部件,比如底盘、轮子、手臂
  • joint:连接两个link的关节,定义它们之间的相对运动

我个人习惯把URDF想象成“乐高说明书”——每个零件(link)怎么摆,用什么方式连接(joint),一目了然。

核心要点:URDF只描述树状结构,不能有闭环。如果你要做四足机器人或者并联机构,得用xacro或者SDF格式。这个后面章节会讲。

2.2 第一个URDF模型:一个底盘加两个轮子

咱们从最简单的开始。一个矩形底盘,两个圆柱轮子。先看完整代码,我再逐行拆解。

<?xml version="1.0"?>
<robot name="my_robot">

  <!-- 底盘 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>
    </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 -->
  <link name="left_wheel">
    <visual>
      <geometry>
        <cylinder radius="0.05" length="0.04"/>
      </geometry>
      <origin xyz="0 0 0" rpy="0 0 0"/>
      <material name="gray">
        <color rgba="0.5 0.5 0.5 1"/>
      </material>
    </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>

  <!-- 右轮 link -->
  <link name="right_wheel">
    <!-- 内容与左轮相同,略 -->
  </link>

  <!-- 底盘到左轮的 joint -->
  <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>

  <!-- 底盘到右轮的 joint -->
  <joint name="right_wheel_joint" type="continuous">
    <parent link="base_link"/>
    <child link="right_wheel"/>
    <origin xyz="-0.15 -0.2 0" rpy="0 0 0"/>
    <axis xyz="0 1 0"/>
  </joint>

</robot>

2.3 逐元素拆解:link的三大件

每个link必须包含三个子元素,缺一不可。我在项目中见过有人只写visual,结果Gazebo里能看到但碰撞检测失效,机器人直接穿模。

元素 作用 个人经验
<visual> 定义外观,Gazebo里你看到的样子 颜色用RGBA,A是透明度,设为0就隐身了
<collision> 定义碰撞体,用于物理引擎计算 尽量用简单几何体,复杂mesh会拖慢仿真
<inertial> 定义惯性参数,质量+惯性张量 惯性张量算不对,机器人会像纸片一样飘

小技巧:collision的几何体可以比visual小一圈。比如底盘visual是0.4x0.3,collision用0.38x0.28。这样机器人过窄缝时更顺滑,不会卡边。

2.4 joint的类型与坐标变换

joint定义了父子link之间的相对位置和运动方式。我建议你记住这六种类型:

  • continuous:无限旋转,轮子专用
  • revolute:有限角度旋转,像手臂关节
  • prismatic:直线滑动,像升降台
  • fixed:刚性连接,两个link焊死
  • floating:6自由度,一般不用
  • planar:平面运动,少见

joint里最关键的属性是<origin>和<axis>。origin定义child link相对于parent link的位置偏移。axis定义旋转轴或滑动方向。

我曾经犯过一个低级错误:把左轮的origin写成了xyz="0.15 0.2 0",结果轮子跑到底盘前面去了。你想想看,轮子应该在底盘两侧,x方向应该和底盘中心对齐才对。

2.5 在Gazebo中加载并显示

模型写好了,怎么让它动起来?你需要一个launch文件。我个人习惯用spawn_model命令把URDF“生”到Gazebo里。

<launch>
  <!-- 启动Gazebo空世界 -->
  <include file="$(find gazebo_ros)/launch/empty_world.launch">
    <arg name="paused" value="false"/>
    <arg name="use_sim_time" value="true"/>
  </include>

  <!-- 将URDF加载到参数服务器 -->
  <param name="robot_description" 
         textfile="$(find my_robot_description)/urdf/my_robot.urdf"/>

  <!-- 在Gazebo中生成机器人 -->
  <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这个参数。底盘高度是0.1,轮子半径0.05,所以底盘中心离地应该是0.05+0.05/2=0.075?不对,这里要算清楚。我建议你直接设-z 0.05,让底盘底面刚好贴地。嗯,这个值你得根据实际模型微调。

2.6 知识体系总览

下面这张图帮你理清URDF的核心逻辑。我画了张SVG,把link、joint、Gazebo三者的关系串起来。

URDF模型核心结构 link(刚体部件) joint(关节连接) Gazebo(仿真环境) visual(外观) collision(碰撞) inertial(惯性) origin(位置) axis(旋转轴) type(关节类型) spawn_model 生成 物理引擎仿真 每个link必须包含visual+collision+inertial joint定义父子关系,Gazebo负责渲染和物理计算

2.7 避坑指南:我踩过的三个坑

坑一:惯性张量瞎填

我曾经为了省事,把所有link的惯性张量都设成ixx="0.01" iyy="0.01" izz="0.01"。结果机器人一跑起来就像在冰面上打转,完全不受控。后来老老实实用urdf_sim_tutorial里的计算脚本才算对。

坑二:joint的origin坐标搞反

joint的origin是child link相对于parent link的偏移。我见过有人把parent和child写反,结果轮子跑到底盘里面去了。记住:parent是“爸爸”,child是“儿子”,儿子站在爸爸的肩膀上。

坑三:忘记加collision

Gazebo里如果没有collision,机器人会直接穿过地面。嗯,我第一次看到机器人“遁地”时还以为是重力设错了,查了半天才发现是collision没写。

2.8 动手练一练

光看不练假把式。我建议你按这个步骤来:

  1. 创建一个ROS包,比如my_robot_description
  2. 在urdf/目录下新建my_robot.urdf,把上面的代码复制进去
  3. 创建launch文件,启动Gazebo并加载模型
  4. 用rosrun rqt_tf_tree rqt_tf_tree查看坐标变换树
  5. 尝试修改轮子的位置和颜色,重新加载看看效果

如果你看到Gazebo里出现一个蓝色底盘、两个灰色轮子,而且轮子能跟着底盘动——恭喜你,URDF入门成功了!

调试小技巧:用check_urdf my_robot.urdf命令可以检查语法错误。我每次写完URDF都会跑一遍这个命令,能省下不少排查时间。

好了,这一章的内容就到这里。URDF是机器人建模的基石,后面所有章节——从传感器仿真到导航调参——都建立在这个基础上。把今天的内容吃透,后面的路就好走了。

可可精品资料库