(02)Cartographer源码无死角解析-(19) SensorBridge→雷达点云数据预处理(函数重载)

news/2024/4/29 6:12:21/文章来源:https://blog.csdn.net/weixin_43013761/article/details/127841694

本人讲解关于slam一系列文章汇总链接:史上最全slam从零开始,针对于本栏目讲解(02)Cartographer源码无死角解析-链接如下:
(02)Cartographer源码无死角解析- (00)目录_最新无死角讲解:https://blog.csdn.net/weixin_43013761/article/details/127350885
 
文末正下方中心提供了本人联系方式,点击本人照片即可显示WX→官方认证{\color{blue}{文末正下方中心}提供了本人 \color{red} 联系方式,\color{blue}点击本人照片即可显示WX→官方认证}WX
 

一、前言

再前面的博客中,已经对 Odome、landmark、Imu、GPS 的预处理进行了讲解,有的朋友或许会觉得GPS的处理挺复杂的,那么再来看看稍微复杂一些的点云处理,如下:

//所有单线雷达topic回调函数
SensorBridge::HandleLaserScanMessage() 
//所有回声波雷达topoc回调函数
SensorBridge::HandleMultiEchoLaserScanMessage()
//所有多线雷达topic回调函数
SensorBridge::HandlePointCloud2Message()

其上函数都实现于 src/cartographer_ros/cartographer_ros/cartographer_ros/sensor_bridge.cc 文件中。另外,这里所谓的复杂,仅仅是代码逻辑上的复杂,而非算法,那么就开始讲解吧。
 

二、雷达重要指标

1.视野范围→数据的最大最小距离以及扫描的角度范围

激光雷达的视野范围决定了这款雷达的应用场景,墙距离20m的屋子里不能可能使用视野范围为4米的雷达,根本扫不到东西。
 

2.角度分辨率或点数→2个数据点间的角度

雷达的角度分辨率决定了激光雷达每帧数据点的个数。如果是0.25度的分辨率,360度视角的激光雷达,那其一帧最多有1441个点。每帧数据的点数越多,越能更好的表征环境的细节信息。当然,点数也不是越多越好,点数越多,计算成本也越高。
 

3.频率:发出数据的频率,如10HZ,就可以理解为1秒10帧数据

雷达的频率是一个十分重要的指标,雷达的频率越高,雷达2帧数据的间隔就越小。假设雷达的频率是20Hz,也就是50ms来一次数据。当我进行定位的时候,这50ms时间内的机器人的运动,只能通过估计来获得,所以雷达的频率越高,我们通过估计的距离也就越小,定位也就越准确。
 

4.强度:发出数据的能量强度,主要物体材质相关

激光雷达的激光点是有能量的,不同品牌激光点的能量也不同。当能量太小时,远距离情况下可能存在返回不了数据的情况。
 

5.精度:点的跳动程度(精度)等等

这是最重要的一个指标,如果一个激光雷达的数据跳动特别大,那这个雷达就没法用了。现在一般厂商的雷达的精度都是2%。也就是100m的情况下,点的跳动幅度为2cm。但是,实际感觉能达到这个精度的雷达不是很多。
 

雷达数据帧概念:\color{blue}雷达数据帧概念:

明白了基本概念之后,这里再讲解以下雷达帧的概念,假设雷达为20HZ,那么他的帧率可以理解为20,也就是每间隔50ms发送一帧数据,一帧数据中包含了雷达视野范围的所有点云数据。如20HZ单线180度视角的雷达,说明其1秒内对180度视野范围扫描了20次(帧),每次的数据为180/0.25=720个点云数据。
 

三、雷达消息

讲解代码之前,先来看消息类型。

1、单线雷达

在终端执行指令:

rosmsg show LaserScan
std_msgs/Header header //消息头uint32 seq //消息time stamp //包含了开始扫描的时间和与开始扫描的时间差string frame_id //数据是基于该坐标系的,本人为scan
float32 angle_min //可检测范围的起始角度(弧度制)
float32 angle_max //可检测范围的终止角度,与angle_min组成激光雷达的可检测范围。像从-180度到+180度就是360度的范围。
float32 angle_increment //雷达数据的角度分辨率(角度增量)
float32 time_increment //雷达数据每个数据点的时间间隔
float32 scan_time //当前帧数据与下一帧数据的时间间隔
float32 range_min //最近可检测深度的阈值
float32 range_max //最远可检测深度的阈值
float32[] ranges //一帧深度数据的存储数组
float32[] intensities //雷达数据每个点对应的强度值

