2021.3.17| ROS静态坐标变换和动态坐标变换


概念:ROS中是通过右手坐标系统来标定物体。

 

一、坐标msg消息

坐标变换实现中最常用的msg是geometry_msgs/TransformStampedgeometry_msgs/PointStamped

前者用于传输坐标系相关位置信息,后者用于传输某个坐标系内坐标点的信息。在坐标变换中,频繁的需要使用到坐标系的相对关系以及坐标点信息。

1.geometry_msgs/TransformStamped

命令行键入 rosmsg info geometry_msgs/TransformStamped

std_msgs/Header header                     #头信息
  uint32 seq                                #|-- 序列号
  time stamp                                #|-- 时间戳
  string frame_id                            #|-- 坐标 ID
string child_frame_id                    #子坐标系的 id
geometry_msgs/Transform transform        #坐标信息
  geometry_msgs/Vector3 translation        #偏移量
    float64 x                                #|-- X 方向的偏移量
    float64 y                                #|-- Y 方向的偏移量
    float64 z                                #|-- Z 方向上的偏移量
  geometry_msgs/Quaternion rotation        #四元数
    float64 x                                
    float64 y                                
    float64 z                                
    float64 w

 其中四元数表示坐标的相对姿态

2.geometry_msgs/PointStamped

命令行键入:rosmsg info geometry_msgs/PointStamped

std_msgs/Header header                    #头
  uint32 seq                                #|-- 序号
  time stamp                                #|-- 时间戳
  string frame_id                            #|-- 所属坐标系的 id
geometry_msgs/Point point                #点坐标
  float64 x                                    #|-- x y z 坐标
  float64 y
  float64 z

 

 参考:

http://docs.ros.org/en/api/geometry_msgs/html/msg/TransformStamped.html

http://docs.ros.org/en/api/geometry_msgs/html/msg/PointStamped.html

二、静态坐标变换

问题:现有一机器人模型,核心构成包含主体与雷达,各对应一坐标系,坐标系的原点分别位于主体与雷达的物理中心,已知雷达原点相对于主体原点位移关系如下: x 0.2 y0.0 z0.5。当前雷达检测到一障碍物,在雷达坐标系中障碍物的坐标为 (2.0 3.0 5.0),请问,该障碍物相对于主体的坐标是多少?

分析:

  • 坐标系相对关系,可以通过发布方发布
  • 订阅方,订阅到发布的坐标系相对关系,再传入坐标点信息(可以写死),然后借助于 tf 实现坐标变换,并将结果输出

流程:

1.新建功能包,添加依赖。创建项目功能包依赖于 tf2、tf2_ros、tf2_geometry_msgs、roscpp std_msgs geometry_msgs

2.新建发布方的cpp文件,编写发布方实现。编写完毕还要配置CMakeLists.txt文件,三处,此略。

/* 
    静态坐标变换发布方:
        发布关于 laser 坐标系的位置信息 

    实现流程:
        1.包含头文件
        2.初始化 ROS 节点
        3.创建静态坐标转换广播器
        4.创建坐标系信息
        5.广播器发布坐标系信息
        6.spin()
*/


// 1.包含头文件
#include "ros/ros.h"
#include "tf2_ros/static_transform_broadcaster.h"
#include "geometry_msgs/TransformStamped.h"
#include "tf2/LinearMath/Quaternion.h"

int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");
    // 2.初始化 ROS 节点
    ros::init(argc,argv,"static_brocast");
    // 3.创建静态坐标转换广播器
    tf2_ros::StaticTransformBroadcaster broadcaster;
    // 4.创建坐标系信息
    geometry_msgs::TransformStamped ts;
    //----设置头信息
    ts.header.seq = 100;
    ts.header.stamp = ros::Time::now();
    ts.header.frame_id = "base_link";
    //----设置子级坐标系
    ts.child_frame_id = "laser";
    //----设置子级相对于父级的偏移量
    ts.transform.translation.x = 0.2;
    ts.transform.translation.y = 0.0;
    ts.transform.translation.z = 0.5;
    //----设置四元数:将 欧拉角数据转换成四元数
    tf2::Quaternion qtn;
    qtn.setRPY(0,0,0);
    ts.transform.rotation.x = qtn.getX();
    ts.transform.rotation.y = qtn.getY();
    ts.transform.rotation.z = qtn.getZ();
    ts.transform.rotation.w = qtn.getW();
    // 5.广播器发布坐标系信息
    broadcaster.sendTransform(ts);
    ros::spin();
    return 0;
}

