飞道的博客

ROS2机器人编程简述humble-第四章-The TF Subsystem

464人阅读  评论(0)

这个是ROS2机器人的核心工具之一。

概念:

TF(坐标系转换)子系统是ROS2机器人框架中的一个重要组件,它的功能是提供坐标系转换服务,使得不同坐标系之间的数据可以转换。比如,机器人的传感器可以产生的数据是基于机器人本体坐标系的,而机器人的末端机械臂可以根据这些数据来控制机械臂的运动,但是机械臂需要的数据是基于机械臂坐标系的,这时候TF子系统就可以把数据从机器人本体坐标系转换成机械臂坐标系,从而实现机械臂的控制。

应用:

ROS2机器人TF子系统是一种基于消息传递的机器人框架,它可以帮助机器人开发者和研究人员更轻松、更快速地开发机器人系统。TF子系统可以帮助机器人开发者实现机器人定位、导航和路径规划等功能。比如,一个机器人可以使用TF子系统来实现室内定位,可以使用TF子系统来实现自主导航,也可以使用TF子系统来实现复杂的路径规划。

坐标变换消息:

ros2 interface show tf2_msgs/msg/TFMessage

geometry_msgs/TransformStamped[] transforms

例如机器人turtlebot、tiago等。

使用:

ros2 run rqt_tf_tree rqt_tf_tree

如果是类人机器人:

注意,如果没有指令,需要安装rqt-tf-tree功能包。

husky

为何选择这个案例呢?

机器人编程基础-ETH ROS 2022-Programming for Robotics_zhangrelay的博客-CSDN博客

2021(noetic):

机器人编程基础-ETH ROS

2020及之前(indigo/kinetic/melodic):

ROS编程基础课程2020更新资料和习题解答说明


书中所有案例均可以用husky和turtlebot3等复现。

典型案例:

odom →base footprint→ detected obstacle

其中base footprint→ detected obstacle:


   
  1. geometry_msgs::msg::TransformStamped detection_tf;
  2. detection_tf.header.frame_id = "base_footprint";
  3. detection_tf.header.stamp = now();
  4. detection_tf.child_frame_id = "detected_obstacle";
  5. detection_tf.transform.translation.x = 1.0;
  6. tf_broadcaster_-> sendTransform(detection_tf);

odom →base footprint→ detected obstacle:


   
  1. tf2_ros::Buffer tfBuffer;
  2. tf2_ros::TransformListener tfListener(tfBuffer);
  3. ...
  4. geometry_msgs::msg::TransformStamped odom2obstacle;
  5. odom2obstacle = tfBuffer_. lookupTransform( "odom", "detected_obstacle", tf2::TimePointZero);

ROS2机器人的tf2::TimePointZero是一个用来表示时间的类,它使用“秒”和“纳秒”来表示时间,可以用来跟踪和比较时间点。它可以用来构建时间戳,并且可以与其他时间类型(例如ros::Time)进行转换。

C++案例:


   
  1. #include <chrono>
  2. #include <functional>
  3. #include <memory>
  4. #include "geometry_msgs/msg/transform_stamped.hpp"
  5. #include "rclcpp/rclcpp.hpp"
  6. #include "tf2_ros/transform_broadcaster.h"
  7. using namespace std::chrono_literals;
  8. class FixedFrameBroadcaster : public rclcpp::Node
  9. {
  10. public:
  11. FixedFrameBroadcaster()
  12. : Node( "fixed_frame_tf2_broadcaster")
  13. {
  14. tf_broadcaster_ = std:: make_shared<tf2_ros::TransformBroadcaster>( this);
  15. timer_ = this-> create_wall_timer(
  16. 100ms, std:: bind(&FixedFrameBroadcaster::broadcast_timer_callback, this));
  17. }
  18. private:
  19. void broadcast_timer_callback()
  20. {
  21. geometry_msgs::msg::TransformStamped t;
  22. t.header.stamp = this-> get_clock()-> now();
  23. t.header.frame_id = "turtle1";
  24. t.child_frame_id = "carrot1";
  25. t.transform.translation.x = 0.0;
  26. t.transform.translation.y = 2.0;
  27. t.transform.translation.z = 0.0;
  28. t.transform.rotation.x = 0.0;
  29. t.transform.rotation.y = 0.0;
  30. t.transform.rotation.z = 0.0;
  31. t.transform.rotation.w = 1.0;
  32. tf_broadcaster_-> sendTransform(t);
  33. }
  34. rclcpp::TimerBase::SharedPtr timer_;
  35. std::shared_ptr<tf2_ros::TransformBroadcaster> tf_broadcaster_;
  36. };
  37. int main(int argc, char * argv[])
  38. {
  39. rclcpp:: init(argc, argv);
  40. rclcpp:: spin(std:: make_shared<FixedFrameBroadcaster>());
  41. rclcpp:: shutdown();
  42. return 0;
  43. }

