在成功初始化之后,会创建地图以及局部地图。

创建地图

在初始化正常过后,紧接着会创建地图

// src/openvslam/module/initializer.cc:67
// create new map, then check the state is succeeded or not
create_map_for_monocular(curr_frm);
创建单目地图
在init_matches_中,将所有匹配点对儿中没有三角化的位置标记为无效
以初始帧为原点,设置当前帧的位姿
创建初始帧和当前帧的关键帧
计算帧描述子对应的bow向量
将关键帧添加入map_db_中
更新帧统计信息
逐个匹配点
创建landmark:lm就是特征点对应的世界座标下的点
将lm和关键帧关联起来
向lm中添加可观测信息:关键帧和对应的特征点id
计算lm的描述子
计算lm的几何信息
将lm加入map_db_
global_bundle_adjuster
计算初始关键帧的中位深度值
更改当前关键帧t的尺度和lm尺度,(将深度中间值设置为1的尺度,1.0 / median_depth)
更新当前帧的位姿
设置地图的起始关键帧

计算lm的描述子

一个lm可能被很多帧看到,每个帧中由于拍摄的时间、空间、光照条件的原因导致相同的特征点的描述子会稍微不同,通过计算找到一个与其他描述子距离相近的描述子作为最终lm的描述子。

计算lm的几何信息

max_valid_dist_ = dist * scale_factor;
min_valid_dist_ = max_valid_dist_ / ref_keyfrm->scale_factors_.at(num_scale_levels - 1); max_valid_dist_: 特征点(世界座标系下)到参考帧相机位置(世界座标系下)的最大有效距离,在计算的时候将特征点所处的图像金字塔层数也考虑进来
min_valid_dist_: 特征点(世界座标系下)到参考帧相机位置(世界座标系下)的最小有效距离

Vec3_t mean_normal = Vec3_t::Zero();
unsigned int num_observations = 0;
for (const auto& observation : observations) {
auto keyfrm = observation.first;
const Vec3_t cam_center = keyfrm->get_cam_center();
const Vec3_t normal = pos_w_ - cam_center;
mean_normal = mean_normal + normal / normal.norm();
++num_observations;
} mean_normal_:

计算初始关键帧的中位深度值

将关键帧中的lm(世界坐标系)转为关键帧坐标系下,对深度排序后获取中间深度值。

更新局部地图及后续

// src/openvslam/tracking_module.cc:183
update_local_map();
更新局部地图
更新局部关键帧(局部关键帧的限制是60个)
统计出当前帧和相邻的关键帧共享的lm数量
通过比较共享lm的数量,将共享lm数量最多的关键帧设置为最近的共视关键帧
将有共视的关键帧都添加到local_keyfrms_
将有共视的关键帧的相邻关键帧中的top10也加入local_keyfrms_
将最近的共视关键帧设置为当前跟踪模块的参考关键帧
更新局部lm:将local_keyfrms_中的lm都添加为local_landmarks_
将所有的关键帧传入mapper_模块
设置跟踪模块状态为Tracking

保存参考关键帧到当前帧的变换矩阵=last_cam_pose_from_ref_keyfrm_

tracking流程

更新lm:全局BA会优化lm的位置,因此将上一帧lm中调整为优化过的值
更新上一帧相机位姿:mapping模块有可能优化该位姿
设置当前帧的参考关键帧:=跟踪模块的参考关键帧
跟踪当前帧(track_current_frame)
如果成功:
更新局部地图
使用局部地图对当前帧进行优化
如果成功:
更新运动学模型update_motion_model
更新帧统计信息
如果跟踪后很快就丢失,则会重新开始跟踪
跟踪成功后,检测是否需要插入关键帧
1.当前的帧id不大于上个关键帧id+max_num_frms_(这里max_num_frms_=fps)
2.当前的帧id不小于上个关键帧id+min_num_frms_(这里max_num_frms_=0)
3.有一定的匹配点但是不能太多,太多说明视觉变化比较小,不需要新关键点;
添加关键帧
单目直接添加
立体和RGBD,按深度降序排列,添加前100深度且大于true_depth_thr_的lm
清理当前帧的lm

跟踪当前帧

