Skip to content

设置变换(Transforms)

在本指南中,我们将研究 Nav2 所需的必要变换。这些变换允许 Nav2 通过将来自各种来源(如传感器和里程计)的信息变换到相应坐标系中加以使用。下图是机器人完整变换树的样子,但我们将从更简单的内容开始。

tf_full_tree

在本教程中,我们首先简要介绍 ROS 中的变换。其次,我们将进行一个简单的 TF2 静态发布器命令行演示,看看它是如何工作的。最后,我们将概述 Nav2 正常运行所需发布的必要变换。

许多 ROS 包都要求使用 TF2 ROS 包发布机器人的变换树。变换树以平移、旋转和相对运动的方式定义不同坐标系之间的关系。为了使这一点更具体,让我们举一个简单机器人的例子:一个移动底座,顶部装有一个激光传感器。

这个机器人有两个已定义的坐标系:一个对应机器人移动底座的中心点,另一个对应安装在底座顶部的激光的中心点。我们将附着在移动底座上的坐标系称为 base_link,将附着在激光上的坐标系称为 base_laser。请注意,我们将在下一节中更多地讨论这些坐标系的命名和约定。

此时,假设我们有一些来自激光的数据,这些数据是从激光中心点出发的距离测量值。换句话说,我们在 base_laser 坐标系中有一些数据。

现在,假设我们想要获取这些数据,并用它来帮助移动底座避开世界中的障碍物。为了成功做到这一点,我们需要一种方法将收到的来自 base_laser 坐标系的激光扫描变换到 base_link 坐标系。本质上,我们需要定义 base_laser 和 base_link 坐标系之间的关系。

simple_robot

在定义这种关系时,假设我们拥有的唯一数据是:激光安装在移动底座中心点前方 10 cm、上方 20 cm 处。这给了我们一个平移偏移量,将 base_link 坐标系与 base_laser 坐标系联系起来。具体来说,我们知道要把数据从 base_link 坐标系变换到 base_laser 坐标系,必须应用平移(x: 0.1m, y: 0.0m, z: 0.2m);反过来,要把数据从 base_laser 坐标系变换到 base_link 坐标系,必须应用相反的平移(x: -0.1m, y: 0.0m, z: -0.20m)。

我们可以选择自己管理这种关系,即在必要时存储和应用坐标系之间的适当平移,但随着坐标系数量的增加,这会变得非常麻烦。幸运的是,我们不必自己做这项工作。相反,我们将使用 TF2 一次性定义 base_link 和 base_laser 之间的关系,让它为我们管理两个坐标系之间的变换。这在处理非静态变换时尤其有用,例如一组相对运动的坐标系,比如地图坐标系中的机器人底座坐标系。

要使用 TF2 定义并存储 base_link 和 base_laser 坐标系之间的关系,我们需要将它们添加到变换树中。从概念上讲,变换树中的每个节点对应一个坐标系,每条边对应从当前节点移动到其子节点所需应用的变换。TF2 使用树结构来保证任意两个坐标系之间只有一条唯一的遍历路径,并假设树中的所有边都从父节点指向子节点。

tf_robot

要为我们的简单示例创建变换树,我们将创建两个节点:一个用于 base_link 坐标系,一个用于 base_laser 坐标系。要创建它们之间的边,我们首先需要决定哪个节点是父节点,哪个是子节点。请记住——这种区分很重要,因为 TF2 假设所有变换都从父节点移动到子节点。

我们选择 base_link 坐标系作为父节点,因为当其他部件/传感器添加到机器人上时,它们通过 base_link 坐标系与 base_laser 坐标系建立联系是最合理的。这意味着连接 base_link 和 base_laser 的边所关联的变换应为(x: 0.1m, y: 0.0m, z: 0.2m)。

有了这个变换树,将 base_laser 坐标系中收到的激光扫描转换到 base_link 坐标系,就只需调用 TF2 库即可。我们的机器人现在可以使用这些信息在 base_link 坐标系中理解激光扫描,并在其环境中安全地规划绕行障碍物。

现在让我们使用 TF2 提供的 static_transform_publisher 工具尝试发布一个非常简单的变换。我们将发布从连杆 base_link 到连杆 base_laser 的变换,平移为(x: 0.1m, y: 0.0m, z: 0.2m)。请注意,我们将根据本教程前面的图表构建这个变换。

