OKVIS(一)初始化流程及代码结构
OKVIS代码结构:
VIO的初始化是一个比较重要的问题,和纯视觉SLAM初始化只需要三角化出3D地图点的深度不同,还需要完成相机IMU外参、陀螺仪零偏、尺度以及重力的估计。但是,OKVIS的初始化流程似乎非常简单,但是需要对传感器各项参数有较好的先验值,例如需要在配置文件中给出一个比较靠谱的IMU零偏prior:
sigma_bg: 0.01 # gyro bias prior [rad/s]
sigma_ba: 0.1 # accelerometer bias prior [m/s^]
根据提供的okvis_app_synchronous.cpp,系统入口在类ThreadedKFVio(该类继承自VioInterface接口)的构造函数中,在okvis_multisensor_processing目录下找到该类对应的文件,构造函数中调用init(),接着调用startThreads(),开启各线程:
void ThreadedKFVio::startThreads() {
// consumer threads
for (size_t i = ; i < numCameras_; ++i) {
frameConsumerThreads_.emplace_back(&ThreadedKFVio::frameConsumerLoop, this, i);
}
for (size_t i = ; i < numCameraPairs_; ++i) {
keypointConsumerThreads_.emplace_back(&ThreadedKFVio::matchingLoop, this);
}
imuConsumerThread_ = std::thread(&ThreadedKFVio::imuConsumerLoop, this);
positionConsumerThread_ = std::thread(&ThreadedKFVio::positionConsumerLoop,
this);
gpsConsumerThread_ = std::thread(&ThreadedKFVio::gpsConsumerLoop, this);
magnetometerConsumerThread_ = std::thread(
&ThreadedKFVio::magnetometerConsumerLoop, this);
differentialConsumerThread_ = std::thread(
&ThreadedKFVio::differentialConsumerLoop, this);
// algorithm threads
visualizationThread_ = std::thread(&ThreadedKFVio::visualizationLoop, this);
optimizationThread_ = std::thread(&ThreadedKFVio::optimizationLoop, this);
publisherThread_ = std::thread(&ThreadedKFVio::publisherLoop, this);
}
其中,positionConsumerLoop,gpsConsumerLoop,magnetmeterConsumerLoop,differentialConsumerLoop均未实现(暂不提供GPS,磁力计以及差分气压计支持),也就是开了6个线程,分别执行6个函数:
void ThreadedKFVio::frameConsumerLoop(size_t cameraIndex)
void ThreadedKFVio::matchingLoop()
void ThreadedKFVio::imuConsumerLoop()
// backend algorithms
void ThreadedKFVio::visualizationLoop()
void ThreadedKFVio::optimizationLoop()
void ThreadedKFVio::publisherLoop()
然后,在okvis_app_synchronous.cpp中,将IMU和camera的数据使用addImage()和addImuMeasurement()传入,注意OKVIS中数据流采用了阻塞式(可以通过ThreadKFVio.setBlocking()设定)的线程安全队列。
1. IMU消费者线程:
在imuConsumerLoop()中主要处理imu的propagation
每次imuMeasurementsReceived_队列中出现IMU数据,就会propagate一次,如果刚完成BA优化(需要repropagationNeeded_),则将优化后的状态值作为propagation的初值,否则在上一状态基础上完成状态propagation。
主要对应ImuError::propagation()函数,该函数大概两百行,主要实现OKVIS论文中的 4.2 IMU Kinematics and bias model。
2. Frame消费者线程
2.1 判断该帧是否关键帧(第一帧是关键帧)
2.2 利用IMU预测pose,为特征点匹配提供方向参考
在frameConsumerLoop()中Image和IMU的同步策略是这样的:
若没有IMU数据,则不处理;IMU第一帧数据之前的那一帧Image也抛弃,下一帧Image(第一帧Frame)才进行特征检测处理。同时第一帧之前的IMU数据会用来计算pose(该函数返回值永远是true,因此initPose是否准确完全依赖IMU给出的读数):
bool success = okvis::Estimator::initPoseFromImu(imuData, T_WS);
第一帧之后的IMU数据进行propagation(注意multiframe在单目情形下就是frame),注意到这里propagation的covariance和jacobian均为0,仅仅用于预测,对特征点检测提供先验的T_WC:
okvis::ceres::ImuError::propagation(imuData, parameters_.imu, T_WS, speedAndBiases, lastTimestamp, multiFrame->timestamp());
2.3 Harris角点检测+BRISK描述子计算
接下来对frame特征检测(Harris)和描述子(BRISK)计算(这里的T_WC由前一步的propagation提供,主要为了获取重力方向,提高描述子匹配鲁棒性):
frontend_.detectAndDescribe(frame->sensorId, multiFrame, T_WC, nullptr);
将检测到的keyPoint都push到队列中,提供给matchingLoop()线程使用:
keypointMeasurements_.PushBlockingIfFull(multiFrame, )
3. Matching线程
该线程需要Frame线程提供的keyPointMeasrements_(阻塞队列)。
在matching之前,通过frame和imuData的信息,将当前状态添加到后端估计中去;这里的imuData包含上一帧和当前帧时间戳±20ms范围内的IMU,因此,在frame附近的IMU数据,是会重复使用一次的。OKVIS的算法可以解决该问题(TODO)。
estimator_.addStates(frame, imuData, asKeyframe)
至此,可以获取通过上一帧和IMU数据计算出的系统状态(T_WS和speedAndBias)。
该线程最主要的函数是:
frontend_.dataAssociationAndInitialization(estimator_, T_WS, parameters_, map_, frame, &asKeyframe);
完成
- 特征点匹配;
- 3D点初始化;
- 外点剔除
- RANSAC
- 关键帧选择
在rotationOnly的运动时,使用2D-2D跟踪,使用IMU给出轨迹;有平移运动可以三角化出3D点时,通过3D-2D匹配计算出pose;这里均使用了Opengv中算法
参考:
1. OKVIS代码框架
OKVIS(一)初始化流程及代码结构的更多相关文章
- u-boot中nandflash初始化流程分析(转)
u-boot中nandflash初始化流程分析(转) 原文地址http://zhuairlunjj.blog.163.com/blog/static/80050945201092011249136/ ...
- Z-stack之OSAL初始化流程
转自点击打开链接 我使用的协议栈版本及例子信息: ZigBee2006\Texas Instruments\ZStack-1.4.3-1.2.1\Projects\zstack\Samples\Sam ...
- Raid1源代码分析--初始化流程
初始化流程代码量比较少,也比较简单.主要是run函数.(我阅读的代码的linux内核版本是2.6.32.61) 四.初始化流程分析 run函数顾名思义,很简单这就是在RAID1开始运行时调用,进行一些 ...
- [uboot] (第三章)uboot流程——uboot-spl代码流程
http://blog.csdn.net/ooonebook/article/details/52957395 以下例子都以project X项目tiny210(s5pv210平台,armv7架构)为 ...
- Vue项目搭建流程 以及 目录结构构建
Vue项目搭建流程 以及 目录结构构建 一个小的Vue项目, 基于微信浏览器的移动端, 做了这么多的练习项目, 这一次准备记录下构建的过程, 以方便以后的调高效率 环境准备 操作系统 我的 windo ...
- Vue.js 源码分析(一) 代码结构
关于Vue vue是一个兴起的前端js库,是一个精简的MVVM.MVVM模式是由经典的软件架构MVC衍生来的,当View(视图层)变化时,会自动更新到ViewModel(视图模型),反之亦然,View ...
- linux驱动由浅入深系列:高通sensor架构实例分析之二(驱动代码结构)【转】
本文转载自:https://blog.csdn.net/radianceblau/article/details/73498303 本系列导航: linux驱动由浅入深系列:高通sensor架构实例分 ...
- [uboot] (第三章)uboot流程——uboot-spl代码流程(转)
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/ooonebook/article/det ...
- 【Java】流程控制 - 顺序结构、 选择(分支)结构(单分支、双分支、多分支、嵌套)、循环结构(for、while、do...while)、跳转语句(break、continue)
流程控制语句结构 文章目录 流程控制语句结构 一. 顺序结构 1. 输出语句 2. 输入语句 3.code 二.复合语句 三. 分支结构 1. 条件判断 1.单分支结构 2.双分支结构 3.多分支结构 ...
随机推荐
- Codeforces Round #434 (Div. 2)【A、B、C、D】
Codeforces Round #434 (Div. 2) codeforces 858A. k-rounding[水] 题意:已知n和k,求n的最小倍数x,要求x后缀至少有k个0. 题解:答案就是 ...
- 创建Spring工程的基本步骤
第一步:加入jar包 第二步:创建配置文件 -申明javabean 配置 <?xml version="1.0" encoding="UTF-8"?> ...
- 总结一下MVC思想
MVC (Model View Controler)本来是存在于Desktop程序中的,M是指数据模型,V是指用户界面,C则是控制器.使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用 ...
- Monad新解-FRP对比——ReactiveCocoa、RxSwift、Bacon以及背后的Functional
ReactiveX Rx的Observable的本质就是一个Event Monad,即上下文(就是图文教程中包裹的盒子)为Event的一个Monad,这里的Event定义,可以对应语言的struct或 ...
- 【[USACO12DEC]第一!First!】
一个串不能成为第一的情况有两种 另外一个单词是它的前缀 在分配字母表大小关系的时候出现了矛盾的情况 第一种很好判断,一旦我们在一个单词没有匹配完之前遇到一个结束标志,那么就说明另外一个单词是它的前缀 ...
- mysql中与 in 相反的语句 find_in_set('数据',字段名)
在 mysql 中,我们经常用 in 来查询众多数据中是否有数据表字段中的值: 如果我们在数据表的字段中添加了很多值,然后查询某个值是否是这个字段中众多值的一个时可以用 find_in_set('数据 ...
- UglifyJS 压缩选项
UglifyJS 压缩选项 1.使用逗号运算符连接简单语句 2.使用点符号代替中括号属性 foo [“bar”]→foo.bar 3.删除逻辑上走不到的代码 4.删除调试代码 debug ...
- MySQL常用API函数
-- 返回数字的绝对值 SELECT ABS(-10) -- 返回不大于X的最大整数值 SELECT FLOOR(10.9) -- 返回不小于X的最大整数值 SELECT CEILING(10.4) ...
- POJ 1949 Chores (很难想到的dp)
传送门: http://poj.org/problem?id=1949 Chores Time Limit: 3000MS Memory Limit: 30000K Total Submissio ...
- 上传组件uploadify在spring中返回406 / Not Acceptable 问题解决
这个问题在chrome中正常.在火狐和ie中就会报这个错误. 原因就是chrome的accept是*/* 火狐和ie的accept是text/* 但是spring的accept清单中是没有text/* ...