DynaSLAM代码详解(5) — Tracking.cc跟踪线程

news2024/11/16 13:28:28

目录

5.1 DynaSLAM中Tracking线程简介

5 .2 RGBD模式下跟踪流程

5.3 DynaSLAM的低成本跟踪

(1) Tracking::LightTrack() 低成本跟踪函数

(2) Tracking::LightTrackWithMotionModel() 低成本的恒速模型跟踪流程

5.4 DynaSLAM的正常跟踪


文章着重将与ORB-SLAM2不同的地方,关于Tracking中具体跟踪过程参考ORB-SLAM2方面的博文

5.1 DynaSLAM中Tracking线程简介

        DynaSLAM的跟踪线程与ORB-SLAM2的跟踪线程类似,负责估计运动信息、跟踪局部地图。
总体来说,ORB-SLAM2跟踪部分主要包括两个阶段,第一个阶段包括三种跟踪方法:用参考关键帧来跟踪、恒速模型跟踪、重定位跟踪,它们的目的是保证能够“跟的上”,但估计出来的位姿可能没那么准确。第二个阶段是局部地图跟踪,将当前帧的局部关键帧对应的局部地图点投影到该帧,得到更多的特征点匹配关系,对第一阶段的位姿再次优化得到相对准确的位姿。

与ORB-SLAM不同:DynaSLAM会有一个Low-Cost Tracking。

为什么会有低成本的跟踪模式:在RGB-D情况下,在获得了动态物体分割的结果后,有必要了解相机位姿,为此,我们实现了一个低成本的跟踪模块,用于在已创建的场景地图中定位相机。

5 .2 RGBD模式下跟踪流程

跟踪线程的流程以RGBD模式为例,输入RGB或RGBA图像、深度图、动态物体分割Mask;首先将图像转为mImGray和imDepth;然后获取深度相机的真实Depth;初始化mCurrentFrame并进行低成本tracking过程;初始化mCurrentFrame进行正常tracking过程;最后输出世界坐标系到该帧相机坐标系的变换矩阵。

与ORB-SLAM不同:DynaSLAM会有一个低成本的跟踪LightTrack()

// 输入RGB或RGBA图像、深度图、动态物体分割Mask
// 1、将图像转为mImGray和imDepth
// 2、获取深度相机的真实Depth
// 3、初始化mCurrentFrame后进行低成本tracking过程
// 4、初始化mCurrentFrame后进行正常tracking过程
// 输出世界坐标系到该帧相机坐标系的变换矩阵
cv::Mat Tracking::GrabImageRGBD(const cv::Mat &imRGB,const cv::Mat &imD, cv::Mat &mask,
                                const double &timestamp, cv::Mat &imRGBOut,
                                cv::Mat &imDOut, cv::Mat &maskOut)
{
    mImGray = imRGB;
    cv::Mat imDepth = imD;
    cv::Mat imMask = mask;
    cv::Mat _imRGB = imRGB;

    // step1:将RGB或RGBA图像转为灰度图像
    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);
    }
    // step2:将深度相机的disparity转为Depth , 也就是转换成为真正尺度下的深度
    if((fabs(mDepthMapFactor-1.0f)>1e-5) || imDepth.type()!=CV_32F)
        imDepth.convertTo(imDepth,      //输出图像
                          CV_32F,       //输出图像的数据类型
                          mDepthMapFactor);     //缩放系数

     // step3:构造Frame用于低成本跟踪
    mCurrentFrame = Frame(
        mImGray,            //灰度图像
        imDepth,            //深度图像
        imMask,             //动态物体检测Mask
        _imRGB,             //RGB图像
        timestamp,          //时间戳
        mpORBextractorLeft, //ORB特征提取器
        mpORBVocabulary,    //词典
        mK,                 //相机内参矩阵
        mDistCoef,          //相机的去畸变参数
        mbf,                //相机基线*相机焦距
        mThDepth);          //内外点区分深度阈值

    // 低成本跟踪
    LightTrack();
    //将低成本跟踪的结果赋值给imRGBOut
    imRGBOut = _imRGB;

    //! 如果低成本跟踪求解的位姿不可用,则利用多视图几何的方法
    if (!mCurrentFrame.mTcw.empty())
    {
        mGeometry.GeometricModelCorrection(mCurrentFrame,imDepth,imMask);
    }

    // step4:构造Frame用于正常跟踪
    mCurrentFrame = Frame(mImGray,imDepth,imMask,imRGBOut,timestamp,mpORBextractorLeft,mpORBVocabulary,mK,mDistCoef,mbf,mThDepth);

    // 正常跟踪
    Track();

    if (!mCurrentFrame.mTcw.empty())
    {
        mGeometry.InpaintFrames(mCurrentFrame, mImGray, imDepth, imRGBOut, imMask);
    }

    mGeometry.GeometricModelUpdateDB(mCurrentFrame);

    // step5:输出跟踪结果
    imDOut = imDepth;
    imDepth.convertTo(imDOut,CV_16U,1./mDepthMapFactor);
    maskOut = imMask;

    return mCurrentFrame.mTcw.clone();
}

