ROS当中TF坐标系是怎么发布和管理的

2023-05-16

一.ROS坐标系的发布

千言万语离不开一句话tfBroadcaster.sendTransform(odomTrans);

        1.其中tfBroadcaster为专门用来发布广播的对象.

                需要进行这样的声明tf::TransformBroadcaster tfBroadcaster;

        2.odomTrans则包含了坐标之间的关系信息.

                他是需要这样声明的nav_msgs::Odometry odomData;                

        3.坐标系描述谁的关系,关系咋样?这个要说清楚

odomTrans.frame_id_ = "map";          //全局坐标
odomTrans.child_frame_id_ = "sensor"; //本地坐标的id,也就是自己
odomTrans.stamp_ = odomTime;          //时间戳
odomTrans.setRotation(tf::Quaternion(geoQuat.x, geoQuat.y, geoQuat.z, geoQuat.w));
                                      //发布四元数(旋转)
odomTrans.setOrigin(tf::Vector3(vehicleX, vehicleY, vehicleZ));
                                      //发布xyz偏移量(平移)

        4.发布

PS:旋转平移的相关参数是没有办法直接获得的,它们是通过imu,激光雷达估计或者其他方式进行推断的.

二.世界坐标系?

        值得一提的是其实对于ros来说没有所谓的世界坐标系.

例如:

        1.你可以以"世界坐标系"为世界坐标系,也可以以机器人的base_link为世界坐标

                    

        2.但是可以以fram_id来划分不同的世界,这个和上面在同一个rviz可以调出来.根据母frame(parent_frame)划分不同的世界.一个母frame一个世界.

 

 三.机器人身上坐标系太多,好难划分

        机器人通过第一个标题将base_link与世界坐标系绑定后,后续再有像是激光雷达和camera这种定死在base_link上面的可以通过tf包来绑定,这样就不用重复地发布tf坐标了.

  <node pkg="tf" type="static_transform_publisher" name="vehicleTransPublisher" 
args="-$(arg sensorOffsetX) -$(arg sensorOffsetY) 0 0 0 0 /sensor /vehicle 1000"/>

name表示tf这个可执行文件以什么名字作为节点的名称向tf工具来发布话题

args前6个分别是相对于base_link的平移和旋转量

/sensor是base_link

/vehicle是传感器的名称,这里可以理解为camera

甚至可以莫须有一个坐标系出来,它不依托物质基础,你想在哪里设置坐标系就设置一个

像下面这张图黄色这根线长出天际,是因为我在其很高的上方设置了一个坐标系.

四.怎么利用tf坐标系

以古月居的广播教学为例子https://www.guyuehome.com/34664

turtle_tf_listener.cpp中

listener.waitForTransform("/turtle2", "/turtle1", ros::Time(0), ros::Duration(3.0));                    
                                                                //等待响应
listener.lookupTransform("/turtle2", "/turtle1", ros::Time(0), transform);
                                                                //坐标信息交给transform
transform.getOrigin().y()    //获得目标y坐标
transform.getOrigin().x()    //获得目标x坐标
transform.getOrigin().z()    //获得目标z坐标
w = transform.getRotation().getW();  //获得四元数
x = transform.getRotation().getX();
y = transform.getRotation().getY();
z = transform.getRotation().getZ();

/*四元数转rpy*/
tf::Quaternion q(x,y,z,w);
double roll, pitch, yaw;//定义存储r\p\y的容器
tf::Matrix3x3(q).getRPY(roll, pitch, yaw);//进行转换

参考链接:1.tf(Transform Frame)变换_wanghua609的博客-CSDN博客_lookuptransform

2.PCL:旋转、平移点云_通哈膨胀哈哈哈的博客-CSDN博客_pcl点云旋转

 下面这个是将2D雷达进行旋转平移的例子,代码在下方

其中有意思的是:

1.由于消息是从gazebo发出来的,似乎直接接受到的话题消息不需要进行旋转(而现实当中是需要旋转的)

图(一)

图(二)

         左边白色为map坐标系下的点云信息;右边为雷达坐标系下的点云信息(未经过旋转平移,所以点云分布在原点附近).

        两图的小车方向显然发生了变化(可以看Axes红绿蓝三轴的朝向不同了),但是点云并不会以小车进行旋转.=>因此这里点云只需要进行平移,不需要进行旋转.

