• ORB-SLAM2从理论到代码实现(十四):KeyFrame类长


    1. 原理分析
      KeyFrame为关键帧,关键帧之所以存在是因为优化需要,所以KeyFrame的几乎所有内容都是位优化服务的。该类中的函数较多,我们需要归类梳理一下,明白其功能原理,才能真正弄懂它的内容。

    图优化需要构建节点和变量,节点很好理解,就是关键帧的位姿,所以需要有读写位姿的功能,边分为两种,第一种边是和MapPoint之间的,所以需要有管理和MapPoint之间关系的函数,第二种边是和其他关键帧之间的,他们之间需要通过MapPoint产生联系,两帧能够共同观测到一定数量的MapPoint时则可以在他俩之间建立边,这种关系叫共视,所以需要有管理共视关系的函数,这种通过共视关系构建的优化模型叫做Covisibility Graph,但是,当需要优化较大范围的数据时,就会需要很大的计算量,因此需要简化,而ORB SLAM2中的Essential Graph就是Covisibility Graph的一种简化版,它通过“生成树(Spanning tree)”来管理各关键帧之间的关系,每个帧都有一个父节点和子节点,节点为其他关键帧,在构建优化模型时,只有具有父子关系的关键帧之间才建立边,换言之,Essential Graph就是Covisibility Graph的子集,这样就大大减少了边的数量,从而起到减小计算量的作用,因此,该类还需要有管理“生成树(Spanning tree)”的函数。

    为了更清晰地了解Covisibility Graph 和 Essential Graph之间的区别,我们可以利用下面几张图再详细解释一下

    图(a)没什么好说的

    图(b)中Covisibility graph是用来描述不同关键帧可以看到多少相同的地图点:每个关键帧是一个节点,如果两个关键帧之间的共视地图点数量大于15,则这两个节点之间建立边,边的权重是共视地图点的数量

    图©中Spanning tree就是生成树,保留了所有的节点(或者说关键帧),但给各个关键帧找了父节点和子节点,每帧只跟各自的父节点和子节点相连,与其他关键帧不连接,此即为spanning tree

    图(d)中即是essential graph,是根据spanning tree建立的图模型,它是是简版的covisibility graph。

    1. 函数功能介绍
      有了以上的分析,我们再回过头来看这个类中的这些函数,分析起来就容易很多了。我们按照上面的分析,对这些函数做一个归类,如下图所示

    下面我们按照分类,讲解其中重要的一些函数

    1. 构造函数 3.1. Frame:有参数构造函数
      构造函数一共有三个参数:

    Frame &F:当前帧

    Map *pMap:地图Map

    KeyFrameDatabase *pKFDB:指针和关键帧数据集的指针

    {
    //将下一帧的帧号赋值给mnId,然后自增1
    mnId = nNextId++;

    //根据栅格的列数重置栅格的size
    mGrid.resize(mnGridCols);
    //将该真的栅格内信息拷贝了一份给关键帧类内的变量
    for (int i = 0; i < mnGridCols; i++)
    {
        mGrid[i].resize(mnGridRows);
        for (int j = 0; j < mnGridRows; j++)
            mGrid[i][j] = F.mGrid[i][j];
    }
    
    //最后将当前帧的姿态赋给该关键帧
    SetPose(F.mTcw);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    }
    3.2. 位姿相关 3.2.1. SetPose:设置位姿
    它只有一个参数Tcw_,这是传入的当前帧的位姿

    void KeyFrame::SetPose(const cv::Mat &Tcw_)
    {
    unique_lock lock(mMutexPose);
    Tcw_.copyTo(Tcw);
    cv::Mat Rcw = Tcw.rowRange(0, 3).colRange(0, 3);
    cv::Mat tcw = Tcw.rowRange(0, 3).col(3);
    cv::Mat Rwc = Rcw.t();
    Ow = -Rwc * tcw;//相机光心

    Twc = cv::Mat::eye(4, 4, Tcw.type());
    Rwc.copyTo(Twc.rowRange(0, 3).colRange(0, 3));
    Ow.copyTo(Twc.rowRange(0, 3).col(3));
    // center为相机坐标系(左目)下,立体相机中心的坐标
    // 立体相机中心点坐标与左目相机坐标之间只是在x轴上相差mHalfBaseline,
    // 因此可以看出,立体相机中两个摄像头的连线为x轴,正方向为左目相机指向右目相机
    cv::Mat center = (cv::Mat_(4, 1) << mHalfBaseline, 0, 0, 1);
    // 世界坐标系下,左目相机中心到立体相机中心的向量,方向由左目相机指向立体相机中心
    Cw = Twc * center;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    }

    4. Covisibility graph相关 4.1. AddConnection:增加连接
    AddConnection(

    KeyFrame *pKF, // 需要关联的关键帧

    const int &weight) // 权重,即该关键帧与pKF共同观测到的3d点数量

    void KeyFrame::AddConnection(KeyFrame *pKF, const int &weight)

  • 相关阅读:
    Go语言学习笔记—xorm
    干了5-6年的测试,没想到最后因为“这个”晋升了,
    8月算法训练------第三天(排序)解题报告
    【人工智能】神经网络优化:复杂度学习率、激活函数、损失函数、缓解过拟合、优化器...
    软件过程与建模学习之:Quality Management
    数据分类分级指南分级方法
    【菜鸡学艺–Vue2–001】模板语法&声明式渲染
    【odoo15】前端自定义模态弹窗
    Android-IO加解密核心与dex文件改造过程分析
    Java 静态代理和动态代理
  • 原文地址:https://blog.csdn.net/zl5186888/article/details/126932532