LocalMapping::Run() 是ORB-SLAM2中的线程主函数,负责维护和优化地图以确保地图的一致性和质量。
无输入参数。无输出。
LocalMapping线程的主要工作流程,包括处理新关键帧、剔除地图点、生成新地图点、融合重复点、局部捆绑调整、冗余关键帧剔除、闭环检测等任务,以维护ORB-SLAM2地图的一致性和质量。
mpCurrentKeyFrame->ComputeBoW()
:将关键帧的特征点描述子转换为词袋模型(BoW),并将关键帧插入到地图中。MapPointCulling
:根据地图点的观测情况,剔除质量不好的地图点。CreateNewMapPoints
:通过当前关键帧与相邻关键帧的三角化操作生成新的地图点。SearchInNeighbors
:检查并融合当前关键帧与相邻关键帧中重复的地图点。Local Bundle Adjustment
:当局部地图中的关键帧数量大于2个时,执行局部捆绑调整(BA)。KeyFrameCulling
:检测并剔除当前帧相邻的冗余关键帧。InsertKeyFrame
:将当前关键帧加入闭环检测队列中。CheckKeyFrames
):
ProcessKeyFrame
),包括计算BoW、更新观测、描述子、共视图等。MapPointCulling
)。CreateNewMapPoints
),当前关键帧与相邻关键帧通过三角化产生新的地图点,使跟踪更稳定。SearchNeighbors
):检查并融合当前关键帧与相邻关键帧中的重复地图点。Optimizer::LocalBundleAdjustmen
t,优化地图和相机位姿。KeyFrameCulling
):检测并剔除当前帧相邻的关键帧中的冗余关键帧。InsertKeyFrame
)。本函数用于ORB-SLAM2的地图维护和优化,作为LocalMapping线程的主要工作函数,在LocalMapping线程启动时被调用。
LocalMapping::Run()
负责处理新关键帧、地图点的剔除和生成、局部捆绑调整、冗余关键帧的剔除以及闭环检测等任务,以维护ORB-SLAM2地图的一致性和质量。该函数通过多个步骤循环执行,直到满足终止条件,同时与Tracking线程进行协同工作,确保地图的连贯性。
ProcessNewKeyFrame
() 函数主要功能是处理新关键帧,包括关联地图点、更新地图点信息,以及将新关键帧插入到地图中。
输入: 无输入参数。
ProcessNewKeyFrame()
调用了以下重要的函数,也就对应着主要执行步骤:
mpCurrentKeyFrame->ComputeBoW()
:计算当前关键帧的词袋(Bag of Words)向量。mpCurrentKeyFrame->GetMapPointMatches()
:获取当前关键帧中的与地图点匹配的特征点。pMP->AddObservation(mpCurrentKeyFrame, i)
:为地图点添加关键帧的观测信息。pMP->UpdateNormalAndDepth()
:更新地图点的平均观测方向和观测距离。pMP->ComputeDistinctiveDescriptors()
:计算地图点的最佳描述子。mpCurrentKeyFrame->UpdateConnections()
:更新当前关键帧与其他关键帧之间的连接关系(共视图)。mpMap->AddKeyFrame(mpCurrentKeyFrame)
:将当前关键帧插入到地图中。ProcessNewKeyFrame()
的执行步骤和功能是处理新的关键帧,包括计算词袋向量、更新观测、描述子、更新共视图连接关系,并将关键帧插入到地图中。
具体步骤如下:
pop front()
,作为当前要处理的关键帧。ComputeBoW
()。GetMapPointMatches
,对地图点进行更新:
pMP->AddObservation(mpCurrentKeyFrame, i)
,pMP->UpdateNormalAndDepth()
,pMP->ComputeDistinctiveDescriptors()
)。mlpRecentAddedMapPoints.push_back(pMP)
),等待后续MapPointCulling函数的检验。这些地图点可能来自双目或RGBD在创建关键帧中新生成的地图点,或者是CreateNewMapPoints 中通过三角化产生的。mpCurrentKeyFrame->UpdateConnections()
)。mpMap->AddKeyFrame(mpCurrentKeyFrame)
)。局部建图线程第一步操作,来自跟踪线程,等待处理的关键帧列表不为空,这些关键帧会进入一个队列中,等待局部建图线程的处理。
ProcessNewKeyFrame
()函数负责处理新的关键帧,它包括计算BoW表示、更新地图点信息、构建共视图以及将关键帧插入地图等多个步骤,以确保ORB-SLAM2地图的一致性和质量。
MapPointCulling
() 函数用于检查新增的地图点,根据地图点的观测情况剔除质量不好的新增地图点,确保地图的质量和一致性。
pMP->GetFoundRatio()
: 获取地图点的观测比例。具体来说,这个比例是通过以下方式计算的:
mnFound
:地图点 pMP
被观测到的总帧数,包括所有观测。mnVisible
:根据地图点的预测,它应该在相机视野中出现的总帧数。(mnFound ÷ mnVisible)
的值。如果这个比例很低,意味着这个地图点很少被观测到lit = mlpRecentAddedMapPoints.erase(lit);
的作用是删除列表中指向的地图点,并将 lit
迭代器更新为指向删除后的下一个元素。用于将新增地图点从列表中删除。MapPoint::SetBadFlag()
: 将地图点标记为坏点。MapPoint::isBad()
: 用于检查地图点是否被标记为坏点。nThObs
。mlpRecentAddedMapPoints
。isBad()
返回 true),则直接从列表中删除。GetFoundRatio()
)小于 25%,则将其标记为坏点并从列表中删除。"发现比例"通常用来衡量一个地图点在相机的视野中有多少时间被观测到。cnThObs
,则将其标记为坏点并从列表中删除。处理列表中的关键帧ProcessNewKeyFrame
之后,CreateNewMapPoints
也就是当前关键帧与相邻关键帧通过三角化产生新的地图点之前。
MapPointCulling
() 函数检查mlpRecentAddedMapPoints
并剔除不合格的新增地图点。通过遍历最近添加的地图点列表,根据观测比例和相机数量等因素进行筛选和标记,确保地图中的地图点质量和一致性。
CreateNewMapPoints() 函数用于根据当前关键帧与相邻关键帧之间的特征点匹配,通过三角化产生新的地图点,以提高地图的稳定性和密度。在局部建图线程中,会在共识关键帧之间重新进行特征匹配、三角化,生成新的地图点。这对于稳定的跟踪非常重要。
KeyFrame::GetBestCovisibilityKeyFrames()
: 获取与当前关键帧具有最佳共视关系的相邻关键帧列表。ORBmatcher::SearchForTriangulation()
: 在相邻关键帧间搜索匹配的特征点对,并进行极线约束剔除误匹配。KeyFrame::ComputeSceneMedianDepth()
: 计算关键帧场景深度的中值。ComputeF12()
: 根据两个关键帧的位姿计算它们之间的基础矩阵。x3D = pKF2->UnprojectStereo(idx2)
用双目信息恢复3D点。将图像上的一个特征点通过双目立体视觉的方法反投影到三维空间中,从而估算出该特征点在三维空间中的坐标。MapPoint
类的成员函数被调用,如 AddObservation()
, ComputeDistinctiveDescriptors()
, UpdateNormalAndDepth()
。执行步骤如下:
CreateNewMapPoints() 函数用于根据当前关键帧与相邻关键帧之间的匹配点,生成新的地图点。主要在LocalMapping::Run()线程中被调用,SearchInNeighbors之前,MapPointCulling之后,新建立的地图点会在后续通过 MapPointCulling() 函数进行检验。这有助于增加地图的稳定性和密度,提高系统的性能。
CreateNewMapPoints() 函数通过三角化恢复3D点,同时进行匹配点质量检查和尺度连续性验证,以确保生成的地图点质量高且位于相机前方、重投影误差小、尺度范围合理。这些新地图点将被添加到地图中,供后续的定位和地图维护使用。
SearchInNeighbors() 函数用于检查并融合当前关键帧与相邻帧(两级相邻)重复的地图点。这个函数主要处理地图点之间的匹配和融合,以维护和更新地图的一致性。具体地,它会在当前关键帧和一级、二级相邻关键帧之间进行投影匹配,并根据匹配结果融合地图点。
ORBmatcher::Fuse():
用于地图点的投影匹配和融合操作。这个函数在 SearchInNeighbor()
函数中调用2次。正向匹配和融合: 第一次调用ORBmatcher::Fuse函数是将当前关键帧的地图点投影到一级相邻关键帧上进行匹配和融合。反向匹配和融合: 第二次调用ORBmatcher::Fuse函数是将一级相邻关键帧的地图点投影到当前关键帧上进行匹配和融合。mpCurrentKeyFrame->GetBestCovisibilityKeyFrames(nn);
获得和当前关键帧相邻的关键帧,也就是一级相邻关键帧KeyFrame::GetMapPointMatches():
获取与关键帧相关的地图点。MapPoint::ComputeDistinctiveDescriptors():
计算地图点的描述子。MapPoint::UpdateNormalAndDepth():
更新地图点的平均观测方向和深度。KeyFrame::UpdateConnections():
更新关键帧之间的共视连接关系。主要步骤有以下4步。1. 获取一级和二级相邻关键帧;2. 正向投影融合:将当前关键帧的地图点投影到相邻关键帧上,查找匹配并融合;3.反向投影融合:将相邻关键帧的地图点投影到当前关键帧上,查找匹配并投影融合。4.更新地图点属性:对当前关键帧的地图点进行属性更新。具体而言:
vpTargetKFs
集合中。vpFuseCandidates
中。如果地图点是坏点,或者已经加进集合候选融合点集合,跳过。SearchInNeighbors() 函数主要在LocalMapping::Run()线程中被调用,在已经处理完队列中的最后的一个关键帧之后(CreateNewMapPoints以及not CheckNewKeyFrames())之后,局部地图BA之前(LocalBundleAdjustment之前)。
局部建图线程中,地图点产生了较大的变动。比如前面讲过的,在共事关键帧之间重新进行特征匹配三角化。生成新的地图点。又如,前面根据一定的规则剔除不合格的地图点,这时就需要对已有的地图点进行整理。包括合并重复的地图点。用更准确的地图点替换旧的地图点,最后统一更新地图点的描述子、深度、平均观测方向等属性。
SearchInNeighbors() 通过地图点投影匹配,寻找并融合当前关键帧与其相邻帧之间的重复地图点,然后更新地图点属性和关键帧之间的共视关系。主要步骤是当前关键帧与其相邻帧之间地图点的正向投影融合与反向投影融合,前者检查当前关键帧的地图点与一级相邻关键帧上的地图点是否一致,以及是否有新的地图点可以在一级相邻关键帧上生成;后者检查一级相邻关键帧的地图点是否与当前关键帧上的地图点一致,以及是否有新的地图点可以在当前关键帧上生成。函数确保地图点的一致性,减少重复地图点的数量,以保持地图的稳定性和准确性。
ORBmatcher::Fuse函数用于将地图点投影到关键帧中进行匹配和融合,实现地图点的更新和扩充。该函数的核心目标是在给定关键帧和一组待投影的地图点之间建立关联,然后根据匹配的情况对地图点进行融合或添加新的观测信息。
ORBmatcher::Fuse函数调用了以下重要的函数:
pKF->GetRotation()
和GetTranslation(): 用于获取关键帧的旋转和平移信息。pMP->GetWorldPos()
获得地图点在世界坐标系下的位置pMP->GetDescriptor();
获得地图点描述子pKF->IsInImage():
输入坐标,用于检查投影点是否在图像范围内。pMP->PredictScale(dist3D,pKF)
: 根据地图点到相机光心的距离来预测匹配点所在的金字塔尺度。GetFeaturesInArea():
根据投影点和搜索半径,在关键帧图像中找到候选匹配点的索引。DescriptorDistance()
: 用于计算两个描述子之间的距离,用于匹配。pMP->AddObservation(pKF,best Index)
; 关键帧中的最佳匹配的地图点,向地图点添加对应的关键帧信息pKF->AddMapPoint(pMP,best Index);
向关键帧中,添加最佳地图点的信息该函数通常在局部建图模块的地图点更新和扩充过程中被调用,LocalMapping::SearchInNeighbor()
函数中调用2次,用于正向匹配和融合,反向匹配和融合。用于将新的地图点与关键帧进行匹配和融合。
ORBmatcher::Fuse
函数用于将待投影的地图点与给定关键帧进行匹配,如果找到匹配点,则根据描述子距离和阈值来决定是融合已有地图点还是添加新的地图点。这一过程有助于更新和扩充地图,提高建图的精度和稳定性。
KeyFrameCulling
() 函数用于检测当前关键帧在共视图中的关键帧,并根据地图点在共视图中的冗余程度来剔除冗余的共视关键帧。冗余关键帧是指那些观测到的地图点在其他关键帧中也有足够数量的观测的关键帧。这个操作有助于优化地图的效率和一致性。
输入:
输出:
变量:
mpCurrentKeyFrame
:当前关键帧,本程序就是判断它是否需要删除pKF
: mpCurrentKeyFrame的某一个共视关键帧vpMapPoints:pKF
对应的所有地图点pMP:vpMapPoints
中的某个地图点observations
:所有能观测到pMP的关键帧pKF i
:observations中的某个关键帧scaleLevel i
:pKFi的金字塔尺度scaleLevel
:pKF的金字塔尺度vpLocalKeyFrames = mpCurrentKeyFrame->GetVectorCovisibleKeyFrames()
: 获取当前关键帧在共视图中的所有共视关键帧。vpMapPoints = pKF->GetMapPointMatches();
: 获取关键帧观测到的地图点。pMP->isBad()
: 检查地图点是否被标记为"坏点"。pMP->Observations()
: 获取地图点被观测到的相机数量。map<KeyFrame*, size-t> observations = pMP->GetObservations():
获取观测到地图点的关键帧列表。pKF->SetBadFlag():
将关键帧标记为"坏帧",需要剔除。整体步骤为3层for循环。对所有的共视关键帧进行遍历,遍历该共视关键帧的所有地图点,判定冗余地图点。通过判定观测到该地图点的关键帧是否超过3个,被3个关键帧观测到就是冗余地图点。如果该关键帧90%以上的有效地图点被判断为冗余的,则认为该关键帧是冗余的,需要删除该关键帧。具体步骤如下:
在少数恶劣情况下需要通过加入更多关键帧来提高跟踪成功率。但太多关键帧会使得局部BA变得非常慢,因此需要及时剔除冗余关键帧。该函数通常在局部地图优化的过程中调用,闭环检测队列添加当前关键帧之前,在局部BA之后。
KeyFrameCulling
() 是一个用于冗余关键帧检测和剔除的关键函数。它通过分析每个关键帧观测到的地图点,判断它所具有的大部分地图点是否可以被其他关键帧替代?具体而言是是90%的大部分地图点,以及3个其他关键帧。