3.新建订阅方的cpp文件,编写订阅方实现。编写完毕还要配置CMakeLists.txt文件,三处,此略。

/*  
    订阅坐标系信息,生成一个相对于 子级坐标系的坐标点数据,转换成父级坐标系中的坐标点

    实现流程:
        1.包含头文件
        2.初始化 ROS 节点
        3.创建 TF 订阅节点
        4.生成一个坐标点(相对于子级坐标系)
        5.转换坐标点(相对于父级坐标系)
        6.spin()
*/
//1.包含头文件
#include "ros/ros.h"
#include "tf2_ros/transform_listener.h"
#include "tf2_ros/buffer.h"
#include "geometry_msgs/PointStamped.h"
#include "tf2_geometry_msgs/tf2_geometry_msgs.h" //注意: 调用 transform 必须包含该头文件

int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");
    // 2.初始化 ROS 节点
    ros::init(argc,argv,"tf_sub");
    ros::NodeHandle nh;
    // 3.创建 TF 订阅节点
    tf2_ros::Buffer buffer;
    tf2_ros::TransformListener listener(buffer);

    ros::Rate r(1);
    while (ros::ok())
    {
    // 4.生成一个坐标点(相对于子级坐标系)
        geometry_msgs::PointStamped point_laser;
        point_laser.header.frame_id = "laser";
        point_laser.header.stamp = ros::Time::now();
        point_laser.point.x = 1;
        point_laser.point.y = 2;
        point_laser.point.z = 7.3;
    // 5.转换坐标点(相对于父级坐标系)
        //新建一个坐标点,用于接收转换结果  
        //--------------使用 try 语句或休眠,否则可能由于缓存接收延迟而导致坐标转换失败------------------------
        try
        {
            geometry_msgs::PointStamped point_base;
            point_base = buffer.transform(point_laser,"base_link");
            ROS_INFO("转换后的数据:(%.2f,%.2f,%.2f),参考的坐标系是:",point_base.point.x,point_base.point.y,point_base.point.z,point_base.header.frame_id.c_str());

        }
        catch(const std::exception& e)
        {
            // std::cerr << e.what() << '\n';
            ROS_INFO("程序异常.....");
        }


        r.sleep();  
        ros::spinOnce();
    }


    return 0;
}

 4.编译执行。

 

三、动态坐标变换

需求:启动 turtlesim_node,该节点中窗体有一个世界坐标系(左下角为坐标系原点),乌龟是另一个坐标系,键盘控制乌龟运动,将两个坐标系的相对位置动态发布。

 

 

 

分析:

  • 乌龟本身不但可以看作坐标系,也是世界坐标系中的一个坐标点

  • 订阅 turtle1/pose,可以获取乌龟在世界坐标系的 x坐标、y坐标、偏移量以及线速度和角速度

  • 将 pose 信息转换成 坐标系相对信息并发布