5.3 DynaSLAM的低成本跟踪

(1) Tracking::LightTrack() 低成本跟踪函数

低成本跟踪的整体流程

与正常跟踪的对比:低成本跟踪相当于是简化版的跟踪流程,没有仅定位模式跟踪和局部地图跟踪的流程。

void Tracking::LightTrack()
{
    // Get Map Mutex -> Map cannot be changed
    unique_lock<mutex> lock(mpMap->mMutexMapUpdate);

    // 是否使用恒速模型跟踪
    bool useMotionModel = true; //set true

    // 如果跟踪线程没有被初始化则退出
    if(mState==NOT_INITIALIZED || mState==NO_IMAGES_YET)
    {
        cout << "Light Tracking not working because Tracking is not initialized..." << endl;
        return;
    }
    // 系统已初始化,开始进行跟踪状态的判断
    else
    {
        // System is initialized. Track Frame.
        bool bOK;
        {
            // Localization Mode:
            // 如果当前状态为LOST,则进行重定位跟踪
            if(mState==LOST)
            {
                bOK = Relocalization(1);
            }
            else
            {
                // 如果上一帧在地图中跟踪到足够多的地图点
                if(!mbVO)
                {
                    // In last frame we tracked enough MapPoints in the map
                    // 且mVelocity不为空且useMotionModel为true,进行低成本的跟踪
                    if(!mVelocity.empty() && useMotionModel)
                    {
                        bool _bOK = false;
                        bOK = LightTrackWithMotionModel(_bOK);// TODO: check out!!!
                    }
                    // 否则进行参考关键帧跟踪
                    else
                    {
                        bOK = TrackReferenceKeyFrame();
                    }
                }
                // mbVO为true,表明此帧匹配了很少的地图点,既做跟踪又做重定位
                else
                {
                    // In last frame we tracked mainly "visual odometry" points.

                    // We compute two camera poses, one from motion model and one doing relocalization.
                    // If relocalization is sucessfull we choose that solution, otherwise we retain
                    // the "visual odometry" solution.

                    // 通过恒速模型进行跟踪的结果
                    bool bOKMM = false;
                    // 通过重定位方法来跟踪的结果
                    bool bOKReloc = false;
                    // 恒速模型中构造的地图点
                    vector<MapPoint*> vpMPsMM;
                    // 在追踪运动模型后发现的外点
                    vector<bool> vbOutMM;
                    // 运动模型得到的位姿
                    cv::Mat TcwMM;
                    // 低成本的跟踪的结果
                    bool lightTracking = false;
                    bool bVO = false;

                    // 且mVelocity不为空且useMotionModel为true,进行低成本的跟踪
                    if(!mVelocity.empty() && useMotionModel)
                    {
                        lightTracking = true;
                        //进行低成本的恒速跟踪
                        bOKMM = LightTrackWithMotionModel(bVO); // TODO: check out!!
                        // 将恒速模型跟踪结果暂存到这几个变量中,因为后面重定位会改变这些变量
                        vpMPsMM = mCurrentFrame.mvpMapPoints;
                        vbOutMM = mCurrentFrame.mvbOutlier;
                        TcwMM = mCurrentFrame.mTcw.clone();
                    }
                    // 使用重定位的方法来得到当前帧的位姿
                    bOKReloc = Relocalization(1);
                    //根据前面的恒速模型、重定位结果来更新状态
                    if(bOKMM && !bOKReloc)
                    {
                         恒速模型成功、重定位失败,重新使用之前暂存的恒速模型结果
                        mCurrentFrame.SetPose(TcwMM);
                        mCurrentFrame.mvpMapPoints = vpMPsMM;
                        mCurrentFrame.mvbOutlier = vbOutMM;

                        if((lightTracking && bVO) || (!lightTracking && mbVO))
                        {
                            // 更新当前帧的地图点被观测次数
                            for(int i =0; i<mCurrentFrame.N; i++)
                            {
                                //如果这个特征点形成了地图点,并且也不是外点的时候
                                if(mCurrentFrame.mvpMapPoints[i] && !mCurrentFrame.mvbOutlier[i])
                                {
                                    //增加能观测到该地图点的帧数
                                    mCurrentFrame.mvpMapPoints[i]->IncreaseFound();
                                }
                            }
                        }
                    }
                    //有一个成功我们就认为执行成功了
                    bOK = bOKReloc || bOKMM;
                }
            }
        }

        // 将当前帧的参考关键帧指针设置为mpReferenceKF
        mCurrentFrame.mpReferenceKF = mpReferenceKF;

        if(!bOK)
        {
            //地图中的关键帧数量小于等于5,则低成本跟踪失败
            if(mpMap->KeyFramesInMap()<=5)
            {
                cout << "Light Tracking not working..." << endl;
                return;
            }
        }

        // 如果当前帧的参考关键帧指不存在,重新设置
            mCurrentFrame.mpReferenceKF = mpReferenceKF;

    }
}

