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. Junit4所需jar包

    在eclipse中新建一个Junit类,运行时出现java.lang.NoClassdeffounderror:org/apache/commons/logging/LogFactory错误,原来是缺 ...

  2. eclipse run on server 时 报的错误APPARENT DEADLOCK!!! Creating emergency threads for unassigned pending tasks!

    写这篇日记记录一下自己的愚蠢行为. 具体报错如下: 信息: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ ...

  3. vim命令set nu

    set nu:为vim设置行号 set nonu:取消vim中的行号

  4. Windows平台JxCore打包

    1.下载JxCore https://raw.githubusercontent.com/jxcore/jxcore-release/master/0311/jx_win64v8.zip 2.解压并配 ...

  5. 【洛谷P1982】小朋友的数字

    小朋友的数字 题目链接 题目翻译: 每个小朋友有一个数字,构成一个数字序列a1,a2…an 我们定义“特征值”fi为a1~ai中的最大连续子段和 再定义“分数”si为1~i-1中最大的(sj+fj), ...

  6. 最长公共子序列问题LCS

    最长公共子序列问题 在这里介绍一种在动态规划中类似于板子题的类型 : 最长公共子序列问题.(Link) 首先来看题面:给出1-n的两个排列P1和P2,求它们的最长公共子序列. 我们看到题之后的第一个想 ...

  7. SPOJ SUBXOR

    SPOJ SUBXOR 题意 给定一个由正整数构成的数组, 求 异或和小于k 的子序列的个数. 题解 假设答案区间为 [L, R], XOR[L, R] 等价于 XOR[1, L - 1] ^ XOR ...

  8. Struts2知识点小结汇总

    Struts2部分 1. JavaEE软件三层结构和MVC的区别? JavaEE软件三层机构是由sun公司提供JavaEE开发规范的:Web层(表现层).业务逻辑层.数据持久层.[其中WEB层会使用前 ...

  9. ELK6.7.0 Windows 环境本地安装

    安装环境环境准备 第一次写博文,排版比较乱,理解万岁 ELK 6.7.0三件套 下载地址:https://www.elastic.co/cn/downloads/ windows环境下默认已配置jdk ...

  10. MySQL Group Replication 搭建[Multi-Primary Mode]

    1. 环境准备 CentOS7.3 percona-server-5.7.18-14 两台服务器ip地址和主机名 10.0.68.206 yhjr-osd-mysql01-uat 10.0.68.20 ...