您当前的位置: 首页 > 
  • 3浏览

    0关注

    405博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

(01)ORB-SLAM2源码无死角解析-(13)追踪总体框架讲解→Tracking::Track()

江南才尽,年少无知! 发布时间:2022-03-17 14:33:55 ,浏览量:3

讲解关于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/Frame.cc 中的 Frame 有了一定了解。那么我们回到 (01)ORB-SLAM2源码无死角解析-(04)单目追踪_总体框架讲解TrackMonocular→GrabImageMonocular 中讲解的 GrabImageMonocular() 函数。根据输入帧创建 Frame 变量的代码如下(在04章节博客已经提及过,位于 src/Tracking.cc):

/**
 * @brief 
 * 输入左目RGB或RGBA图像,输出世界坐标系到该帧相机坐标系的变换矩阵
 * 
 * @param[in] im 单目图像
 * @param[in] timestamp 时间戳
 * @return cv::Mat 
 * 
 * Step 1 :将彩色图像转为灰度图像
 * Step 2 :构造Frame
 * Step 3 :跟踪
 */
cv::Mat Tracking::GrabImageMonocular(const cv::Mat &im,const double ×tamp)
{
    mImGray = im;

    // Step 1 :将彩色图像转为灰度图像
    //若图片是3、4通道的,还需要转化成灰度图
    if(mImGray.channels()==3)
    {
        if(mbRGB)
            cvtColor(mImGray,mImGray,CV_RGB2GRAY);
        else
            cvtColor(mImGray,mImGray,CV_BGR2GRAY);
    }
    else if(mImGray.channels()==4)
    {
        if(mbRGB)
            cvtColor(mImGray,mImGray,CV_RGBA2GRAY);
        else
            cvtColor(mImGray,mImGray,CV_BGRA2GRAY);
    }

    // Step 2 :构造Frame
    //判断该帧是不是初始化
    if(mState==NOT_INITIALIZED || mState==NO_IMAGES_YET) //没有成功初始化的前一个状态就是NO_IMAGES_YET
        mCurrentFrame = Frame(
            mImGray,
            timestamp,
            mpIniORBextractor,      //初始化ORB特征点提取器会提取2倍的指定特征点数目
            mpORBVocabulary,
            mK,
            mDistCoef,
            mbf,
            mThDepth);
    else
        mCurrentFrame = Frame(
            mImGray,
            timestamp,
            mpORBextractorLeft,     //正常运行的时的ORB特征点提取器,提取指定数目特征点
            mpORBVocabulary,
            mK,
            mDistCoef,
            mbf,
            mThDepth);

    // Step 3 :跟踪
    Track();

    //返回当前帧的位姿
    return mCurrentFrame.mTcw.clone();
}

对于 mCurrentFrame 的由来我们以及有了足够的了解, (01)ORB-SLAM2源码无死角解析-(04)单目追踪_总体框架讲解TrackMonocular→GrabImageMonocular 博客中提到,SLAM2系统中主要存在三个核心线程线程,于 src\System.cc 初始化函数中抗议看到:

	//追踪器,负责追踪的一些相关操作
    mpTracker = new Tracking(this,mpVocabulary,mpFrameDrawer,mpMapDrawer,mpMap,mpKeyFrameDatabase,strSettingsFile,mSensor);
    //局部建图器,负责局部地图的构建			
    mpLocalMapper = new LocalMapping(mpMap,mSensor==MONOCULAR);	
    //闭环器,闭环检测以及闭环操作
    mpLoopCloser = new LoopClosing(mpMap,mpKeyFrameDatabase,mpVocabulary,mSensor!=MONOCULAR);		

我们现在主要讲解的是其中的追踪函数,那么我们在来看看后调用的 Track() 函数,其实现于 src/Tracking.cc 文件中: void Tracking::Track().

核心思想 : \color{red}{核心思想:} 核心思想: 当前帧与上一帧(或者最近关键帧)进行特征点匹配,如果匹配点足够,则能计算出上一帧到当前帧的位姿变换,同时能够获得该关键点对在三维空间的坐标,显然易知对应的地图点。依次循环即可。

但是大家需要注意一个点,只有关键帧产生的新关键点才会进行地图更新,普通帧(双目或者深度相机)生成新的地图点是临时地图点,主要用于优化位姿估算,不会参与到全局地图的更新。

 

二、代码流程