(2) Tracking::LightTrackWithMotionModel() 低成本的恒速模型跟踪流程

Tracking::LightTrackWithMotionModel()函数的作用是用上一帧地图点来对当前帧进行跟踪。

  1. 更新上一帧的位姿,目的是为了得到更好的地图点位姿;对于双目或RGB-D相机,还会根据深度值生成临时地图点。
  2. 根据之前估计的速度,用恒速模型得到当前帧的初始位姿,恒速模型利用上一帧的位姿作为当前帧的位姿的初始值。
  3. 用上一帧地图点进行投影匹配,如果匹配点不够,则扩大搜索半径再来一次
  4. 利用3D-2D投影关系,优化当前帧位姿 ,上一帧的位姿只是初始值,优化后更加精确
  5. 剔除地图点中外点
  6. 最后判断,如果匹配数大于20,认为跟踪成功,返回true;在正常跟踪下匹配数设置为10。

与正常恒速模型跟踪的对比:低成本的恒速模型跟踪和正常恒速模型跟踪基本相同,代码存在冗余;低成本的恒速模型跟踪比正常恒速模型跟踪会多一个仅定位模式的判断。

/**
 * @brief 根据恒定速度模型用上一帧地图点来对当前帧进行跟踪
 * Step 1:更新上一帧的位姿;对于双目或RGB-D相机,还会根据深度值生成临时地图点
 * Step 2:根据之前估计的速度,用恒速模型得到当前帧的初始位姿
 * Step 3:用上一帧地图点进行投影匹配,如果匹配点不够,则扩大搜索半径再来一次
 * Step 4:利用3D-2D投影关系,优化当前帧位姿 
 * Step 5:剔除地图点中外点
 * @return 如果匹配数大于20,认为跟踪成功,返回true
 */