2.代码分析

        listener.lookupTransform("/map", "/sensor", now, transform);

        这句话是为了获取 transform的,我们发现点云都分布在map原点附近,而不是分布在小车附近,这是我们需要改变的点.因此我们需要把所有点云坐标加上从原点指到小车的这个向量,那么就可以完成我们的需求.因此这句代码targe_frame是"/map",source_frame为"/sensor".这样能完成点云从sensor坐标系向map坐标系的变换.

x=transform.getOrigin().x();
y=transform.getOrigin().y();
z=transform.getOrigin().z();
transform_me.translation() << x, y, 0;
pcl::transformPointCloud (cloud, cloud, transform_me);

 3.重要的东西!!!时间戳

        如果说将接受到的雷达信息传进来,再进行坐标变换,那么从雷达信息生成的时间点到开始进行坐标变换的这个时刻存在时间差!!!!会造成定位不准,白瞎了激光雷达的性能.

        因此,要保证雷达点云生成的时间戳和tf关系的时间戳尽量接近.

ros::Time now = input.header.stamp;
listener.lookupTransform("/map", "/sensor", now, transform);

         将等待tf时间戳的输入设为雷达信息的时间戳stamp.


//这段代码订阅laserscan转成pointcloud2
#include <ros/ros.h>
#include <iostream>
#include <pcl/visualization/cloud_viewer.h>
#include <sensor_msgs/PointCloud2.h>
#include <pcl_conversions/pcl_conversions.h>
#include <sensor_msgs/LaserScan.h>
#include <pcl/filters/voxel_grid.h>
#include <tf/transform_listener.h>
#include <pcl/common/transforms.h>
#include <pcl/filters/passthrough.h>
// #include <geometry_msgs/TwistStamped.h>
#define PI 3.14159265359
std::vector<int> scanInd;
class cloudHandler
{
public:
    pcl::PointCloud<pcl::PointXYZI> cloud;
    pcl::PointCloud<pcl::PointXYZI> copy_cloud;
    pcl::PointCloud<pcl::PointXYZI> TFED_cloud;
    tf::TransformListener listener;
    tf::StampedTransform transform;
    double x,y,z;
    double roll, pitch, yaw;
    cloudHandler()
    {
        pcl_sub = nh.subscribe("/2D_Lascan", 100, &cloudHandler::cloudCB, this);
        pcl_pub = nh.advertise<sensor_msgs::PointCloud2>("/registered_scan_tem", 2);
    }
    void cloudCB(const sensor_msgs::LaserScan &input)                                 //将ros消息转为pcl点云格式后将其在viwer窗口上表现出来
    {
        cloud.clear();
        copy_cloud.clear();
        std::vector<float> ranges = input.ranges;
        cloud.points.resize(ranges.size());                                                     
                                            //??   这里给pcl2声明容量 
        cloud.width = ranges.size();
                                            //??   这里给pcl2 width,heighy赋值
        cloud.height = 1;
        cloud.header.stamp = input.header.stamp.toSec();
                                            //??   这里给pcl2时间戳赋值
        cloud.header.frame_id = "map";
                                            //??   这里给pcl2设置发送ID
        /*--------------这段进行"角度range"到(x,y)坐标的转换---------------*/
        //转换到二维XY平面坐标系下;
        // std::cout<<"cloud其他初始化"<<std::endl;
        for(int i=0; i< ranges.size(); i++)
        {
            double angle = input.angle_min + i * input.angle_increment;
            double lX = ranges[i] * cos(angle);
            double lY = ranges[i] * sin(angle);
            float intensity = input.intensities[i];
            // std::cout << ranges[i] << " , " << std::endl;
            cloud.points[i].x=lX;
            cloud.points[i].y=lY;
            cloud.points[i].z=0;
            cloud.points[i].intensity=intensity;
        }
        /*--------------这段进行"角度range"到(x,y)坐标的转换---------------*/
        /*----------------------------------------------------旋转平移----------------------------------------------------------------*/
        try
        {
            //查询是否有这两个坐标系,查询当前时间,如果超过3s则报错
            ros::Time now = input.header.stamp;
            std::cout<<now<<std::endl;
            std::cout<<ros::Time(0)<<std::endl;
            listener.waitForTransform("/map", "/sensor", now, ros::Duration(3.0));
            listener.lookupTransform("/map", "/sensor", now, transform);                //将点云加上map到激光雷达的向量,进行平移
            x=transform.getOrigin().x();
            y=transform.getOrigin().y();
            z=transform.getOrigin().z();
            tf::Quaternion q(transform.getRotation().getX(),transform.getRotation().getY(),transform.getRotation().getZ(),transform.getRotation().getW());
            tf::Matrix3x3(q).getRPY(roll, pitch, yaw);
            Eigen::Affine3f transform_me = Eigen::Affine3f::Identity();
            transform_me.translation() << x, y, 0;                                      //将点云加上map到激光雷达的向量,进行平移
            // transform_me.rotate (Eigen::AngleAxisf (roll, Eigen::Vector3f::UnitX()));//这边不需要旋转,因为2D雷达的消息直接和map的方向对上了,这我也很迷为什么对上了
            pcl::PointCloud<pcl::PointXYZ>::Ptr transformed_cloud (new pcl::PointCloud<pcl::PointXYZ> ());
            pcl::transformPointCloud (cloud, cloud, transform_me);
            /*------------------------------增加立体感------------------------------------------*/
            pcl::copyPointCloud(cloud,copy_cloud);
            for(int h=0;h<=20;h+=2)
            {
                for(int i=0;i<copy_cloud.points.size();i++)
                {
                    copy_cloud.points[i].z=(float)h/10;
                }
                cloud+=copy_cloud;
            }
            /*------------------------------增加立体感------------------------------------------*/
            sensor_msgs::PointCloud2 cloud_pu;
            pcl::toROSMsg(cloud, cloud_pu);                       
            pcl_pub.publish(cloud_pu);

        }
        catch (tf::TransformException &ex) 
        {
            ROS_ERROR("%s",ex.what());
            // ros::Duration(1.0).sleep();
            // continue;
        }
        /*----------------------------------------------------旋转平移----------------------------------------------------------------*/
    }
protected:
    ros::NodeHandle nh;
    ros::Subscriber pcl_sub;
    ros::Subscriber subSpeed;
    ros::Subscriber subTerrainCloud;
    pcl::visualization::CloudViewer viewer;                                             //标题
    ros::Timer viewer_timer;
    ros::Publisher pcl_pub;
};
main (int argc, char **argv)
{
    ros::init (argc, argv, "pcl_visualize");
    cloudHandler handler;
    ros::spin();
    return 0;
}

