ORB-SLAM2源码学习:rgbd_tum.cc源文件

前言

1.标注好的源码放在最后边了,可直接看标注好的源码,前边的内容只是对编写代码做的简单梳理,如果能够对大家学习代码时有帮助那就希望大家能够点赞+收藏吧!(嘿嘿)

2.如果大家在学习的过程中发现我存在错误,请大家在评论区指出来,我会及时修改并回复。(谢谢)

一、代码逻辑

1.包含必要的标准库和第三方库头文件:

#include <iostream>
#include <vector>
#include <fstream>
#include <opencv2/core/core.hpp>
#include <System.h>

2.声明函数原型:

a.函数原型一般放在main函数前边确保在调用函数时,编译器已经知道该函数的声明和参数类型。

b.由于ORB-SLAM2提供了三种模式(单目、双目、RGBD),故这个加载图像函数LoadImages在每个程序中不同(参数列表里参数的不同)。

void LoadImages(const string &strAssociationFilename, vector<string> &vstrImageFilenamesRGB,
                vector<string> &vstrImageFilenamesD, vector<double> &vTimestamps);

3. 编写main 函数:

a.参数验证:检查传入参数的数量并给出提示。

if (argc != 5) {
    cerr << "Usage: ..." << endl;
    return 1;
}

b.初始化数据结构:定义并初始化保存图像文件名、深度图文件名和时间戳的向量。

vector<string> vstrImageFilenamesRGB;
vector<string> vstrImageFilenamesD;
vector<double> vTimestamps;

c.调用 LoadImages 函数:读取关联文件数据。

LoadImages(strAssociationFilename, vstrImageFilenamesRGB, vstrImageFilenamesD, vTimestamps);

d.检查图像和深度图的数量一致性

if (vstrImageFilenamesRGB.empty() || vstrImageFilenamesD.size() != vstrImageFilenamesRGB.size()) {
    cerr << "Error: ..." << endl;
    return 1;
}

e.创建 SLAM 系统实例,配置参数。

ORB_SLAM2::System SLAM(argv[1], argv[2], ORB_SLAM2::System::RGBD, true);

f.主处理循环,逐帧处理图像并记录处理时间。

for (int ni = 0; ni < nImages; ni++) {
    // 读取图像和深度图
    imRGB = cv::imread(...);
    imD = cv::imread(...);

    // 处理图像
    SLAM.TrackRGBD(imRGB, imD, tframe);
    
    // 记录时间
    ...
}

g.停止 SLAM 系统,清理资源。

SLAM.Shutdown();

h.输出处理时间统计信息

4. 自定义函数的实现:

void LoadImages(...) {
    ifstream fAssociation(strAssociationFilename);
    while (!fAssociation.eof()) {
        string s;
        getline(fAssociation, s);
        // 解析并存储数据
    }
}

二、已标注的源码

1.这个文件我大概学了一天,我认为学习ORB-SLAM2的代码还是要慢慢来,不要急于求成。

2.代码标注的风格可能有点糟糕,但也不妨碍学习。(嘻嘻)

#include<iostream>
#include<algorithm>
#include<fstream>
#include<chrono>
#include<opencv2/core/core.hpp>
#include<System.h>

using namespace std;

/*
 1. strAssociationFilename 常量字符串,关联文件路径
 2. vstrImageFilenamesRGB  字符串像向量,储存RGBD彩色图的文件名
 3. vstrImageFilenamesD 字符串向量,储存深度图的文件名
 4. vTimestamps  字符串向量,储存时间戳
*/

//LoadImages函数的原型,确保在调用函数时,编译器已经知道该函数的声明和参数类型。
//具体的定义在代码的最后。
void LoadImages(const string &strAssociationFilename, vector<string> &vstrImageFilenamesRGB,
                vector<string> &vstrImageFilenamesD, vector<double> &vTimestamps);
//参数列表中的参数可以直接访问原始变量(即在main函数中定义的变量),而不是其副本。

/*
  argc表示传入的参数个数,包括程序名在内。
  argv是一个字符指针数组(char* 类型),每个元素都指向命令行输入的一个参数。
 */