bool Tracking::LightTrackWithMotionModel(bool &bVO)
{
    // 最小距离 < 0.9 * 次小距离 匹配成功,检查旋转
    ORBmatcher matcher(0.9,true);

    // Update last frame pose according to its reference keyframe
    // Create "visual odometry" points if in Localization Mode
    Frame lastFrameBU = mLastFrame;
    list<MapPoint*> lpTemporalPointsBU = mlpTemporalPoints;

    // Step 1:更新上一帧的位姿;对于双目或RGB-D相机,还会根据深度值生成临时地图点
    UpdateLastFrame(); //TODO: check out!

    // Step 2:根据之前估计的速度,用恒速模型得到当前帧的初始位姿
    mCurrentFrame.SetPose(mVelocity*mLastFrame.mTcw);

    // 清空当前帧的地图点
    fill(mCurrentFrame.mvpMapPoints.begin(),mCurrentFrame.mvpMapPoints.end(),static_cast<MapPoint*>(NULL)); //TODO:Checkout

    // Project points seen in previous frame
    // 设置特征匹配过程中的搜索半径
    int th;
    if(mSensor!=System::STEREO)
        th=15;    //单目
    else
        th=7;     //双目
    // Step 3:用上一帧地图点进行投影匹配,如果匹配点不够,则扩大搜索半径再来一次
    int nmatches = matcher.SearchByProjection(mCurrentFrame,mLastFrame,th,mSensor==System::MONOCULAR);//TODO:Checkout

    // If few matches, uses a wider window search
    // 如果匹配点太少,则扩大搜索半径再来一次
    if(nmatches<20)
    {
        fill(mCurrentFrame.mvpMapPoints.begin(),mCurrentFrame.mvpMapPoints.end(),static_cast<MapPoint*>(NULL));//TODO:Checkout
        nmatches = matcher.SearchByProjection(mCurrentFrame,mLastFrame,2*th,mSensor==System::MONOCULAR);//TODO:Checkout
    }

    // 如果还是不能够获得足够的匹配点,那么就认为跟踪失败
    if(nmatches<20)
        return false;

    // Optimize frame pose with all matches
    // Step 4:利用3D-2D投影关系,优化当前帧位姿
    Optimizer::PoseOptimization(&mCurrentFrame);

    // Discard outliers
    // Step 5:剔除地图点中外点
    int nmatchesMap = 0;
    for(int i =0; i<mCurrentFrame.N; i++)
    {
        if(mCurrentFrame.mvpMapPoints[i])
        {
            if(mCurrentFrame.mvbOutlier[i])
            {
                // 如果优化后判断某个地图点是外点,清除它的所有关系
                MapPoint* pMP = mCurrentFrame.mvpMapPoints[i];

                mCurrentFrame.mvpMapPoints[i]=static_cast<MapPoint*>(NULL);
                mCurrentFrame.mvbOutlier[i]=false;
                pMP->mbTrackInView = false;
                pMP->mnLastFrameSeen = mCurrentFrame.mnId;
                nmatches--;
            }
            else if(mCurrentFrame.mvpMapPoints[i]->Observations()>0)
                // 累加成功匹配到的地图点数目
                nmatchesMap++;
        }
    }
    mLastFrame = lastFrameBU;
    mlpTemporalPoints = lpTemporalPointsBU;

    // 如果成功追踪的地图点非常少, 那么这里的bVO标志就会置位
    bVO = nmatchesMap<10;

    // Step 6:匹配超过20个点就认为跟踪成功
    return nmatches>20;

}

5.4 DynaSLAM的正常跟踪

        DynaSLAM的正常跟踪 Tracking::Track() 与ORB-SLAM2的跟踪函数相同,跟踪部分主要包括两个阶段,第一个阶段包括三种跟踪方法:用参考关键帧来跟踪、恒速模型跟踪、重定位跟踪,它们的目的是保证能够“跟的上”,但估计出来的位姿可能没那么准确。第二个阶段是局部地图跟踪,将当前帧的局部关键帧对应的局部地图点投影到该帧,得到更多的特征点匹配关系,对第一阶段的位姿再次优化得到相对准确的位姿。

        关于参考关键帧来跟踪、恒速模型跟踪、重定位跟踪、局部地图跟踪可以参考其他博主的解析。