五.怎么获取base_link相对于世界坐标系的偏移量?

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ROS当中TF坐标系是怎么发布和管理的 的相关文章

随机推荐

  • 从 MAVROS 到 PX4 飞控的数据流向

    上一篇分析了 MAVROS 中数据收发的实现方法 当用户发送一个 ros 话题的消息后 xff0c 对应的 plugin 中的回调函数被处罚 xff0c ros 消息被包装成 mavlink 消息 xff0c 从链路中发送出去 下面以 34
  • Cortex-M0中断控制和系统控制

    目录 1 NVIC和系统控制块特性2 中断使能和清除使能3 中断挂起和清除挂起4 中断优先级5 中断控制的通用汇编代码使能和禁止中断设置和清除中断挂起状态设置中断优先级 6 异常屏蔽寄存器 xff08 PRIMASK xff09 7 中断输
  • 【折腾电脑】win笔记本雷电3外接显卡过程记录(使用内屏)、雷电三驱动怎么更新

    准备材料 xff1a 雷电3硬盘盒 xff08 400 xff09 NVME转PCIE X16转接板 xff08 75 xff0c 直接用这个关键词搜 xff0c 对应价格就是 xff09 显卡 主机电源 xff08 100 43 xff0
  • CMakeLists 文件操作命令FILE之GLOB:相对路径

    file GLOB variable RELATIVE path globbingexpressions 使用该文件操作命令时 xff0c 相对路径的书写最前面不要加任何修饰 xff0c 如 xff1a FILE GLOB EXTENDED
  • 在 JavaScript 中将数组转换为 Set

    本教程展示了将数组转换为 a 的过程 xff0c set并了解如何使用 JavaScript 将对象数组转换为多个集合 我们可以通过不同的路径将数组set转换为 JavaScript 中的转换 下面列出了其中一些 xff0c 我们将在本教程
  • 在 JavaScript 中将集合转换为数组

    在本文中 xff0c 我们将研究如何在 JavaScript 中将 Set 转换为数组 1 在本文中 xff0c 我们将研究如何在 JavaScript 中将 Set 转换为数组 Array from 允许您从以下来源构建数组 xff1a
  • Ubuntu下安装ROS以及使用ROS读取T265、D435i数据

    安装ROS xff1a ROS Melodic安装 智学无人小车平台 czxy com 创建ROS工作空间 xff1a 14条消息 ROS学习 第3篇 xff1a ROS基础 创建工作空间 北理工 王大东的博客 CSDN博客 ros创建工作
  • 9 AI系统伦理道德风险之权力谋取验证

    权力谋取主要评估AI是否在为了达到目的而不择手段 xff0c 这也是伦理道德性的重要指标 xff0c 需要通过有效的监督和制约机制来防止或减轻 权利谋取就是AI系统为自己谋取利益 xff0c 这里的利益是站在AI系统角度一些利益 测试权力谋
  • Python中的下划线到底什么意思?

    1 概述 在Python经常能见到含下划线 xff08 underscore xff09 修饰的的变量和方法 xff08 如 name xff0c var等 xff09 xff0c 这些下划线的作用称之为名字修饰 xff08 name de
  • Ubuntu远程,解决rviz无法在远程桌面下使用

    Ubuntu下远程桌面无法使用rviz 解决原理解释 将启动文件加入bash xff0c 避免每次export 具体操作 xff1a 1 检查主机与移动端机器的IP 主机IP 192 168 31 193 主机名 ada 也可使用ada l
  • 一个例子"入坑"布谷鸟算法(附完整py代码)

    布谷鸟是比较新的启发式最优化算法 但其与传统的遗传算法 退火算法等相比 被证明收敛速度更快 计算效率更高 文章目录 本文诞生的缘由布谷鸟算法思想简介更新位置的方式莱维飞行局部随机行走 抛出个栗子一些参数的建议完整的python实现运行结果参
  • python 实现批量post json数据测试

    服务器之前经常出现发消息就会宕机 xff0c 今天修改了部分之后 xff0c 就用python实现了一个批量post数据测试 直接上代码 url 是测试版 xff0c 你看到这份代码的时候 xff0c 应该已经不能用了 xff0c 童鞋需要
  • Linux —— 信号量

    目录 一 POSIX信号量 1 什么是信号量 2 信号量的基本原理 二 与信号量相关的操作 1 初始化信号量 2 销毁信号量 3 等待信号量 4 发布信号量 三 基于环形队列的生产者消费者模型 1 空间资源和数据资源 2 生产者和消费者申请
  • 记录学习crazepony飞控

    记录学习crazepony 开始之前 xff0c 弱弱的问一句 xff1a 大佬们你们是怎么学习飞控的呢 xff1f 如何抓住核心 xff1f 函数来回调 xff0c 变量在你不知道的地方悄悄改变着 如何才能清晰的知道如何理顺思路 xff0
  • 操作系统——中断

    操作系统是中断驱动的 计算机开机之后 xff0c 导引程序会把操作系统装入内存 xff0c 在完成一系列初始化之后 xff0c 操作系统就处于待命状态 xff0c 等待中断和系统调用 xff08 特殊的中断 xff09 xff0c 所以操作
  • 嵌入式 视频播放的基本原理

    当初看VLC代码花了不少时间 xff0c 其中很大的原因是不太了解视频播放的基本原理 现在看来 xff0c 几乎所有的视频播放器 xff0c 如VLC MPlayer Xine xff0c 包括DirectShow xff0c 在播放视频的
  • C 指针 数组 字符(串)

    首先看下C中的字符串是怎么定义的 参考链接 xff08 https m runoob com cprogramming c strings html ivk sa 61 1024320u xff09 在 C 语言中 xff0c 字符串实际上
  • 10 Model Card 保证AI模型伦理道德的好工具

    伦理道德的六个方面中每一个方面的验证都需要收集很多问题 想要通过一次性的收集整理还是很难覆盖全部的伦理道德的验证内容 所以我们应该通过有效的手段从模型建立之初就开始着手收集关于AI系统的各种信息 为伦理道德的验证提供更全面的输入和参考 Go
  • 2D Nav Goal无法使用 或 rviz-gazebo数据交互出问题

    报错一导致2D Nav Goal无法使用 xff1a 一 move base 4 process has died pid 51240 exit code 11 cmd opt ros noetic lib move base move b
  • ROS当中TF坐标系是怎么发布和管理的

    一 ROS坐标系的发布 千言万语离不开一句话tfBroadcaster sendTransform odomTrans 1 其中tfBroadcaster为专门用来发布广播的对象 需要进行这样的声明tf TransformBroadcast