流程:

  1. 新建功能包,添加依赖。创建项目功能包依赖于 tf2、tf2_ros、tf2_geometry_msgs、roscpp rospy std_msgs geometry_msgs、turtlesim

  2. 创建坐标相对关系发布方(同时需要订阅乌龟位姿信息),新建发布方的cpp文件,编写发布方实现。编写完毕还要配置CMakeLists.txt文件,三处,此略。

    /*  
        动态的坐标系相对姿态发布(一个坐标系相对于另一个坐标系的相对姿态是不断变动的)
    
        需求: 启动 turtlesim_node,该节点中窗体有一个世界坐标系(左下角为坐标系原点),乌龟是另一个坐标系,键盘
        控制乌龟运动,将两个坐标系的相对位置动态发布
    
        实现分析:
            1.乌龟本身不但可以看作坐标系,也是世界坐标系中的一个坐标点
            2.订阅 turtle1/pose,可以获取乌龟在世界坐标系的 x坐标、y坐标、偏移量以及线速度和角速度
            3.将 pose 信息转换成 坐标系相对信息并发布
    
        实现流程:
            1.包含头文件
            2.初始化 ROS 节点
            3.创建 ROS 句柄
            4.创建订阅对象
            5.回调函数处理订阅到的数据(实现TF广播)
                5-1.创建 TF 广播器
                5-2.创建 广播的数据(通过 pose 设置)
                5-3.广播器发布数据
            6.spin
    */
    // 1.包含头文件
    #include "ros/ros.h"
    #include "turtlesim/Pose.h"
    #include "tf2_ros/transform_broadcaster.h"
    #include "geometry_msgs/TransformStamped.h"
    #include "tf2/LinearMath/Quaternion.h"
    
    void doPose(const turtlesim::Pose::ConstPtr& pose){
        //  5-1.创建 TF 广播器
        static tf2_ros::TransformBroadcaster broadcaster;
        //  5-2.创建 广播的数据(通过 pose 设置)
        geometry_msgs::TransformStamped tfs;
        //  |----头设置
        tfs.header.frame_id = "world";
        tfs.header.stamp = ros::Time::now();
    
        //  |----坐标系 ID
        tfs.child_frame_id = "turtle1";
    
        //  |----坐标系相对信息设置
        tfs.transform.translation.x = pose->x;
        tfs.transform.translation.y = pose->y;
        tfs.transform.translation.z = 0.0; // 二维实现,pose 中没有z,z 是 0
        //  |--------- 四元数设置
        tf2::Quaternion qtn;
        qtn.setRPY(0,0,pose->theta);
        tfs.transform.rotation.x = qtn.getX();
        tfs.transform.rotation.y = qtn.getY();
        tfs.transform.rotation.z = qtn.getZ();
        tfs.transform.rotation.w = qtn.getW();
    
    
        //  5-3.广播器发布数据
        broadcaster.sendTransform(tfs);
    }
    
    int main(int argc, char *argv[])
    {
        setlocale(LC_ALL,"");
        // 2.初始化 ROS 节点
        ros::init(argc,argv,"dynamic_tf_pub");
        // 3.创建 ROS 句柄
        ros::NodeHandle nh;
        // 4.创建订阅对象
        ros::Subscriber sub = nh.subscribe<turtlesim::Pose>("/turtle1/pose",1000,doPose);
        //     5.回调函数处理订阅到的数据(实现TF广播)
        //        
        // 6.spin
        ros::spin();
        return 0;
    }
    
  3. 创建坐标相对关系订阅方。

    //1.包含头文件
    #include "ros/ros.h"
    #include "tf2_ros/transform_listener.h"
    #include "tf2_ros/buffer.h"
    #include "geometry_msgs/PointStamped.h"
    #include "tf2_geometry_msgs/tf2_geometry_msgs.h" //注意: 调用 transform 必须包含该头文件
    
    int main(int argc, char *argv[])
    {
        setlocale(LC_ALL,"");
        // 2.初始化 ROS 节点
        ros::init(argc,argv,"dynamic_tf_sub");
        ros::NodeHandle nh;
        // 3.创建 TF 订阅节点
        tf2_ros::Buffer buffer;
        tf2_ros::TransformListener listener(buffer);
    
        ros::Rate r(1);
        while (ros::ok())
        {
        // 4.生成一个坐标点(相对于子级坐标系)
            geometry_msgs::PointStamped point_laser;
            point_laser.header.frame_id = "turtle1";
            point_laser.header.stamp = ros::Time();
            point_laser.point.x = 1;
            point_laser.point.y = 1;
            point_laser.point.z = 0;
        // 5.转换坐标点(相对于父级坐标系)
            //新建一个坐标点,用于接收转换结果  
            //--------------使用 try 语句或休眠,否则可能由于缓存接收延迟而导致坐标转换失败------------------------
            try
            {
                geometry_msgs::PointStamped point_base;
                point_base = buffer.transform(point_laser,"world");
                ROS_INFO("坐标点相对于 world 的坐标为:(%.2f,%.2f,%.2f)",point_base.point.x,point_base.point.y,point_base.point.z);
    
            }
            catch(const std::exception& e)
            {
                // std::cerr << e.what() << '\n';
                ROS_INFO("程序异常:%s",e.what());
            }
    
    
            r.sleep();  
            ros::spinOnce();
        }
    
    
        return 0;
    }
    
  4. 编译执行。

参考:

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM