详谈ORB-SLAM2的关键帧

关键帧是ORB-SLAM2地图里面重要的组成部分之一。关键帧中要重视的两部分:共视图和生成树。共视图和生成树都是来描述关键帧权重关系的,不同之处在于共视图更为稠密,而生成树更为稀疏。

一、共视图(mConnectedKeyFrameWeights)

ORB-SLAM2论文中的描述图,蓝色表示关键帧,黑色和红色部分表示特征点,绿色的实际上是一条条线,每条线表示附近的两个关键帧的共视关系
详谈ORB-SLAM2的关键帧_第1张图片
共视图仅作为参考用,在做优化的时候不会选择共视图。优化共视图是耗时严重的线程,而且优化共视图的时候要给其它变量加锁,其它线程无法工作,所以进一步导致了优化共视图是一个很长、很复杂的过程。
生成树就能很好的简化这个工作

1、成员函数/变量

mConnectedKeyFrameWeights是共视图变量,key是KeyFrame关键帧,value值是int,是当前关键帧的与这个关键帧的权重,无序的

mvpOrderedConnectedKeyFrames、vOrderedWeights变量是所有共视关键帧,实际上就是mConnectedKeyFrameWeights变量的keys和vlaues的两个列表,是有序的

UpdateConnections()函数,是基于当前关键帧对地图点的观测构造共视图,是调用非常多,非常重要的函数

AddConnection(KeyFrame* pKF, int &weight)函数、EraseConnection(KeyFrame* pKF)函数、UpdateBestCovisibles()函数都是内部函数

成员函数/变量 访问控制 意义
std::map mConnectedKeyFrameWeights protected 当前关键帧的共视关键帧及权重
std::vector mvpOrderedConnectedKeyFrames protected 所有共视关键帧,按权重从大到小排序
std::vector mvOrderedWeights protected 所有共视权重,按从大到小排序
void UpdateConnections() public 基于当前关键帧对地图点的观测构造共视图
void AddConnection(KeyFrame* pKF, int &weight) public应为private 添加共视关键帧
void EraseConnection(KeyFrame* pKF) public应为private 删除共视关键帧
void UpdateBestCovisibles() public应为private 基于共视图信息修改对应变量
std::set GetConnectedKeyFrames() public get方法
std::vector GetVectorCovisibleKeyFrames() public get方法
std::vector GetBestCovisibilityKeyFrames(int &N) public get方法
std::vector GetCovisiblesByWeight(int &w) public get方法
int GetWeight(KeyFrame* pKF) public get方法

共视图结构由3个成员变量维护
(1)mConnectedKeyFrameWeights是一个字典,无序地保存当前关键帧的共视关键帧及权重
(2)mvOrderedWeights按权重降序,保存当前关键帧的共视关键帧列表
(3)mvpOrderedConnectedKeyFrame按权重降序,分别保存当前关键帧的权重列表

2、基于对地图点的观测重新构造共视图: UpdateConnections()

共视图构造的关系,这个函数是基于当前所有看到的地图点来构造共视关键帧,首先得到当前地图的所有地图点,然后基于这些地图点找共视关键帧,构建KFCounter,每次找到一个共视关键帧的权重,就是value权重加一,找到之后把所有的keys和values按照降序排列,得到mvpOrderedConnectedKeyFrames和mvOrderedWeights变量,这样就更新了三个变量(mConnectedKeyFrameWeights、mvOrderedWeights和mvpOrderedConnectedKeyFrame),

这个函数中也会更新生成树的信息,第一次加入生成树的关键帧,设一个父关键帧

void KeyFrame::UpdateConnections() {
    
    // 1. 通过遍历当前帧地图点获取其与其它关键帧的共视程度,存入变量KFcounter中
    vector<MapPoint *> vpMP;
    {
        unique_lock<mutex> lockMPs(mMutexFeatures);
        vpMP = mvpMapPoints;
    }
    map<KeyFrame *, int> KFcounter; 
    for (MapPoint *pMP : vpMP) {
        map<KeyFrame *, size_t> observations = pMP->GetObservations();
        for (map<KeyFrame *, size_t>::iterator mit = observations.begin(); mit != observations.end(); mit++) {
            if (mit->first->mnId == mnId)		// 与当前关键帧本身不算共视
                continue;
            KFcounter[mit->first]++;
        }
    }
  
    // step2. 找到与当前关键帧共视程度超过15的关键帧,存入变量vPairs中
    vector<pair<int, KeyFrame *> > vPairs;
    int th = 15;
    int nmax = 0;
    KeyFrame *pKFmax = NULL;   
    for (map<KeyFrame *, int>::iterator mit = KFcounter.begin(), mend = KFcounter.end(); mit != mend; mit++) {
        if (mit->second > nmax) {
            nmax = mit->second;
            pKFmax = mit->first;
        }
        if (mit->second >= th) {
            vPairs.push_back(make_pair(mit->second, mit->first));
            (mit->first)->AddConnection(this, mit->second);				// 对超过阈值的共视边建立连接
        }
    }

    //  step3. 对关键帧按照共视权重降序排序,存入变量mvpOrderedConnectedKeyFrames和mvOrderedWeights中
    sort(vPairs.begin(), vPairs.end());
    list<KeyFrame *> lKFs;
    list<int> lWs;
    for (size_t i = 0; i < vPairs.size(); i++) {
        lKFs.push_front(vPairs[i].second);
        lWs.push_front(vPairs[i].first);
    }
    {
        unique_lock<mutex> lockCon(mMutexConnections);
        mConnectedKeyFrameWeights = KFcounter;
        mvpOrderedConnectedKeyFrames = vector<KeyFrame *>(lKFs.begin(), lKFs.end());
        mvOrderedWeights = vector<int>(lWs.begin(), lWs.end());

        // step4. 对于第一次加入生成树的关键帧,取共视程度最高的关键帧为父关键帧
        if (mbFirstConnection && mnId != 0) {
            mpParent = mvpOrderedConnectedKeyFrames.front();
            mpParent->AddChild(this);
            mbFirstConnection = false;
        }
    }
}

调用的时机是非常重要的,这是一条暗线,这个调用错误的话后面的优化会出现问题,关键帧与地图点间的连接关系发生变化,调用UpdateConnections()函数进行共视图的更新,变化分为两种情况:
(1)关键帧创建
(2)地图点重新匹配关键帧特征点

更新共视图应该为RebulidConnections重新构造共视图,更新共视图的时候直接把原来的三个变量(mConnectedKeyFrameWeights、mvOrderedWeights和mvpOrderedConnectedKeyFrame)清空了,直接根据当前的观测点构造的新的共视图,和原来的共视图无关系,所以这是一个重操作,UpdateConnections()函数在程序里很多次调用,这个可能也是一个优化的地方

你可能感兴趣的:(SLAM,━═━═━◥,MR,◤━═━═━,ORB-SLAM2,keyframe,多线程)