1.SpanningTree存了一些什么,结构是什么样的?
每个KF都有两个指针类的成员
KeyFrame* mpParent; 父亲
std::set<KeyFrame*> mspChildrens; 儿子,可能有多个
这些指针把的KF组织起来成为一个树形结构。
2.SpanningTree是如何形成的?
在kf.cc中有以下函数涉及到修改mpParent、mspChildrens
AddChild、EraseChild、ChangeParent
下面以AddChild为例进行说明
AddChild执行的是mspChildrens.insert(pKF)操作
AddChild 在 UpdateConnections中被调用
UpdateConnections 在 tracking.cc 中的 ProcessNewKeyFrame中被调用
所以就是在处理每个新关键帧时把这个关键帧加入了SpanningTree中
与Covisibigraph不同,他是把所有共视帧按照共视点数量排序,然后把共视点数量最多的那个帧相连
解释里说:个关键帧只和共视地图点最多的关键帧相连形成边,但是childrens是多个的,这是为什么呢?用如下例子解释
kf1的与kf0共视20个,处理kf1时把kf1设置为kf0的儿子
kf2的与kf0共视20个,处理kf2时把kf2设置为kf0的儿子
那么kf0就有了很多儿子
这说明共视地图点最多是从儿子的角度看的(这个爹最亲了)
3.SpanningTree是如何使用的?
Tracking.cc 中 定义了局部地图
//Local Map
KeyFrame* mpReferenceKF;//局部地图中有一个作为参考的关键帧
std::vector<KeyFrame*> mvpLocalKeyFrames;//局部地图中的所有KF
std::vector<MapPoint*> mvpLocalMapPoints;//局部地图中的所有MP
当完成帧间追踪后会对局部地图进行追踪
TrackLocalMap
//局部建图过程被激活
// 步骤2.2:在帧间匹配得到初始的姿态后,现在对local map进行跟踪得到更多的匹配,并优化当前位姿
// local map:当前帧、当前帧的MapPoints、当前关键帧与其它关键帧共视关系
// 在步骤2.1中主要是两两跟踪(恒速模型跟踪上一帧、跟踪参考帧),这里搜索局部关键帧后搜集所有局部MapPoints,
// 然后将局部MapPoints和当前帧进行投影匹配,得到更多匹配的MapPoints后进行Pose优化
1)该过程首先调用UpdateLocalMap对局部地图进行更新
调用UpdateLocalKeyFrames更新局部地图中的关键帧
//step1
//计数器map<KeyFrame*,int> keyframeCounter;存的是每一个关键帧观测到了当前帧中地图点的次数
//遍历当前帧中所有地图点,得到能观测到该地图点的所有KF map<KeyFrame*,size_t> observations
//例如:
//当前帧CF有20个MP,其中MP1、MP2
//遍历每个MP,得到其OB,MP1的OB中有KF1,KF2
//遍历OB中的所有组合,遍历到KF1就把计数器中KF1的次数加1,说明KF1与当前帧有1个共视的MP1,
//如果遍历MP2时也有共视关系,那么计数器中KF1就再加1变为2
//最终的keyframeCounter记录了和当前帧有共视关系的所有KF以及他们的共视数量
//step2
//更新mvpLocalKeyFrames,也就是得到所谓的局部地图,和优化过程中那个局部地图不同
//方法1
//遍历上一步得到的计数器keyframeCounter中的所有KF
//把他们都加入到局部地图mvpLocalKeyFrames中,并把他们的参考帧ID更新成当前帧的ID
//这个过程中找到共视点最多的KF,记录下来备用pKFmax
//经过方法1以后,所有与当前帧有共视关系的KF都加入到了局部地图
//方法2
//如果局部地图中已经有了超过80个关键帧,说明足够了,就不添加了
//如果不够80
//遍历已经在局部地图中的关键帧
//2.1首先把该KF的covisibigraph中前10个共视的KF也加入到局部地图中,同时更新他们的参考关键帧ID
//2.2再找到该KF在SpanningTree中的儿子们(也就是与之共视程度最高的KF)
//2.3在找到该KF在SpanningTree中的爸爸
//step3
//收尾工作
//把之前得到的pKFmax作为当前帧的参考关键帧
4.总结
所以说SpanningTree的作用之一就是在tracking.cc中创建局部地图时用的,在Loopclosing中也有调用,但是没仔细看