(01)ORB-SLAM2源码无死角解析-(26) 关键帧KeyFrame→如何创建、插入关键帧

本文详细介绍了ORB-SLAM2中创建新关键帧的步骤,包括如何构造关键帧、设置参考关键帧以及对双目或RGBD摄像头生成新的MapPoints。关键步骤包括将当前帧转化为关键帧、设置当前关键帧为参考帧,并为有深度信息的特征点创建地图点。最后,新关键帧被插入到本地建图线程的队列中。
摘要由CSDN通过智能技术生成

讲解关于slam一系列文章汇总链接:史上最全slam从零开始,针对于本栏目讲解的(01)ORB-SLAM2源码无死角解析链接如下(本文内容来自计算机视觉life ORB-SLAM2 课程课件):
(01)ORB-SLAM2源码无死角解析-(00)目录_最新无死角讲解:https://blog.csdn.net/weixin_43013761/article/details/123092196
 
文末正下方中心提供了本人 联系方式, 点击本人照片即可显示 W X → 官方认证 {\color{blue}{文末正下方中心}提供了本人 \color{red} 联系方式,\color{blue}点击本人照片即可显示WX→官方认证} 文末正下方中心提供了本人联系方式,点击本人照片即可显示WX官方认证
 

一、前言

通过上一篇博客,已经知道什么时候需要关键帧,那么接下来就是创建关键帧。其核心函数位于 src/Tracking.cc 的 Tracking::CreateNewKeyFrame() 函数中,分析如下。

 

二、步骤

该函数主要的步骤如下:

1:将当前帧构造成关键帧
2:将当前关键帧设置为当前帧的参考关键帧
3:对于双目或rgbd摄像头,为当前帧生成新的MapPoints

 

三、源码注释

后面有细节分析,可以往下查看

/**
 * @brief 创建新的关键帧
 * 对于非单目的情况,同时创建新的MapPoints
 * 
 * Step 1:将当前帧构造成关键帧
 * Step 2:将当前关键帧设置为当前帧的参考关键帧
 * Step 3:对于双目或rgbd摄像头,为当前帧生成新的MapPoints
 */
