1、在 Initializer.cc 中 Initializer::ReconstructH() 函数中,出现的下面代码,大概在 684 行左右
cv::Mat n = V*np;
if(n.at<float>(2)<0) // 这里为什么这么做?????
n=-n;
vn.push_back(n);
不知道为什么要判断,n 既然是平面的单位法向量,不知道其正负有什么影响?
在程序中这个值虽然保存了,但是没有用过,推测这里可能就是为了获得一个正的法向量。
2、在 Tracking.cc 中 Tracking::UpdateLastFrame() 中第三行代码那里。对于单目来说是否有必要处理?
答:仅仅对单目经过分析,其实这里不需要调用这个函数
3、在 int ORBmatcher::SearchByProjection(Frame &F, const vector<MapPoint*> &vpMapPoints, const float th)
中 89 行左右。为什么地图点被关键帧观测过就不处理了???
答:因为哪里主要为了找到追踪过程的局部地图点投影到 Frame 中,此时说明该 Frame 的关键点已经有了匹配的 3d 点了。那么此时这个关键点就不参与其他 3d 点匹配了
4、在 MapPoint.cc 中这个函数 MapPoint::GetMinDistanceInvariance() 上面的哪个函数底部。 mfMaxDistance mfMinDistance 这两个距离的原理是什么? mfMaxDistance 这个值为什么不会变化??在 ORBmatcher::SearchByProjection(Frame, KeyFrame, set<>) 函数中,下面代码中的判断条件是依据什么决定的?
// 不知道这里判断的依据??? if(dist3D<minDistance || dist3D>maxDistance) continue;
首先代码部分:
mfMaxDistance = dist*levelScaleFactor; mfMinDistance = mfMaxDistance/pRefKF->mvScaleFactors[nLevels-1];
因为对于一个实际物体点,以及同一个相机。只有在距离该点特定位置处才能恰好投影到相机图像上。如果在相机方向不变的情况下,离物体点距离远点或者近点。相机图像上一个像素就可能无法代表这个实际物体点了(视离物体点远多少和近多少而定)。假设在某个位置,相机原始图像恰好看到这个物体点。记此时的图像为理想图像A。进而可以计算出此时相机到物体点的实际距离。前/后移动了一定距离的话,相机就没法看到该物体点了。所以对于同一个物体点,与相机的实际距离是固定的也就是这里的 mfMaxDistance。
但是可能会问,为什么会有最大值和最小值之分,这是因为在 orb-slam 中考虑了金字塔图像,因为相机的图像可以降采样。那么只要相机移动后获得了一帧图像 B,那么此时就会得到对应的金字塔图像 BS,只要 B 和 BS 中有一帧图像能够包含到图像A(包含关系从距离上判断)。那么就相当于相机也是可以看到这个物体点。所以才会有最大值和最小值。下面具体分析最大值和最小值的含义
dist 计算的是地图点和其参考关键帧(处理该关键帧时进行三角化该地图点)之间的世界坐标系的距离。这个距离实际上是利用原始图像关键点之间进行三角化时的点坐标。因为这个点对应此时相机位置的金字塔图像第 L 层,那么就是说,相机最远移动到 L 层金字塔图像所在的实际位置后,在远离一点,那么该点就没法被当前相机检测到了(即使降采样也无法获得该点)。所以这是相机离地图点最远的距离。对于最近点,其实就像上面代码所写。只要相机采集到图像,然后对图像进行降采样金字塔化,那么就可以检测到该点。那么最近距离所在位置,实际上就是相机在那个位置获得图像,然后金字塔化后,最后一层金字塔能够检测到该点。上面公式展开就是 dist*1.2^L/1.2^(nLevels-1) = dist /1.2^(nLevels-1-L)
5、在 ORBmatcher::SearchByProjection(Frame &CurrentFrame, const Frame &LastFrame, const float th, const bool bMono) 函数内部,搜索窗口半径是按照当前关键点对应的金字塔层数。以及相应的尺度因子来确定的。同理在重载函数中 1571 行左右也有这种方式!
float radius = th*CurrentFrame.mvScaleFactors[nLastOctave];
答: 因为确定好关键点所属金字塔层数。那么就会用该金字塔层数代表的尺度因子乘以半径。使得该半径值能够覆盖金字塔图像上的临近关键点。(降采样的图像关键点距离会变大!)
6、在自动初始化时,低视差是如何确定的???选择 H/F
答:所谓的低视差是说两个相机共同观测同一个世界地图点,链接 2 个相机光心与地图点构成的夹角比较小。此时说明该地图点距离相机是非常远的。按照人眼观看远处物体。所有的点基本上是在一个平面上,因为选择 H 矩阵,如果夹角比较大,说明该点距离相机平面很近,多个点不在一个平面上,那么就选择 F 矩阵,通过两种方式都可以恢复运动,但是如何确定 H/F ,论文中是用了一个评分机制,就是通过分别求解 H/F 然后看投影误差构成的评分函数。最后选择评分相对高的 H/F
7、在 Map Initialization 初始化设置的 sigma = 1.0 ,Ransac 迭代次数 为 200,这里参数选择问题?
答:sigma = 1.0 表示测量误差是一个像素误差,实际上这是相机能够测量的最小误差。对于 Ransac = 200 迭代次数,其实可以参考 mvg 中的迭代次数的确定部分。
8、在 Optimizer::PoseOptimization(Frame *pFrame) 函数内部。4 次优化过程中。阈值是如何计算?
答:这里对于单目给的 4 个参数是说余维度 = 2,但是双目给的参数是余维度 3
9、在 Tracking::NeedNewKeyFrame() 中 1047 行左右,哪个 if || 的两个条件的判断。以及在后面 if(bLocalMappingIdle) 这里面,为什么对于单目来说。仍然需要停止打断 BA 优化??
答:这里是说,此时说明需要紧急插入一个新的关键帧了,让繁忙的局部建图赶紧停下来。否则关键帧插入不及时就会影响下次的跟踪。
10、下面一段代码是在 ORBextractor.cc 中提取完毕关键点后,计算对应描述子部分。具体代码是在 operator() 函数内部。这里有一个问题:为什么需要在正式计算描述子之前对每层图像进行高斯模糊?为什么在计算关键点的时候没有进行模糊?
Mat workingMat = mvImagePyramid[level].clone();
// 此时是自己根据 Size(7,7) 2,2自己计算高斯核,然后进行图像的高斯模糊,具体如何进行自定义计算高斯核:
// 可以参考:https://www.cnblogs.com/tornadomeet/archive/2012/03/10/2389617.html
GaussianBlur(workingMat, workingMat, Size(7, 7), 2, 2, BORDER_REFLECT_101);
// Compute the descriptors // 内部包含了多层图像的描述子,从上到下依次存储每层的描述子
Mat desc = descriptors.rowRange(offset, offset + nkeypointsLevel);
// desc 返回当前层所有关键点对应的描述子
computeDescriptors(workingMat, keypoints, desc, pattern);
答:计算描述子这里是说,为了去除噪声对计算的影响。但是关键点计算前为什么没有
11、在 LocalMapping:ProcessNewKeyFrame() 函数中其中一个变量 mlpRecentAddedMapPoints 不理解怎么回事?
答:保存最近新增加的地图点,看看这个地图点是否是冗余的,如果是冗余的话就会剔除。这个变量在当前这个函数中其实会增加多次,经过测试发现,是因为 mpCurrentKeyFrame 关键帧包含了重复的地图点导致的。
12、在 ORBmatcher.cc 中 ORBmatcher::SearchForTriangulation() 函数内部在下面代码块中,判断极点和 kp2 点之间的距离,哪个阈值如何选取?有什么意义???
if(!bStereo1 && !bStereo2) // 对于单目来说,这里成立
{
const float distex = ex-kp2.pt.x;
const float distey = ey-kp2.pt.y;
if(distex*distex+distey*distey<100*pKF2->mvScaleFactors[kp2.octave])
continue;
}
答:if 判断的目的:限制地图点到关键帧 1 的距离,因为 e2 和 kp2 的距离与光心 1 到 地图点的距离成比例,只要限制 e2 和 kp2 距离即可。
解释: 距离如果越接近。在平移和旋转不变的情况下。三角化的不确定度越大。可以看三角测量中,变化同样的角度时,deta(d) 的变化情况。
13、在优化函数时,信息矩阵为什么需要加上关键帧所在尺度的 sigma?
答:如果按照原始图像上的关键点来说,1 个像素误差就是一个像素误差。但是对于某个关键点处于不同的金字塔图像上,因此,在同层金字塔图像(假设处在第 level 层)上相差一个像素误差,实际上应该相差一个 level*1 个像素误差。因为投影时是按照原始图像计算误差。所以信息矩阵需要将计算出来的误差变为相应金字塔层数上的误差, 所以要乘以 1/level
14、在局部建图线程中一些不好的点的剔除在 MapPoint::SetBadFlag() 这里,但是可以看到不好的地图点的内存在调用这个函数后没有清理。是因为这些地图点可能被其他地方用吗???到底是在哪里清理的这个资源?如果不清理的话,内存肯定会越来越大。实际上最好的方式应该是在 mpMap->EraseMapPoint() 中 delete 这个值才对。经过测试在 Map::EraseMapPoint() 函数中,直接 delete 删除地图点会出现错误!!!目前不知道如何改进
答:orb-slam2 根本没有清理一些不好的地图点资源。首先是代码本身耦合成都有些高,比如
11
问题那里代码处可以发现的 bug,按理说不同关键点不能匹配上相同的地图点。但是经过测试那里确实有这种情况,所以说明程序本身没有理清地图点这种资源。找不到合适的地方释放资源。只能在系统重置后进行释放。但是在长时间运行程序时会造成内存泄露。当然一种解决方案是可以使用智能指针。
15、在共视图更新方面(UpdateConnections()函数)实际上都是在跟踪线程、局部建图以及闭环线程中进行更新。对于跟踪线程来说,就是在单目初始化时创建关键帧后更新的共视图关系,对于局部建图线程来说。仅仅针对当前线程正在处理的关键帧,对于闭环来说,处理更新的是基准闭环的临近关键帧的共视关系。
16、Sim3Solver::Sim3Solver() 函数中下面代码处:
mvnMaxError1.push_back(9.210*sigmaSquare1); // ???? 为什么选取这个参数???:
mvnMaxError2.push_back(9.210*sigmaSquare2);
答:这是一个像素误差且 alpha = 0.99 对应的 ransac 阈值。参考 mvg A2.2 卡方分布
17、在 Optimizer::LocalBundleAdjustment(KeyFrame pKF, bool pbStopFlag, Map* pMap) 在 Check inlier observation 哪里,为什么不设置鲁棒核函数?
答:在那里经过优化一次后,杂点已经被排除,再次优化时仅仅是利用内点集进行优化,没必要使用鲁棒核,鲁棒核仅仅用来让外点不好的影响减弱小。
18、