目录
学习程序2: ORB_SLAM2::System SLAM( )
一、ORB-SLAM2 程序运行步骤
https://blog.csdn.net/hltt3838/article/details/113962104
二、ORB-SLAM2 系统框架
要了解一个系统的代码实现,总是要先从熟悉系统的框架开始,ORB-SLAM2的系统框架如下图所示:
我们可以看到 TRACKING线程 中做了提取ORB特征、初始位姿估计等操作,那么这个线程应该属于SLAM中的“前端VO”;LOCAL MAPPING线程 里有Local BA,我们可以知道这个线程属于后端,主要做后端优化和建图工作;LOOP CLOSING线程从字面意思就能判断出是做回环检测的,分为回环检测和回环矫正。再需要知道的就是TRACKING线程接收了一帧一帧的图片后开始工作的,而LOCAL MAPPING线程接收的KeyFrame来自TRACKING线程“加工提炼”之后,LOOP CLOSING线程也是接收LOCAL MAPPING线程“进一步加工提炼”后的 KeyFrame 来进行工作。
注意:LOCAL MAPPING线程 开始就是对 关键帧 进行操作了 !
遗留问题: PLACE RECOGNITION和MAP 是什么? 后面进行解读
三、开始代码
mono_euroc.cc文件主要的函数如下
cd orb_slam/src/ORB_SLAM2 // 我的程序放在orb_slam/src/ORB_SLAM2里面
运行程序输入:
./Examples/Monocular/mono_euroc Vocabulary/ORBvoc.txt Examples/Monocular/EuRoC.yaml /home/hltt3838/EuRoC_data/MH_01_easy/mav0/cam0/data Examples/Monocular/EuRoC_TimeStamps/MH01.txt
int main(int argc, char **argv)
{
// step 0 检查输入参数个数是否足够
if(argc != 5)
{
cerr << endl << "Usage: ./mono_tum path_to_vocabulary path_to_settings path_to_image_folder path_to_times_file" << endl;
return 1;
}
// step 1 加载图像
vector<string> vstrImageFilenames; // 图像序列的文件名字符串序列
vector<double> vTimestamps; // 时间戳
LoadImages(string(argv[3]), // 3-保存图像的路径
string(argv[4]), // 4-保存图像时间的路径
vstrImageFilenames, // 读取到的图像名称数组
vTimestamps); // 时间戳数组
// 当前图像序列中的图像数目
int nImages = vstrImageFilenames.size();
if(nImages<=0)
{
cerr << "ERROR: Failed to load images" << endl;
return 1;
}
// step 2 加载SLAM系统
ORB_SLAM2::System SLAM(
argv[1], // 1-词典文件路径
argv[2], // 2-相机参数文件路径
ORB_SLAM2::System::MONOCULAR, // 单目模式
true); // 启用可视化查看器
// step 3 运行前准备,Vector for tracking time statistics
vector<float> vTimesTrack; // 统计追踪一帧耗时 (仅Tracker线程)
vTimesTrack.resize(nImages);
cout << endl << "-------" << endl;
cout << "Start processing sequence ..." << endl;
cout << "Images in the sequence: " << nImages << endl << endl;
// step 4 ,主循环,依次追踪序列中的每一张图像
cv::Mat im;
for(int ni=0; ni<nImages; ni++)
{
// step 4.1 读根据前面获得的图像文件名读取图像,读取过程中不改变图像的格式
im = cv::imread(vstrImageFilenames[ni],CV_LOAD_IMAGE_UNCHANGED);
double tframe = vTimestamps[ni];
// step 4.2 图像的合法性检查
if(im.empty())
{
cerr << endl << "Failed to load image at: "
<< vstrImageFilenames[ni] << endl;
return 1;
}
// step 4.3 开始计时
#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
// step 4.4 追踪当前图像;输入:图像+时间
SLAM.TrackMonocular(im,tframe);
// step 4.5 追踪完成,停止当前帧的图像计时, 并计算追踪耗时
#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;
// step 4.6 根据图像时间戳中记录的两张图像之间的时间和现在追踪当前图像所耗费的时间,继续等待指定的时间以使得下一张图像能够 按照时间戳被送入到SLAM系统中进行跟踪
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);
}
// step 5 如果所有的图像都预测完了,那么终止当前的SLAM系统
SLAM.Shutdown();
// step 6 计算平均耗时
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;
// step 7 保存TUM格式的相机轨迹
// 估计是单目时有尺度漂移, 而LGA GBA都只能优化关键帧使尺度漂移最小, 普通帧所产生的轨迹漂移这里无能为力, 我猜作者这样就只保存了关键帧的位姿,从而避免普通帧带有尺度漂移的位姿对最终误差计算的影响
SLAM.SaveKeyFrameTrajectoryTUM("KeyFrameTrajectory.txt");
return 0;
}
分析:程序很容易看懂,主要是 读取图像信息,初始化系统,追踪图像等,其程序的思维导图如下图所示:
学习程序1: void LoadImages( )
/**
* @brief 获取图像文件的信息
* @param[in] strImagePath 图像文件存放路径
* @param[in] strPathTimes 时间戳文件的存放路径
* @param[out] vstrImages 图像文件名数组
* @param[out] vTimeStamps 时间戳数组
*/
// 从文件中加载图像序列中每一张图像的文件路径和时间戳
void LoadImages(const string &strImagePath, const string &strPathTimes,
vector<string> &vstrImages, vector<double> &vTimeStamps)
{
// 打开文件
ifstream fTimes;
fTimes.open(strPathTimes.c_str());
vTimeStamps.reserve(5000);
vstrImages.reserve(5000);
// 遍历文件
while(!fTimes.eof())
{
string s;
getline(fTimes,s);
// 只有在当前行不为空的时候执行
if(!s.empty())
{
stringstream ss;
ss << s;
// 生成当前行所指出的RGB图像的文件名称
vstrImages.push_back(strImagePath + "/" + ss.str() + ".png");
double t;
ss >> t;
// 记录该图像的时间戳
vTimeStamps.push_back(t/1e9);//转换单位:秒, 具体看 MH01.txt
}
}
}
理解:
程序写的很好,有很大的借鉴意义,但要注意自己采集数据的时候怎么根据自己的图像数据去读取图像的名称和对应的时间序列,上面的程序对应的图片名字如下,好好体会:
下面的程序是把 时间转换成单位:秒
vTimeStamps.push_back(t/1e9); //转换单位:秒, 具体看 MH01.txt
学习程序2: ORB_SLAM2::System SLAM( )
这个程序在 System.cc 程序里面, 具体的分析看连接:https://mp.csdn.net/editor/html/115089937