ORB-SLAM3源码阅读笔记14:系统重置相关函数及对应情况解析

Nov 8,2022   5714 words   21 min

Tags: SLAM

本篇博客,我们以ORB-SLAM3系统什么情况下会重置(Reset)以及到底重置了什么为主题,分析ORB-SLAM3系统中的重置逻辑和可能引发重置的情况。

1.重置相关函数与调用关系

首先,Reset是通过System类的TrackStereo()函数中的判断来决定是否对系统进行Reset的,如下。 而这个判断条件就是System类的成员变量mbResetmbResetActiveMap。这个两个变量默认都为false。而这两个变量分别在System类的Reset()函数和ResetActiveMap()函数中被设为true,如下。 然而,可以看到,整个系统中,SystemReset()函数从来没有在任何地方被调用过(函数名为灰色),也就是说mbReset永远都为false,系统其实不会进入上面的第一个分支,进一步来说就是,系统不会通过Tracking::Reset()函数重置,只会通过Tracking::ResetActiveMap()进行。所以,如果系统出现了Reset,那么只可能是因为ResetActiveMap()函数调用导致的(调用了该函数,mbResetActiveMap变量被设为true,这样下次进入TrackStereo()函数的时候,就会进入reset分支,具体通过调用Tracking::ResetActiveMap()函数进行重置)。 那么下面的问题是,System::ResetActiveMap()函数在哪里被调用了呢?我们可以很轻松的找到如下调用: 可以看到,除去在可视化或者单目初始化时候的地图重置,绝大部分调用都在Tracking::Track()函数中。所以,我们只需要分别找到Track()函数中的这7处调用的条件,就可以知道,ORB-SLAM系统在什么情况下会Reset当前地图。下面分别介绍。

2.可能导致重置的情况

2.1 情况1

情况1是LocalMapping的成员变量mbBadImu为true的时候。所以,我们进一步寻找mbBadImu在哪里被设为了true。通过查找,我们重点关注value write,可以看到如下。 可以看到,整个系统中,只在LocalMappingRun()函数里被设为true。我们进一步点进去看,如下。 可以看到,判断逻辑还是挺复杂的。但总体而言就是:(1) 如果当前地图中的关键帧个数大于2; (2) 使用了IMU且IMU已经初始化; (3) 当前还没有完成IMU的BA2优化; (4) 初始化时间小于10s且运动距离小于0.02m。如果是这样的话,就认为没有足够的运动给IMU进行初始化,我们就把mbBadImu设为true。这里需要注意的是mTinit默认为0,且只有当当前关键帧和上一个关键帧之间的运动大于0.05m时才会累加。所以即使有很多的关键帧,但每个关键帧之间的运动距离都很小,就算跑了很远,但mTinit还是为0。所以需要注意,这个变量不单单是时间,而是一个根运动距离有关的时间累加量。并且这个变量其实跟IMU初始化密切相关。因为一个基本的思想是,只有足够的运动,IMU的初始化才可靠。所以在ORB-SLAM系统中,IMU会进行两次初始化(也可以理解为两次优化,而这两次优化就分别被叫做BA1和BA2),如下图所示。 可以看到,(1) 如果使用IMU且当前初始化时间不多于100秒,(2) IMU已经初始化完成并且当前Tracking状态为OK,就尝试进行视觉-IMU联合优化。第一次优化发生在累计5秒以后,第二次优化发生在累计15秒以后。这两次优化不同之处就在于传入InitializeIMU()的函数不同(因为这篇博客不是终点介绍IMU初始化的,这里就不再仔细展开)。当然了,如果是单目情况,还会有个额外的尺度精化过程(双目情况其实尺度完全没做任何精化)。而且会发现很有意思的是,为了尽可能准确,每隔10秒会重新做一次,这样最多可以达到6次。稍微扯远了,下面回归正题。

情况1小节就是:如果运动过小不足以IMU初始化,那么LocalMapping::mbBadImu就被设为true,进而会进入Tracking::Track()的异常判断分支,进而调用System::ResetActiveMap()函数,将System::mbResetActiveMap变量设为true。当下次系统运行到System::TrackStereo()函数时,就会进入异常分支,进而调用Tracking::ResetActiveMap()函数进行实质上的重置,在该函数中系统状态会被设置为NO_IMAGES_YET