\\ src/openvslam/tracking_module.cc:278
bool tracking_module::track_current_frame()

有三种跟踪方法:

motion_based_track:

bow_match_based_track:

robust_match_based_track:

一种重点位方法:

relocalize

// Tracking mode
if (velocity_is_valid_ && last_reloc_frm_id_ + 2 < curr_frm_.id_) {
// if the motion model is valid
succeeded = frame_tracker_.motion_based_track(curr_frm_, last_frm_, velocity_);
}
if (!succeeded) {
succeeded = frame_tracker_.bow_match_based_track(curr_frm_, last_frm_, ref_keyfrm_);
}
if (!succeeded) {
succeeded = frame_tracker_.robust_match_based_track(curr_frm_, last_frm_, ref_keyfrm_);
}

bow_match_based_track

计算当前帧BoW
当前帧与关键帧做匹配:使用bow tree
如果匹配点数大于num_matches_thr_=10,将上一帧的位姿作为当前帧的位姿的初始值,使用pose_optimizer_进行优化。
剔除当前帧中不好的lm(优化中会将一些lm设值为outliers)

robust_match_based_track

计算当前帧和参考关键帧的匹配情况(match_frame_and_keyframe)
使用暴力匹配的方式获取匹配点(brute_force_match)
计算本质矩阵E,获取匹配点(essential_solver)
如果匹配点数大于num_matches_thr_=10,将上一帧的位姿作为当前帧的位姿的初始值,使用pose_optimizer_进行优化。
剔除当前帧中不好的lm(优化中会将一些lm设值为outliers)

在计算本质矩阵E的时候使用的是归一化平面上的点对儿,而不是图像上的像素点对儿。

motion_based_track

基于运动模型跟踪,这里的运动模型就是恒速运动模型。

首先使用恒速运动模型更新当前位姿
将最后一帧中观察到的3D点重新投影到当前帧并在帧中记录相应的lm信息(match_current_and_last_frames)
计算当前帧到上一帧的平移向量trans_lc
非单目可以判断运动方向
将最后一帧的特征点对应的3D点重新投影到当前帧,在重投影位置寻找特征点,进行匹配
pose optimization
剔除当前帧中不好的lm(优化中会将一些lm设值为outliers)

重定位

跟踪失败后会调用该函数进行重定位。

tracking_module初始化中会对module::relocalizer进行初始化

relocalizer( data::bow_database* bow_db,
const double bow_match_lowe_ratio = 0.75, const double proj_match_lowe_ratio = 0.9,
const unsigned int min_num_bow_matches = 20, const unsigned int min_num_valid_obs = 50);

重定位有关的变量

//! initial candidates for loop or relocalization
std::unordered_set<keyframe*> init_candidates_; //! number of shared words between the query and the each of keyframes contained in the database
std::unordered_map<keyframe*, unsigned int> num_common_words_; //! similarity scores between the query and the each of keyframes contained in the database
std::unordered_map<keyframe*, float> scores_; //! pairs of score and keyframe which has the larger score than the minimum one
std::vector<std::pair<float, keyframe*>> score_keyfrm_pairs_; //! pairs of total score and keyframe which has the larger score than the minimum one
std::vector<std::pair<float, keyframe*>> total_score_keyfrm_pairs_;
获取候选关键帧(acquire_relocalization_candidates)
统计地图中所有当前帧有共享的单词的关键帧以及共享单词的数量(set_candidates_sharing_words)
将最大共享单词数量*0.8作为过滤门限
计算大于门限的关键帧与当前帧的相似性得分(DBoW内置计算方法)
设置相似分门限过滤
计算每个候选关键帧(score_keyfrm_pairs)邻域的得分并取总和,返回最高分best_total_score
将大于0.75*best_total_score的关键帧设置为候选关键帧
遍历候选帧
关键帧与当前帧进行特征点匹配(bow_matcher_.match_frame_and_keyframe),>50个才算有效
构建pnp_solvers
使用PnP(+RANSAC)求解位姿
使用pose_optimizer优化
重投影匹配检验proj_matcher_.match_frame_and_keyframe
再次使用pose_optimizer优化
有效特征点>50,即认为重定位成功

