BLAM源码解析(三)—— 定时器总揽大局
上一节介绍了激光数据的回调,最终被依次推入到容器之中,存储在pcl_pcld_queue。那么激光数据的使用是怎么实现的,就是这一节要介绍的内容。
回想第一节我们介绍的定时器estimate_update_timer_,一秒进行20次的回调,进入回调函数BlamSlam::EstimateTimerCallback,接下来介绍此函数。
BlamSlam::EstimateTimerCallback
此函数位于src\blam_slam\src\BlamSlam.cc,内容如下:
void BlamSlam::EstimateTimerCallback(const ros::TimerEvent& ev) {// Sort all messages accumulated since the last estimate update.synchronizer_.SortMessages();// Iterate through sensor messages, passing to update functions.MeasurementSynchronizer::sensor_type type;unsigned int index = 0;while (synchronizer_.GetNextMessage(&type, &index)) {switch(type) {// Point cloud messages.case MeasurementSynchronizer::PCL_POINTCLOUD: {const MeasurementSynchronizer::Message<PointCloud>::ConstPtr& m =synchronizer_.GetPCLPointCloudMessage(index);ProcessPointCloudMessage(m->msg);break;}// Unhandled sensor messages.default: {ROS_WARN("%s: Unhandled measurement type (%s).", name_.c_str(),MeasurementSynchronizer::GetTypeString(type).c_str());break;}}}// Remove processed messages from the synchronizer.synchronizer_.ClearMessages();
}
下面逐行进行介绍:
synchronizer_.SortMessages();
从上一次位姿更新后,排序激光消息。实现位于src\measurement_synchronizer\src\MeasurementSynchronizer.cc,内容为:
void MeasurementSynchronizer::SortMessages() {sensor_ordering_.clear(); // 清空排序后的容器// Accumulate all new messages in a single list.unsigned int ii = 0;for (pcld_queue::const_iterator it = pending_pclds_.begin(); // 无视此循环it != pending_pclds_.end(); ++it, ++ii) {TimestampedType::Ptr p = TimestampedType::Ptr(new TimestampedType((*it)->msg->header.stamp.toSec(), POINTCLOUD, ii));sensor_ordering_.push_back(p); }ii = 0;for (pcl_pcld_queue::const_iterator it = pending_pcl_pclds_.begin(); it != pending_pcl_pclds_.end(); ++it, ++ii) {// 循环激光回调容器ros::Time stamp;stamp.fromNSec((*it)->msg->header.stamp*1e3);TimestampedType::Ptr p = TimestampedType::Ptr(new TimestampedType(stamp.toSec(), PCL_POINTCLOUD, ii));sensor_ordering_.push_back(p);//将激光对应的时间戳信息推进排序容器}// Sort the list by time.std::sort(sensor_ordering_.begin(), sensor_ordering_.end(),MeasurementSynchronizer::CompareTimestamps);pending_index_ = 0;
}
里面pending_pclds_容器是哪来的呢?发现并没有在其他地方用到,所以直接不用管上面那个循环。直接看关于pending_pcl_pclds_的循环,将激光对应的时间戳信息推进排序容器,然后将排序容器sensor_ordering_进行排序,排序规则为:
* 时间小的在前
* 若时间相等,POINTCLOUD类型在前,PCL_POINTCLOUD类型在后
感觉第二条规则用不上。最后初始化pending_index_ = 0;
回到void BlamSlam::EstimateTimerCallback,继续:
// Iterate through sensor messages, passing to update functions.MeasurementSynchronizer::sensor_type type;unsigned int index = 0;while (synchronizer_.GetNextMessage(&type, &index)) {switch(type) {// Point cloud messages.case MeasurementSynchronizer::PCL_POINTCLOUD: {const MeasurementSynchronizer::Message<PointCloud>::ConstPtr& m =synchronizer_.GetPCLPointCloudMessage(index);ProcessPointCloudMessage(m->msg);break;}// Unhandled sensor messages.default: {ROS_WARN("%s: Unhandled measurement type (%s).", name_.c_str(),MeasurementSynchronizer::GetTypeString(type).c_str());break;}}}
此部分开启激光数据处理迭代。GetNextMessage函数,传入index为0,依次取出排序容器中的激光数据索引,这个索引用来在激光回调容器中寻找对应索引的激光数据,按照时间进行取出。取出的激光数据被赋予m,使用ProcessPointCloudMessage处理点云。
BlamSlam::ProcessPointCloudMessage
首先使用过滤器对点云进行过滤
filter_.Filter(msg, msg_filtered);
filter_就是我们第一节所介绍的初始化过的过滤器。Filter函数中实施了几种点云过滤方法,包括:
(1)random_filter的随机滤波
(2)grid.filter的体素滤波
(3)sor.filter的统计滤波
(4)rad.filter的半径滤波
(5)由于源程序对具有nan的点云不适配,而后加入的NaN去除:
if (!points->is_dense){// points_filtered->is_dense = false;std::vector<int> indices;pcl::removeNaNFromPointCloud(*points_filtered,*points_filtered, indices);}
这几种滤波根据实际情况进行选择,参数位于src\point_cloud_filter\config\parameters.yaml。随机滤波不介绍了,体素滤波可调节分辨率,越小保留点越多,后续的计算量也越大;统计滤波过多消耗计算量,不推荐打开;半径滤波有利于去除离散点,可以考虑,但当点云较稀疏时可不开。
接下来就是重要的,使用ICP进行位姿更新了。
if (!odometry_.UpdateEstimate(*msg_filtered)) {
PointCloudOdometry::UpdateEstimate
此函数实现关于基于ICP的位姿更新,下一节进行介绍。
BLAM源码解析(三)—— 定时器总揽大局相关推荐
- Disruptor源码解析三 RingBuffer解析
目录 系列索引 前言 主要内容 RingBuffer的要点 源码解析 系列索引 Disruptor源码解析一 Disruptor高性能之道 Disruptor源码解析二 Sequence相关类解析 D ...
- OkHttp3源码解析(三)——连接池复用
OKHttp3源码解析系列 OkHttp3源码解析(一)之请求流程 OkHttp3源码解析(二)--拦截器链和缓存策略 本文基于OkHttp3的3.11.0版本 implementation 'com ...
- ReactiveSwift源码解析(三) Signal代码的基本实现
上篇博客我们详细的聊了ReactiveSwift源码中的Bag容器,详情请参见<ReactiveSwift源码解析之Bag容器>.本篇博客我们就来聊一下信号量,也就是Signal的的几种状 ...
- BLAM源码解析(五)—— 回环检测
上一节介绍了BLAM的帧间匹配和帧图匹配,代码简洁明了. 本节介绍BLAM的回环检测模块.具体代码块如下: // Check for new loop closures.bool new_keyfra ...
- 并发编程与源码解析 (三)
并发编程 (三) 1 Fork/Join分解合并框架 1.1 什么是fork/join Fork/Join框架是JDK1.7提供的一个用于并行执行任务的框架,开发者可以在不去了解如Thread.R ...
- 前端入门之(vuex源码解析三)
上两节前端入门之(vuex源码解析二)我们把vuex的源码大概的撸了一遍,还剩下(插件.getters跟module),我们继续哈~ 插件童鞋们可以去看看vuex在各个浏览器的状态显示插件,小伙伴可以 ...
- 拆轮子-RxDownload2源码解析(三)
本文为博主原创文章,未经允许不得转载 造轮子者:Season_zlc 轮子用法请戳作者链接 ↑ 前言 本文主要讲述 RxDownload2 的多线程断点下载技术. 断点下载技术前提 服务器必须支持按 ...
- Tomcat源码解析三:tomcat的启动过程
Tomcat组件生命周期管理 在Tomcat总体结构 (Tomcat源代码解析之二)中,我们列出了Tomcat中Server,Service,Connector,Engine,Host,Context ...
- 【Vue.js源码解析 三】-- 模板编译和组件化
前言 笔记来源:拉勾教育 大前端高薪训练营 阅读建议:建议通过左侧导航栏进行阅读 模板编译 模板编译的主要目的是将模板 (template) 转换为渲染函数 (render) <div> ...
- Cesium源码解析三(metadata元数据拓展中行列号的分块规则解析)
目录 1.前言 2.layer.json中available参数意义 3.EPSG:4626切片及terrain分块原理 4.Cesium的terrain分块规则 5.自定义terrain分块规则 6 ...
最新文章
- html select 多选取值,下拉框多选实现 jquery-multiselect 并获取选中的值
- 使用subgit进行svn迁移至git(branch,tags)
- 自定义通配器导入bean对象
- jstack调试_增压的jstack:如何以100mph的速度调试服务器
- linux安装虚拟环境virtualenv
- 掌握 Ajax,第 11 部分: 服务器端的 JSON
- 20-10-026-安装-KyLin-2.6.0-单机版安装(MAC官网下载)-spark引擎
- webmin账户重置密码
- python自学入门-初学 Python 者自学 Anaconda 的正确姿势是什么?
- redis的基础配置
- c语言范式编程之lsearch
- 华硕台式计算机编号,怎么查看华硕电脑设备序列号
- 放量十字星——黎明前的曙光还是黑暗前的夕阳
- 计划制定与管理-日事清
- 【业务办理】广州市户口市内迁移流程
- 网络安全 Windows用户密码破解 使用破解MD5值的在线网站和监听工具Cain
- 分享117个HTML个性简实模板,总有一款适合您
- Processing 案例 | 郭锐文先生的 worms
- 不相信九江大桥事故专家鉴定的五大理由
- 简要分析一年期人民币零存整取