system.cc
这个文件是orb系统初始化文件,主要包括了读取预先存储的地图、初始化词袋、关键帧数据库和地图集、初始化可视化窗口、初始化tracking追踪线程和loopclose闭环线程、轨迹保存等工作。下面是具体介绍。
地图集相关工作,包括词袋、关键帧数据库和地图集的初始化工作。
首先读取配置文件,之后从文件加载地图集,判断是否回环检测
如果没有加载成功地图集,就加载词袋,创建关键帧数据集,并创建地图集,mpAtlas = new Atlas(0)。
加载成功地图集,加载词袋,创建关键帧数据集,mpAtlas->CreateNewMap()。
namespace ORB_SLAM3
{
//输出调试信息等级
Verbose::eLevel Verbose::th = Verbose::VERBOSITY_NORMAL;
System::System(const string &strVocFile, //词典文件
const string &strSettingsFile, //配置文件
const eSensor sensor,//传感器类型
const bool bUseViewer, //可视化窗口
const int initFr, const string &strSequence):
mSensor(sensor), mpViewer(static_cast<Viewer*>(NULL)), mbReset(false), mbResetActiveMap(false),
mbActivateLocalizationMode(false), mbDeactivateLocalizationMode(false), mbShutDown(false)
{
// Output welcome message
cout << endl <<
"ORB-SLAM3 Copyright (C) 2017-2020 Carlos Campos, Richard Elvira, Juan J. Gómez, José M.M. Montiel and Juan D. Tardós, University of Zaragoza." << endl <<
"ORB-SLAM2 Copyright (C) 2014-2016 Raúl Mur-Artal, José M.M. Montiel and Juan D. Tardós, University of Zaragoza." << endl <<
"This program comes with ABSOLUTELY NO WARRANTY;" << endl <<
"This is free software, and you are welcome to redistribute it" << endl <<
"under certain conditions. See LICENSE.txt." << endl << endl;
cout << "Input sensor was set to: ";
if(mSensor==MONOCULAR)
cout << "Monocular" << endl;
else if(mSensor==STEREO)
cout << "Stereo" << endl;
else if(mSensor==RGBD)
cout << "RGB-D" << endl;
else if(mSensor==IMU_MONOCULAR)
cout << "Monocular-Inertial" << endl;
else if(mSensor==IMU_STEREO)
cout << "Stereo-Inertial" << endl;
else if(mSensor==IMU_RGBD)
cout << "RGB-D-Inertial" << endl;
//读取配置文件
cv::FileStorage fsSettings(strSettingsFile.c_str(), cv::FileStorage::READ);//配置文件名转为字符串
if(!fsSettings.isOpened())
{
cerr << "Failed to open settings file at: " << strSettingsFile << endl;
exit(-1);
}
cv::FileNode node = fsSettings["File.version"];
if(!node.empty() && node.isString() && node.string() == "1.0"){//加载地图集,相应的函数在atlas里面
settings_ = new Settings(strSettingsFile,mSensor);
mStrLoadAtlasFromFile = settings_->atlasLoadFile();
mStrSaveAtlasToFile = settings_->atlasSaveFile();
cout << (*settings_) << endl;
}
else{
settings_ = nullptr;
cv::FileNode node = fsSettings["System.LoadAtlasFromFile"];
if(!node.empty() && node.isString())
{
mStrLoadAtlasFromFile = (string)node;
}
node = fsSettings["System.SaveAtlasToFile"];
if(!node.empty() && node.isString())
{
mStrSaveAtlasToFile = (string)node;
}
}
node = fsSettings["loopClosing"];
bool activeLC = true;
if(!node.empty())
{
activeLC = static_cast<int>(fsSettings["loopClosing"]) != 0;//强制类型转换判断是否有回环检测
}
mStrVocabularyFilePath = strVocFile;//词袋文件路径
bool loadedAtlas = false;
if(mStrLoadAtlasFromFile.empty())
{
//Load ORB Vocabulary加载词袋
cout << endl << "Loading ORB Vocabulary. This could take a while..." << endl;
mpVocabulary = new ORBVocabulary();//词袋创建函数ORBVocabulary()
bool bVocLoad = mpVocabulary->loadFromTextFile(strVocFile);
if(!bVocLoad)
{
cerr << "Wrong path to vocabulary. " << endl;
cerr << "Falied to open at: " << strVocFile << endl;
exit(-1);
}
cout << "Vocabulary loaded!" << endl << endl;
//Create KeyFrame Database//使用词袋创建关键帧数据库KeyFrameDatabase(*mpVocabulary)
mpKeyFrameDatabase = new KeyFrameDatabase(*mpVocabulary);
//Create the Atlas//创建地图集
cout << "Initialization of Atlas from scratch " << endl;
mpAtlas = new Atlas(0);
}
else
{
//Load ORB Vocabulary
cout << endl << "Loading ORB Vocabulary. This could take a while..." << endl;
mpVocabulary = new ORBVocabulary();
bool bVocLoad = mpVocabulary->loadFromTextFile(strVocFile);
if(!bVocLoad)
{
cerr << "Wrong path to vocabulary. " << endl;
cerr << "Falied to open at: " << strVocFile << endl;
exit(-1);
}
cout << "Vocabulary loaded!" << endl << endl;
//Create KeyFrame Database
mpKeyFrameDatabase = new KeyFrameDatabase(*mpVocabulary);
cout << "Load File" << endl;
// Load the file with an earlier session
//clock_t start = clock();
cout << "Initialization of Atlas from file: " << mStrLoadAtlasFromFile << endl;
bool isRead = LoadAtlas(FileType::BINARY_FILE);
if(!isRead)
{
cout << "Error to load the file, please try with other session file or vocabulary file" << endl;
exit(-1);
}
//mpKeyFrameDatabase = new KeyFrameDatabase(*mpVocabulary);
//cout << "KF in DB: " << mpKeyFrameDatabase->mnNumKFs << "; words: " << mpKeyFrameDatabase->mnNumWords << endl;
loadedAtlas = true;
mpAtlas->CreateNewMap();
//clock_t timeElapsed = clock() - start;
//unsigned msElapsed = timeElapsed / (CLOCKS_PER_SEC / 1000);
//cout << "Binary file read in " << msElapsed << " ms" << endl;
//usleep(10*1000*1000);
}
IMU标志位、跟踪和闭环线程
if (mSensor==IMU_STEREO || mSensor==IMU_MONOCULAR || mSensor==IMU_RGBD)
mpAtlas->SetInertialSensor();//IMU标志位
//Create Drawers. These are used by the Viewer
mpFrameDrawer = new FrameDrawer(mpAtlas);
mpMapDrawer = new MapDrawer(mpAtlas, strSettingsFile, settings_);
//Initialize the Tracking thread//初始化tracking线程
//(it will live in the main thread of execution, the one that called this constructor)
cout << "Seq. Name: " << strSequence << endl;
mpTracker = new Tracking(this, mpVocabulary, mpFrameDrawer, mpMapDrawer,
mpAtlas, mpKeyFrameDatabase, strSettingsFile, mSensor, settings_, strSequence);
//Initialize the Local Mapping thread and launch、、初始化本地建图线程,线程函数为LocalMapping::Run()
mpLocalMapper = new LocalMapping(this, mpAtlas, mSensor==MONOCULAR || mSensor==IMU_MONOCULAR,
mSensor==IMU_MONOCULAR || mSensor==IMU_STEREO || mSensor==IMU_RGBD, strSequence);
mptLocalMapping = new thread(&ORB_SLAM3::LocalMapping::Run,mpLocalMapper);
mpLocalMapper->mInitFr = initFr; //initFr表示初始化帧的id,代码里设置为0
if(settings_) //设置区分远点和近点的阈值,不过配置文件中并没有这个参数,而是有ThDepth,在tracking线程构造函数中读取
mpLocalMapper->mThFarPoints = settings_->thFarPoints();
else
mpLocalMapper->mThFarPoints = fsSettings["thFarPoints"];
if(mpLocalMapper->mThFarPoints!=0)
{
cout << "Discard points further than " << mpLocalMapper->mThFarPoints << " m from current camera" << endl;
mpLocalMapper->mbFarPoints = true;
}
else
mpLocalMapper->mbFarPoints = false;
//Initialize the Loop Closing thread and launch
// mSensor!=MONOCULAR && mSensor!=IMU_MONOCULAR//初始化闭环线程,线程主函数为LoopClosing::Run()
mpLoopCloser = new LoopClosing(mpAtlas, mpKeyFrameDatabase, mpVocabulary, mSensor!=MONOCULAR, activeLC); // mSensor!=MONOCULAR);
mptLoopClosing = new thread(&ORB_SLAM3::LoopClosing::Run, mpLoopCloser);
//Set pointers between threads
// 设置线程句柄
mpTracker->SetLocalMapper(mpLocalMapper);
mpTracker->SetLoopClosing(mpLoopCloser);
mpLocalMapper->SetTracker(mpTracker);
mpLocalMapper->SetLoopCloser(mpLoopCloser);
mpLoopCloser->SetTracker(mpTracker);
mpLoopCloser->SetLocalMapper(mpLocalMapper);
//usleep(10*1000*1000);
可视化初始化工作
//Initialize the Viewer thread and launch
if(bUseViewer)//可视化
//if(false) // TODO
{
mpViewer = new Viewer(this, mpFrameDrawer,mpMapDrawer,mpTracker,strSettingsFile,settings_);
mptViewer = new thread(&Viewer::Run, mpViewer);
mpTracker->SetViewer(mpViewer);
mpLoopCloser->mpViewer = mpViewer;
mpViewer->both = mpFrameDrawer->both;
}
// Fix verbosity //设置打印信息的等级,只有小于这个等级的信息才会被打印
Verbose::SetTh(Verbose::VERBOSITY_QUIET);
之后是跟踪线程的具体初始化过程,以双目为例,包括了图像校正、裁剪、纯定位模式选择、是否重置地图以及一些数据(如当前地图关键点)的保存。最终会返回一个位姿矩阵tcw。主要计算函数为mpTracker->GrabImageStereo(imLeftToFeed,imRightToFeed,timestamp,filename);
类似的还有用于处理RGBD的和单目monocular
//双目track
Sophus::SE3f System::TrackStereo(const cv::Mat &imLeft, const cv::Mat &imRight, const double ×tamp, const vector<IMU::Point>& vImuMeas, string filename)
{
if(mSensor!=STEREO && mSensor!=IMU_STEREO)
{
cerr << "ERROR: you called TrackStereo but input sensor was not set to Stereo nor Stereo-Inertial." << endl;
exit(-1);
}
cv::Mat imLeftToFeed, imRightToFeed;
//如果需要图像校正
if(settings_ && settings_->needToRectify()){
cv::Mat M1l = settings_->M1l();
cv::Mat M2l = settings_->M2l();
cv::Mat M1r = settings_->M1r();
cv::Mat M2r = settings_->M2r();
cv::remap(imLeft, imLeftToFeed, M1l, M2l, cv::INTER_LINEAR);
cv::remap(imRight, imRightToFeed, M1r, M2r, cv::INTER_LINEAR);
}
//如果需要更改尺寸
else if(settings_ && settings_->needToResize()){
cv::resize(imLeft,imLeftToFeed,settings_->newImSize());
cv::resize(imRight,imRightToFeed,settings_->newImSize());
}
else{
imLeftToFeed = imLeft.clone();
imRightToFeed = imRight.clone();
}
// Check mode change
{
unique_lock<mutex> lock(mMutexMode);
if(mbActivateLocalizationMode)//如果为纯定位模式
{
mpLocalMapper->RequestStop();//请求关闭建图
// Wait until Local Mapping has effectively stopped
while(!mpLocalMapper->isStopped())
{
usleep(1000);
}
mpTracker->InformOnlyTracking(true);//开启纯定位模式
mbActivateLocalizationMode = false; // 设置为false,避免重复进行以上操作
}
//关闭纯定位模式,重新打开局部建图线程
if(mbDeactivateLocalizationMode)
{
mpTracker->InformOnlyTracking(false);
mpLocalMapper->Release();
mbDeactivateLocalizationMode = false;
}
}
// Check reset是否重置跟踪/地图
{
unique_lock<mutex> lock(mMutexReset);
if(mbReset)
{
mpTracker->Reset();
mbReset = false;
mbResetActiveMap = false;
}
else if(mbResetActiveMap)
{
mpTracker->ResetActiveMap();
mbResetActiveMap = false;
}
}
//保存IMU数据
if (mSensor == System::IMU_STEREO)
for(size_t i_imu = 0; i_imu < vImuMeas.size(); i_imu++)
mpTracker->GrabImuData(vImuMeas[i_imu]);
// std::cout << "start GrabImageStereo" << std::endl;处理双目和IMU数据
Sophus::SE3f Tcw = mpTracker->GrabImageStereo(imLeftToFeed,imRightToFeed,timestamp,filename);
// std::cout << "out grabber" << std::endl;
unique_lock<mutex> lock2(mMutexState);
mTrackingState = mpTracker->mState;//状态标志
mTrackedMapPoints = mpTracker->mCurrentFrame.mvpMapPoints;//当前帧的地图点
mTrackedKeyPointsUn = mpTracker->mCurrentFrame.mvKeysUn;//当前帧的去畸变后关键点
return Tcw;
}
此外有一些标志位函数以及轨迹保存函数
标志位:
void System::ActivateLocalizationMode()//mbActivateLocalizationMode=true,进入纯定位模式
void System::DeactivateLocalizationMode()//mbDeactivateLocalizationMode=true,关闭纯定位模式
bool System::MapChanged()//查询从上次查询后地图是否有大变动(回环,全局BA)
void System::Reset()//重置跟踪和地图
void System::ResetActiveMap()//仅重置地图
void System::Shutdown()//请求结束,请求局部建图和回环结束,保存Atlas
bool System::isShutDown() //查询系统是否已经请求结束
int System::GetTrackingState()//查询跟踪状态
vector<MapPoint*> System::GetTrackedMapPoints()//查询跟踪的地图点
vector<cv::KeyPoint> System::GetTrackedKeyPointsUn()//查询跟踪的关键点
轨迹和地图保存,主要有tum,euroc,kitti三种格式。以tum为例
void System::SaveTrajectoryTUM(const string &filename)//保存TUM格式的轨迹,Monocular不可用
{
cout << endl << "Saving camera trajectory to " << filename << " ..." << endl;
if(mSensor==MONOCULAR)
{
cerr << "ERROR: SaveTrajectoryTUM cannot be used for monocular." << endl;
return;
}
vector<KeyFrame*> vpKFs = mpAtlas->GetAllKeyFrames();//获取地图集中所有关键帧
sort(vpKFs.begin(),vpKFs.end(),KeyFrame::lId);//使用ID排序
// Transform all keyframes so that the first keyframe is at the origin.
// After a loop closure the first keyframe might not be at the origin.
Sophus::SE3f Two = vpKFs[0]->GetPoseInverse();//第一针关键帧作为世界坐标系
ofstream f;//打开存储位姿的文件
f.open(filename.c_str());
f << fixed;
// Frame pose is stored relative to its reference keyframe (which is optimized by BA and pose graph).
// We need to get first the keyframe pose and then concatenate the relative transformation.
// Frames not localized (tracking failure) are not saved.
// For each frame we have a reference keyframe (lRit), the timestamp (lT) and a flag
// which is true when tracking failed (lbL).
//每一帧都有个参考关键帧,在闭环中不断变化,位姿等于该帧相对于参考关键帧的位姿乘以参考关键帧的世界位姿。
list<ORB_SLAM3::KeyFrame*>::iterator lRit = mpTracker->mlpReferences.begin();//参考关键帧
list<double>::iterator lT = mpTracker->mlFrameTimes.begin();//时间戳
list<bool>::iterator lbL = mpTracker->mlbLost.begin();//是否跟踪失败
for(list<Sophus::SE3f>::iterator lit=mpTracker->mlRelativeFramePoses.begin(),//相对参考关键帧的位姿
lend=mpTracker->mlRelativeFramePoses.end();lit!=lend;lit++, lRit++, lT++, lbL++)
{
if(*lbL)//跟踪失败
continue;
KeyFrame* pKF = *lRit;//成功,取出参考关键帧
Sophus::SE3f Trw;
// If the reference keyframe was culled, traverse the spanning tree to get a suitable keyframe.
while(pKF->isBad())//如果参考关键帧坏了,位姿选取其父位姿
{
Trw = Trw * pKF->mTcp;//父帧到原参考关键帧的位姿变换
pKF = pKF->GetParent();//父帧
}
Trw = Trw * pKF->GetPose() * Two;//参考关键帧的位姿
Sophus::SE3f Tcw = (*lit) * Trw;//参考关键帧到当前帧的相对位姿*参考关键帧位姿=当前帧位姿
Sophus::SE3f Twc = Tcw.inverse();
Eigen::Vector3f twc = Twc.translation();//平移
Eigen::Quaternionf q = Twc.unit_quaternion();//四元数输出旋转
f << setprecision(6) << *lT << " " << setprecision(9) << twc(0) << " " << twc(1) << " " << twc(2) << " " << q.x() << " " << q.y() << " " << q.z() << " " << q.w() << endl;
}
f.close();
// cout << endl << "trajectory saved!" << endl;
}
保存初始化信息的函数void System::SaveDebugData(const int &initIdx),主要将一些调试信息保存到txt文件里。
atlas地图集的保存和加载判断函数void System::SaveAtlas(int type)、bool System::LoadAtlas(int type)