ROS位置导航基础之坐标变换

创建项目功能包依赖于:
tf2、tf2_ros、tf2_geometry_msgs、roscpp rospy std_msgs geometry_msgs

单坐标静态变换

需求描述

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

相对关系

在这里插入图片描述

广播方节点

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

    实现流程:
        1.包含头文件
        2.初始化 ROS 节点
        3.创建静态坐标转换广播器
        4.创建坐标系信息
        5.广播器发布坐标系信息
        6.spin()
*/
##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, "");
    ros::init(argc, argv, "static_pub");
    ros::NodeHandle nh;
    tf2_ros::StaticTransformBroadcaster pub; //类似ros::Publisher
    geometry_msgs::TransformStamped tfs;     //传输的数据,是两个位置相对位置数据类型
    /*
    表示:child_frame_id在frame_id的那个地方?包括距离和方向
    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
    注意:因为角度表示一般是我们熟知的欧拉角,但是需要四元数传输要调用tf2::Quaternion对象中的setRPY函数转化获取
     */
    tfs.header.seq = 100;
    tfs.header.stamp = ros::Time::now();
    tfs.header.frame_id = "base_link";
    tfs.child_frame_id = "laser";
    tfs.transform.translation.x = 0.2;
    tfs.transform.translation.y = 0.0;
    tfs.transform.translation.z = 0.5;
    tf2::Quaternion qtn; // 将欧拉角转化成四元数
    qtn.setRPY(0.0, 0, 0);//用欧拉角初始化
    // 获取转换后的四元数
    tfs.transform.rotation.x = qtn.getX();
    tfs.transform.rotation.y = qtn.getY();
    tfs.transform.rotation.z = qtn.getZ();
    tfs.transform.rotation.w = qtn.getW();
    pub.sendTransform(tfs); //发布广播
    ros::spin();// 回调挂起
    return 0;
}

