OKVIS代码结构:

okvis_apps: your own app
okvis_ceres: backend main code, estimator, error term; 
okvis_common: interface need to be derived, parameterReader;
okvis_cv: frame and camera;
okvis_frontend: visual detection, matching, triangulation and initialization?;
okvis_kinematics: transformation, quaternion algebra;
okvis_matcher: keypoint matcher, which needs several threads;
okvis_multisensor_processing: synchronizer, thread safe queue for sensor data flow; also start multiple threads, and initialization!
okvis_time, okvis_timing: timing;
okvis_util: etc.

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(一)初始化流程及代码结构的更多相关文章

  1. u-boot中nandflash初始化流程分析(转)

    u-boot中nandflash初始化流程分析(转) 原文地址http://zhuairlunjj.blog.163.com/blog/static/80050945201092011249136/ ...

  2. Z-stack之OSAL初始化流程

    转自点击打开链接 我使用的协议栈版本及例子信息: ZigBee2006\Texas Instruments\ZStack-1.4.3-1.2.1\Projects\zstack\Samples\Sam ...

  3. Raid1源代码分析--初始化流程

    初始化流程代码量比较少,也比较简单.主要是run函数.(我阅读的代码的linux内核版本是2.6.32.61) 四.初始化流程分析 run函数顾名思义,很简单这就是在RAID1开始运行时调用,进行一些 ...

  4. [uboot] (第三章)uboot流程——uboot-spl代码流程

    http://blog.csdn.net/ooonebook/article/details/52957395 以下例子都以project X项目tiny210(s5pv210平台,armv7架构)为 ...

  5. Vue项目搭建流程 以及 目录结构构建

    Vue项目搭建流程 以及 目录结构构建 一个小的Vue项目, 基于微信浏览器的移动端, 做了这么多的练习项目, 这一次准备记录下构建的过程, 以方便以后的调高效率 环境准备 操作系统 我的 windo ...

  6. Vue.js 源码分析(一) 代码结构

    关于Vue vue是一个兴起的前端js库,是一个精简的MVVM.MVVM模式是由经典的软件架构MVC衍生来的,当View(视图层)变化时,会自动更新到ViewModel(视图模型),反之亦然,View ...

  7. linux驱动由浅入深系列:高通sensor架构实例分析之二(驱动代码结构)【转】

    本文转载自:https://blog.csdn.net/radianceblau/article/details/73498303 本系列导航: linux驱动由浅入深系列:高通sensor架构实例分 ...

  8. [uboot] (第三章)uboot流程——uboot-spl代码流程(转)

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/ooonebook/article/det ...

  9. 【Java】流程控制 - 顺序结构、 选择(分支)结构(单分支、双分支、多分支、嵌套)、循环结构(for、while、do...while)、跳转语句(break、continue)

    流程控制语句结构 文章目录 流程控制语句结构 一. 顺序结构 1. 输出语句 2. 输入语句 3.code 二.复合语句 三. 分支结构 1. 条件判断 1.单分支结构 2.双分支结构 3.多分支结构 ...

随机推荐

  1. webConfig详细跳转配置.[转]

    站更换域名,把旧域名用301指到新域名来. 从iis中设置url永久转向就可以,看上去很容易,用了一会儿才发现,参数都没有带上. 从微软网站上找到如下说明,果然好使: 重定向参考 (IIS 6.0,7 ...

  2. Spring Framework5.0 学习(4)—— Bean的命名id和name区别

    Spring中Bean的命名 1.每个Bean可以有一个id属性,并可以根据该id在IoC容器中查找该Bean,该id属性值必须在IoC容器中唯一: 2.可以不指定id属性,只指定全限定类名,如: & ...

  3. BZOJ1002:[FJOI2007]轮状病毒(找规律,递推)

    Description 轮状病毒有很多变种,所有轮状病毒的变种都是从一个轮状基产生的.一个N轮状基由圆环上N个不同的基原子 和圆心处一个核原子构成的,2个原子之间的边表示这2个原子之间的信息通道.如下 ...

  4. zabbix-agent安装

    1.下载yum源库 rpm -i http://repo.zabbix.com/zabbix/3.4/rhel/7/x86_64/zabbix-release-3.4-2.el7.noarch.rpm ...

  5. 配置git服务

    https://www.cnblogs.com/sumuncle/p/6362697.html

  6. HDU 2030 汉字统计(汉字Asics码为负,占两个char)

    传送门: http://acm.hdu.edu.cn/showproblem.php?pid=2030 汉字统计 Time Limit: 2000/1000 MS (Java/Others)    M ...

  7. Office365学习笔记—创建WikiPage

    1,项目有个需求:项目表每更新一次,就把跟该项目有关的任务创建一个静态页(历史版本功能)! 注意事项:需要在页面上拖一个ContentEditer!将代码放在ContentEditer里面,因为我试过 ...

  8. 闭包和let块级作用域

    还是先从一个题目开始: 写一个隔1s输出数组的一项的函数. 如果可以用ES6语法,则可以这么写: function print (arr) { for (let i = 0; i < arr.l ...

  9. 关于SQL优化这些你了解吗?

    目录树 背景 优化点 前提必备知识 优化之一 - 从数据库设计方面考虑 优化之二 - 从SQL语句优化方面考虑 优化之三 - 读写分离与分库分表 背景 在当今这个互联网的时代无非要解决两大难题,其一是 ...

  10. Unity 游戏框架搭建 (二十三) 重构小工具 Platform

    在日常开发中,我们经常遇到或者写出这样的代码 var sTrAngeNamingVariable = "a variable"; #if UNITY_IOS || UNITY_AN ...