第4章:机器人模型与TF树:URDF模型编写,机器人底盘与激光雷达的TF变换配置

各位同学,欢迎来到第四章。这一章我们聊聊机器人建模和TF树。说实话,很多新手一上来就被URDF和TF搞懵了。我当年第一次写URDF时,也是对着XML标签发了好几天呆。但别怕,这东西说白了就是描述机器人长什么样、各个部件怎么连接的。

4.1 URDF模型:机器人的“身份证”

URDF,全称Unified Robot Description Format。你可以把它理解成机器人的“身份证”。它用XML格式告诉系统:机器人有几个轮子、底盘多大、激光雷达装在哪。

一个完整的URDF模型由两部分组成:link(刚体)和joint(关节)。link描述的是每个部件的物理属性,比如质量、惯性、碰撞形状。joint描述的是部件之间的连接关系,比如轮子怎么装在底盘上。

我在项目中遇到过最坑的事:有次写URDF时忘了给激光雷达link加<origin>标签,结果雷达直接和底盘重合了。嗯,这个后面细说。

4.2 底盘与激光雷达的URDF编写

我们先写一个最简单的两轮差速底盘。底盘是一个长方体,激光雷达装在底盘正上方。

<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"/>
    </visual>
    <collision>
      <geometry>
        <box size="0.4 0.3 0.1"/>
      </geometry>
    </collision>
    <inertial>
      <mass value="10.0"/>
      <inertia ixx="0.1" ixy="0" ixz="0" iyy="0.1" iyz="0" izz="0.1"/>
    </inertial>
  </link>

  <!-- 激光雷达link -->
  <link name="laser_link">
    <visual>
      <geometry>
        <cylinder radius="0.05" length="0.03"/>
      </geometry>
      <origin xyz="0 0 0" rpy="0 0 0"/>
    </visual>
  </link>

  <!-- 连接底盘和激光雷达的joint -->
  <joint name="laser_joint" type="fixed">
    <parent link="base_link"/>
    <child link="laser_link"/>
    <origin xyz="0 0 0.15" rpy="0 0 0"/>
  </joint>
</robot>

注意看laser_joint里的<origin xyz="0 0 0.15">。这表示激光雷达装在底盘正上方15厘米处。你想想看,如果这里写错了,雷达扫描的数据和实际位置对不上,导航就会出大问题。

警告:joint的origin是相对于parent link的坐标系。千万别搞反了parent和child的顺序。我曾经见过有人把parent写成laser_link,child写成base_link,结果TF树直接乱套。

4.3 TF树:坐标变换的“交通图”

URDF写好了,系统会自动生成TF树。TF树说白了就是一张坐标变换的“交通图”。它告诉每个节点:我从哪来,我到哪去。

对于我们的机器人,TF树长这样:

核心TF变换:

  • base_link → laser_link:平移(0, 0, 0.15),无旋转
  • base_link → left_wheel_link:平移(0, 0.15, -0.05),旋转(0, 0, 0)
  • base_link → right_wheel_link:平移(0, -0.15, -0.05),旋转(0, 0, 0)

我建议你每次写完URDF后,用tf2_tools里的view_frames.py看一下TF树。这个工具会生成一张PDF图,一目了然。

# 查看TF树
ros2 run tf2_tools view_frames.py
# 会生成 frames.pdf 文件

4.4 静态TF发布:手动配置变换

有时候URDF不方便直接定义所有变换,比如传感器装在可调节支架上。这时候可以用static_transform_publisher手动发布静态TF。

ros2 run tf2_ros static_transform_publisher \
  --x 0 --y 0 --z 0.15 \
  --roll 0 --pitch 0 --yaw 0 \
  --frame-id base_link \
  --child-frame-id laser_link

这个命令和URDF里的joint效果一样。我个人习惯在launch文件里用这种方式,因为修改起来更方便,不用重新编译URDF。

小技巧:如果你用static_transform_publisher,记得在launch文件中加上--ros-args -r __node:=laser_tf_broadcaster,这样节点名不会冲突。

4.5 避坑指南:TF超时与丢失

导航中最常见的错误就是“TF timeout”或“Could not find transform”。我遇到过好几次,都是因为TF树没发布或者发布频率太低。

检查步骤:

  1. 运行ros2 run tf2_ros tf2_monitor,查看所有TF变换的状态
  2. 检查每个TF的发布频率,一般要求至少10Hz
  3. 确认所有frame_id拼写正确,大小写敏感

我曾经在调试时发现激光雷达数据一直报错,查了半天才发现是laser_link写成了laser。嗯,这种低级错误最让人抓狂。

4.6 本章知识体系

下面这张图展示了本章的核心逻辑:

第4章:机器人模型与TF树 URDF模型编写 TF树配置 静态TF发布 link + joint 定义 底盘与雷达模型 base_link → laser_link 坐标变换关系 static_transform_publisher launch文件配置 核心:URDF定义模型 → 自动生成TF树 → 导航使用TF进行坐标变换 ⚠ 常见坑:TF超时、frame_id拼写错误、joint parent/child顺序颠倒

这张图把本章内容串起来了。你从URDF开始,定义好机器人的物理模型和关节关系。系统自动生成TF树,告诉你每个部件在哪。如果有些变换不方便写在URDF里,就用静态TF发布手动补上。最后,导航系统通过TF树知道激光雷达的数据对应到世界坐标系的哪个位置。

核心要点:

  • URDF是机器人的物理描述,TF树是坐标变换描述
  • 每个link对应一个坐标系,joint定义坐标系之间的变换
  • 静态TF发布是URDF的补充,适合灵活配置
  • 导航要求所有传感器数据都有对应的TF变换

好了,这一章就到这里。URDF和TF树是导航的基础,你写好了模型,后面的事情才能顺利进行。记住,多检查TF树,多用tf2_monitor,别让低级错误浪费你的时间。

可可精品资料库