/*
 * @brief Main tracking function. It is independent of the input sensor.
 *
 * track包含两部分:估计运动、跟踪局部地图
 * 
 * Step 1:初始化
 * Step 2:跟踪
 * Step 3:记录位姿信息,用于轨迹复现
 */
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<mutex> 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.mnId<mnLastRelocFrameId+2)
                {
                    // 用最近的关键帧来跟踪当前的普通帧
                    // 通过BoW的方式在参考帧中找当前帧特征点的匹配点
                    // 优化每个特征点都对应3D点重投影误差即可得到位姿
                    bOK = TrackReferenceKeyFrame();
                }
                else
                {
                    // 用最近的普通帧来跟踪当前的普通帧
                    // 根据恒速模型设定当前帧的初始位姿
                    // 通过投影的方式在参考帧中找当前帧特征点的匹配点
                    // 优化每个特征点所对应3D点的投影误差即可得到位姿
                    bOK = TrackWithMotionModel();
                    if(!bOK)
                        //根据恒速模型失败了,只能根据参考关键帧来跟踪
                        bOK = TrackReferenceKeyFrame();
                }
            }
            else
            {
                // 如果跟踪状态不成功,那么就只能重定位了
                // BOW搜索,EPnP求解位姿
                bOK = Relocalization();
            }
        }
        else        
        {
            // Localization Mode: Local Mapping is deactivated
            // Step 2:只进行跟踪tracking,局部地图不工作
            if(mState==LOST)
            {
                // Step 2.1 如果跟丢了,只能重定位
                bOK = Relocalization();
            }
            else    
            {
                // mbVO是mbOnlyTracking为true时的才有的一个变量
                // mbVO为false表示此帧匹配了很多的MapPoints,跟踪很正常 (注意有点反直觉)
                // mbVO为true表明此帧匹配了很少的MapPoints,少于10个,要跪的节奏
                if(!mbVO)
                {
                    // Step 2.2 如果跟踪正常,使用恒速模型 或 参考关键帧跟踪
                    // In last frame we tracked enough MapPoints in the map
                    if(!mVelocity.empty())
                    {
                        bOK = TrackWithMotionModel();
                        // ? 为了和前面模式统一,这个地方是不是应该加上
                        // if(!bOK)
                        //    bOK = TrackReferenceKeyFrame();
                    }
                    else
                    {
                        // 如果恒速模型不被满足,那么就只能够通过参考关键帧来定位
                        bOK = TrackReferenceKeyFrame();
                    }
                }
                else
                {
                    // In last frame we tracked mainly "visual odometry" points.
                    // We compute two camera poses, one from motion model and one doing relocalization.
                    // If relocalization is sucessfull we choose that solution, otherwise we retain
                    // the "visual odometry" solution.

                    // mbVO为true,表明此帧匹配了很少(小于10)的地图点,要跪的节奏,既做跟踪又做重定位

                    //MM=Motion Model,通过运动模型进行跟踪的结果
                    bool bOKMM = false;
                    //通过重定位方法来跟踪的结果
                    bool bOKReloc = false;
                    
                    //运动模型中构造的地图点
                    vector<MapPoint*> vpMPsMM;
                    //在追踪运动模型后发现的外点
                    vector<bool> vbOutMM;
                    //运动模型得到的位姿
                    cv::Mat TcwMM;

                    // Step 2.3 当运动模型有效的时候,根据运动模型计算位姿
                    if(!mVelocity.empty())
                    {
                        bOKMM = TrackWithMotionModel();

                        // 将恒速模型跟踪结果暂存到这几个变量中,因为后面重定位会改变这些变量
                        vpMPsMM = mCurrentFrame.mvpMapPoints;
                        vbOutMM = mCurrentFrame.mvbOutlier;
                        TcwMM = mCurrentFrame.mTcw.clone();
                    }

                    // Step 2.4 使用重定位的方法来得到当前帧的位姿
                    bOKReloc = Relocalization();

                    // Step 2.5 根据前面的恒速模型、重定位结果来更新状态
                    if(bOKMM && !bOKReloc)
                    {
                        // 恒速模型成功、重定位失败,重新使用之前暂存的恒速模型结果
                        mCurrentFrame.SetPose(TcwMM);
                        mCurrentFrame.mvpMapPoints = vpMPsMM;
                        mCurrentFrame.mvbOutlier = vbOutMM;

                        //? 疑似bug!这段代码是不是重复增加了观测次数?后面 TrackLocalMap 函数中会有这些操作
                        // 如果当前帧匹配的3D点很少,增加当前可视地图点的被观测次数
                        if(mbVO)
                        {
                            // 更新当前帧的地图点被观测次数
                            for(int i =0; i<mCurrentFrame.N; i++)
                            {
                                //如果这个特征点形成了地图点,并且也不是外点的时候
                                if(mCurrentFrame.mvpMapPoints[i] && !mCurrentFrame.mvbOutlier[i])
                                {
                                    //增加能观测到该地图点的帧数
                                    mCurrentFrame.mvpMapPoints[i]->IncreaseFound();
                                }
                            }
                        }
                    }
                    else if(bOKReloc)
                    {
                        // 只要重定位成功整个跟踪过程正常进行(重定位与跟踪,更相信重定位)
                        mbVO = false;
                    }
                    //有一个成功我们就认为执行成功了
                    bOK = bOKReloc || bOKMM;
                }
            }
        }

        // 将最新的关键帧作为当前帧的参考关键帧
        mCurrentFrame.mpReferenceKF = mpReferenceKF;

        // If we have an initial estimation of the camera pose and matching. Track the local map.
        // Step 3:在跟踪得到当前帧初始姿态后,现在对local map进行跟踪得到更多的匹配,并优化当前位姿
        // 前面只是跟踪一帧得到初始位姿,这里搜索局部关键帧、局部地图点,和当前帧进行投影匹配,得到更多匹配的MapPoints后进行Pose优化
        if(!mbOnlyTracking)
        {
            if(bOK)
                bOK = TrackLocalMap();
        }
        else
        {
            // mbVO true means that there are few matches to MapPoints in the map. We cannot retrieve
            // a local map and therefore we do not perform TrackLocalMap(). Once the system relocalizes
            // the camera we will use the local map again.

            // 重定位成功
            if(bOK && !mbVO)
                bOK = TrackLocalMap();
        }

        //根据上面的操作来判断是否追踪成功
        if(bOK)
            mState = OK;
        else
            mState=LOST;

        // Step 4:更新显示线程中的图像、特征点、地图点等信息
        mpFrameDrawer->Update(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; i<mCurrentFrame.N; i++)
            {
                MapPoint* pMP = mCurrentFrame.mvpMapPoints[i];
                if(pMP)
                    if(pMP->Observations()<1)
                    {
                        mCurrentFrame.mvbOutlier[i] = false;
                        mCurrentFrame.mvpMapPoints[i]=static_cast<MapPoint*>(NULL);
                    }
            }

            // Delete temporal MapPoints
            // Step 7:清除恒速模型跟踪中 UpdateLastFrame中为当前帧临时添加的MapPoints(仅双目和rgbd)
            // 步骤6中只是在当前帧中将这些MapPoints剔除,这里从MapPoints数据库中删除
            // 临时地图点仅仅是为了提高双目或rgbd摄像头的帧间跟踪效果,用完以后就扔了,没有添加到地图中
            for(list<MapPoint*>::iterator lit = mlpTemporalPoints.begin(), lend =  mlpTemporalPoints.end(); lit!=lend; lit++)
            {
                MapPoint* pMP = *lit;
                delete pMP;
            }

            // 这里不仅仅是清除mlpTemporalPoints,通过delete pMP还删除了指针指向的MapPoint
            // 不能够直接执行这个是因为其中存储的都是指针,之前的操作都是为了避免内存泄露
            mlpTemporalPoints.clear();

            // Check if we need to insert a new keyframe
            // Step 8:检测并插入关键帧,对于双目或RGB-D会产生新的地图点
            if(NeedNewKeyFrame())
                CreateNewKeyFrame();

            // We allow points with high innovation (considererd outliers by the Huber Function)
            // pass to the new keyframe, so that bundle adjustment will finally decide
            // if they are outliers or not. We don't want next frame to estimate its position
            // with those points so we discard them in the frame.
            // 作者这里说允许在BA中被Huber核函数判断为外点的传入新的关键帧中,让后续的BA来审判他们是不是真正的外点
            // 但是估计下一帧位姿的时候我们不想用这些外点,所以删掉

            //  Step 9 删除那些在bundle adjustment中检测为outlier的地图点
            for(int i=0; i<mCurrentFrame.N;i++)
            {
                // 这里第一个条件还要执行判断是因为, 前面的操作中可能删除了其中的地图点
                if(mCurrentFrame.mvpMapPoints[i] && mCurrentFrame.mvbOutlier[i])
                    mCurrentFrame.mvpMapPoints[i]=static_cast<MapPoint*>(NULL);
            }
        }

        // Reset if the camera get lost soon after initialization
        // Step 10 如果初始化后不久就跟踪失败,并且relocation也没有搞定,只能重新Reset
        if(mState==LOST)
        {
            //如果地图中的关键帧信息过少的话,直接重新进行初始化了
            if(mpMap->KeyFramesInMap()<=5)
            {
                cout << "Track lost soon after initialisation, reseting..." << endl;
                mpSystem->Reset();
                return;
            }
        }

        //确保已经设置了参考关键帧
        if(!mCurrentFrame.mpReferenceKF)
            mCurrentFrame.mpReferenceKF = mpReferenceKF;

        // 保存上一帧的数据,当前帧变上一帧
        mLastFrame = Frame(mCurrentFrame);
    }

    // Store frame pose information to retrieve the complete camera trajectory afterwards.
    // Step 11:记录位姿信息,用于最后保存所有的轨迹
    if(!mCurrentFrame.mTcw.empty())
    {
        // 计算相对姿态Tcr = Tcw * Twr, Twr = Trw^-1
        cv::Mat Tcr = mCurrentFrame.mTcw*mCurrentFrame.mpReferenceKF->GetPoseInverse();
        //保存各种状态
        mlRelativeFramePoses.push_back(Tcr);
        mlpReferences.push_back(mpReferenceKF);
        mlFrameTimes.push_back(mCurrentFrame.mTimeStamp);
        mlbLost.push_back(mState==LOST);
    }
    else
    {
        // This can happen if tracking is lost
        // 如果跟踪失败,则相对位姿使用上一次值
        mlRelativeFramePoses.push_back(mlRelativeFramePoses.back());
        mlpReferences.push_back(mlpReferences.back());
        mlFrameTimes.push_back(mlFrameTimes.back());
        mlbLost.push_back(mState==LOST);
    }

}// Tracking 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/737950.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

