openvslam map-localization
Published:
OPENVSLAM Map-Localization
这篇blog主要想描述一下特征点法slam中基于prior feature-map进行localization的细节,这里以openvslam为例,和orbslam2中的流程基本一致,只是openvslam中提供了地图的dump, load, localizaton and slam等多种功能
Feature-Map Format
在运行openvslam slam模式的条件下,可以生成特征点地图,这也是后续用来定位的先验地图,这里对其地图内容做简要的描述:
存储的地图中包括了map_db, cam_db等信息,其map_database的数据结构,也就是ORBSLAM2中map的结构如下所示:
- keyframes
- landmarks
- local landmarks
- origin_keyframes (起始关键帧)
存下来的地图文件中包含的信息主要有关键帧信息,路标信息和它们的对应关系,将地图中的上述信息decode出来
- 构建关键帧
首先对关键帧进行解码,将地图中的关键帧信息解析到map_database中的keyframes里面,首先注入当前关键帧的meta data, 包括:
n_keypts keypoint的个数
ts 时间戳
cam 相机名称
depth_thr 深度阈值
然后注入当前关键帧的其他信息:
- pose信息
- 关键点信息(坐标,方向,层级),
- 关键点的描述子
- x_right和depth
通过这些信息来构造关键帧,将其加入map_database中的keyframes中。
- 构建landmark
然后对路标点进行解码,将存储的路标信息解析到map_database中的landmarks里面,地图中的路标信息主要有:
- 1st_keyfrm 第一个看到该路标点的关键帧
- pow 三维位置
- ref keyframe 该路标点的参考关键帧
- n_vis
- n_fnd
- 构建spanning tree
然后根据存储的关键帧的链接关系构建spanning tree, 形成关键帧之间的connection graph, 解码的关键帧信息主要有:
- span_parent 当前关键帧的父亲节点(唯一)
- span_children 当前关键帧的孩子节点(可能有多个)
- loop_edges 当前帧的回环帧 通过上述信息可以建立起当前关键帧的graph node(parent , children, loop edges)
- 建立关键帧和地图点的链接
然后建立解析出的关键帧和地图点互相之间的链接关系,解析的关键帧信息为lm_ids,标记了当前关键帧的特征点所对应的地图点在landmarks中的索引,然后就是两个链接关系:
keyfrm->add_landmark(lm, idx) \\设定关键帧的第idx个特征点所对应的路标
lm->add_observation(keyfrm, idx) \\设定那个路标点观测到了关键帧的第idx个特征点
- update covisibility graph
根据关键帧和地图点的对应关系,通过当前关键帧的地图点的观测信息来更新关键帧的共视关系,形成共视图
- update landmark geometry and desc
根据观测到路标点的多个关键帧更新路标点的法向,深度以及描述子
构建的特征点地图如图所示:
包括了路标点,关键帧, graph
#### Map-Based Relocalization
在有了先验地图的条件下,不需要像slam模式下的单目那样需要初始化,初始的时候跟踪器的状态为lost,然后在第一帧的时候进入重定位模式,利用当前帧和keyframe_database进行重定位,主要步骤包括:
- 根据当前帧的词袋向量计算当前帧的候选关键帧
- 对当前帧和候选关键帧逐一进行bow match
- 根据特征点和对应的路标构建pnp-solver, EPNP-RANSAC计算当前相机的pose的初值
- 根据pnp的inlier correspondence利用pose-optimizer对当前帧的pose进行优化
- 利用优化后的pose进行投影匹配来获得更多的2d-3d的对应关系,重新用pose-optimizer 进行优化
- 如果inlier大于阈值,则重定位成功,否则基于已经找到的路标,在进行一次投影匹配,尝试pose-optimize
可以看到其始终秉承这样一种pose求解的范式:先用代数算法解算一个初始的pose(用了最少的correspondence),然后利用更多的correspondence优化解算pose,再利用更准确的pose通过投影匹配得到更多的correspondence来进行优化求解。Pose的精度from coarse to fine, 所利用的数据量from less to more
如图显示了重定位成功的一帧:
如图显示了每一帧都做重定位的运行结果,基本上都可以进行稳定定位
Map-Based Tracking
重定位成功后即开始了定位跟踪模式,其首先会对前一帧或参考帧进行跟踪,跟踪成功后更新局部地图,利用局部地图进行跟踪,重要代码如下:
//设置系统内的参考关键帧为当前帧的参考关键帧
curr_frm.ref_keyfrm_ = ref_keyfrm_;
auto succeeded = track_current_frame();
if(succeeded) {
update_local_map();
succeeded = optimize_current_frame_with_local_map();
}
if(succeeded) {
update_motion_model();
}
其中,track_current_frame包括四种跟踪模式,分别是跟踪状态下的:
- motion_based_track
- bow_match_based_track
- robust_match_based_track 以及丢失状态下的relocalize(如上已分析过)
正常情况下,在velocity有效的情况下利用motion_track就可以完成跟踪,当(1)失败的时候会利用bow_match的方法完成跟踪,当(2)也失败的时候,利用特征点暴力匹配来完成跟踪。其中motion_track的细节如下:
- 根据运动模型更新当前帧的pose
- 根据pose利用投影匹配对当前帧和上一帧进行匹配,确定当前帧的特征点所对应的地图点,如果匹配数过少,尝试扩大搜索半径进行匹配,如果还是太少则运动跟踪失败
- 根据对应关系利用pose_optimizer进行位姿优化,优化过程中根据路标点的投影误差确定是否是outlier
- discard当前帧的outlier观测
bow_match_track的流程和motion_track基本一致,只是匹配改为和当前帧的关键帧进行bow match,通过匹配确定当前帧的特征点所对应的地图点,然后设定当前帧的初值,即上一帧的初值,利用pose_optimizer进行位姿优化,并在优化后进行discard_outlier.
robust_match_track的流程和其他类型的track基本一致
只用上一帧或参考帧进行跟踪的效果如图所示:
帧间跟踪成功后,进行update_local_map (update local keyframes, update local landmarks),其中更新局部关键帧的细节如下所示:
- 根据当前帧匹配上的路标点及其在其他关键帧上的观测,构建keyfrm_weights
- 将有共视的关键帧加入local_keyfrms,并计算最共视帧
- 扩大局部关键帧的范围,将first-order keyframe的共视帧,spanning_children, spanning_parent也加入局部关键帧
- 将最共视帧设为系统的参考关键帧和当前帧的参考关键帧
更新局部路标点的细节如下所示:
- 遍历局部关键帧
- 将每一关键帧观测到的路标点添加进入局部路标点
最后根据更新后的局部地图,进行optimize_current_frame_with_local_map(),其实现如下:
- 过滤出能被当前帧观测到的局部路标点(通过3d点投影)
- 利用投影匹配获取当前帧的更多的特征点和地图点对应关系
- 基于correspondence进行pose optimize