2、多回声雷达

在终端执行指令:

rosmsg show MultiEchoLaserScan

可以看到与 LaserScan 的结构基本一致的,不同点如下所示

sensor_msgs/LaserEcho[] rangesfloat32[] echoes
sensor_msgs/LaserEcho[] intensitiesfloat32[] echoes

普通的激光扫描消息(LaserScan)表述的是:每个激光脉冲的单个返回深度和强度值,如果返回了多个,通常只会选取其中强度最强的一个。

多回波传感器(MultiEchoLaserScan)不同的是,它能够为每个激光脉冲接收多个回波。例如,如果您扫描窗户,则通常会从玻璃以及玻璃后面的墙壁中接收到回波;如果在两个物体的边界处,则会收取到不同深度的回波。在创建地图和定位机器人的位置时,这些数据可以为您提供更多附加信息。因此,使用 MultiEchoLaserScan 的节点可以充分利用这种类型的传感器。

理解其为高级传感器,简单的说,每次激光打出,可以接受多个反馈信息,也就是多少深度值。

2、多线雷达

在终端执行指令:

rosmsg show PointCloud2
std_msgs/Header header  //消息头uint32 seq //消息序列time stamp //时间戳string frame_id
uint32 height //点云的高度,如果是无序点云,则为1
uint32 width //每行点云的宽度
sensor_msgs/PointField[] fields  //表示一个点的结构,x、y、z代表三维坐标, intensity代表反射强度,uint8 INT8=1  //INT8为一个字节uint8 UINT8=2 //UINT8为两个字节uint8 INT16=3 //INT16为3个字节uint8 UINT16=4  //UINT16为四个字节uint8 INT32=5 //INT32为五个字节uint8 UINT32=6 //UINT32为六个字节uint8 FLOAT32=7 //FLOAT32为七个字节uint8 FLOAT64=8 //FLOAT64为八个字节string name //一般填写为 X,Y,Z,intensityuint32 offset //表一个点结构内的起始地址uint8 datatype //上面的八种之一uint32 count //count代表field的个数
bool is_bigendian //是否大端存储,计算机一般是小端存储,所以默认设置为flase即可
uint32 point_step //每个点占用的比特数,1个字节对应8个比特数
uint32 row_step //每一行占用的beye数
uint8[] data //为序列化后的数据,直接获得不了信息,序列化是为了方便信息传输和交换,使用时需要反序列化
bool is_dense //是否有非法数据点,true表示没有

比如下面来看一帧多线点云的数据,再终端执行如下指令:

//查看消息,本人可以看到 /rslidar_points
rostopic  list
//打印话题内容,并且输入到 rslidar_points.txt 文件中
rostopic  echo /rslidar_points > rslidar_points.txt
header: seq: 7390stamp: secs: 1606808670nsecs: 787610000frame_id: "front_laser_link"
height: 16
width: 1032
fields: - name: "x"offset: 0datatype: 7count: 1- name: "y"offset: 4datatype: 7count: 1- name: "z"offset: 8datatype: 7count: 1- name: "intensity"offset: 16datatype: 7count: 1
is_bigendian: False
point_step: 32
row_step: 33024
data: [181, 159, 96, 64, 101, 243, 14, 191, 239, 125, 113, ......]
is_dense: False

根据上面的内容,可以知道每点云数据包含 x,y,z,intensity信息,其数据类型为 FLOAT32,也就是x,y,z,intensity各占用4个字节,共16个字节,共32(point_step)个beye数。使用小端存储,不含非法数据。height=16,可以知道其为16线雷达,每线(行) width=1032点云数据,共16x1032=16512个点云数据,共占用beyes数16512x32=528384。执行代码 msg->data.size() 可以直接获得共占用的beyes数。
 

