4、机器人模型基础:URDF文件格式详解、link与joint的定义、在Gazebo中加载简单机器人模型

好,咱们开始聊机器人模型。说实话,做SLAM也好,搞仿真也罢,第一步永远绕不开URDF。你想想看,连机器人的“身体”都没搭好,后面的建图、导航全是空中楼阁。我个人习惯把URDF比作机器人的“身份证”——它告诉Gazebo:你长什么样,关节在哪,能怎么动。

4.1 URDF到底是什么?

URDF全称是Unified Robot Description Format,统一机器人描述格式。说白了,就是一个XML文件。它用标签来描述机器人的物理结构。我在项目中遇到过不少新手,一上来就急着写代码,结果连URDF的树形结构都没搞懂,后面调试起来特别痛苦。

URDF的核心就两个东西:link(连杆)和joint(关节)。一个机器人就是由若干个link通过joint连接起来的树状结构。嗯,这里要注意:URDF只支持树形结构,不支持闭环。如果你要做四足机器人或者并联机构,那得用别的格式,比如SDF。

核心要点:URDF是树形结构,每个link有且只有一个父joint,但可以有多个子joint。根link没有父joint。

4.2 link的定义——机器人的“骨头”

link就是机器人的一个刚体部件。比如一个轮子、一个底盘、一个机械臂的臂杆。每个link需要定义它的视觉、碰撞和惯性属性。

咱们来看一个最简单的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>
    <origin xyz="0 0 0" rpy="0 0 0"/>
  </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:视觉部分,就是你在Gazebo里能看到的样子。可以设置几何形状(box、cylinder、sphere、mesh),颜色或者材质。
  • collision:碰撞部分,用于物理引擎计算碰撞。我建议你尽量用简单的几何体代替复杂的mesh,否则仿真会卡成PPT。曾经有个项目,我用STL文件做碰撞检测,结果Gazebo直接跑不动,换成box之后流畅多了。
  • inertial:惯性部分,包括质量和惯性张量。这个最容易被人忽略,但恰恰是最关键的。惯性参数不对,机器人动起来就跟喝醉了一样。

小技巧:如果你不确定惯性张量怎么算,可以用一个近似公式:对于长方体,Ixx = (1/12)*m*(h²+d²),其中m是质量,h是高度,d是深度。网上也有在线计算器,直接搜“惯性张量计算”就行。

4.3 joint的定义——机器人的“关节”

joint连接两个link,定义了它们之间的相对运动关系。URDF支持六种joint类型:

类型 说明 自由度
revolute 旋转关节,有限位 1
continuous 连续旋转关节,无限位 1
prismatic 平移关节 1
fixed 固定关节,两个link焊死 0
floating 浮动关节,6自由度 6
planar 平面关节,在平面内运动 3

来看一个joint的例子:

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

这里要注意几个点:

  • parent和child:joint的方向是从父link指向子link。这个方向会影响坐标变换的计算。
  • origin:joint在父link坐标系下的位置和姿态。rpy是翻滚角、俯仰角、偏航角。
  • axis:旋转轴或者平移轴的方向。对于revolute和continuous,这个轴就是旋转轴。

我曾经犯过一个低级错误:把轮子的旋转轴设成了Z轴,结果轮子在地上打转就是不往前走。后来才发现,轮子应该绕着Y轴转才对。你想想看,一个轮子要往前滚,它的旋转轴肯定是水平的,对吧?

4.4 在Gazebo中加载简单机器人模型

好,模型写好了,怎么让它跑起来?Gazebo加载URDF有两种方式:

  1. 直接加载URDF文件:用spawn_model命令
  2. 通过ROS launch文件加载:更推荐的方式

先看第一种,直接命令行操作:

# 启动Gazebo空世界
gazebo worlds/empty.world &

# 加载机器人模型
rosrun gazebo_ros spawn_model -file my_robot.urdf -urdf -model my_robot

第二种,用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_description)/urdf/my_robot.urdf"/>

  <!-- 生成机器人 -->
  <node name="spawn_urdf" 
        pkg="gazebo_ros" 
        type="spawn_model" 
        args="-param robot_description -urdf -model my_robot" 
        output="screen"/>
</launch>

避坑指南:我曾经在加载模型时遇到Gazebo直接闪退,查了半天发现是URDF里忘了加<gazebo>插件标签。如果你想让机器人在Gazebo里动起来,必须添加对应的控制器插件,比如libgazebo_ros_diff_drive.so(差速驱动)或者libgazebo_ros_joint_position_reposition.so(关节位置控制)。

4.5 知识体系总览

下面这张图是我自己整理的URDF知识结构,你可以对照着看:

URDF机器人模型知识体系 URDF机器人描述 link(连杆) visual(视觉) collision(碰撞) inertial(惯性) joint(关节) parent/child origin(位姿) axis(轴) Gazebo加载与仿真 spawn_model命令 launch文件加载

这张图把URDF的核心脉络理清楚了。你写模型的时候,就照着这个结构来:先定义link,再定义joint,最后用Gazebo加载。每一步都有对应的标签和属性,缺一不可。

4.6 一个完整的简单例子

咱们来写一个两轮差速机器人的URDF。这个模型很简单:一个底盘,两个轮子,一个万向轮。

<?xml version="1.0"?>
<robot name="simple_robot">
  <!-- 底盘 -->
  <link name="base_link">
    <visual>
      <geometry><box size="0.4 0.3 0.1"/></geometry>
      <material name="blue"/>
    </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 name="left_wheel">
    <visual>
      <geometry><cylinder radius="0.05" length="0.04"/></geometry>
      <material name="gray"/>
    </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>

  <!-- 左轮关节 -->
  <joint name="left_wheel_joint" type="continuous">
    <parent link="base_link"/>
    <child link="left_wheel"/>
    <origin xyz="-0.15 0.15 0" rpy="0 0 0"/>
    <axis xyz="0 1 0"/>
  </joint>

  <!-- 右轮 -->
  <link name="right_wheel">
    <visual>
      <geometry><cylinder radius="0.05" length="0.04"/></geometry>
      <material name="gray"/>
    </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>

  <!-- 右轮关节 -->
  <joint name="right_wheel_joint" type="continuous">
    <parent link="base_link"/>
    <child link="right_wheel"/>
    <origin xyz="-0.15 -0.15 0" rpy="0 0 0"/>
    <axis xyz="0 1 0"/>
  </joint>
</robot>

把这个文件保存为simple_robot.urdf,然后用Gazebo加载。如果一切顺利,你会看到一个蓝色的底盘,两边各有一个灰色的轮子。嗯,虽然它现在还不能动,但至少模型已经站起来了。

调试小技巧:用check_urdf命令可以检查URDF语法是否正确。运行check_urdf simple_robot.urdf,它会告诉你有没有错误。另外,urdf_to_graphiz可以生成模型的结构图,方便你检查父子关系。

好了,URDF的基础就这些。说白了,它就是一套标签语言,把机器人的物理结构描述清楚。你只要记住:link是骨头,joint是关节,两者组合起来就是完整的机器人。下一节咱们会在这个基础上,给机器人加上传感器,让它真正开始感知世界。


可可精品资料库