使用局部地图对当前帧进行优化

//src/openvslam/tracking_module.cc:209
succeeded = optimize_current_frame_with_local_map();
通过将局部lm重投影到当前帧的方式获取更多的2D-3D点对儿(search_local_landmarks)
当前帧中的lm不需要重投影,标记对应的lm
在局部lm中逐个使用can_observe函数检测可以被观测到的lm
投影匹配当前帧和局部lm
pose_optimizer
计算跟踪到的lm数量,判断是否少于门限
can_observe
获取lm的世界座标值pos_w
判断该lm是否可以重投影到当前帧的图像平面
通过判断有效距离检查是否在orb_scale中
检测角度是否有效0.5度
预测当前lm所对应的图像金字塔层数

更新运动学模型

这里的运动学模型是恒速模型:

更新速度velocity = curr_frm.cam_pose_cw * last_frm_cam_pose_wc

后面用来更新位姿:curr_frm.set_cam_pose(velocity * last_frm.cam_pose_cw) 这时候last_frm.cam_pose_cw就是上一帧的curr_frm.cam_pose_cw

问题

  1. 恒速模型没搞明白;

    \(T_{C_k}^W\)表示k时刻相机坐标系到世界坐标系的变换矩阵,恒速模型是说相机k到k-1时刻的变化等于k-1到k-2时刻的变化,即\(T_{C_k}^{C_{k-1}}=T_{C_{k-1}}^{C_{k-2}}\)。因此

\[T_{C_k}^W = T_{C_{k-1}}^W*T_{C_{k}}^{C_{k-1}} = T_{C_{k-1}}^W*T_{C_{k-1}}^{C_{k-2}}
\]

又\(T_{C_{k-1}}^W=T_{C_{k-2}}^W*T_{C_{k-1}}^{C_{k-2}}\)可得

\[T_{C_{k-1}}^{C_{k-2}}=(T_{C_{k-2}}^W)^{-1}*T_{C_{k-1}}^W
\]

因此

\[T_{C_k}^W = T_{C_{k-1}}^W*(T_{C_{k-2}}^W)^{-1}*T_{C_{k-1}}^W
\]

再取逆,整理得到

\[T_{W_k}^C = T_W^{C_{k-1}}*(T_{C_{k-2}}^W)^{-1}*T_W^{C_{k-1}}
\]

// src/openvslam/tracking_module.cc:310
velocity_ = curr_frm_.cam_pose_cw_ * last_frm_cam_pose_wc; // src/openvslam/module/frame_tracker.cc:22
curr_frm.set_cam_pose(velocity * last_frm.cam_pose_cw_);

对应程序,velocity_就是\(T_W^{C_{k-1}}*(T_{C_{k-2}}^W)^{-1}\)