订阅方节点

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

    实现流程:
        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);
    //这里实例化并接受pub广播的信息后的buffer中存储着base_link和laser的相对位置
    //可以用于坐标位置和相对位置生成指定的坐标位置

    ros::Rate r(1);
    while (ros::ok())
    {
        // 4.生成一个坐标点(相对于子级坐标系)
        geometry_msgs::PointStamped point_laser;// 相对于原点位置数据类型和相对坐标位置不同
        /*
        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

        */
        point_laser.header.frame_id = "laser"; // 坐标点名称
        point_laser.header.stamp = ros::Time::now();
        point_laser.point.x = 2; //物体相对于laser的坐标值
        point_laser.point.y = 3;
        point_laser.point.z = 5;
        // 5.转换坐标点(相对于父级坐标系)
        //新建一个坐标点,用于接收转换结果
        //--------------使用 try 语句或休眠,否则可能由于缓存接收延迟而导致坐标转换失败------------------------
        try
        {
            geometry_msgs::PointStamped point_base; //存储目标位置(相对base_link)
            //buffer中base_link和laser两个相对位置和point_laser(障碍物相对于laser位置即坐标位置)生成障碍物相对于base_link的位置并赋值给point_base
            point_base = buffer.transform(point_laser, "base_link");//包含头文件##include "tf2_geometry_msgs/tf2_geometry_msgs.h"
            ROS_INFO("转换后的数据:(%.2f,%.2f,%.2f),参考的坐标系是:%s",point_base.point.x, point_base.point.y, point_base.point.z, point_base.header.frame_id.c_str());
        }
        catch (const std::exception &e)
        {
            ROS_INFO("程序异常.....");
        }

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

广播的封装

rosrun tf2_ros static_transform_publisher x偏移量 y偏移量 z偏移量 z偏航角度 y俯仰角度 x翻滚角度 父级坐标系 子级坐标系
rosrun tf2_ros static_transform_publisher 0.2 0 0.5 0 0 0 /base_link /laser
这一句就相当于广播方节点发布
静态写入launch文件

单坐标动态变换

需求描述

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

在这里插入图片描述

发布方节点启动

rosrun turtlesim turtlesim_node

订阅方节点

/*  
    动态的坐标系相对姿态发布(一个坐标系相对于另一个坐标系的相对姿态是不断变动的)

    需求: 启动 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.创建订阅对象,第一个乌龟位置的默认话题是/turtle1/pose
    ros::Subscriber sub = nh.subscribe<turtlesim::Pose>("/turtle1/pose", 1000, doPose);
    //     5.回调函数处理订阅到的数据(实现TF广播)
    //
    // 6.spin
    ros::spin();
    return 0;
}

多坐标变换

需求描述

现有坐标系统,父级坐标系统 world,下有两子级系统 son1,son2,son1 相对于 world,以及 son2 相对于 world 的关系是已知的,求 son1原点在 son2中的坐标,又已知在 son1中一点的坐标,要求求出该点在 son2 中的坐标

发布方节点

<launch>
	<!--tf2_ros是相对坐标位置关系,args参数分别是三个坐标位置,三个方向位置 主节点名 子节点名-->
    <node pkg="tf2_ros" type="static_transform_publisher" name="son1" args="0.2 0.8 0.3 0 0 0 /world /son1" output="screen" />
    <node pkg="tf2_ros" type="static_transform_publisher" name="son2" args="0.5 0 0 0 0 0 /world /son2" output="screen" />
</launch>

订阅方

/*

需求:
    现有坐标系统,父级坐标系统 world,下有两子级系统 son1,son2,
    son1 相对于 world,以及 son2 相对于 world 的关系是已知的,
    求 son1 与 son2中的坐标关系,又已知在 son1中一点的坐标,要求求出该点在 son2 中的坐标
实现流程:
    1.包含头文件
    2.初始化 ros 节点
    3.创建 ros 句柄
    4.创建 TF 订阅对象
    5.解析订阅信息中获取 son1 坐标系原点在 son2 中的坐标
      解析 son1 中的点相对于 son2 的坐标
    6.spin

*/
//1.包含头文件
#include "ros/ros.h"
#include "tf2_ros/transform_listener.h"
#include "tf2/LinearMath/Quaternion.h"
#include "tf2_geometry_msgs/tf2_geometry_msgs.h"
#include "geometry_msgs/TransformStamped.h"
#include "geometry_msgs/PointStamped.h"

int main(int argc, char *argv[])
{   setlocale(LC_ALL,"");
    // 2.初始化 ros 节点
    ros::init(argc,argv,"sub_frames");
    // 3.创建 ros 句柄
    ros::NodeHandle nh;
    // 4.创建 TF 订阅对象
    tf2_ros::Buffer buffer; 
    tf2_ros::TransformListener listener(buffer);//buffer相当于一个集和,在里面搜索坐标位置参数
    // 5.解析订阅信息中获取 son1 坐标系原点在 son2 中的坐标
    ros::Rate r(1);
    while (ros::ok())
    {
        try
        {
        //   解析 son1 中的点相对于 son2 的坐标
            geometry_msgs::TransformStamped tfs = buffer.lookupTransform("son2","son1",ros::Time(0));
            ROS_INFO("Son1 相对于 Son2 的坐标关系:父坐标系ID=%s",tfs.header.frame_id.c_str());
            ROS_INFO("Son1 相对于 Son2 的坐标关系:子坐标系ID=%s",tfs.child_frame_id.c_str());
            ROS_INFO("Son1 相对于 Son2 的坐标关系:x=%.2f,y=%.2f,z=%.2f",
                    tfs.transform.translation.x,
                    tfs.transform.translation.y,
                    tfs.transform.translation.z
                    );

            // 坐标点解析
            geometry_msgs::PointStamped ps;
            ps.header.frame_id = "son1";
            ps.header.stamp = ros::Time::now();
            // 当xyz都等于0时,调用buffer.transform相当于buffer.lookupTransform方法,不过返回类型相同,不过没角度
            ps.point.x = 1.0;
            ps.point.y = 2.0;
            ps.point.z = 3.0;

            geometry_msgs::PointStamped psAtSon2;
            psAtSon2 = buffer.transform(ps,"son2");//buffer中实际上有很多参数坐标,只是在这些集合中搜索目标位置
            ROS_INFO("在 Son2 中的坐标:x=%.2f,y=%.2f,z=%.2f",
                    psAtSon2.point.x,
                    psAtSon2.point.y,
                    psAtSon2.point.z
                    );
        }
        catch(const std::exception& e)
        {
            // std::cerr << e.what() << '\n';
            ROS_INFO("异常信息:%s",e.what());
        }
        r.sleep();
        // 6.spin
        ros::spinOnce();
    }
    return 0;
}

查看节点位置关系

  1. 图形界面

rviz

  1. pdf文档

rosrun tf2_tools view_frames.py
注意安装:sudo apt-get install ros-$ROS_DISTRO-tf2-tools
生成的关系文档生成在运行命令的相对目录下

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

「 25' h 」

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值