-The TF Subsystem
Posted zhangrelay
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了-The TF Subsystem相关的知识,希望对你有一定的参考价值。
这个是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):
2020及之前(indigo/kinetic/melodic):
书中所有案例均可以用husky和turtlebot3等复现。
典型案例:
odom →base footprint→ detected obstacle
其中base footprint→ detected obstacle:
geometry_msgs::msg::TransformStamped detection_tf;
detection_tf.header.frame_id = "base_footprint";
detection_tf.header.stamp = now();
detection_tf.child_frame_id = "detected_obstacle";
detection_tf.transform.translation.x = 1.0;
tf_broadcaster_->sendTransform(detection_tf);
odom →base footprint→ detected obstacle:
tf2_ros::Buffer tfBuffer;
tf2_ros::TransformListener tfListener(tfBuffer);
...
geometry_msgs::msg::TransformStamped odom2obstacle;
odom2obstacle = tfBuffer_.lookupTransform("odom", "detected_obstacle", tf2::TimePointZero);
ROS2机器人的tf2::TimePointZero是一个用来表示时间的类,它使用“秒”和“纳秒”来表示时间,可以用来跟踪和比较时间点。它可以用来构建时间戳,并且可以与其他时间类型(例如ros::Time)进行转换。
C++案例:
#include <chrono>
#include <functional>
#include <memory>
#include "geometry_msgs/msg/transform_stamped.hpp"
#include "rclcpp/rclcpp.hpp"
#include "tf2_ros/transform_broadcaster.h"
using namespace std::chrono_literals;
class FixedFrameBroadcaster : public rclcpp::Node
public:
FixedFrameBroadcaster()
: Node("fixed_frame_tf2_broadcaster")
tf_broadcaster_ = std::make_shared<tf2_ros::TransformBroadcaster>(this);
timer_ = this->create_wall_timer(
100ms, std::bind(&FixedFrameBroadcaster::broadcast_timer_callback, this));
private:
void broadcast_timer_callback()
geometry_msgs::msg::TransformStamped t;
t.header.stamp = this->get_clock()->now();
t.header.frame_id = "turtle1";
t.child_frame_id = "carrot1";
t.transform.translation.x = 0.0;
t.transform.translation.y = 2.0;
t.transform.translation.z = 0.0;
t.transform.rotation.x = 0.0;
t.transform.rotation.y = 0.0;
t.transform.rotation.z = 0.0;
t.transform.rotation.w = 1.0;
tf_broadcaster_->sendTransform(t);
rclcpp::TimerBase::SharedPtr timer_;
std::shared_ptr<tf2_ros::TransformBroadcaster> tf_broadcaster_;
;
int main(int argc, char * argv[])
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<FixedFrameBroadcaster>());
rclcpp::shutdown();
return 0;
发布:
#include <functional>
#include <memory>
#include <sstream>
#include <string>
#include "geometry_msgs/msg/transform_stamped.hpp"
#include "rclcpp/rclcpp.hpp"
#include "tf2/LinearMath/Quaternion.h"
#include "tf2_ros/transform_broadcaster.h"
#include "turtlesim/msg/pose.hpp"
class FramePublisher : public rclcpp::Node
public:
FramePublisher()
: Node("turtle_tf2_frame_publisher")
// Declare and acquire `turtlename` parameter
turtlename_ = this->declare_parameter<std::string>("turtlename", "turtle");
// Initialize the transform broadcaster
tf_broadcaster_ =
std::make_unique<tf2_ros::TransformBroadcaster>(*this);
// Subscribe to a turtle12/pose topic and call handle_turtle_pose
// callback function on each message
std::ostringstream stream;
stream << "/" << turtlename_.c_str() << "/pose";
std::string topic_name = stream.str();
subscription_ = this->create_subscription<turtlesim::msg::Pose>(
topic_name, 10,
std::bind(&FramePublisher::handle_turtle_pose, this, std::placeholders::_1));
private:
void handle_turtle_pose(const std::shared_ptr<turtlesim::msg::Pose> msg)
geometry_msgs::msg::TransformStamped t;
// Read message content and assign it to
// corresponding tf variables
t.header.stamp = this->get_clock()->now();
t.header.frame_id = "world";
t.child_frame_id = turtlename_.c_str();
// Turtle only exists in 2D, thus we get x and y translation
// coordinates from the message and set the z coordinate to 0
t.transform.translation.x = msg->x;
t.transform.translation.y = msg->y;
t.transform.translation.z = 0.0;
// For the same reason, turtle can only rotate around one axis
// and this why we set rotation in x and y to 0 and obtain
// rotation in z axis from the message
tf2::Quaternion q;
q.setRPY(0, 0, msg->theta);
t.transform.rotation.x = q.x();
t.transform.rotation.y = q.y();
t.transform.rotation.z = q.z();
t.transform.rotation.w = q.w();
// Send the transformation
tf_broadcaster_->sendTransform(t);
rclcpp::Subscription<turtlesim::msg::Pose>::SharedPtr subscription_;
std::unique_ptr<tf2_ros::TransformBroadcaster> tf_broadcaster_;
std::string turtlename_;
;
int main(int argc, char * argv[])
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<FramePublisher>());
rclcpp::shutdown();
return 0;
订阅:
#include <chrono>
#include <functional>
#include <memory>
#include <string>
#include "geometry_msgs/msg/transform_stamped.hpp"
#include "geometry_msgs/msg/twist.hpp"
#include "rclcpp/rclcpp.hpp"
#include "tf2/exceptions.h"
#include "tf2_ros/transform_listener.h"
#include "tf2_ros/buffer.h"
#include "turtlesim/srv/spawn.hpp"
using namespace std::chrono_literals;
class FrameListener : public rclcpp::Node
public:
FrameListener()
: Node("turtle_tf2_frame_listener"),
turtle_spawning_service_ready_(false),
turtle_spawned_(false)
// Declare and acquire `target_frame` parameter
target_frame_ = this->declare_parameter<std::string>("target_frame", "turtle1");
tf_buffer_ =
std::make_unique<tf2_ros::Buffer>(this->get_clock());
tf_listener_ =
std::make_shared<tf2_ros::TransformListener>(*tf_buffer_);
// Create a client to spawn a turtle
spawner_ =
this->create_client<turtlesim::srv::Spawn>("spawn");
// Create turtle2 velocity publisher
publisher_ =
this->create_publisher<geometry_msgs::msg::Twist>("turtle2/cmd_vel", 1);
// Call on_timer function every second
timer_ = this->create_wall_timer(
1s, std::bind(&FrameListener::on_timer, this));
private:
void on_timer()
// Store frame names in variables that will be used to
// compute transformations
std::string fromFrameRel = target_frame_.c_str();
std::string toFrameRel = "turtle2";
if (turtle_spawning_service_ready_)
if (turtle_spawned_)
geometry_msgs::msg::TransformStamped t;
// Look up for the transformation between target_frame and turtle2 frames
// and send velocity commands for turtle2 to reach target_frame
try
t = tf_buffer_->lookupTransform(
toFrameRel, fromFrameRel,
tf2::TimePointZero);
catch (const tf2::TransformException & ex)
RCLCPP_INFO(
this->get_logger(), "Could not transform %s to %s: %s",
toFrameRel.c_str(), fromFrameRel.c_str(), ex.what());
return;
geometry_msgs::msg::Twist msg;
static const double scaleRotationRate = 1.0;
msg.angular.z = scaleRotationRate * atan2(
t.transform.translation.y,
t.transform.translation.x);
static const double scaleForwardSpeed = 0.5;
msg.linear.x = scaleForwardSpeed * sqrt(
pow(t.transform.translation.x, 2) +
pow(t.transform.translation.y, 2));
publisher_->publish(msg);
else
RCLCPP_INFO(this->get_logger(), "Successfully spawned");
turtle_spawned_ = true;
else
// Check if the service is ready
if (spawner_->service_is_ready())
// Initialize request with turtle name and coordinates
// Note that x, y and theta are defined as floats in turtlesim/srv/Spawn
auto request = std::make_shared<turtlesim::srv::Spawn::Request>();
request->x = 4.0;
request->y = 2.0;
request->theta = 0.0;
request->name = "turtle2";
// Call request
using ServiceResponseFuture =
rclcpp::Client<turtlesim::srv::Spawn>::SharedFuture;
auto response_received_callback = [this](ServiceResponseFuture future)
auto result = future.get();
if (strcmp(result->name.c_str(), "turtle2") == 0)
turtle_spawning_service_ready_ = true;
else
RCLCPP_ERROR(this->get_logger(), "Service callback result mismatch");
;
auto result = spawner_->async_send_request(request, response_received_callback);
else
RCLCPP_INFO(this->get_logger(), "Service is not ready");
// Boolean values to store the information
// if the service for spawning turtle is available
bool turtle_spawning_service_ready_;
// if the turtle was successfully spawned
bool turtle_spawned_;
rclcpp::Client<turtlesim::srv::Spawn>::SharedPtr spawner_nullptr;
rclcpp::TimerBase::SharedPtr timer_nullptr;
rclcpp::Publisher<geometry_msgs::msg::Twist>::SharedPtr publisher_nullptr;
std::shared_ptr<tf2_ros::TransformListener> tf_listener_nullptr;
std::unique_ptr<tf2_ros::Buffer> tf_buffer_;
std::string target_frame_;
;
int main(int argc, char * argv[])
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<FrameListener>());
rclcpp::shutdown();
return 0;
时间:
transformStamped = tf_buffer_->lookupTransform(
toFrameRel,
fromFrameRel,
tf2::TimePointZero);
时间旅行:
rclcpp::Time when = this->get_clock()->now() - rclcpp::Duration(5, 0);
t = tf_buffer_->lookupTransform(
toFrameRel,
fromFrameRel,
when,
50ms);
以上是关于-The TF Subsystem的主要内容,如果未能解决你的问题,请参考以下文章