上一节介绍了激光数据的回调,最终被依次推入到容器之中,存储在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源码解析(三)—— 定时器总揽大局相关推荐

  1. Disruptor源码解析三 RingBuffer解析

    目录 系列索引 前言 主要内容 RingBuffer的要点 源码解析 系列索引 Disruptor源码解析一 Disruptor高性能之道 Disruptor源码解析二 Sequence相关类解析 D ...

  2. OkHttp3源码解析(三)——连接池复用

    OKHttp3源码解析系列 OkHttp3源码解析(一)之请求流程 OkHttp3源码解析(二)--拦截器链和缓存策略 本文基于OkHttp3的3.11.0版本 implementation 'com ...

  3. ReactiveSwift源码解析(三) Signal代码的基本实现

    上篇博客我们详细的聊了ReactiveSwift源码中的Bag容器,详情请参见<ReactiveSwift源码解析之Bag容器>.本篇博客我们就来聊一下信号量,也就是Signal的的几种状 ...

  4. BLAM源码解析(五)—— 回环检测

    上一节介绍了BLAM的帧间匹配和帧图匹配,代码简洁明了. 本节介绍BLAM的回环检测模块.具体代码块如下: // Check for new loop closures.bool new_keyfra ...

  5. 并发编程与源码解析 (三)

    并发编程 (三) 1 Fork/Join分解合并框架 1.1 什么是fork/join ​ Fork/Join框架是JDK1.7提供的一个用于并行执行任务的框架,开发者可以在不去了解如Thread.R ...

  6. 前端入门之(vuex源码解析三)

    上两节前端入门之(vuex源码解析二)我们把vuex的源码大概的撸了一遍,还剩下(插件.getters跟module),我们继续哈~ 插件童鞋们可以去看看vuex在各个浏览器的状态显示插件,小伙伴可以 ...

  7. 拆轮子-RxDownload2源码解析(三)

    本文为博主原创文章,未经允许不得转载 造轮子者:Season_zlc 轮子用法请戳作者链接 ↑ 前言 本文主要讲述 RxDownload2 的多线程断点下载技术. 断点下载技术前提 服务器必须支持按 ...

  8. Tomcat源码解析三:tomcat的启动过程

    Tomcat组件生命周期管理 在Tomcat总体结构 (Tomcat源代码解析之二)中,我们列出了Tomcat中Server,Service,Connector,Engine,Host,Context ...

  9. 【Vue.js源码解析 三】-- 模板编译和组件化

    前言 笔记来源:拉勾教育 大前端高薪训练营 阅读建议:建议通过左侧导航栏进行阅读 模板编译 模板编译的主要目的是将模板 (template) 转换为渲染函数 (render) <div> ...

  10. Cesium源码解析三(metadata元数据拓展中行列号的分块规则解析)

    目录 1.前言 2.layer.json中available参数意义 3.EPSG:4626切片及terrain分块原理 4.Cesium的terrain分块规则 5.自定义terrain分块规则 6 ...

最新文章

  1. html select 多选取值,下拉框多选实现 jquery-multiselect 并获取选中的值
  2. 使用subgit进行svn迁移至git(branch,tags)
  3. 自定义通配器导入bean对象
  4. jstack调试_增压的jstack:如何以100mph的速度调试服务器
  5. linux安装虚拟环境virtualenv
  6. 掌握 Ajax,第 11 部分: 服务器端的 JSON
  7. 20-10-026-安装-KyLin-2.6.0-单机版安装(MAC官网下载)-spark引擎
  8. webmin账户重置密码
  9. python自学入门-初学 Python 者自学 Anaconda 的正确姿势是什么?
  10. redis的基础配置
  11. c语言范式编程之lsearch
  12. 华硕台式计算机编号,怎么查看华硕电脑设备序列号
  13. 放量十字星——黎明前的曙光还是黑暗前的夕阳
  14. 计划制定与管理-日事清
  15. 【业务办理】广州市户口市内迁移流程
  16. 网络安全 Windows用户密码破解 使用破解MD5值的在线网站和监听工具Cain
  17. 分享117个HTML个性简实模板,总有一款适合您
  18. Processing 案例 | 郭锐文先生的 worms
  19. 不相信九江大桥事故专家鉴定的五大理由
  20. 简要分析一年期人民币零存整取

热门文章

  1. Intel公布Penryn四核CPU价格 最低209美元
  2. 卡尔曼滤波的使用举例
  3. 基于以太坊的区块链浏览器搭建
  4. 电力电子技术第二章总结
  5. 关于Keystone 服务运维学习
  6. ggplot2——柱状图
  7. Android—在WebView中下载Blob协议文件
  8. 分组密码体制【密码学笔记】
  9. c2-00支持java_诺基亚双卡双待C2-00亮相
  10. 一张图理解EOS是什么