void Tracking::CreateNewKeyFrame()
{
    // 如果局部建图线程关闭了,就无法插入关键帧
    if(!mpLocalMapper->SetNotStop(true))
        return;

    // Step 1:将当前帧构造成关键帧
    KeyFrame* pKF = new KeyFrame(mCurrentFrame,mpMap,mpKeyFrameDB);

    // Step 2:将当前关键帧设置为当前帧的参考关键帧
    // 在UpdateLocalKeyFrames函数中会将与当前关键帧共视程度最高的关键帧设定为当前帧的参考关键帧
    mpReferenceKF = pKF;
    mCurrentFrame.mpReferenceKF = pKF;

    // 这段代码和 Tracking::UpdateLastFrame 中的那一部分代码功能相同
    // Step 3:对于双目或rgbd摄像头,为当前帧生成新的地图点;单目无操作
    if(mSensor!=System::MONOCULAR)
    {
        // 根据Tcw计算mRcw、mtcw和mRwc、mOw
        mCurrentFrame.UpdatePoseMatrices();

        // We sort points by the measured depth by the stereo/RGBD sensor.
        // We create all those MapPoints whose depth < mThDepth.
        // If there are less than 100 close points we create the 100 closest.
        // Step 3.1:得到当前帧有深度值的特征点(不一定是地图点)
        vector<pair<float,int> > vDepthIdx;
        vDepthIdx.reserve(mCurrentFrame.N);
        for(int i=0; i<mCurrentFrame.N; i++)
        {
            float z = mCurrentFrame.mvDepth[i];
            if(z>0)
            {
                // 第一个元素是深度,第二个元素是对应的特征点的id
                vDepthIdx.push_back(make_pair(z,i));
            }
        }

        if(!vDepthIdx.empty())
        {
            // Step 3.2:按照深度从小到大排序
            sort(vDepthIdx.begin(),vDepthIdx.end());

            // Step 3.3:从中找出不是地图点的生成临时地图点 
            // 处理的近点的个数
            int nPoints = 0;
            for(size_t j=0; j<vDepthIdx.size();j++)
            {
                int i = vDepthIdx[j].second;

                bool bCreateNew = false;

                // 如果这个点对应在上一帧中的地图点没有,或者创建后就没有被观测到,那么就生成一个临时的地图点
                MapPoint* pMP = mCurrentFrame.mvpMapPoints[i];
                if(!pMP)
                    bCreateNew = true;
                else if(pMP->Observations()<1)
                {
                    bCreateNew = true;
                    mCurrentFrame.mvpMapPoints[i] = static_cast<MapPoint*>(NULL);
                }

                // 如果需要就新建地图点,这里的地图点不是临时的,是全局地图中新建地图点,用于跟踪
                if(bCreateNew)
                {
                    cv::Mat x3D = mCurrentFrame.UnprojectStereo(i);
                    MapPoint* pNewMP = new MapPoint(x3D,pKF,mpMap);
                    // 这些添加属性的操作是每次创建MapPoint后都要做的
                    pNewMP->AddObservation(pKF,i);
                    pKF->AddMapPoint(pNewMP,i);
                    pNewMP->ComputeDistinctiveDescriptors();
                    pNewMP->UpdateNormalAndDepth();
                    mpMap->AddMapPoint(pNewMP);

                    mCurrentFrame.mvpMapPoints[i]=pNewMP;
                    nPoints++;
                }
                else
                {
                    // 因为从近到远排序,记录其中不需要创建地图点的个数
                    nPoints++;
                }

                // Step 3.4:停止新建地图点必须同时满足以下条件:
                // 1、当前的点的深度已经超过了设定的深度阈值(35倍基线)
                // 2、nPoints已经超过100个点,说明距离比较远了,可能不准确,停掉退出
                if(vDepthIdx[j].first>mThDepth && nPoints>100)
                    break;
            }
        }
    }

    // Step 4:插入关键帧
    // 关键帧插入到列表 mlNewKeyFrames中,等待local mapping线程临幸
    mpLocalMapper->InsertKeyFrame(pKF);

    // 插入好了,允许局部建图停止
    mpLocalMapper->SetNotStop(false);

    // 当前帧成为新的关键帧,更新
    mnLastKeyFrameId = mCurrentFrame.mnId;
    mpLastKeyFrame = pKF;
}

 

三、细节分析

( 1 ) : \color{blue}{(1)}: (1): KeyFrame pKF* = new KeyFrame(mCurrentFrame,mpMap,mpKeyFrameDB)
        其首先是根据当前帧,全局地图,以及关键帧数据库创建一个关键帧

( 2 ) : \color{blue}{(2)}: (2): mCurrentFrame.mpReferenceKF = pKF;
        把创建出来的关键帧赋值为追踪器的参考关键帧,同时赋值给当前帧的参考关键帧(其会在UpdateLocalKeyFrames函数中被更新,替换城与该关键帧共视程度最高的关键帧)。

( 3 ) : \color{blue}{(3)}: (3): mCurrentFrame.UpdatePoseMatrices();
        mTcw→相机姿态 世界坐标系到相机坐标坐标系的变换矩阵,是我们常规理解中的相机位姿
        根据mTcw可以计算出mOw(当前相机光心在世界坐标系下坐标),mRcw(世界坐标系到相机坐标系的旋转矩阵),mtcw(世界坐标系到相机坐标系的平移向量),mRwc(相机坐标系到世界坐标系的旋转矩阵)。

( 4 ) : \color{blue}{(4)}: (4): cv::Mat x3D = mCurrentFrame.UnprojectStereo(i);
当某个特征点的深度信息或者双目信息有效时,将它反投影到三维世界坐标系中。计算出特征点在世界坐标系下的3D坐标。

剩下一些之前讲解过的函数,这里就不再重复了

 

四、总结

通过上面的分析,可以知道创建关键帧的时候,如果是双目或者深度相机,会为其为当前帧生成新的地图点,单目无操作。创建关键帧之后,会把关键帧插入到列表 mlNewKeyFrames中,等待local mapping线程临幸。

 
 
本文内容来自计算机视觉life ORB-SLAM2 课程课件

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值