• 详谈ORB-SLAM2的帧


    ORB-SLAM2中帧分为两个部分:帧和关键帧。相机每次接收到一帧图像(双目或RGB-D接收两张图像)构成一帧,关键帧是来自于普通帧,普通帧只能解决当前帧的相机位姿和跟踪状态,只有被选为关键帧,才能决定回环检测、局部建图等工作。普通帧只能解决当前帧的情况,而关键帧可以对整个系统的后续产生影响,了解了普通帧和关键帧的区别能让我们更好的理解ORB-SLAM2算法的代码。

    普通帧的几乎所有成员属性和变量关键帧都是具有的,但是ORB-SLAM2作者尚待改进的就是普通帧的很多代码,在关键帧部分又重新定义了复写了一套,其实使用继承逻辑上也不合适,但是作者其实完全可以使用组合的方式,在关键帧中加入成员变量是普通帧,也可能作者设计的时候还有其他原因我们不得而知,在ORB-SLAM2中普通帧和关键帧是完全没有任何联系的两个类,但是他们之间有很多成员变量和方法是重合的,关键帧包含了普通帧几乎所有的属性。

    普通帧中包含相机相关信息、特征点的信息、对图像做畸变矫正和特征点分配。普通帧对双目/RGBD特征点的预处理。

    ORB-SLAM2对双目相机,分为左目和右目,每一目中有图像特征点,两组图像特征点进行立体匹配,通过立体匹配和视差公式可以得到每个特征点的深度,那就能得到双目特征点。如果有一些特征点计算的深度过大,带来的结果是误差也特别大,那个点就是不准确的,那么这个点就把它视作单目特征点,就是以左目为基准创建的,而不把它当做双目特征点,判断这个点的深度过大以至于只能视为单目还是这个特征点的深度足够小,判断的标准在于TUM1.yaml中定义的ThDepth

    如果通过双目匹配得到的帧计算后的特征点深度是双目相机基线的40倍或更多,就认为这个深度已经不准确了(误差已经很大了),就把它当做一个单目点来判断,这个点的深度信息是错的,就不考虑这个点的深度信息了,如果通过双目匹配是双目相机基线的40倍以内,认为这个点就在误差在接受范围内,得到深度信息。
    在这里插入图片描述

    notes:双目相机的基线就是双目相机两个摄像头之间的距离

    在这里插入图片描述
    对于RGBD相机来说,左图图像可以取特征点,右图深度,根据视差公式反向推到得到。 生成双目坐标(虚拟的右目),根据相机深度假设存在一个右目的位置,好处就是在程序的其他部分不区分传感器类型是双目还是RGBD了,把他们都当做双目点来看待,之后程序的健壮性就会很好。

    在这里插入图片描述

    一、成员函数和变量

    1、相机相关信息

    一个帧里面有相机的相关信息,而且在SLAM系统中不同帧相机的相关信息是一样的,在做SLAM的过程中相机的基线不会发生漂移,中心点、焦距等不会发生变化,所以相机的内参矩阵都是static变量,对于Frame类里面只有一份,只需要第一次创建Frame对象的时候为这个static对象赋值,其他时候就不需要赋值了。

    Frame类与相机相关的参数大部分设为static类型,整个系统内的所有Frame对象共享同一份相机参数

    成员函数/变量访问控制意义
    mbInitialComputationspublic static是否需要为Frame类的相机参数赋值
    初始化为false,第一次为相机参数赋值后变为false
    float fx, float fy float cx, float cy float invfx, float invfypublic static相机内参矩阵
    设为static是否更好?
    cv::Mat mKpublic相机基线,相机双目间的距离
    float mbfpublic相机基线与焦距的乘积

    跟踪对象首先从配置文件TUM1读入这些参数yaml,然后传递给Frame类的构造函数。在第一次调用Frame构造函数时,将值赋给这些成员变量。

    Tracking::Tracking(const string &strSettingPath, ...) {
    
        // 从配置文件中读取相机参数并构造内参矩阵
        cv::FileStorage fSettings(strSettingPath, cv::FileStorage::READ);
        float fx = fSettings["Camera.fx"];
        float fy = fSettings["Camera.fy"];
        float cx = fSettings["Camera.cx"];
        float cy = fSettings["Camera.cy"];
    
        cv::Mat K = cv::Mat::eye(3, 3, CV_32F);
        K.at<float>(0, 0) = fx;
        K.at<float>(1, 1) = fy;
        K.at<float>(0, 2) = cx;
        K.at<float>(1, 2) = cy;
        K.copyTo(mK);
    
        // ...
    }
    
    // 每传来一帧图像,就调用一次该函数
    cv::Mat Tracking::GrabImageStereo(..., const cv::Mat &imRectLeft, const cv::Mat &imRectRight, const double &timestamp) {
    	mCurrentFrame = Frame(mImGray, mK, mDistCoef, mbf, mThDepth);
    
        Track();
    
        // ...
    }
    
    // Frame构造函数
    Frame::Frame(cv::Mat &K, cv::Mat &distCoef, const float &bf, const float &thDepth)
    	: mK(K.clone()), mDistCoef(distCoef.clone()), mbf(bf), mThDepth(thDepth) {
        
    	// 中间省略...
            
    	// 第一次调用Frame()构造函数时为所有static变量赋值
        if (mbInitialComputations) {
            fx = K.at<float>(0, 0);
            fy = K.at<float>(1, 1);
            cx = K.at<float>(0, 2);
            cy = K.at<float>(1, 2);
            invfx = 1.0f / fx;
            invfy = 1.0f / fy;
            
            // ...
            mbInitialComputations = false;		// 赋值完毕后将mbInitialComputations复位
        }
    
        mb = mbf / fx;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    2、特征点提取

    成员变量mpORBextractorLeft和mpORBextractorRight的()操作符在Frame类构造函数中被调用,用于特征点提取

    (1)其畸变矫正前的左目特征点是mvKeys[i].
    (2)其畸变矫正后的左目特征点是mvKeysUn[i].
    (3)其在右目图片中对应特征点的横坐标为mvuRight[i],纵坐标与mvKeys[i]的纵坐标相同.
    (4)特征点的深度是mvDepth[i].

    此博客参考ncepu_Chen的《5小时让你假装大概看懂ORB-SLAM2源码》

  • 相关阅读:
    c++ goto语句
    DiFi A Go-as-You-Pay Wi-Fi Access System 精读笔记(三)
    Linux Vim批量注释和自定义注释
    【博学谷学习记录】超强总结,用心分享|架构师-sentinel
    JarFile实例多 Finalizer占用内存过大 引起的YGC时间过长 的问题排查和解决办法
    Linux Centos 根目录扩展分区(保级教程)
    SpringCloud - 项目搭建
    当个 PM 式程序员「GitHub 热点速览」
    Codeforces Round 901 (Div. 2)
    【Linux内核系列】进程调度
  • 原文地址:https://blog.csdn.net/Prototype___/article/details/127758382