本博客结合哔哩大学视频ORBSLAM2原理代码详解-1简介安装运行总结。
代码参照github的ORB_SLAM2_detailed_comments
我对代码要求环境的配置做了一个总结,解决了配置中的一些常见问题,详见slam的环境配置大全–保姆教学
ORBSLAM2代码很经典,而且代码量大,会分成多个博客研究。
ORBSLAM2代码很经典,支持单目、双目相机,在cpu上就可以运行,是特征点slam的巅峰之作,定位精度很高。
如上图,先是单目或者双目输入进来,做一个预处理即提取特征点,后边的方框都是跟踪过程,包括重定位、跟踪局部地图、关键帧的选择。这些完成以后就是右侧竖着方框里的工作,关键帧一直进行插入并作地图点的筛查工作,去除无效的地图点,增加新的点和局部BA的工作,关键帧也不会一直不变。再左侧的方框就是回环检测,做回环融合、回环校正、整体BA。其中还包括例如重定位的视觉磁带,整个代码很块化,比较好理解。
博客开头有github的链接,如图为其要求的配置环境。
其中c++一般都支持了,Pangolin是用作显示的,Opencv一般安装Opencv3,ROS为可选项。
我对其要求环境的配置做了一个总结,解决了配置中的一些常见问题,详见slam的环境配置大全–保姆教学
1、数据集的下载:
安装完成后跑个demo,在如图路径下有三个对应数据集的可执行文件。
先下载数据集,mono_tum对应的tum数据集,github中有下载链接。
随便找一个下载后,除associate外其他和图片一致。包括depth图、rgb图、加速度、depth里包括depth的时间戳和depth名称,rgb类似,groundtruth里有如图的时间戳中和四元数表示的各位姿。
最后associate.py的python文件是从官网下载的,需要在python2下运行。如下为官网截图:
图中上半段交代了彩色图是640480的8位PNG格式,深度图是640480的16位的png图,注意上半段最后一句说:深度的规模是5000,里面是16位,假设记录的值是5000的话,除以scale,即5000/5000=1,这个点距离相机1米。下面Ground-truth交代了,相机在一个固定的坐标系下的坐标。
而associate.py的作用是在rgbd模式下(单目直接跑就行)因为时间戳不一致,rgbd模式下需要把彩色图和深度图做一个关联,哪一帧的彩色图对应哪一帧的深度图,不是严格关联有时间差。打开associate.py如图,associate.py的内容为此处,可以复制下载自己做一个python
文件放到数据集里。
运行的指令为:(> 之后的是自己命名的文件),第二句指令和注意我没看懂
python associate.py rgb.txt depth.txt > associate.txt
python associate.py associate.txt groundtruth.txt > associate_with_groundtrurh.txt
注意:
直接associate后出问题,生成的结果
associate.txt 1641行
associate_with_groundtrurh.txt 1637行
也就是说,associate的不一定有groundtruth,所以要以associate_with_groundtrurh.txt的关联结果为准。
2、介绍完数据集就可以跑程序了
指令为
./Examples/Monocular/mono_tum Vocabulary/ORBvoc.txt Examples/Monocular/TUMX.yaml PATH_TO_SEQUENCE_FOLDER
首先是mono_tum文件,ORBvoc.txt是orb的一个训练好的磁带,TUMX.yaml在文件夹中有三个,我们需要把X改成1、2、3,最后一个是指定一个地方放数据集。
我们详细看一下TUM1.yaml里有什么。这是一个配置文件,为什么要有这个配置文件呢,比如我们要跑一个代码,测试一下某个参数对某个性能的影响。若每次都把参数写死在代码里,则每次都要重新编译,所以我们就把经常修改的参数或者路劲放到配置文件里,让他读进去直接改就行了不需要重现编译。内容注释如图:
有以上这些东西就可以跑了
为什么有以上这些东西就可以跑了呢?
比如单目的tum版本,如图示例划线的地方所对应的文件
参照下面代码中的注释理解
using namespace std;
void LoadImages(const string &strFile, vector<string> &vstrImageFilenames,
vector<double> &vTimestamps);
int main(int argc, char **argv)#argv是输入的一些参数
{
if(argc != 4)
{
cerr << endl << "Usage: ./mono_tum path_to_vocabulary path_to_settings path_to_sequence" << endl;
return 1;
}
// Retrieve paths to images
vector<string> vstrImageFilenames;
vector<double> vTimestamps;
string strFile = string(argv[3])+"/rgb.txt";#argv[3]就是划线部分第三个path_to_sequence,再加一个+"/rgb.txt"代表读取这个目录下的rgb包。同理argv[1]代表path_to_vocabulary
LoadImages(strFile, vstrImageFilenames, vTimestamps);
int nImages = vstrImageFilenames.size();
3、最后跑示例代码
在ORB_SLAM2包下代开终端,输入上边更改后的命令,例如:
./Examples/Monocular/mono_tum Vocabulary/ORBvoc.txt Examples/Monocular/TUM3.yaml /home/cxl/Download/DataSet/TUM/rgbd_dataset_freiburg1_long_office_househlod
跑出来的最后效果和对应怎么操作也很重要,但是不能上传视频,可以参照博客开头的哔哩大学视频24分钟。
运行过程和代码结果如下两图:
安装编译若有下图错误,可以按下边第一个评论解决。
最后把代码大概注释一遍:
/**
* This file is part of ORB-SLAM2.
*
* Copyright (C) 2014-2016 Raúl Mur-Artal (University of Zaragoza)
* For more information see
*
* ORB-SLAM2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ORB-SLAM2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ORB-SLAM2. If not, see .
*/
// 外层结构很简单,就是45行左右的读入图片,68行读图,86行track,110行shutdown,116行打印出运行时间,124行保存轨迹
#include
#include
#include
#include
#include
#include
#include
using namespace std;
void LoadImages(const string &strFile, vector<string> &vstrImageFilenames,
vector<double> &vTimestamps);
// 主函数
int main(int argc, char **argv)
{
if(argc != 4)
{
cerr << endl << "Usage: ./mono_tum path_to_vocabulary path_to_settings path_to_sequence" << endl;
return 1;
}
// Retrieve paths to images
vector<string> vstrImageFilenames;
vector<double> vTimestamps;
string strFile = string(argv[3])+"/rgb.txt"; // 单目只需要rgb
LoadImages(strFile, vstrImageFilenames, vTimestamps); // 把rgb的strfile给load进来,函数loadimage解释在136行
int nImages = vstrImageFilenames.size();// 所有帧的总数
// Create SLAM system. It initializes all system threads and gets ready to process frames.
ORB_SLAM2::System SLAM(argv[1],argv[2],ORB_SLAM2::System::MONOCULAR,true);// 把orb_slam2的系统初始化
// Vector for tracking time statistics
vector<float> vTimesTrack;
vTimesTrack.resize(nImages);
cout << endl << "-------" << endl;
cout << "Start processing sequence ..." << endl;
cout << "Images in the sequence: " << nImages << endl << endl;
// Main loop
cv::Mat im;
for(int ni=0; ni<nImages; ni++)// 初始化完成后开始每一帧的读
{
// Read image from file
im = cv::imread(string(argv[3])+"/"+vstrImageFilenames[ni],CV_LOAD_IMAGE_UNCHANGED);// read刚才路径下图片的名字
double tframe = vTimestamps[ni];
if(im.empty())// 如果没有图片就报错
{
cerr << endl << "Failed to load image at: "
<< string(argv[3]) << "/" << vstrImageFilenames[ni] << endl;
return 1;
}
#ifdef COMPILEDWITHC11
std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now();
#else
std::chrono::monotonic_clock::time_point t1 = std::chrono::monotonic_clock::now();
#endif
// Pass the image to the SLAM system
SLAM.TrackMonocular(im,tframe);// 输入的图片和时间戳,开始track(轨道)
#ifdef COMPILEDWITHC11
std::chrono::steady_clock::time_point t2 = std::chrono::steady_clock::now();
#else
std::chrono::monotonic_clock::time_point t2 = std::chrono::monotonic_clock::now();
#endif
double ttrack= std::chrono::duration_cast<std::chrono::duration<double> >(t2 - t1).count();
vTimesTrack[ni]=ttrack;
// Wait to load the next frame
double T=0;// 记录时间
if(ni<nImages-1)
T = vTimestamps[ni+1]-tframe;
else if(ni>0)
T = tframe-vTimestamps[ni-1];
if(ttrack<T)
usleep((T-ttrack)*1e6);
}
// Stop all threads
SLAM.Shutdown();// 关闭
// Tracking time statistics
sort(vTimesTrack.begin(),vTimesTrack.end());
float totaltime = 0;
for(int ni=0; ni<nImages; ni++)
{
totaltime+=vTimesTrack[ni];
}
cout << "-------" << endl << endl;
cout << "median tracking time: " << vTimesTrack[nImages/2] << endl;
cout << "mean tracking time: " << totaltime/nImages << endl;
// Save camera trajectory
SLAM.SaveKeyFrameTrajectoryTUM("KeyFrameTrajectory.txt");// 最后保存一下轨迹
return 0;
}
/**
* @brief 导入图片
*
* @param[in] strFile 读入的文件名称
* @param[in&out] vstrImageFilenames 彩色图片名称
* @param[in&out] vTimestamps 记录时间戳
*/
void LoadImages(const string &strFile, vector<string> &vstrImageFilenames, vector<double> &vTimestamps)
{
ifstream f;
f.open(strFile.c_str());
// skip first three lines
// 前三行是注释,跳过
string s0;
getline(f,s0);
getline(f,s0);
getline(f,s0);
while(!f.eof())
{
string s;
getline(f,s);
if(!s.empty())// load进来后判断不为空
{// 把这个rgb名字给他,记录名字,记录完后开始迭代
stringstream ss;
ss << s;
double t;
string sRGB;
ss >> t;
vTimestamps.push_back(t);
ss >> sRGB;
vstrImageFilenames.push_back(sRGB);
}
}
}