ORB-SLAM2线程内各方法阈值设置–LocalMapping线程
ORB-SLAM2线程内各方法阈值设置--LocalMapping线程
Run(): 执行局部BA优化的时机
当前地图中的关键帧大于2个的时候,执行局部地图的BA优化
// Step 6:如果当前地图中的关键帧大于2个的时候,执行局部地图的BA优化
if(mpMap->KeyFramesInMap() > 2)
/// [实时性编程技巧]
// 执行局部地图的BA优化
// 其中mbAbortBA参数是以地址的形式传递,当mbAbortBA状态改变时,系统能够立即获取地址中值的改变并及时执行/停止BA
Optimizer::LocalBundleAdjustment(mpCurrentKeyFrame,&mbAbortBA, mpMap);
MapPointCulling(): 新增地图点检测与剔除限制
- 跟踪到该地图点的帧数比可观测到该地图点的帧数的比例小于25%,则从地图中删除该地图点
// Step 2.2: 跟踪到该地图点的帧数比可观测到该地图点的帧数的比例小于25%,则从地图中删除该地图点
// (mnFound / mnVisible) < 25%
// mnFound :地图点被多少帧(包括普通帧)中的特征点匹配到,帧数越多越好
// mnVisible:地图点应该被看到的次数
// (mnFound / mnVisible):对于大FOV镜头这个比例会高,对于窄FOV镜头这个比例会低
else if(static_cast<float>(mnFound) / mnVisible < 0.25f )
{
pMP->SetBadFlag();
lit = mlpRecentAddedMapPoints.erase(lit);
}
- 从该地图点建立开始,到现在已经经历了不小于2个关键帧, 但是观测到该点的相机数不超过阈值cnThObs的话,将该点从地图中删除
// Step 2.3: 从该地图点建立开始,到现在已经经历了不小于2个关键帧
// 但是观测到该点的相机数不超过阈值cnThObs的话,将该点从地图中删除
else if(((int)nCurrentKFid - (int)pMP->mnFirstKFid) >= 2 && pMP->Observations() <= cnThObs)
{
pMP->SetBadFlag();
lit = mlpRecentAddedMapPoints.erase(lit);
}
3.从建立该点开始,已经经过了不小于3个关键帧都没有被剔除,则认为该地图点是高质量的地图点,对于高质量地图点.仅从待检查列表中删除
else if(((int)nCurrentKFid - (int)pMP->mnFirstKFid) >= 3)
lit = mlpRecentAddedMapPoints.erase(lit);
CreateNewMapPoints():生成新的地图点的情况
双目相机:关键帧的间距(两帧相机间的距离 < 基线长度), 不生成地图点
if(!mbMonocular)
{
// 如果是双目相机,关键帧间距 < 本身的基线长度则不生成3D点
if(baseline < pKF2->mb)
continue;
}
单目相机:计算相邻关键帧中所有地图点的深度中值和基线与场景深度中值的比例,比例<0.01 不生成地图点
else
{
// 单目相机的情况
// 计算相邻关键帧中所有地图点的深度中值
const float medianDepthKF2 = pKF2->ComputeSceneMedianDepth(2);
// 计算基线与场景深度中值指定比例
const float ratioBaselineDepth = baseline / medianDepthKF2;
// 如果比例特别小,说明基线太短恢复3D点不准确,则跳过当前邻接的关键帧,不生成3D点
if(ratioBaselineDepth < 0.01)
continue;
}
SearchInNeighbors():检查并融合当前关键帧与相邻帧(两级相邻)重复的地图点
正向投影融合: 是"每个关键帧和当前关键帧的地图点进行融合"
matcher.Fuse(pKFi,
vpMapPointMatches);
反向投影融合: 是"当前关键帧和所有邻接关键帧(一级+二级)的地图点进行融合"
matcher.Fuse(mpCurrentKeyFrame,
vpFuseCandidates);
ORBmatcher::Fuse():地图点匹配融合:
将地图点投影到关键帧中进行匹配和融合;融合策略如下
1.如果地图点能匹配关键帧中的特征点,并且该特征点有对应的地图点,那么选择观测数目多的替换两个地图点
2.如果地图点能匹配关键帧中的特征点,并且该特征点没有对应的地图点,那么为该点添加该投影地图点
-
**KeyFrameCulling()😗*检测当前关键帧在共视图中的关键帧,根据地图点在共视图中的冗余程度剔除该共视关键帧
冗余关键帧的判定:90%以上的地图点能被其他关键帧(至少3个)观测到
// 对所有的共视关键帧进行遍历 for(vector<KeyFrame*>::iterator vit = vpLocalKeyFrames.begin(), vend = vpLocalKeyFrames.end(); vit != vend; vit++) { KeyFrame* pKF = *vit; // 第1个共视关键帧不能删除,跳过 if(pKF->mnId == 0) continue; // Step 2:提取每个共视关键帧的地图点 const vector<MapPoint*> vpMapPoints = pKF->GetMapPointMatches(); // 记录某个点被观测次数,后面并未使用 int nObs = 3; // 观测次数阈值,默认为3 const int thObs = nObs; // 记录冗余观测点的数目 int nRedundantObservations = 0; int nMPs = 0; // Step 3:遍历该共视关键帧的所有地图点,其中能被其它至少3个关键帧观测到的地图点为冗余地图点 for(size_t i = 0, iend = vpMapPoints.size(); i < iend; i++) { MapPoint* pMP = vpMapPoints[i]; if(pMP) { if(!pMP->isBad()) { if(!mbMonocular) { // 对于双目或RGB-D,仅考虑近处(不超过基线的40倍 )的地图点 if(pKF->mvDepth[i] > pKF->mThDepth || pKF->mvDepth[i] < 0) continue; } nMPs++; // pMP->Observations() 是观测到该地图点的相机总数目(单目1,双目2) if(pMP->Observations() > thObs) { const int &scaleLevel = pKF->mvKeysUn[i].octave; // Observation存储的是可以看到该地图点的所有关键帧的集合 const map<KeyFrame*, size_t> observations = pMP->GetObservations(); int nObs = 0; // 遍历观测到该地图点的关键帧 for(map<KeyFrame*, size_t>::const_iterator mit = observations.begin(), mend = observations.end(); mit != mend; mit++) { KeyFrame* pKFi = mit->first; if(pKFi == pKF) continue; const int &scaleLeveli = pKFi->mvKeysUn[mit->second].octave; // 尺度约束 // Q: 为什么pKF 尺度+1 要大于等于 pKFi 尺度? // A:因为同样或更低金字塔层级的地图点更准确 if(scaleLeveli <= scaleLevel+1) { nObs++; // 已经找到3个满足条件的关键帧,就停止不找了 if(nObs >= thObs) break; } } // 地图点至少被3个关键帧观测到,就记录为冗余点,更新冗余点计数数目 if(nObs >= thObs) { nRedundantObservations++; } } } } } // Step 4:如果该关键帧90%以上的有效地图点被判断为冗余的,则认为该关键帧是冗余的,需要删除该关键帧 if(nRedundantObservations > 0.9 * nMPs) pKF->SetBadFlag(); }