ORB_SLAM2 源码解析 ORB_SLAM2简介
Posted 小负不负
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ORB_SLAM2 源码解析 ORB_SLAM2简介相关的知识,希望对你有一定的参考价值。
目录
2、编译ORB-SLAM2,第三方库中的DBoW2和g2o,并解压ORB词典
一、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 | 指针类型 | 变量,用于存放地址的变量 |
n | int类型 | 整数 |
b | bool类型 | True和False 非0值为真,0值为假 |
s | std::set | set存放的元素仅仅是键,而没有关键的值 |
v | std:;vector | 向量类型 |
l | std::list | list1.assign()给list赋值 list1.back()返回最后一个元素 |
KF | KeyFrame | 关键帧 |
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、三个线程是怎样运转的
LocalMapping
和LoopClosing
线程都是靠Tracking
线程产生的关键帧而运转的,在Tracking
线程没有产生关键帧时,LocalMapping
和LoopClosing
线程都是处于空转状态,直到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简介的主要内容,如果未能解决你的问题,请参考以下文章