打开你的命令行并执行以下命令:

Terminal window
ros2 run tf2_ros static_transform_publisher --x 0.1 --y 0 --z 0.2 --roll 0 --pitch 0 --yaw 0 --frame-id base_link --child-frame-id base_laser

这样,我们现在就在 TF2 中成功发布了从 base_link 到 base_laser 的变换。现在让我们通过 tf2_echo 检查它是否正常工作。打开一个单独的命令行窗口并执行以下命令:

Terminal window
ros2 run tf2_ros tf2_echo base_link base_laser

你应该能够观察到类似下面的重复输出。

Terminal window
At time 0.0
- Translation: [0.100, 0.000, 0.200]
- Rotation: in Quaternion [0.000, 0.000, 0.000, 1.000]

这个简短演示就到此为止——我们成功使用 TF2 库发布了从 base_link 到 base_laser 的变换。请注意,我们不建议在实际机器人项目中使用上述演示来发布变换,它只是一个快速演示,让你看看 TF2 的实际工作方式。对于真实的机器人系统,我们会创建一个 URDF 文件,其中包含这些信息以及更多关于机器人的信息,供 robot_state_publisher 使用,而不是使用 static_transform_publisher。还有更合适、更实用的方法,这些将在 URDF 教程 中讨论。

有两个重要的 ROS REP,我们强烈建议你去查看。这些文档详细说明了 ROS 社区制定的一些标准,以确保不同包之间的正常运行。Nav2 也遵守这些标准和约定。

  1. REP 105 - 移动平台坐标系
  2. REP 103 - 标准度量单位与坐标约定

简要总结一下 REP 105,该文档规定了 ROS 中使用的不同坐标系的命名约定和语义含义。与本教程相关的是 base_link、odom 和 map 坐标系。base_link 是附着在机器人上固定位置的坐标系,通常在其主底盘和旋转中心处。odom 坐标系是相对于机器人起始位置的固定坐标系,主要用于局部一致的距离表示。最后,map 坐标系是用于全局一致距离表示的世界固定坐标系。

另一方面,REP 103 讨论了一些标准度量单位和其他相关约定,以最大限度地减少不同 ROS 包之间的集成问题。基本概述是:坐标系使用右手定则定义,Z 轴向上、X 轴向前,单位应为标准 SI 单位。

现在让我们进入一些使 Navigation2 包正常工作的具体细节。Nav2 要求在 ROS 中发布以下变换:

  1. map => odom
  2. odom => base_link
  3. base_link => base_laser(传感器基坐标系)

第一个变换 map => odom 通常由处理定位和建图的不同 ROS 包(如 AMCL)提供。这个变换在使用过程中会实时更新,所以我们不会在机器人的 TF 树中为它设置静态值。关于如何设置它的更多细节可能相当复杂,因此我们强烈建议你查看你平台上使用的建图或定位包的文档。所有符合 ROS 规范的 SLAM 和定位包都会在启动时自动为你提供这个变换。

odom => base_link 通常由我们的里程计系统使用车轮编码器等传感器发布。这通常通过 robot_localization 包对里程计传感器(IMU、车轮编码器、VIO 等)进行传感器融合来计算。

所有其他静态定义的变换(例如 base_link => base_laser、base_link => wheels、wheels => IMU 等)将是我们本指南其余部分要讨论的内容。Nav2 使用这个变换树来正确地将传感器或其他感兴趣坐标系的信息与机器人其余部分联系起来。这两个坐标系之间的变换通常通过机器人状态发布器(Robot State Publisher)和统一机器人描述文件(URDF)提供给 Nav2。如果你的平台上有更多传感器坐标系,那么需要发布从 base_link 到每个传感器坐标系的变换树。

在本教程中,我们讨论了变换的概念以及它们如何在 Nav2 中使用。

在最后一节中,我们还探索了使用 TF2 的 static_transform_publisher 发布我们的变换。你可以用它来为 Nav2 设置变换,但这通常不是最佳方式。在大多数机器人项目中,我们使用机器人状态发布器(Robot State Publisher),因为它更容易使用,并且随着机器人的复杂化扩展性也很好。我们将在下一个 URDF 教程 中讨论机器人状态发布器、URDF 以及如何设置它们。

最后,我们还讨论了 Nav2 的三个已发布变换要求和设置它们时需要记住的必要 REP。