int main(int argc, char **argv)
{
    if(argc != 5)
    {

      /*
         cerr 标准错误输出流,专门用于显示错误或警告信息,通常是非缓冲的,这意味着错误信息会立即显示在终端中,不会因为缓冲延迟。
         *cout 标准输出流,是缓冲的,可能会等到输出缓冲区满了或程序结束时才输出到终端。
      */
        cerr << endl << "Usage: ./rgbd_tum path_to_vocabulary path_to_settings path_to_sequence path_to_association" << endl;
        //./rgbd_tum 是运行程序的命令,代表程序的名字。
        //path_to_vocabulary 词汇表文件的路径,词汇表是一个预训练的字典,用于图像特征的匹配,在 ORB-SLAM2 中用于视觉词袋模型。
        //path_to_settings 配置文件的路径(通常是 settings.yaml),配置文件包含一些相机的内参、图像的分辨率、深度摄像头的设置等重要的参数。
        //path_to_sequence 图像和深度数据的文件夹路径,里面包含程序要处理的 RGB 图像和深度图像。
        //path_to_association 关联文件的路径,包含了 RGB 图像和深度图像的对应关系(即每一对 RGB 图像和深度图像的时间戳),程序通过这个文件来找到每一帧图像的对应关系。
        return 1;
    }

    // Retrieve paths to images
    vector<string> vstrImageFilenamesRGB;
    vector<string> vstrImageFilenamesD;
    vector<double> vTimestamps;
    string strAssociationFilename = string(argv[4]);//即输入参数的最后的一个路径。
    //将关联文件中的数据加载到后三个参数中去。
    LoadImages(strAssociationFilename, vstrImageFilenamesRGB, vstrImageFilenamesD, vTimestamps);


    // Check consistency in the number of images and depthmaps 检查图像和深度图的数量一致性
    int nImages = vstrImageFilenamesRGB.size();
    //.size() 是一个成员函数,通常用于 STL 容器(如 std::vector, std::string, std::map 等),用于返回容器中元素的数量。
    if(vstrImageFilenamesRGB.empty())
    {
        cerr << endl << "No images found in provided path." << endl;
        return 1;
    }
    else if(vstrImageFilenamesD.size()!=vstrImageFilenamesRGB.size())
    {
        cerr << endl << "Different number of images for rgb and depth." << endl;
        return 1;
    }


    // Create SLAM system. It initializes all system threads and gets ready to process frames.
    //创建一个 SLAM系统的实例,并初始化所有的系统线程,以准备处理输入的帧数据。
    ORB_SLAM2::System SLAM(argv[1],argv[2],ORB_SLAM2::System::RGBD,true);//需要词汇表文件以及配置文件路径。
    //ORB_SLAM2::System::RGBD 是一种配置选项,使 SLAM 系统能够处理 RGB-D 数据
    //如果是 true,系统将尽量快速处理输入的每一帧图像,以实现实时效果。
    //如果是 false,系统可能会以较慢的速度处理图像,可能会在计算上更为集中,但不一定能达到实时的速度。


    // Vector for tracking time statistics
    vector<float> vTimesTrack;//一个动态数组(向量),用于存储每一帧处理的时间。
    vTimesTrack.resize(nImages);
    //resize 是 std::vector 类的一个成员函数。这个函数的作用是调整向量的大小,使其能够容纳指定数量的元素。
    //有多少图像就需要多少储存每一帧处理的时间的容量。
    cout << endl << "-------" << endl;
    cout << "Start processing sequence ..." << endl;//sequence序列。
    cout << "Images in the sequence: " << nImages << endl << endl;


    // Main loop逐帧读取图像和深度图,传递给 SLAM 系统进行处理,并记录每帧的处理时间。
    cv::Mat imRGB, imD;
    for(int ni=0; ni<nImages; ni++)
    {
        // Read image and depthmap from file
        /*cv::imread 是 OpenCV 中用于读取图像的函数。
         将文件夹路径和文件名组合成一个完整的文件路径,argv[3]即为path_to_sequence。
        */
        imRGB = cv::imread(string(argv[3])+"/"+vstrImageFilenamesRGB[ni],CV_LOAD_IMAGE_UNCHANGED);
        imD = cv::imread(string(argv[3])+"/"+vstrImageFilenamesD[ni],CV_LOAD_IMAGE_UNCHANGED);

        double tframe = vTimestamps[ni];//ni的时间戳,后边计算时间差需要用到。

        if(imRGB.empty())
        {
            cerr << endl << "Failed to load image at: "
                 << string(argv[3]) << "/" << vstrImageFilenamesRGB[ni] << endl;
            return 1;
        }


/*这段代码使用条件编译来确定是否启用了 C++11 标准。如果代码是用支持 C++11 的编译器编译的,
*那么就会使用 std::chrono::steady_clock,否则使用 std::chrono::monotonic_clock。
* 条件编译常用于确保代码能够在不同的编译环境下运行。
*/
#ifdef COMPILEDWITHC11//用11进行编译

        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.TrackRGBD(imRGB,imD,tframe);//SLAM系统处理图像,更新状态。

#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
        //计算每一帧处理的时间,储存在vTimesTrack中。
        double ttrack= std::chrono::duration_cast<std::chrono::duration<double> >(t2 - t1).count();

        vTimesTrack[ni]=ttrack;//ttrack 是当前帧处理的实际时间


        // Wait to load the next frame,根据时间戳加载下一张图片。
        double T=0;
        if(ni<nImages-1)//这里判断当前帧 ni 是否不是最后一帧(nImages-1)。如果不是最后一帧,则计算下一帧的时间差。
            T = vTimestamps[ni+1]-tframe;
        else if(ni>0)//如果当前帧 ni 不是第一帧(也就是说,ni 大于 0),则计算与前一帧的时间间隔。
            T = tframe-vTimestamps[ni-1];

        if(ttrack<T)//这里判断处理时间 ttrack 是否小于预期的时间间隔 T。如果是,说明处理速度较快,还有时间可以等待。
            usleep((T-ttrack)*1e6);//等待时间计算
    }

    // Stop all threads
    //停止 SLAM 系统的所有线程并进行清理的函数。
    SLAM.Shutdown();//System.h中的成员函数

    // Tracking time statistics
    //这部分代码主要用来分析 SLAM 系统在处理每帧图像时的性能表现。
    //程序统计并输出了每一帧处理的时间统计信息,包括中位数和平均处理时间。
    sort(vTimesTrack.begin(),vTimesTrack.end());//sort 是 C++ 标准库中的一个函数,用于对容器中的元素进行排序。
    //begin() 和 end() 是 STL(标准模板库)容器的成员函数,通常用于获取容器中第一个和最后一个元素的迭代器。
    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保存相机的轨迹
    //轨迹数据正在以与 TUM 数据集标准兼容的方式格式化或保存,这使得与该领域其他研究或数据集的结果进行比较变得更容易。
    SLAM.SaveTrajectoryTUM("CameraTrajectory.txt");//Trajectory轨迹。
    //保存所有关键帧的轨迹
    SLAM.SaveKeyFrameTrajectoryTUM("KeyFrameTrajectory.txt");   

    return 0;
}