智能工厂:智能制造数字化转型解决方案

数字化已经成为制造业发展的必由之路。要提高生产效率和管理水平&#xff0c;就需要提高对生产运维各环节的数据采集、处理和利用效率。当前工厂的数据采集仍存在诸多不足&#xff0c;可以利用具有多种设备接入能力、通信协议转换能力、数据通信能力、控制维护能力的工业智能网…

UE5《Electric Dreams》项目PCG技术解析 之 PCGCustomNodes详解(一)

《Electric Dreams》项目中提供了一些自定义节点和子图&#xff08;文件位置:“/Content/PCG/Assets/PCGCustomNodes”&#xff09;&#xff0c;这些节点和子图在《Electric Dreams》被广泛使用&#xff0c;对于理解《Electric Dreams》非常重要&#xff0c;而且它们可以直接移…

Qt的对话框与窗口--多文档界面MDI(Multi-document Interface))

多文档界面MDI MDI应用程序就是在主窗口里创建多个同类型的MDI子窗口&#xff0c;这些MDI子窗口在主窗口里显示&#xff0c;并共享主窗口上的工具栏和菜单等操作功能&#xff0c;主窗口上的操作都针对当前活动的MDI子窗口进行。 设计MDI应用程序需要在主窗口工作区放置一个QMdi…