发布:


   
  1. #include <functional>
  2. #include <memory>
  3. #include <sstream>
  4. #include <string>
  5. #include "geometry_msgs/msg/transform_stamped.hpp"
  6. #include "rclcpp/rclcpp.hpp"
  7. #include "tf2/LinearMath/Quaternion.h"
  8. #include "tf2_ros/transform_broadcaster.h"
  9. #include "turtlesim/msg/pose.hpp"
  10. class FramePublisher : public rclcpp::Node
  11. {
  12. public:
  13. FramePublisher()
  14. : Node( "turtle_tf2_frame_publisher")
  15. {
  16. // Declare and acquire `turtlename` parameter
  17. turtlename_ = this-> declare_parameter<std::string>( "turtlename", "turtle");
  18. // Initialize the transform broadcaster
  19. tf_broadcaster_ =
  20. std:: make_unique<tf2_ros::TransformBroadcaster>(* this);
  21. // Subscribe to a turtle{1}{2}/pose topic and call handle_turtle_pose
  22. // callback function on each message
  23. std::ostringstream stream;
  24. stream << "/" << turtlename_. c_str() << "/pose";
  25. std::string topic_name = stream. str();
  26. subscription_ = this-> create_subscription<turtlesim::msg::Pose>(
  27. topic_name, 10,
  28. std:: bind(&FramePublisher::handle_turtle_pose, this, std::placeholders::_1));
  29. }
  30. private:
  31. void handle_turtle_pose(const std::shared_ptr<turtlesim::msg::Pose> msg)
  32. {
  33. geometry_msgs::msg::TransformStamped t;
  34. // Read message content and assign it to
  35. // corresponding tf variables
  36. t.header.stamp = this-> get_clock()-> now();
  37. t.header.frame_id = "world";
  38. t.child_frame_id = turtlename_. c_str();
  39. // Turtle only exists in 2D, thus we get x and y translation
  40. // coordinates from the message and set the z coordinate to 0
  41. t.transform.translation.x = msg->x;
  42. t.transform.translation.y = msg->y;
  43. t.transform.translation.z = 0.0;
  44. // For the same reason, turtle can only rotate around one axis
  45. // and this why we set rotation in x and y to 0 and obtain
  46. // rotation in z axis from the message
  47. tf2::Quaternion q;
  48. q. setRPY( 0, 0, msg->theta);
  49. t.transform.rotation.x = q. x();
  50. t.transform.rotation.y = q. y();
  51. t.transform.rotation.z = q. z();
  52. t.transform.rotation.w = q. w();
  53. // Send the transformation
  54. tf_broadcaster_-> sendTransform(t);
  55. }
  56. rclcpp::Subscription<turtlesim::msg::Pose>::SharedPtr subscription_;
  57. std::unique_ptr<tf2_ros::TransformBroadcaster> tf_broadcaster_;
  58. std::string turtlename_;
  59. };
  60. int main(int argc, char * argv[])
  61. {
  62. rclcpp:: init(argc, argv);
  63. rclcpp:: spin(std:: make_shared<FramePublisher>());
  64. rclcpp:: shutdown();
  65. return 0;
  66. }