四、函数重载

关于 HandleLaserScanMessage()、HandleMultiEchoLaserScanMessage()、HandlePointCloud2Message() 函数,注释如下:

// 处理LaserScan数据, 先转成点云,再传入trajectory_builder_
void SensorBridge::HandleLaserScanMessage(const std::string& sensor_id, const sensor_msgs::LaserScan::ConstPtr& msg) {carto::sensor::PointCloudWithIntensities point_cloud;carto::common::Time time;std::tie(point_cloud, time) = ToPointCloudWithIntensities(*msg);HandleLaserScan(sensor_id, time, msg->header.frame_id, point_cloud);
}// 处理MultiEchoLaserScan数据, 先转成点云,再传入trajectory_builder_
void SensorBridge::HandleMultiEchoLaserScanMessage(const std::string& sensor_id,const sensor_msgs::MultiEchoLaserScan::ConstPtr& msg) {carto::sensor::PointCloudWithIntensities point_cloud;carto::common::Time time;std::tie(point_cloud, time) = ToPointCloudWithIntensities(*msg);HandleLaserScan(sensor_id, time, msg->header.frame_id, point_cloud);
}// 处理ros格式的PointCloud2, 先转成点云,再传入trajectory_builder_
void SensorBridge::HandlePointCloud2Message(const std::string& sensor_id,const sensor_msgs::PointCloud2::ConstPtr& msg) {carto::sensor::PointCloudWithIntensities point_cloud;carto::common::Time time;std::tie(point_cloud, time) = ToPointCloudWithIntensities(*msg);HandleRangefinder(sensor_id, time, msg->header.frame_id, point_cloud.points);
}

可以看到其处理过程都是非常类似的,其核心都是调用 ToPointCloudWithIntensities() 函数把 msg 转换成 carto 算法需要的
carto::sensor::PointCloudWithIntensities 类型。但是这里注意,虽然其调用函数的的函数名都是 ToPointCloudWithIntensities(),但是其处理过程都是不一样的,因为其有三个重载函数,根据输入的数据类型,分别调用不同的函数,重载函数如下(src/cartographer_ros/cartographer_ros/cartographer_ros/msg_conversion.cc):

// 由ros格式的LaserScan转成carto格式的PointCloudWithIntensities
std::tuple<::cartographer::sensor::PointCloudWithIntensities,::cartographer::common::Time>
ToPointCloudWithIntensities(const sensor_msgs::LaserScan& msg) {return LaserScanToPointCloudWithIntensities(msg);
}// 由ros格式的MultiEchoLaserScan转成carto格式的PointCloudWithIntensities
std::tuple<::cartographer::sensor::PointCloudWithIntensities,::cartographer::common::Time>
ToPointCloudWithIntensities(const sensor_msgs::MultiEchoLaserScan& msg) {return LaserScanToPointCloudWithIntensities(msg);
}// 由ros格式的PointCloud2转成carto格式的PointCloudWithIntensities
std::tuple<::cartographer::sensor::PointCloudWithIntensities,::cartographer::common::Time>
ToPointCloudWithIntensities(const sensor_msgs::PointCloud2& msg){............
}

这里需要注意,LaserScanToPointCloudWithIntensities()为模板函数(理解为高级重载),那么就先对齐进行讲解,然后再分析 ToPointCloudWithIntensities(const sensor_msgs::PointCloud2& msg) 吧
 

五、LaserScanToPointCloudWithIntensities()

该函数为模板函数,大家这里可能会觉得奇怪,上面代码再调用的时候,明明没有指定模板参数,却可以调用模板函数,这主要来自于编译器的自动模板参数推导。

在这里插入图片描述
这里举一个例子,假设 first_echo=19.6152687,angle= -3.0089736

那么该点在雷达坐标系的占位置为 (-19.4430275,-2.59373975, 0)

 
 
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_224075.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

3.35 OrCAD中怎么产生Cadence Allegro的第一方网表?OrCAD软件输出Cadence Allegro第一方网表报错时应该怎么处理?

