ORB_SLAM2 源码解析 ORB_SLAM2简介

Posted 小负不负

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ORB_SLAM2 源码解析 ORB_SLAM2简介相关的知识,希望对你有一定的参考价值。

目录

一、ORB_SLAM2的特点

二、算法流程框架

ORB-SLAM整体流程如下图所示

a、跟踪(Tracking)  

b、建图(LocalMapping) 

c、闭环检测(LoopClosing)

三、安装教程

1、克隆仓库

2、编译ORB-SLAM2,第三方库中的DBoW2和g2o,并解压ORB词典

四、TUM数据集

1、orb-slam2在TUM数据集上面的表现

2、不同颜色地图点的含义

 五、变量名命名规则

六、多线程

1、使用多线程是为了加快运算速度

2、三个线程是怎样运转的

3、加锁


一、ORB_SLAM2的特点

   1、ORB_SLAM2是首个支持单目、双目和RGB-D相机的完整开源SLAM方案,能够实现地图重用,回环检测和重新定位的功能。

  2、能够在GPU上进行实时工作,比如手机、无人机、汽车。

  3、特征点法的巅峰之作,定位精度非常高。

  4、能够实时计算处相机的位姿,并生成场景的稀疏三维重建地图。

二、算法流程框架

ORB-SLAM整体流程如下图所示

它主要有三个线程组成:跟踪、Local Mapping(又称小图)、Loop Closing(又称大图)

a、跟踪(Tracking)
  

        这一部分主要工作是从图像中提取ORB特征,根据上一帧进行姿态估计,或者进行通过全局重定位初始化位姿,然后跟踪已经重建的局部地图,优化位姿,再根据一些规则确定新的关键帧。

跟踪线程相当于一个视觉里程计,流程如下:

  • 首先,对原始图像提取ORB特征并计算描述子。
  • 根据特征描述,在图像间进行特征匹配。
  • 根据匹配特征点估计相机运动。
  • 根据关键帧判别准则,判断当前帧是否为关键帧。

b、建图(LocalMapping)

 这一部分主要完成局部地图构建。包括对关键帧的插入,验证最近生成的地图点并进行筛选,然后生成新的地图点,使用局部捆集调整(Local BA),最后再对插入的关键帧进行筛选,去除多余的关键帧。

c、闭环检测(LoopClosing)

  这一部分主要分为两个过程,分别是闭环探测和闭环校正。闭环检测先使用WOB进行探测,然后通过Sim3算法计算相似变换。闭环校正,主要是闭环融合和Essential Graph的图优化

三、安装教程

操作系统:Ubuntu 16.04

1、克隆仓库

git clone https://github.com/raulmur/ORB_SLAM2.git ORB_SLAM2

2、编译ORB-SLAM2,第三方库中的DBoW2和g2o,并解压ORB词典

cd ORB_SLAM2
chmod +x build.sh
./build.sh

四、TUM数据集

1、orb-slam2在TUM数据集上面的表现

 

2、不同颜色地图点的含义

 黑色点表示所有地图点,红色点属于黑色点的一部分

 五、变量名命名规则

ORB-SLAM2中的变量遵循一套命名规则:

  • 变量名的第一个字母为m表示该变量为某类的成员变量.
  • 变量名的第一、二个字母表示数据类型:
p指针类型变量,用于存放地址的变量
nint类型整数
bbool类型True和False  非0值为真,0值为假
sstd::setset存放的元素仅仅是键,而没有关键的值
vstd:;vector向量类型
lstd::list

list1.assign()给list赋值 list1.back()返回最后一个元素

KFKeyFrame关键帧

KeyFrame

列表初始化,将每个关键帧分成若干网格,设置关键帧位姿信息

KeyFrame::KeyFrame(Frame&F,Map*pMap,KeyFrameDatebase,*pkFDB)

由Tcw求出R,T,Twc=Tcw的逆

Void KeyFrame::setPose(const cv::Mat&Tcw_)