2.2 情况2

如下图所示,同样是在Tracking::Track()函数的异常分支里。 可以看到,需要满足以下条件,重置地图才会触发:(1) 当前状态不是NO_IMAGES_YET状态; (2) 当前帧时间戳和上一帧相差了1s以上; (3) 使用了IMU; (4) IMU已经被初始化; (5) 当前地图IMU没有完成BA2。同时满足以上条件,系统就会重置当前地图。

情况2小节就是:输入数据出现了时间跳变,并且使用了IMU且IMU已经被初始化,但还没执行BA2优化。此时就会调用System::ResetActiveMap()函数,将System::mbResetActiveMap变量设为true。当下次系统运行到System::TrackStereo()函数时,就会进入异常分支,进而调用Tracking::ResetActiveMap()函数进行实质上的重置,在该函数中系统状态会被设置为NO_IMAGES_YET

2.3 情况3

可以看到,情况3和情况2其实是紧密关联的。都是检测到了时间跳变,并且系统使用了IMU。所不同的是如果此时IMU还没有初始化,那么直接重置当前地图。所以其实情况2可以看作是在遇到时间跳变以后“最后的尝试”。假如IMU已经初始化并且做完了BA2,那么这种情况我们就不重置当前地图,而是新建一个地图。

情况3小节就是: 输入数据出现了时间跳变,并且使用了IMU但IMU没初始化,直接调用System::ResetActiveMap()函数,将System::mbResetActiveMap变量设为true。当下次系统运行到System::TrackStereo()函数时,就会进入异常分支,进而调用Tracking::ResetActiveMap()函数进行实质上的重置,在该函数中系统状态会被设置为NO_IMAGES_YET

2.4 情况4

情况4是在系统运行的时候出现的,需同时满足以下条件:(1) 当前系统状态不为NOT_INITIALIZED; (2) 当前系统状态也不为OK; (3) 当前系统状态为LOST; (4) 当前地图中的关键帧个数小于10。此时就会重置当前地图。

情况4小节就是:如果当前系统状态为LOST,并且当前地图中关键帧个数小于10,就会调用System::ResetActiveMap()函数,将System::mbResetActiveMap变量设为true。当下次系统运行到System::TrackStereo()函数时,就会进入异常分支,进而调用Tracking::ResetActiveMap()函数进行实质上的重置,在该函数中系统状态会被设置为NO_IMAGES_YET

2.5 情况5

情况5的条件是:(1) 当前帧的Tracking失败了(bOK为false); (1) 上一次系统Tracking状态为OK; (2) 使用了IMU; (3) IMU没有初始化或者IMU的BA2优化没有完成。此时会重置地图。可以看到,在这个分支里,假如IMU可用且状态良好的话,其实当前系统就进入了RECENTLY_LOST状态。但加入IMU不可用或有问题,那么就重置当前地图。这也就是ORB-SLAM3的特色之一,也是和ORB-SLAM2不同的地方,也就是失败之前再“挣扎一下”。可以看到,假如没有IMU,那么此时系统会直接将当前系统状态设为LOST。但如果使用了IMU,且IMU状态良好,那么系统直接进入RECENTLY_LOST状态,不然的话,重置当前地图(进入NO_IMAGES_YET状态)。

情况5小节就是:如果当前帧视觉跟踪失败了,但上一帧是好的,我会根据IMU状态判断系统进入什么状态。假如IMU可用且状态良好,不重置地图,否则调用System::ResetActiveMap()函数,将System::mbResetActiveMap变量设为true。当下次系统运行到System::TrackStereo()函数时,就会进入异常分支,进而调用Tracking::ResetActiveMap()函数进行实质上的重置,在该函数中系统状态会被设置为NO_IMAGES_YET

2.6 情况6

在情况6中,满足以下条件重置地图:(1) 系统当前状态为LOST; (2) 当前地图中关键帧个数小于等于5; (3)。从判断条件可以看出来,其实这种情况是对应系统刚刚运行起来就进入LOST状态的情况。ORB-SLAM系统就把前5帧给舍弃了。