笔者电子信息专业硕士毕业&#xff0c;获得过多次电子设计大赛、大学生智能车、数学建模国奖&#xff0c;现就职于南京某半导体芯片公司&#xff0c;从事硬件研发&#xff0c;电路设计研究。对于学电子的小伙伴&#xff0c;深知入门的不易&#xff0c;特开次博客交流分享经验&a…

cpu与指令集

讨论一下 作为一个java程序员&#xff0c;我们都知道&#xff0c;当我们写完代码&#xff0c;java文件会被编译为class文件&#xff0c;然后交给jvm去执行&#xff0c;那么这个执行过程是啥样的呢&#xff1f;&#xff1f; 一般我们得到的解答都是&#xff0c;class代码会被解…

2、HTML——标题分组、居中、引用标签、水平线标签下划线标签、删除标签、<font>标签、图像标签

目录 一、基本标签 1、标题分组&#xff1a;hgroup 2、居中&#xff1a;center 3、引用标签 3.1 块&#xff08;长&#xff09;引用标签&#xff1a;blockquote 3.2 短引用标签&#xff1a;q 4、水平线标签&#xff1a;hr 5、下划线标签&#xff1a;ins 6、删除标…

【论文笔记之 BLMS】Block Implementation of Adaptive Digital Filters

本文对 GREGORY A. CLARK 于 1981 年在 IEEE Transactions on Circuits and Systems 上发表的论文进行简单地翻译。如有表述不当之处欢迎批评指正。欢迎任何形式的转载&#xff0c;但请务必注明出处。 论文链接&#xff1a;https://ieeexplore.ieee.org/abstract/document/108…

安利几个小技巧教会你ppt如何转pdf

作为一名打工人&#xff0c;特别是办公类&#xff0c;经常是要处理大大小小的文件&#xff0c;有时候甚至要做多种文件转换。并且老板都是多变的&#xff0c;经常突然性就让你把辛苦制作一大半的PPT转成PDF格式的文件再给他。那刚入门的职场小白肯定就会选择&#xff0c;老老实…

我终于读懂了适配器模式。。。

文章目录&#x1f5fe;&#x1f306;什么是适配器模式&#xff1f;&#x1f3ef;类适配器模式&#x1f3f0;对象适配器模式⛺️接口适配器模式&#x1f3ed;适配器模式在SpringMVC 框架应用的源码剖析&#x1f5fc;适配器模式的注意事项和细节&#x1f306;什么是适配器模式&am…

自学软件测试?一般人我还是劝你算了吧...

本人7年测试经验&#xff0c;在学测试之前对电脑的认知也就只限于上个网&#xff0c;玩个办公软件。这里不能跑题&#xff0c;我为啥说&#xff1a;自学软件测试&#xff0c;一般人我还是劝你算了吧&#xff1f;因为我就是那个一般人&#xff01; 软件测试基础真的很简单&…

C# Socket

一 两个人在两个房间里打电话的图 ① 人通过【电话】可以通信&#xff1b; ② 程序通过【Socket】来通信&#xff1b; ③ *套接字 就是 程序间的电话机&#xff1b; ④ 我和孙权打电话 电话 规定好的语言&#xff1b; ⑤ 电脑和电话进行联系 协议&#xff1b; 二 Socket相关…

JVM(二十三)—— 垃圾回收器(三)G1垃圾回收器

G1垃圾回收器:区域化分代式G1概述G1的特点&#xff08;优势&#xff09;G1的缺点G1的参数设置G1的适用场景分区region&#xff1a;化整为零记忆集和写屏障G1回收器垃圾回收过程年轻代GC并发标记过程混合回收G1概述 应用程序所应对的业务越来越庞大&#xff0c;复杂&#xff0c…

【大数据】flink 读取文件数据写入ElasticSearch

前言 es是大数据存储的必备中间件之一&#xff0c;通过flink可以读取来自日志文件&#xff0c;kafka等外部数据源的数据&#xff0c;然后写入到es中&#xff0c;本篇将通过实例演示下完整的操作过程&#xff1b; 一、前置准备 1、提前搭建并开启es服务&#xff08;本文使用doc…

图像分割 - Hough变换直线检测

