ORB-SLAM2代码详解01:ORB-SLAM2代码运⾏流程
pdf版本笔记的下载地址: ,排版更美观⼀点,这个⽹站的默认排版太丑了(访问密码:3834)
ORB-SLAM2代码详解01: ORB-SLAM2代码运⾏流程
可以看看我录制的视频
运⾏官⽅Demo
以TUM数据集为例,运⾏Demo的命令:
./Examples/RGB-D/rgbd_tum Examples/RGB-D/TUM1.yaml PATH_TO_SEQUENCE_FOLDER ASSOCIATIONS_FILE 的源码:
int main(int argc,char**argv){
// 判断输⼊参数个数
if(argc !=5){
cerr << endl <<"Usage: ./rgbd_tum path_to_vocabulary path_to_ttings path_to_quence path_to_association"<< endl;
return1;
}
// step1. 读取图⽚及左右⽬关联信息
vector<string> vstrImageFilenamesRGB;
vector<string> vstrImageFilenamesD;
vector<double> vTimestamps;
string strAssociationFilename =string(argv[4]);
LoadImages(strAssociationFilename, vstrImageFilenamesRGB, vstrImageFilenamesD, vTimestamps);
// step2. 检查图⽚⽂件及输⼊⽂件的⼀致性
int nImages = vstrImageFilenamesRGB.size();
pty()){
cerr << endl <<"No images found in provided path."<< endl;
return1;
}el if(vstrImageFilenamesD.size()!= vstrImageFilenamesRGB.size()){
cerr << endl <<"Different number of images for rgb and depth."<< endl;
return1;
}
// step3. 创建SLAM对象,它是⼀个 ORB_SLAM2::System 类型变量
ORB_SLAM2::System SLAM(argv[1], argv[2], ORB_SLAM2::System::RGBD,true);
vector<float> vTimesTrack;
cv::Mat imRGB, imD;
// step4. 遍历图⽚,进⾏SLAM
for(int ni =0; ni < nImages; ni++){
// step4.1. 读取图⽚
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];
// step4.2. 进⾏SLAM
SLAM.TrackRGBD(imRGB, imD, tframe);
// step4.3. 加载下⼀张图⽚
double T =0;
if(ni < nImages -1)
T = vTimestamps[ni +1]- tframe;
el if(ni >0)
T = tframe - vTimestamps[ni -1];
if(ttrack < T)
usleep((T - ttrack)*1e6);
}
// step5. 停⽌SLAM
SLAM.Shutdown();
}
运⾏程序rgbd_tum时传⼊了⼀个重要的配置⽂件TUM1.yaml,其中保存了相机参数和ORB特征提取参数:
%YAML:1.0
## 相机参数
Camera.fx:517.306408
Camera.fy:516.469215
<:318.643040
<:255.313989
Camera.k1:0.262383
Camera.k2:-0.953104
Camera.p1:-0.005358
Camera.p2:0.002628
Camera.k3:1.163314
吃饺子的作文
Camera.width:640
Camera.height:480
Camera.fps:30.0# Camera frames per cond
Camera.bf:40.0# IR projector baline times fx (aprox.)
Camera.RGB:1# Color order of the images (0: BGR, 1: RGB. It is ignored if images are grayscale) ThDepth:40.0# Clo/Far threshold. Baline times.
DepthMapFactor:5000.0# Deptmap values factor
## ORB特征提取参数
ORBextractor.nFeatures:1000# ORB Extractor: Number of features per image ORBextractor.scaleFactor:1.2# ORB Extractor: Scale factor between levels in the scale pyramid ORBextractor.nLevels:8# ORB Extractor: Number of levels in the scale pyramid ORBextractor.iniThFAST:20
ORBextractor.minThFAST:7
阅读代码之前你应该知道的事情
吃什么补羊水最快变量命名规则
ORB-SLAM2中的变量遵循⼀套命名规则:
变量名的第⼀个字母为m表⽰该变量为某类的成员变量.
大班舞蹈教案变量名的第⼀、⼆个字母表⽰数据类型:
p表⽰指针类型
溥杰的后代n表⽰int类型
b表⽰bool类型
s表⽰std::t类型
v表⽰std::vector类型
l表⽰std::list类型
KF表⽰KeyFrame类型
这种将变量类型写进变量名的命名⽅法叫做匈⽛利命名法.
理解多线程
为什么要使⽤多线程?
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. 因为系统的随机性,各步骤的运⾏顺序是不确定的.
Tracking线程不产⽣关键帧时,LocalMapping和LoopClosing线程基本上处于空转的状态.
⽽Tracking线程产⽣关键帧的频率和时机不是固定的,因此需要3个线程同时运⾏,LocalMapping和LoopClosing线程不断循环查询Tracking线程是否产⽣关键帧,产⽣了的话就处理.
// Tracking线程主函数
void Tracking::Track(){
// 进⾏跟踪
// ...
怎样创业才能赚钱// 若跟踪成功,根据条件判定是否产⽣关键帧
if(NeedNewKeyFrame())
// 产⽣关键帧并将关键帧传给LocalMapping线程
经验哲学
KeyFrame *pKF =new KeyFrame(mCurrentFrame, mpMap, mpKeyFrameDB);
mpLocalMapper->InrtKeyFrame(pKF);
}
// LocalMapping线程主函数
void LocalMapping::Run(){
// 死循环
while(1){
// 判断是否接收到关键帧
if(CheckNewKeyFrames()){
// 处理关键帧
// ...
// 将关键帧传给LoopClosing线程
mpLoopClor->InrtKeyFrame(mpCurrentKeyFrame);
}
// 线程暂停3毫秒,3毫秒结束后再从while(1)循环⾸部运⾏
芭比娃娃怎么做
std::this_thread::sleep_for(std::chrono::milliconds(3));
}
}
// LoopClosing线程主函数
void LoopClosing::Run(){
// 死循环
while(1){
// 判断是否接收到关键帧
if(CheckNewKeyFrames()){
if(CheckNewKeyFrames()){
// 处理关键帧
/
/ ...
}
// 查看是否有外部线程请求复位当前线程
RetIfRequested();
// 线程暂停5毫秒,5毫秒结束后再从while(1)循环⾸部运⾏
std::this_thread::sleep_for(std::chrono::milliconds(5));
}
}
多线程中的锁
为防⽌多个线程同时操作同⼀变量造成混乱,引⼊锁机制:
将成员函数本⾝设为私有变量(private或protected),并在操作它们的公有函数内加锁.
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;
}
}
⼀把锁在某个时刻只有⼀个线程能够拿到,如果程序执⾏到某个需要锁的位置,但是锁被别的线程拿着不释放的话,当前线程就会暂停下来;直到其它线程释放了这个锁,当前线程才能拿⾛锁并继续向下执⾏.
什么时候加锁和释放锁?
unique_lock<mutex> lockCon(mMutexConnections);这句话就是加锁,锁的有效性仅限于⼤括号{}之内,也就是说,程序运⾏出⼤括号之后就释放锁了.因此可以看到有⼀些代码中加上了看似莫名其妙的⼤括号.
void KeyFrame::EraConnection(KeyFrame *pKF){
// 第⼀部分加锁
{
unique_lock<mutex>lock(mMutexConnections);
unt(pKF)){
bUpdate =true;
}
}// 程序运⾏到这⾥就释放锁,后⾯的操作不需要抢到锁就能执⾏
UpdateBestCovisibles();
}
SLAM主类System
李重元
System类是ORB-SLAM2系统的主类,先分析其主要的成员函数和成员变量: