利用时间 (C++)
目标: 学习如何在特定时间获取变换,并使用 lookupTransform()
函数等待 tf2 树上可用的变换。
教程级别: 中级
时间: 10 分钟
背景
在之前的教程中,我们通过编写 tf2 广播器 和 tf2 监听器 重新创建了 turtle 演示。
我们还学习了如何 向转换树添加新框架,并学习了 tf2 如何跟踪坐标系树。
这棵树会随着时间而变化,并且 tf2 会为每个转换存储一个时间快照(默认情况下最多 10 秒)。
到目前为止,我们使用 lookupTransform()
函数来访问该 tf2 树中最新的可用转换,而不知道该转换是在什么时候记录的。
本教程将教您如何在特定时间获取转换。
任务
1 更新侦听器节点
让我们回到 添加框架教程 中结束的地方。
转到 learning_tf2_cpp
包。
打开 turtle_tf2_listener.cpp
并查看 lookupTransform()
调用:
try {
t = tf_buffer_->lookupTransform(
toFrameRel,
fromFrameRel,
tf2::TimePointZero);
} catch (const tf2::TransformException & ex) {
您可以看到,我们通过调用“tf2::TimePointZero”指定了等于 0 的时间。
Note
tf2
包有自己的时间类型 tf2::TimePoint
,它与 rclcpp::Time
不同。
包 tf2_ros
中的许多 API 会自动在 rclcpp::Time
和 tf2::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();
try {
t = tf_buffer_->lookupTransform(
toFrameRel, fromFrameRel,
now);
} catch (const tf2::TransformException & ex) {
现在构建包并尝试运行启动文件。
ros2 launch learning_tf2_cpp turtle_tf2_demo_launch.py
您会注意到它失败了并输出类似这样的内容:
[INFO] [1629873136.345688064] [listener]: Could not transform turtle2 to turtle1: Lookup would
require extrapolation into the future. Requested time 1629873136.345539 but the latest data
is at time 1629873136.338804, when looking up transform from frame [turtle1] to frame [turtle2]
它告诉您该帧不存在或数据在将来。
要了解为什么会发生这种情况,我们需要了解缓冲区的工作原理。 首先,每个侦听器都有一个缓冲区,用于存储来自不同 tf2 广播器的所有坐标变换。 其次,当广播器发出变换时,该变换需要一些时间才能进入缓冲区(通常需要几毫秒)。 因此,当您在“现在”请求帧变换时,您应该等待几毫秒才能获得该信息。
2 修复侦听器节点
tf2 提供了一个很好的工具,它会等到变换可用。 您可以通过向“lookupTransform()”添加超时参数来使用它。 要解决此问题,请按如下所示编辑代码(添加最后一个超时参数):
rclcpp::Time now = this->get_clock()->now();
try {
t = tf_buffer_->lookupTransform(
toFrameRel,
fromFrameRel,
now,
50ms);
} catch (const tf2::TransformException & ex) {
lookupTransform()
可以接受四个参数,其中最后一个是可选的超时。
它将阻塞最多该时间,直到超时。
3 检查结果
您现在可以构建包并运行启动文件。
ros2 launch learning_tf2_cpp turtle_tf2_demo_launch.py
您应该注意到 lookupTransform()
实际上会阻塞,直到两个海龟之间的变换可用(这通常需要几毫秒)。
一旦达到超时(在这种情况下为 50 毫秒),只有当变换仍然不可用时才会引发异常。
摘要
在本教程中,您学习了如何在特定时间戳获取变换以及如何在使用 lookupTransform()
函数时等待 tf2 树上的变换可用。