MySQL:我的从库竟是我自己!?

本文将通过复制场景下的异常分析&#xff0c;介绍手工搭建MySQL主从复制时需要注意的关键细节。 作者&#xff1a;秦福朗 爱可生 DBA 团队成员&#xff0c;负责项目日常问题处理及公司平台问题排查。热爱互联网&#xff0c;会摄影、懂厨艺&#xff0c;不会厨艺的 DBA 不是好司机…

多行文本转成一行的实现方法

哈喽大家好&#xff0c;我是咸鱼 不知道你们有没有遇到过下面的情况&#xff0c;以我为例 有时候我会收到批量操作服务器的需求&#xff0c;且我会拿到一个服务器 ip 列表&#xff0c;它是一个多行的形式&#xff0c;如下所示 # ip 列表 192.168.0.1 192.168.0.2 192.168.0.…

原油天然气的区别和用途

原油天然气在市场交易中都是重要的交易产品&#xff0c;经常有小伙伴在后台咨询Forexclub&#xff0c;原油天然气的区别和用途&#xff0c;今天这篇文章就和小伙伴一起交流研究。 其实在Forexclub看来原油和天然气的提取方法、来源和用途几乎相同&#xff0c;只是在适用范围和运…

推荐系统构建

从0到1打造推荐系统工程实战_推荐系统_Jay Wu_InfoQ写作社区

并行程序设计 pthread

配置环境 pthread是c的扩展库&#xff0c;需要配置环境&#xff0c;不过vscode的mingw里面本来就有&#xff0c;谢谢呢^_^ cd “d:\sylvia\文件夹等等等” ; if ($?) { g helloworld.cpp -o helloworld } ; //编译 if ($?) { .\helloworld 线程数} //运行 常用变量声明及函…

三雄极光“设计有光·亚洲设计师迪拜对话”逐光之旅圆满收官

7月8日&#xff0c;三雄极光照明学院“设计有光亚洲设计师迪拜对话”游学旅程圆满收官。过去两周内&#xff0c;此次活动备受关注&#xff0c;设计大咖纷纷为此次迪拜游学一带一路逐光之旅打call。出发前&#xff0c;启动礼于三雄极光总部隆重举行&#xff0c;总裁张宇涛出席并…

F - Desktop Rearrangement

大意: 给你一个桌面状态,每次俩种操作桌面可以表示为一个大小为nm的矩形矩阵&#xff0c;由字符.&#xff08;桌面上的空单元格&#xff09;和*&#xff08;一个图标&#xff09;组成。 操作: 输入<x,y>表示改变其状态的单元格的位置&#xff08;如果该单元格以前包含…