订阅:


   
  1. #include <chrono>
  2. #include <functional>
  3. #include <memory>
  4. #include <string>
  5. #include "geometry_msgs/msg/transform_stamped.hpp"
  6. #include "geometry_msgs/msg/twist.hpp"
  7. #include "rclcpp/rclcpp.hpp"
  8. #include "tf2/exceptions.h"
  9. #include "tf2_ros/transform_listener.h"
  10. #include "tf2_ros/buffer.h"
  11. #include "turtlesim/srv/spawn.hpp"
  12. using namespace std::chrono_literals;
  13. class FrameListener : public rclcpp::Node
  14. {
  15. public:
  16. FrameListener()
  17. : Node( "turtle_tf2_frame_listener"),
  18. turtle_spawning_service_ready_( false),
  19. turtle_spawned_( false)
  20. {
  21. // Declare and acquire `target_frame` parameter
  22. target_frame_ = this-> declare_parameter<std::string>( "target_frame", "turtle1");
  23. tf_buffer_ =
  24. std:: make_unique<tf2_ros::Buffer>( this-> get_clock());
  25. tf_listener_ =
  26. std:: make_shared<tf2_ros::TransformListener>(*tf_buffer_);
  27. // Create a client to spawn a turtle
  28. spawner_ =
  29. this-> create_client<turtlesim::srv::Spawn>( "spawn");
  30. // Create turtle2 velocity publisher
  31. publisher_ =
  32. this-> create_publisher<geometry_msgs::msg::Twist>( "turtle2/cmd_vel", 1);
  33. // Call on_timer function every second
  34. timer_ = this-> create_wall_timer(
  35. 1s, std:: bind(&FrameListener::on_timer, this));
  36. }
  37. private:
  38. void on_timer()
  39. {
  40. // Store frame names in variables that will be used to
  41. // compute transformations
  42. std::string fromFrameRel = target_frame_. c_str();
  43. std::string toFrameRel = "turtle2";
  44. if (turtle_spawning_service_ready_) {
  45. if (turtle_spawned_) {
  46. geometry_msgs::msg::TransformStamped t;
  47. // Look up for the transformation between target_frame and turtle2 frames
  48. // and send velocity commands for turtle2 to reach target_frame
  49. try {
  50. t = tf_buffer_-> lookupTransform(
  51. toFrameRel, fromFrameRel,
  52. tf2::TimePointZero);
  53. } catch ( const tf2::TransformException & ex) {
  54. RCLCPP_INFO(
  55. this-> get_logger(), "Could not transform %s to %s: %s",
  56. toFrameRel. c_str(), fromFrameRel. c_str(), ex. what());
  57. return;
  58. }
  59. geometry_msgs::msg::Twist msg;
  60. static const double scaleRotationRate = 1.0;
  61. msg.angular.z = scaleRotationRate * atan2(
  62. t.transform.translation.y,
  63. t.transform.translation.x);
  64. static const double scaleForwardSpeed = 0.5;
  65. msg.linear.x = scaleForwardSpeed * sqrt(
  66. pow(t.transform.translation.x, 2) +
  67. pow(t.transform.translation.y, 2));
  68. publisher_-> publish(msg);
  69. } else {
  70. RCLCPP_INFO( this-> get_logger(), "Successfully spawned");
  71. turtle_spawned_ = true;
  72. }
  73. } else {
  74. // Check if the service is ready
  75. if (spawner_-> service_is_ready()) {
  76. // Initialize request with turtle name and coordinates
  77. // Note that x, y and theta are defined as floats in turtlesim/srv/Spawn
  78. auto request = std:: make_shared<turtlesim::srv::Spawn::Request>();
  79. request->x = 4.0;
  80. request->y = 2.0;
  81. request->theta = 0.0;
  82. request->name = "turtle2";
  83. // Call request
  84. using ServiceResponseFuture =
  85. rclcpp::Client<turtlesim::srv::Spawn>::SharedFuture;
  86. auto response_received_callback = [ this](ServiceResponseFuture future) {
  87. auto result = future. get();
  88. if ( strcmp(result->name. c_str(), "turtle2") == 0) {
  89. turtle_spawning_service_ready_ = true;
  90. } else {
  91. RCLCPP_ERROR( this-> get_logger(), "Service callback result mismatch");
  92. }
  93. };
  94. auto result = spawner_-> async_send_request(request, response_received_callback);
  95. } else {
  96. RCLCPP_INFO( this-> get_logger(), "Service is not ready");
  97. }
  98. }
  99. }
  100. // Boolean values to store the information
  101. // if the service for spawning turtle is available
  102. bool turtle_spawning_service_ready_;
  103. // if the turtle was successfully spawned
  104. bool turtle_spawned_;
  105. rclcpp::Client<turtlesim::srv::Spawn>::SharedPtr spawner_{ nullptr};
  106. rclcpp::TimerBase::SharedPtr timer_{ nullptr};
  107. rclcpp::Publisher<geometry_msgs::msg::Twist>::SharedPtr publisher_{ nullptr};
  108. std::shared_ptr<tf2_ros::TransformListener> tf_listener_{ nullptr};
  109. std::unique_ptr<tf2_ros::Buffer> tf_buffer_;
  110. std::string target_frame_;
  111. };
  112. int main(int argc, char * argv[])
  113. {
  114. rclcpp:: init(argc, argv);
  115. rclcpp:: spin(std:: make_shared<FrameListener>());
  116. rclcpp:: shutdown();
  117. return 0;
  118. }

时间:


   
  1. transformStamped = tf_buffer_-> lookupTransform(
  2. toFrameRel,
  3. fromFrameRel,
  4. tf2::TimePointZero);

时间旅行:


   
  1. rclcpp::Time when = this-> get_clock()-> now() - rclcpp:: Duration( 5, 0);
  2. t = tf_buffer_-> lookupTransform(
  3. toFrameRel,
  4. fromFrameRel,
  5. when,
  6. 50ms);


转载:https://blog.csdn.net/ZhangRelay/article/details/128933531
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场