情况6小节就是:如果系统刚运行起来(当前地图中关键帧个数小于等于5)就进入LOST状态,就调用System::ResetActiveMap()函数,将System::mbResetActiveMap变量设为true。当下次系统运行到System::TrackStereo()函数时,就会进入异常分支,进而调用Tracking::ResetActiveMap()函数进行实质上的重置,在该函数中系统状态会被设置为NO_IMAGES_YET

2.7 情况7

和上面类似的,情况7其实和情况6是紧密关联的。当系统刚启动就进入LOST状态以后,如果没有IMU,那么没别的选择,直接重置。但如果有IMU且IMU已经初始化的话,那么就尝试新建地图(这样就相当于可以保住之前5个关键帧建立的地图,之后等待时机,看是否能融合)。但相反,如果IMU 没有初始化的话,那么也没得选择,重置当前地图。

情况7小节就是: 如果系统刚运行起来(当前地图中关键帧个数小于等于5)就进入LOST状态,且IMU没有初始化,就调用System::ResetActiveMap()函数,将System::mbResetActiveMap变量设为true。当下次系统运行到System::TrackStereo()函数时,就会进入异常分支,进而调用Tracking::ResetActiveMap()函数进行实质上的重置,在该函数中系统状态会被设置为NO_IMAGES_YET

2.8 所有可能情况总结

我们可以对上述7种情况简单整理个表格,如下。

3.Tracking中重置的具体操作

以上,我们便分析完了ORB-SLAM系统中所有Reset的可能情况。那么下面我们关心的是到底重置了什么?但也正如前面说的,实质的重置是通过Tracking::ResetActiveMap()函数完成的,那么在这个函数里到底做了什么呢?下面就进行分析,如下。 可以看到,在这个函数中还是做了挺多事情的。首先,如果有可视化窗口,通过Viewer::RequestStop()函数请求停止可视化。类似的,再获得当前地图,通过LocalMapping::RequestResetActiveMap()函数请求停止Mapping。然后再调用LoopClosing::RequestResetActiveMap()函数请求停止回环并将潜在回环关键帧队列清空。再调用KeyFrameDatabase::clearMap()函数清空当前地图中的所有关键帧。调用Atlas::clearMap()将地图点和关键帧全部清空。然后,我们对一些Tracking过程中修改的累计变量重新赋值,并且把当前系统状态设为NO_IMAGES_YET。然后再获取到地图集中其它的关键帧,把当前地图初始帧ID之后的所有帧都设为lost,并累加个数。最后,对Tracking类中的一些迭代变量重新默认的空值。至此,系统重置完成。

4.LocalMapping中重置的具体操作

当然,这里可以稍微展开说一说LocalMapping::RequestResetActiveMap()函数。首先,我们在Tracking::ResetActiveMap()函数中调用了它,如下。 这个函数本身并不复杂,如下。可以看到,函数首先把LocalMapping的成员变量mbResetRequestActiveMap设为了true。然后函数会执行到下面的while循环。因为此时mbResetRequestActiveMap为true,判断条件不满足,这个循环就会一直被执行。当然为了不过快,给重置局部地图一点时间,设置了每3妙查询一次的频率。然后,我们找到调用这个变量的地方,也就是LocalMapping::ResetIfRequested()函数,我们再找调用,如下。 可以看到,函数有且只有一个调用,也就是在LocalMapping的Run()函数中。换句话说,它是一直被反复执行的。那么现在,我们将mbResetRequestActiveMap设为了true,再进入到这个函数中来,他就会开始实质性的重置,具体如下。 可以看到,首先函数内部的临时变量executed_reset被设为true,然后我们直接清空了局部的关键帧和局部新添加的地图点。又把一些状态变量恢复默认值。最后,我们把mbResetRequestActiveMap设为了false。然后我们再回到LocalMapping::RequestResetActiveMap()函数中去。此时,就可以跳出函数内部的while循环,并且输出信息,提示重置完成。

本文作者原创,未经许可不得转载,谢谢配合

返回顶部