目录 1. Hough 直线检测 2. HoughLinesP 函数 1. Hough 直线检测 霍夫变换&#xff08;Hough 变换&#xff09;&#xff1a;利用对偶原理&#xff0c;把原空间的问题转换到对偶空间去求解 这里涉及到空间转换&#xff0c;将原来的笛卡尔空间&#xff08;xy空间&#xff09;…

App安全架构之前端安全防护

近年来&#xff0c;随着互联网、物联网、移动设备、5G通讯等技术的齐头发展&#xff0c;人类的生活和工作越来越离不开软件和互联网&#xff0c;正如人类社会文明发展到一定程度以后&#xff0c;会需要法律等社会规范来保护一样&#xff0c;线上环境也是一样道理。 Gartner 对…

Python学习小组课程-课程大纲与Python开发环境安装

一、前言 注意&#xff1a;此为内部小组学习资料&#xff0c;非售卖品&#xff0c;仅供学习参考。 为提升项目落地的逻辑思维能力&#xff0c;以及通过自我创造工具来提升工作效率&#xff0c;特成立Python学习小组。计划每周花一个小时进行在线会议直播学习&#xff0c;面向…

国内访问Github超级慢?那是你没有用我这个脚本。直接起飞。

导语 之前很多朋友咨询过国内访问Github较慢的问题&#xff0c;然后我一般让他们自己去知乎上找攻略&#xff0c;但今天我才发现网上竟然没有一个一键配置的脚本&#xff0c;一般都需要我们跟着教程一步步地去做才行。这也太麻烦了&#xff0c;于是自己动手写了个脚本&#xf…

ceph浅谈

总谈 ceph简介 用上ceph&#xff0c;多台机器的磁盘空间在一起了&#xff0c;在一台机器上就可以看到使用所有空间。 还可以保存多份安全备份 存储先ceph&#xff0c;自我管理修复&#xff0c;跨机房&#xff0c;节点越多&#xff0c;并行化&#xff0c;论上&#xff0c;节点越…

1-(3-磺酸基)丙基-1-甲基-2-吡咯烷酮三氟甲磺酸盐[C3SO3Hnmp]CF3SO3

1-(3-磺酸基)丙基-1-甲基-2-吡咯烷酮三氟甲磺酸盐[C3SO3Hnmp]CF3SO3 离子液体(IonicLiquids)是完全由离子组成&#xff0c;现在多指在低于100摄氏度时呈液体状态的熔盐。通常由特定的有机阳离子和无机阴离子&#xff08;或有机阴离子&#xff09;构成。 离子液体特点 蒸汽压…

C++基础——模板讲解

目录 一. 泛型编程 二. 函数模板 1.格式&#xff1a; 2.定义&#xff1a; 1.隐式实例化 2.显式实例化 3.解决方法3&#xff1a;使用多个T类型 4.在C中编译器允许非模板函数和模板函数同时存在 一. 泛型编程 先来看一段代码&#xff1a; void Swap(int& p1, int&am…

LeetCode:8. 字符串转换整数 (atoi)

8. 字符串转换整数 &#xff08;atoi&#xff09;1&#xff09;题目2&#xff09;思路3&#xff09;代码4&#xff09;结果1&#xff09;题目 请你来实现一个 myAtoi(string s) 函数&#xff0c;使其能将字符串转换成一个 32 位有符号整数&#xff08;类似 C/C 中的 atoi 函数…

逻辑判断与正则表达式文本处理

上一篇文章分享了正则表达式的操作&#xff0c;这一篇文章就让我们一起看看正则表达式与逻辑判断结合起来会发生什么吧&#xff01;感兴趣的小伙伴欢迎评论区或者是私信留言&#xff01; 一、题目描述&#xff1a; 输入一个字符串,检查其是否为合法的python变量。输入$$$结束:…

数据结构【队列】

文章目录&#xff08;一&#xff09;队列定义&#xff08;二&#xff09;队列实现&#xff08;1&#xff09;创建结构体&#xff08;2&#xff09;具体函数实现及解析1.1 初始化队列1.2入队列1.3出队列1.4取队首元素1.5取队尾元素1.6返回队列个数1.7判断是否为空1.8销毁队列&am…