//定义LoadImages函数
void LoadImages(const string &strAssociationFilename, vector<string> &vstrImageFilenamesRGB,
                vector<string> &vstrImageFilenamesD, vector<double> &vTimestamps)
{
    ifstream fAssociation;
    //ifstream 是 C++ 标准库中定义的一个类,表示输入文件流(input file stream)。它用于从文件中读取数据。
    fAssociation.open(strAssociationFilename.c_str());
    //open() 是 ifstream 类的一个成员函数,用于打开指定路径的文件。
    //c_str() 方法将 std::string 对象转换为 C 风格的字符串(const char*),这是因为 open() 函数接受 C 风格字符串作为参数。
    while(!fAssociation.eof())
    //eof() 是 ifstream 类的一个成员函数,返回一个布尔值,指示文件流是否到达文件末尾(EOF, End of File)。
    {
        string s;
        getline(fAssociation,s);
        //getline() 是一个标准库函数,用于从输入流中读取一行文本,直到遇到换行符为止。
        if(!s.empty())
        {
           //提取流程:
           /*读取一行数据(getline)。
           将这行数据放入 stringstream 流。
           按顺序提取时间戳、RGB 图像文件名、深度图像文件名。
           每提取一个字段,就将其存储到相应的向量中。*/
            stringstream ss;
            ss << s;
            double t;
            string sRGB, sD;
            ss >> t;
            vTimestamps.push_back(t);
            //.push_back 是 C++ 中 std::vector 类的一个成员函数,用于在向量的末尾添加一个新元素。
            ss >> sRGB;
            vstrImageFilenamesRGB.push_back(sRGB);
            ss >> t;
            ss >> sD;
            vstrImageFilenamesD.push_back(sD);

        }
    }
}

 

您好!要在ORB-SLAM2中运行TUM数据集,需要进行以下步骤: 1. 下载TUM数据集:您可以从TUM官方网站上下载TUM RGB-D数据集(例如,TUM RGB-D数据集)。 2. 准备数据:解压缩下载的数据集,并确保数据集的文件结构符合ORB-SLAM2的要求。ORB-SLAM2需要RGB图像和深度图像作为输入。您可以将RGB图像和深度图像放在同一文件夹中,并使用与图像序列对应的时间戳命名图像文件(例如,rgb/,depth/ 文件夹下的文件名为 "rgb/1305031910.938850.png" 和 "depth/1305031910.938850.png")。 3. 配置参数:ORB-SLAM2提供了一个配置文件,您可以根据需要进行修改。在ORB-SLAM2的主目录下,有一个名为"Examples/RGB-D/TUMX.yaml"的配置文件,其中X代表数据集的名称(例如,TUM1.yaml, TUM2.yaml等)。您可以打开该配置文件并根据需要进行修改,例如设置相机内参、深度图缩放系数等。 4. 运行ORB-SLAM2:在终端中导航到ORB-SLAM2的主目录,并执行以下命令来运行ORB-SLAM2: ``` ./Examples/RGB-D/rgbd_tum Vocabulary/ORBvoc.txt Examples/RGB-D/TUMX.yaml 数据集文件夹路径 ``` 其中,Vocabulary/ORBvoc.txt 是ORB-SLAM2的词典文件路径,Examples/RGB-D/TUMX.yaml 是您在第3步中修改的配置文件路径,数据集文件夹路径是您存储TUM数据集的文件夹路径。 5. 可视化结果:ORB-SLAM2将输出相机轨迹和地图。您可以使用ORB-SLAM2提供的可视化工具(例如,rgbd_tum)来查看结果。在终端中导航到ORB-SLAM2的主目录,并执行以下命令来可视化结果: ``` ./Examples/RGB-D/rgbd_tum Vocabulary/ORBvoc.txt Examples/RGB-D/TUMX.yaml 数据集文件夹路径 ``` 此命令将打开一个窗口,显示相机轨迹和地图。 这些是在ORB-SLAM2中运行TUM数据集的基本步骤。请确保按照上述步骤进行操作,并根据需要进行相应的配置和参数调整。祝您成功运行ORB-SLAM2!如果您有任何问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值