第四篇 跟踪过程以及openvslam中的相关实现详解的更多相关文章

  1. 第三篇 视觉里程计(VO)的初始化过程以及openvslam中的相关实现详解

    视觉里程计(Visual Odometry, VO),通过使用相机提供的连续帧图像信息(以及局部地图,先不考虑)来估计相邻帧的相机运动,将这些相对运行转换为以第一帧为参考的位姿信息,就得到了相机载体( ...

  2. 第二篇 特征点匹配以及openvslam中的相关实现详解

    配置文件 在进入正题之前先做一些铺垫,在openvslam中,配置文件是必须要正确的以.yaml格式提供,通常需要指明使用的相机模型,ORB特征检测参数,跟踪参数等. #==============# ...

  3. 第一篇 特征提取以及openvslam中的相关实现详解

    参考尺度空间理论 金字塔 当用一个机器视觉系统分析未知场景时,计算机没有办法预先知道图像中物体尺度,因此,我们需要同时考虑图像在多尺度下的描述,获知感兴趣物体的最佳尺度.所以在很多时候,我们会在将图像 ...

  4. 【HANA系列】【第四篇】SAP HANA XS使用服务器JavaScript Libraries详解

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[HANA系列][第四篇]SAP HANA XS ...

  5. Python笔记_第四篇_高阶编程_魔法(术)方法详解(重载的再详解)

    1. 魔法方法是什么? 魔法方法(Magic Method)是Python比较独特的应用,它可以给你的类增加特殊的方法,如果你的对象实现了(重载),这些方法中的某一个,就会被Python所调用.正如装 ...

  6. 【转】PCB中3D相关功能详解

    如果PCB Layout工程师能够在设计过程中,使用设计工具直观地看到自己设计板子的实际情况,将能够有效的帮助他们的工作.尤其现在PCB板的设计越来越复杂,密度越来越高,如果能够洞察多层板内部则可以帮 ...

  7. C#中web.config文件详解

    C#中web.config文件详解 一.认识Web.config文件 Web.config 文件是一个XML文本文件,它用来储存 ASP.NET Web 应用程序的配置信息(如最常用的设置ASP.NE ...

  8. Java I/O : Java中的进制详解

    作者:李强强 上一篇,泥瓦匠基础地讲了下Java I/O : Bit Operation 位运算.这一讲,泥瓦匠带你走进Java中的进制详解. 一.引子 在Java世界里,99%的工作都是处理这高层. ...

  9. linux-2.6.26内核中ARM中断实现详解(转)

    转载:http://www.cnblogs.com/leaven/archive/2010/08/06/1794293.html 更多文档参见:http://pan.baidu.com/s/1dDvJ ...

随机推荐

  1. 同时启动多个tomcat,端口修改

    所用Tomcat服务器都为zip 版,非安装版.以 tomcat8 为例: 安装第二个Tomcat完成后,打开 tomcat/conf/server.xml 文件,查找以下三处: 1. 修改http访 ...

  2. Spring Boot 整合 JPA 使用多个数据源

    介绍 JPA(Java Persistence API)Java 持久化 API,是 Java 持久化的标准规范,Hibernate 是持久化规范的技术实现,而 Spring Data JPA 是在 ...

  3. iview中page组件的跳转功能BUG解决方案

    xl_echo编辑整理,欢迎转载,转载请声明文章来源.欢迎添加echo微信(微信号:t2421499075)交流学习. 百战不败,依不自称常胜,百败不颓,依能奋力前行.--这才是真正的堪称强大!! 在 ...

  4. Go中的interface学习

    学过Java的同学都知道在Java中接口更像是一种规范,用接口定义了一组方法,下面实现这个接口的类只管按照写好的方法名和返回值去实现就好,内部如何实现是各个方法自己的事情,接口本身不关注. 另外Jav ...

  5. 大陆争霸[SDOI2010]带限制最短路

    只要你有无限个自爆机器人,你就能为所欲为 斯普林·布拉泽 [题目描述] 略 一句话题意: 杰森国有 \(N\) 个城市,由 \(M\) 条单向道 路连接.杰森国的首都是城市 \(N\).你只需摧毁杰森 ...

  6. CAD2015 C#二次开发 字体变形

    开发环境:VS2012问题描述:一个简单的WinForm窗口,一个群组控件和一个Label,都是微软雅黑12pxCAD2015下,看起来却不一样,一个明显细得多. CAD2014下,无此问题.实验了C ...

  7. Java - 手动解析不带引号的JSON字符串

    目录 1 需求说明 2 解析代码 2.1 实现思路 2.2 详细代码 2.3 测试样例 1 需求说明 项目中遇到了一批不带引号的类JSON格式的字符串: {Name:Heal,Age:20,Tag:[ ...

  8. app发布当天,用户无法登录

    原因:当用户登录时候有商城用户的触发器存在,它会让商城用户也更新成登录状态. 由于用户量大,导致数据库锁死. 最后解决案:删掉触发器,在app的接口登录程序里,追加商城用户更新成登录的操作. 他案1: ...

  9. Java多线程之线程的状态迁移

    Java多线程之线程的状态迁移 下图整理了线程的状态迁移.图中的线程状态(Thread.Stat 中定义的Enum 名)NEW.RUNNABLE .TERMINATED.WAITING.TIMED_W ...

  10. Leetcode之二分法专题-35. 搜索插入位置(Search Insert Position)

    Leetcode之二分法专题-35. 搜索插入位置(Search Insert Position) 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引.如果目标值不存在于数组中,返回它将会 ...