如何在.NET 自动安装包项目(Visual Studio Installer Projects)中设置安装包自动安装 .NET Framework环境

如何在.NET 自动安装包项目(Visual Studio Installer Projects)中设置安装包自动安装 .NET Framework环境 前言 ​ Microsoft Visual Studio Installer Projects是一组用于创建安装程序的工具&#xff0c;它是Microsoft Visual Studio的扩展。这些工具允许开发人员在Visual St…

Java开发基础系列(二):数据类型

&#x1f60a; 作者&#xff1a; 一恍过去 &#x1f496; 主页&#xff1a; https://blog.csdn.net/zhuocailing3390 &#x1f38a; 社区&#xff1a; Java技术栈交流 &#x1f389; 主题&#xff1a; Java开发基础系列(二):数据类型 ⏱️ 创作时间&#xff1a; 2023年07月…

生产消费者模型

生产消费者模型概念 生产消费者模型实际上就是通过一个容器&#xff0c;将生产者和消费者之间的强耦合问题解决掉。 没有使用生产者消费者模型时&#xff0c;生产者和消费者之间直接相互联通&#xff0c;两者之间强耦合&#xff0c;若是一方更换&#xff0c;那另一方也需要随之…

在 Jetpack Compose 中使用 ViewPager

简介 Jetpack Compose 是一个现代化的&#xff0c;声明式的 UI 工具包&#xff0c;让我们可以更方便地构建原生 Android UI。在本篇文章中&#xff0c;我们将会讨论如何在 Jetpack Compose 中使用 ViewPager。 什么是 ViewPager? ViewPager 是一个提供左右滑动切换视图的 U…

DynaSLAM代码详解(2) — Mask RCNN物体检测框架

目录 2.1 前言 2.2 Mask R-CNN优点 2.3 Mask R-CNN框架解析 (1) Mask R-CNN算法步骤 (2) Faster-R-CNN (3) FCN (4) ROIPooling和ROIAlign的分析与比较 (5) Mask R-CNN损失 参考链接&#xff1a; &#xff08;1&#xff09;Mask R-CNN网络详解_fcn太阳花的小绿豆_太…

Java开发专家阿里P6-P7面试题大全及答案汇总(持续更新)二十七、Ribbon和Feign的区别...

一、CPU100%问题如何快速定位 答案 1.执行top -c &#xff0c;显示进程运行信息列表 键入P (大写p)&#xff0c;进程按照CPU使用率排序 2.找到最耗CPU的线程 top -Hp 10765 &#xff0c;显示一个进程的线程运行信息列表 键入P (大写p)&#xff0c;线程按照CPU使用率排序 …

IDEA集成Maven

目录 配置Maven环境 创建Maven项目 Maven坐标 导入Maven项目 Maven依赖管理&#xff08;核心&#xff09; 配置Maven环境 两种方法 每没创建一个maven项目都需要在项目中配置一遍在所有设置中进行全局设置&#xff0c;适用于所有的maven项目 步骤 在idea的初始界面中找到所…

ASEMI整流桥2W10的结构特点和应用领域

编辑-Z 整流桥2W10是一种常用的电子元件&#xff0c;用于将交流电转换为直流电。本文将从工作原理、结构特点、应用领域和发展趋势四个方面对整流桥2W10进行详细阐述。 工作原理 整流桥2W10是由四个二极管组成的桥式整流电路。当输入的交流电信号通过整流桥时&#xff0c;根据…

文心大模型3.5完成内测

据报道&#xff0c;日前&#xff0c;百度文心大模型3.5版本已经完成内测应用&#xff0c;并在三大公开测试集上展现了出色的表现&#xff0c;其综合能力评测得分已经超过ChatGPT&#xff0c;部分中文能力甚至超越了GPT-4。 根据《中国科学报》的报道&#xff0c;3月份&#xf…

2023年全球零信任现状报告发布丨面临集成挑战,如何突破知易行难?

近日&#xff0c;专注网络与安全融合的全球网络安全领导者Fortinet&#xff08;NASDAQ&#xff1a;FTNT&#xff09;宣布发布《2023年全球零信任现状报告》及其调查发现。该报告揭示了零信任安全当前部署和实施现状&#xff0c;以及 IT 团队在应对后疫情时代的混合办公模式的安…