当前位置:   article > 正文

【ROS2】中级:tf2- 使用时间 (C++)

rclcpp::time(0)

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

教程级别:中级

 时间:10 分钟

 目录

  •  背景

  •  任务

    • 1 更新侦听器节点

    • 2. 修复监听器节点

    • 3 检查结果

  •  摘要

 背景

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

 任务

1 更新监听器节点

让我们回到添加框架教程结束的地方。转到 learning_tf2_cpp 包。打开 turtle_tf2_listener.cpp 并查看 lookupTransform() 调用:

  1. try {
  2. t = tf_buffer_->lookupTransform(
  3. toFrameRel,
  4. fromFrameRel,
  5. tf2::TimePointZero);
  6. } catch (const tf2::TransformException & ex) {

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

 便条

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

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

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

  1. rclcpp::Time now = this->get_clock()->now();
  2. try {
  3. t = tf_buffer_->lookupTransform(
  4. toFrameRel, fromFrameRel,
  5. now);
  6. } catch (const tf2::TransformException & ex) {

现在构建包并尝试运行启动文件。

ros2 launch learning_tf2_cpp turtle_tf2_demo_launch.py

您会注意到它失败并输出类似于以下内容:

  1. [INFO] [1629873136.345688064] [listener]: Could not transform turtle2 to turtle1: Lookup would
  2. require extrapolation into the future. Requested time 1629873136.345539 but the latest data
  3. is at time 1629873136.338804, when looking up transform from frame [turtle1] to frame [turtle2]

它告诉你该帧不存在或数据在未来

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

2 修复监听器节点

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

  1. rclcpp::Time now = this->get_clock()->now();
  2. try {
  3. t = tf_buffer_->lookupTransform(
  4. toFrameRel,
  5. fromFrameRel,
  6. now,
  7. 50ms);
  8. } catch (const tf2::TransformException & ex) {

lookupTransform() 可以接受四个参数,其中最后一个是可选的超时。它将阻塞到该持续时间等待超时。

3 检查结果

您现在可以构建包并运行启动文件。

  1. cxy@ubuntu2404-cxy:~$ cd ~/ros2_ws
  2. cxy@ubuntu2404-cxy:~/ros2_ws$ colcon build --packages-select learning_tf2_cpp
  3. Starting >>> learning_tf2_cpp
  4. Finished <<< learning_tf2_cpp [19.0s]
  5. Summary: 1 package finished [29.5s]
  6. cxy@ubuntu2404-cxy:~/ros2_ws$ . install/setup.bash
  7. cxy@ubuntu2404-cxy:~/ros2_ws$ ros2 launch learning_tf2_cpp turtle_tf2_demo_launch.py
  8. [INFO] [launch]: All log files can be found below /home/cxy/.ros/log/2024-07-12-20-26-45-751031-ubuntu2404-cxy-11892
  9. [INFO] [launch]: Default logging verbosity is set to INFO
  10. [INFO] [turtlesim_node-1]: process started with pid [11895]
  11. [INFO] [turtle_tf2_broadcaster-2]: process started with pid [11896]
  12. [INFO] [turtle_tf2_broadcaster-3]: process started with pid [11897]
  13. [INFO] [turtle_tf2_listener-4]: process started with pid [11898]
  14. [turtlesim_node-1] [INFO] [1720787207.560817235] [sim]: Starting turtlesim with node name /sim
  15. [turtlesim_node-1] [INFO] [1720787207.570010819] [sim]: Spawning turtle [turtle1] at x=[5.544445], y=[5.544445], theta=[0.000000]
  16. [turtlesim_node-1] [INFO] [1720787208.449319544] [sim]: Spawning turtle [turtle2] at x=[4.000000], y=[2.000000], theta=[0.000000]
  17. [turtle_tf2_listener-4] [INFO] [1720787209.441088294] [listener]: Successfully spawned
cxy@ubuntu2404-cxy:~ros2 run turtlesim turtle_teleop_key

7e170eaaf4bdf6e9c4e3aaffa4dd3425.png

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

 摘要

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

48c9d69f3da47f076e6e48a3fc6c7b3e.png

示例代码

以下是一个使用 lookupTransform 函数的示例代码:

 
 
  1. #include <rclcpp/rclcpp.hpp>
  2. #include <tf2_ros/buffer.h>
  3. #include <tf2_ros/transform_listener.h>
  4. #include <geometry_msgs/msg/transform_stamped.hpp>
  5. class TransformListenerNode : public rclcpp::Node
  6. {
  7. public:
  8. TransformListenerNode()
  9. : Node("transform_listener_node")
  10. {
  11. tf_buffer_ = std::make_shared<tf2_ros::Buffer>(this->get_clock());
  12. tf_listener_ = std::make_shared<tf2_ros::TransformListener>(*tf_buffer_);
  13. rclcpp::Time now = this->get_clock()->now();
  14. try {
  15. geometry_msgs::msg::TransformStamped transform_stamped = tf_buffer_->lookupTransform(
  16. "target_frame", "source_frame", now, rclcpp::Duration::from_seconds(1.0));
  17. RCLCPP_INFO(this->get_logger(), "Transform: %f, %f, %f",
  18. transform_stamped.transform.translation.x,
  19. transform_stamped.transform.translation.y,
  20. transform_stamped.transform.translation.z);
  21. } catch (tf2::TransformException & ex) {
  22. RCLCPP_ERROR(this->get_logger(), "Could not transform: %s", ex.what());
  23. }
  24. }
  25. private:
  26. std::shared_ptr<tf2_ros::Buffer> tf_buffer_;
  27. std::shared_ptr<tf2_ros::TransformListener> tf_listener_;
  28. };
  29. int main(int argc, char ** argv)
  30. {
  31. rclcpp::init(argc, argv);
  32. auto node = std::make_shared<TransformListenerNode>();
  33. rclcpp::spin(node);
  34. rclcpp::shutdown();
  35. return 0;
  36. }

这个示例代码创建了一个 ROS2 节点,并在节点中使用 lookupTransform 函数查找从 source_frame 到 target_frame 的变换关系。如果查找成功,会输出变换的平移信息;如果失败,会输出错误信息。

39d148d4c000b59aaa1f67bcefd997d4.png

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/885284
推荐阅读
相关标签
  

闽ICP备14008679号