使用时间 (C++)

目标: 了解如何在特定时间获取转换,并使用 lookupTransform() 函数等待 tf2 树上可用的转换。

教程级别: 中级

时间: 10 分钟

背景

在之前的教程中,我们通过编写 tf2 广播器tf2 监听器 重新创建了 turtle 演示。 我们还学习了如何 向转换树添加新框架,并学习了 tf2 如何跟踪坐标系树。 这棵树会随着时间而变化,并且 tf2 会为每个转换存储一个时间快照(默认情况下最多 10 秒)。 到目前为止,我们使用 lookupTransform() 函数来访问该 tf2 树中最新的可用转换,而不知道该转换是在什么时候记录的。 本教程将教您如何在特定时间获取转换。

任务

1 更新侦听器节点

让我们回到 添加框架教程 中结束的地方。

转到 learning_tf2_cpp 包。

打开 turtle_tf2_listener.cpp 并查看 lookupTransform() 调用:

transformStamped = tf_buffer_->lookupTransform(
   toFrameRel,
   fromFrameRel,
   tf2::TimePointZero);

您可以看到,我们通过调用“tf2::TimePointZero”指定了等于 0 的时间。

Note

tf2 包有自己的时间类型 tf2::TimePoint,它与 rclcpp::Time 不同。 包 tf2_ros 中的许多 API 会自动在 rclcpp::Timetf2::TimePoint 之间进行转换。

这里可以使用 rclcpp::Time(0, 0, this->get_clock()->get_clock_type()),但无论如何它都会被转换为 tf2::TimePointZero

对于 tf2,时间 0 表示缓冲区中的“最新可用”变换。 现在,更改此行以获取当前时间的变换,“this->get_clock()->now()”:

rclcpp::Time now = this->get_clock()->now();
transformStamped = tf_buffer_->lookupTransform(
   toFrameRel,
   fromFrameRel,
   now);

现在尝试运行启动文件。

ros2 launch learning_tf2_cpp turtle_tf2_demo.launch.py

您会注意到它失败了并输出类似这样的内容:

[INFO] [1629873136.345688064] [listener]: 无法将 turtle2 转换为 turtle1:查找需要推断未来。请求时间为 1629873136.345539,但最新数据为时间 1629873136.338804,当查找从帧 [turtle1] 到帧 [turtle2] 的变换时

它告诉您该帧不存在或数据在将来。

要了解为什么会发生这种情况,我们需要了解缓冲区的工作原理。 首先,每个侦听器都有一个缓冲区,用于存储来自不同 tf2 广播器的所有坐标变换。 其次,当广播器发出变换时,该变换需要一些时间才能进入缓冲区(通常需要几毫秒)。 因此,当您在“现在”请求帧变换时,您应该等待几毫秒才能获得该信息。

2 修复侦听器节点

tf2 提供了一个很好的工具,它会等到变换可用。 您可以通过向“lookupTransform()”添加超时参数来使用它。 要解决此问题,请按如下所示编辑代码(添加最后一个超时参数):

rclcpp::Time now = this->get_clock()->now();
transformStamped = tf_buffer_->lookupTransform(
   toFrameRel,
   fromFrameRel,
   now,
   50ms);

lookupTransform() 可以接受四个参数,其中最后一个是可选的超时。

它将阻塞最多该时间,直到超时。

3 检查结果

您现在可以运行启动文件。

ros2 launch learning_tf2_cpp turtle_tf2_demo.launch.py

您应该注意到 lookupTransform() 实际上会阻塞,直到两个海龟之间的变换可用(这通常需要几毫秒)。 一旦达到超时(在这种情况下为 50 毫秒),只有当变换仍然不可用时才会引发异常。

摘要

在本教程中,您学习了如何在特定时间戳获取变换以及如何在使用 lookupTransform() 函数时等待 tf2 树上的变换可用。