【TF】

【TF】

坐标关系

ROS2可视化
基于话题通信

【例】 对于不同坐标系 某物有多远
base_link机器人基坐标
base_laser雷达坐标系
image

命令行使用TF

发布静态坐标 static_transform_publisher
image
※rotation用四元数表示
发布坐标变换 tf2_echo

ros2 run tf2_ros tf2_echo base_link wall_point

image
利用工具查看所有坐标系连接关系

ros2 run tf2_tools view_frames

TF原理

发布动态TF数据被发布到/tf话题上
查询坐标关系变化:订阅/tf和/tf_static话题->查询数据
image
image
image
image

【例】地图坐标变换

发布静态TF

依赖

消息接口依赖 geometry_msgs
提供消息类型的转换函数 tf2_geometry_msgs

代码

#include<memory>
#include"geometry_msgs/msg/transform_stamped.hpp"
#include"rclcpp/rclcpp.hpp"
#include"tf2/LinearMath/Quaternion.h"
#include"tf2_geometry_msgs/tf2_geometry_msgs.hpp"
#include"tf2_ros/static_transform_broadcaster.h"

class StaticTFBroadcastrer : public rclcpp::Node{
    public:
	//在构造函数中进行
        StaticTFBroadcastrer() : Node("tf_broadcaster_node"){
            //创建静态广播发布器并发布
            broadcaster_ = std::make_shared<tf2_ros::StaticTransformBroadcaster>(this);//初始化
            this->publish_tf();//进行发布
        }

        void publish_tf(){
	    //构建消息接口对象
            geometry_msgs::msg::TransformStamped transform;
            transform.header.stamp=this->get_clock()->now();//时间戳赋值1
            transform.header.frame_id = "map";//父节点
            transform.child_frame_id = "target_point";//子结点
            transform.transform.translation.x = 5.0;
            transform.transform.translation.y = 3.0;
            transform.transform.translation.z = 0.0;
            tf2::Quaternion quat;
            quat.setRPY(0,0,60*M_PI/180);//弧度值欧拉角转四元数
            transform.transform.rotation = tf2::toMsg(quat);//转成消息接口类型
            broadcaster_->sendTransform(transform);//将坐标变换发布出去(静态变换只需要发布一次)
        }

    private:
        std::shared_ptr<tf2_ros::StaticTransformBroadcaster> broadcaster_;
};

int main(int argc,char** argv){
    rclcpp::init(argc,argv);
    auto node=std::make_shared<StaticTFBroadcastrer>();
    rclcpp::spin(node);
    rclcpp::shutdown();
    return 0;
}

CMakeLists.txt添加依赖

ament_target_dependencies(static_tf_broadcaster rclcpp tf2 tf2_ros geometry_msgs tf2_geometry_msgs)

发布动态TF

不断向外发布->timer定时器包装定时发布

代码

#include<memory>
#include"geometry_msgs/msg/transform_stamped.hpp" //提供消息接口
#include"rclcpp/rclcpp.hpp"
#include"tf2/LinearMath/Quaternion.h"             //提供tf2::Quaternion类
#include"tf2_geometry_msgs/tf2_geometry_msgs.hpp" //提供消息类型转换函数
#include"tf2_ros/transform_broadcaster.h"         //提供坐标广播器类:动态广播
#include<chrono>
using namespace std::chrono_literals;

class DynamicTFBroadcaster : public rclcpp::Node{
    public:
        DynamicTFBroadcaster() : Node("dynamic_tf_broadcaster"){
            tf_broadcaster_=std::make_shared<tf2_ros::TransformBroadcaster>(this);
            //定时器
            timer_=create_wall_timer(10ms,std::bind(&DynamicTFBroadcaster::publishTransform,this));
        }

        void publishTransform(){
            geometry_msgs::msg::TransformStamped transform;
            transform.header.stamp=this->get_clock()->now();
            transform.header.frame_id = "map";
            transform.child_frame_id = "base_link";
            transform.transform.translation.x=2.0;
            transform.transform.translation.y=3.0;
            transform.transform.translation.z=0.0;
            tf2::Quaternion quat;
            quat.setRPY(0,0,30*M_PI/180);
            transform.transform.rotation = tf2::toMsg(quat);
            tf_broadcaster_->sendTransform(transform);
        }

    private:
        std::shared_ptr<tf2_ros::TransformBroadcaster> tf_broadcaster_;
        rclcpp::TimerBase::SharedPtr timer_;
};

int main(int argc,char** argv){
    rclcpp::init(argc,argv);
    auto node=std::make_shared<DynamicTFBroadcaster>();
    rclcpp::spin(node);
    rclcpp::shutdown();
    return 0;
}

查询TF关系

#include<memory>//智能指针相关
#include"geometry_msgs/msg/transform_stamped.hpp"//提供消息接口
#include"rclcpp/rclcpp.hpp"
#include"tf2/LinearMath/Quaternion.h"
#include"tf2/utils.h"//提供tf2:getEulerYPR函数->将四元数转换为欧拉角
#include"tf2_geometry_msgs/tf2_geometry_msgs.hpp"
#include"tf2_ros/buffer.h"//提供TF缓冲类Buffer->存储订阅到的TF数据
#include"tf2_ros/transform_listener.h"//提供坐标监听器类->订阅TF变换
#include<chrono>
using namespace std::chrono_literals;

class TFListener : public rclcpp::Node{
    public:
        TFListener() : Node("tf_listener"){
            buffer_=std::make_shared<tf2_ros::Buffer>(this->get_clock());
            listener_=std::make_shared<tf2_ros::TransformListener>(*buffer_,this);
                                                                //*buffer_:获取指针指向的原始对象再传入
            timer_=this->create_wall_timer(5s,std::bind(&TFListener::getTransform,this));
        }
        void getTransform(){
            //使用try-catch对异常进行捕获
            try{
                //查询坐标变换
                const auto transform = buffer_->lookupTransform(
                    "base_link","target_point",this->get_clock()->now(),//数据开始时间
                    rclcpp::Duration::from_seconds(1.0f));//超时时间
                //转换结果并输出
                const auto &translation = transform.transform.translation;
                const auto &rotation = transform.transform.rotation;
                double yaw,pitch,roll;
                tf2::getEulerYPR(rotation,yaw,pitch,roll);//四元数转欧拉角
                RCLCPP_INFO(get_logger(),"平移分量:(%f,%f,%f)",translation.x,translation.y,translation.z);
                RCLCPP_INFO(get_logger(),"旋转分量:(%f,%f,%f)",roll,pitch,yaw);
            }
            catch(tf2::TransformException &ex){//处理异常
                RCLCPP_WARN(get_logger(),"异常:%s",ex.what());
            }
        }
    
    private:
        std::shared_ptr<tf2_ros::Buffer> buffer_;
        std::shared_ptr<tf2_ros::TransformListener> listener_;
        rclcpp::TimerBase::SharedPtr timer_;
};

int main(int argc,char** argv){
    rclcpp::init(argc,argv);
    auto node=std::make_shared<TFListener>();
    rclcpp::spin(node);
    rclcpp::shutdown();
    return 0;
}
posted @ 2024-12-31 20:52  White_ink  阅读(776)  评论(0)    收藏  举报