更新地图点的连接

Void KeyFrame::UpdateConnections()

六、多线程

1、使用多线程是为了加快运算速度

bool Initializer::Initialize(const Frame &CurrentFrame) {
 	// ...
    thread threadH(&Initializer::FindHomography, this, ref(vbMatchesInliersH), ref(SH), ref(H));
    thread threadF(&Initializer::FindFundamental, this, ref(vbMatchesInliersF), ref(SF), ref(F));
    // ...
}

2、三个线程是怎样运转的

  LocalMappingLoopClosing线程都是靠Tracking线程产生的关键帧而运转的,在Tracking线程没有产生关键帧时,LocalMappingLoopClosing线程都是处于空转状态,直到Tracking线程产生关键帧时,LocalMapping、LoopClosing线程和Tracking线程一起运转。

// Tracking线程主函数
void Tracking::Track() {
	// 进行跟踪
    // ...
	
    // 若跟踪成功,根据条件判定是否产生关键帧
    if (NeedNewKeyFrame())
        // 产生关键帧并将关键帧传给LocalMapping线程
        KeyFrame *pKF = new KeyFrame(mCurrentFrame, mpMap, mpKeyFrameDB);
        mpLocalMapper->InsertKeyFrame(pKF);	
}

// LocalMapping线程主函数
void LocalMapping::Run() {
	// 死循环
    while (1) {
        // 判断是否接收到关键帧
        if (CheckNewKeyFrames()) {
            // 处理关键帧
            // ...
            
            // 将关键帧传给LoopClosing线程
            mpLoopCloser->InsertKeyFrame(mpCurrentKeyFrame);
        }
        
        // 线程暂停3毫秒,3毫秒结束后再从while(1)循环首部运行
        std::this_thread::sleep_for(std::chrono::milliseconds(3));
    }
}

// LoopClosing线程主函数
void LoopClosing::Run() {
    // 死循环
    while (1) {
        // 判断是否接收到关键帧
        if (CheckNewKeyFrames()) {
            // 处理关键帧
            // ...
        }

        // 查看是否有外部线程请求复位当前线程
        ResetIfRequested();

        // 线程暂停5毫秒,5毫秒结束后再从while(1)循环首部运行
        std::this_thread::sleep_for(std::chrono::milliseconds(5));
    }
}

3、加锁

      因为三个线程同时运转程序可能会发生混乱,所以我们需要给他们加锁。所谓加锁就是只有拿到锁的线程才可以正常运转,运转结束之后自动释放锁。如果一个线程要运转但是锁被其他线程占用着,那只能一直等到其他线程释放锁之后拿到锁才可以继续运行。

class KeyFrame {
protected:
	KeyFrame* mpParent;
    
public:
    void KeyFrame::ChangeParent(KeyFrame *pKF) {
        unique_lock<mutex> lockCon(mMutexConnections);		// 加锁
        mpParent = pKF;
        pKF->AddChild(this);
    }

    KeyFrame *KeyFrame::GetParent() {
        unique_lock<mutex> lockCon(mMutexConnections);		// 加锁
        return mpParent;
    }
}


void KeyFrame::EraseConnection(KeyFrame *pKF) {
    // 第一部分加锁
    {
        unique_lock<mutex> lock(mMutexConnections);
        if (mConnectedKeyFrameWeights.count(pKF)) {
            mConnectedKeyFrameWeights.erase(pKF);
            bUpdate = true;
        }
    }// 程序运行到这里就释放锁,后面的操作不需要抢到锁就能执行
	
    UpdateBestCovisibles();
}

以上是关于ORB_SLAM2 源码解析 ORB_SLAM2简介的主要内容,如果未能解决你的问题,请参考以下文章

ORB_SLAM2 源码解析 单目初始化器Initializer

ORB_SLAM2编译与测试

智能机器人|实验四

ORB_SLAM3源码阅读笔记

ORB_SLAM3安装运行&性能测试对比

ORB_SLAM2_Android