简单看一下即可,目前是比较难理解了,在后面的博客中,会对每个细节进行详细的讲解。

	//如果地图没有初始化则进行初始化
	if(mState==NOT_INITIALIZED){
		StereoInitialization();或者MonocularInitialization();}
	else{//如果已经完成初始化
		if(!mbOnlyTracking)//非仅追踪模式
            if(mState==OK)// 如果跟踪正常
            	//如果有必要则对地图点进行替换
				CheckReplacedInLastFrame();
				//刚完成初始化或者跟丢,逸或者上帧进行了重定位
				if(mVelocity.empty() || mCurrentFrame.mnIdUpdate(this);
		if(bOK)	//	只有在成功追踪时才考虑生成关键帧的问题		
            if(!mLastFrame.mTcw.empty())// Step 5:跟踪成功,更新恒速运动模型
            	mLastFrame.GetRotationInverse().copyTo(LastTwc.rowRange(0,3).colRange(0,3));
                mLastFrame.GetCameraCenter().copyTo(LastTwc.rowRange(0,3).col(3));
            else
                //否则速度为空
                mVelocity = cv::Mat();
            //更新显示中的位姿
            mpMapDrawer->SetCurrentCameraPose(mCurrentFrame.mTcw);
			// Step 8:检测并插入关键帧,对于双目或RGB-D会产生新的地图点
            if(NeedNewKeyFrame())
                CreateNewKeyFrame();
		if(mState==LOST)
			mpSystem->Reset();
	}		

上面大概的代码流程。源码注释在下面粘贴出来。

 

三、源码注释
void Tracking::Track()
{
    // track包含两部分:估计运动、跟踪局部地图
    
    // mState为tracking的状态,包括 SYSTME_NOT_READY, NO_IMAGE_YET, NOT_INITIALIZED, OK, LOST
    // 如果图像复位过、或者第一次运行,则为NO_IMAGE_YET状态
    if(mState==NO_IMAGES_YET)
    {
        mState = NOT_INITIALIZED;
    }

    // mLastProcessedState 存储了Tracking最新的状态,用于FrameDrawer中的绘制
    mLastProcessedState=mState;

    // Get Map Mutex -> Map cannot be changed
    // 地图更新时加锁。保证地图不会发生变化
    // 疑问:这样子会不会影响地图的实时更新?
    // 回答:主要耗时在构造帧中特征点的提取和匹配部分,在那个时候地图是没有被上锁的,有足够的时间更新地图
    unique_lock lock(mpMap->mMutexMapUpdate);

    // Step 1:地图初始化
    if(mState==NOT_INITIALIZED)
    {
        if(mSensor==System::STEREO || mSensor==System::RGBD)
            //双目RGBD相机的初始化共用一个函数
            StereoInitialization();
        else
            //单目初始化
            MonocularInitialization();

        //更新帧绘制器中存储的最新状态
        mpFrameDrawer->Update(this);

        //这个状态量在上面的初始化函数中被更新
        if(mState!=OK)
            return;
    }
    else
    {
        // System is initialized. Track Frame.
        // bOK为临时变量,用于表示每个函数是否执行成功
        bool bOK;

        // Initial camera pose estimation using motion model or relocalization (if tracking is lost)
        // mbOnlyTracking等于false表示正常SLAM模式(定位+地图更新),mbOnlyTracking等于true表示仅定位模式
        // tracking 类构造时默认为false。在viewer中有个开关ActivateLocalizationMode,可以控制是否开启mbOnlyTracking
        if(!mbOnlyTracking)
        {
            // Local Mapping is activated. This is the normal behaviour, unless
            // you explicitly activate the "only tracking" mode.

            // Step 2:跟踪进入正常SLAM模式,有地图更新
            // 是否正常跟踪
            if(mState==OK)
            {
                // Local Mapping might have changed some MapPoints tracked in last frame
                // Step 2.1 检查并更新上一帧被替换的MapPoints
                // 局部建图线程则可能会对原有的地图点进行替换.在这里进行检查
                CheckReplacedInLastFrame();

                // Step 2.2 运动模型是空的或刚完成重定位,跟踪参考关键帧;否则恒速模型跟踪
                // 第一个条件,如果运动模型为空,说明是刚初始化开始,或者已经跟丢了
                // 第二个条件,如果当前帧紧紧地跟着在重定位的帧的后面,我们将重定位帧来恢复位姿
                // mnLastRelocFrameId 上一次重定位的那一帧
                if(mVelocity.empty() || mCurrentFrame.mnIdUpdate(this);

        // If tracking were good, check if we insert a keyframe
        //只有在成功追踪时才考虑生成关键帧的问题
        if(bOK)
        {
            // Update motion model
            // Step 5:跟踪成功,更新恒速运动模型
            if(!mLastFrame.mTcw.empty())
            {
                // 更新恒速运动模型 TrackWithMotionModel 中的mVelocity
                cv::Mat LastTwc = cv::Mat::eye(4,4,CV_32F);
                mLastFrame.GetRotationInverse().copyTo(LastTwc.rowRange(0,3).colRange(0,3));
                mLastFrame.GetCameraCenter().copyTo(LastTwc.rowRange(0,3).col(3));
                // mVelocity = Tcl = Tcw * Twl,表示上一帧到当前帧的变换, 其中 Twl = LastTwc
                mVelocity = mCurrentFrame.mTcw*LastTwc; 
            }
            else
                //否则速度为空
                mVelocity = cv::Mat();

            //更新显示中的位姿
            mpMapDrawer->SetCurrentCameraPose(mCurrentFrame.mTcw);

            // Clean VO matches
            // Step 6:清除观测不到的地图点   
            for(int i=0; iObservations()            
关注
打赏
1573813941
查看更多评论
0.3546s