本篇博客,我们以ORB-SLAM3系统什么情况下会重置(Reset)以及到底重置了什么为主题,分析ORB-SLAM3系统中的重置逻辑和可能引发重置的情况。
1.重置相关函数与调用关系
首先,Reset是通过System
类的TrackStereo()
函数中的判断来决定是否对系统进行Reset的,如下。
而这个判断条件就是System
类的成员变量mbReset
和mbResetActiveMap
。这个两个变量默认都为false。而这两个变量分别在System
类的Reset()
函数和ResetActiveMap()
函数中被设为true,如下。
然而,可以看到,整个系统中,System
的Reset()
函数从来没有在任何地方被调用过(函数名为灰色),也就是说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,可以看到如下。
可以看到,整个系统中,只在LocalMapping
的Run()
函数里被设为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循环,并且输出信息,提示重置完成。
本文作者原创,未经许可不得转载,谢谢配合