1.关键帧
如果当前帧被认为是关键帧,则进入函数FullSystem::makeKeyFrame
:
- 和非关键帧一样,利用当前帧对前面关键帧中的未成熟点进行逆深度更新FullSystem::traceNewCoarse;
- 标记后面需要边缘化(从活动窗口踢出)的帧FullSystem::flagFramesForMarginalization;
- 将当前帧加入到滑动窗口中frameHessians.push_back(fh),并计算一下该窗口中其他帧与当前帧之间的一些参数比如相对光度、距离等FullSystem::setPrecalcValues;
- 将当前帧加入到总的能量函数中EnergyFunctional(ef);
- 遍历窗口中之前所有帧的成熟点pointHessians,构建它们和新的关键帧的点帧误差PointFrameResidual,加入到ef中;
- 激活窗口中之前所有帧中符合条件的未成熟点,将其加入到ef中FullSystem::activatePointsMT;
- 利用高斯牛顿法对活动窗口中的所有变量进行优化FullSystem::optimize;
- 去除外点FullSystem::removeOutliers;
- 边缘化不需要的点EnergyFunctional::marginalizePointsF;
- 在当前帧中提取未成熟点FullSystem::makeNewTraces;
- 边缘化不需要的帧FullSystem::marginalizeFrame。
1.1 边缘化决策
主要两点(FullSystem::flagFramesForMarginalization):
- 当活跃的帧的数量大于最低要求(5个)时,边缘化一帧中活跃的点少于5%或者和最新的帧相比光度参数变化剧烈( |a1−a2|>0.7)的帧(从最早的帧开始遍历);
- 如果过程1没有找到需要边缘化的帧,则从全部帧中找到除最近的两帧外离当前帧最远的一帧。
1.2 点的激活
DSO代码中PointHessian表示可用于追踪和参与局部优化的点,除了初始化的第一帧外,它来源于每次生成关键帧时对未成熟点的提取FullSystem::makeNewTraces,并在后续关键帧生成时进行激活FullSystem::activatePointsMT,成功激活的点就由ImmaturePoint变为PointHessian,激活的基本步骤如下:
- 根据当前窗口中已有的成熟点的数量ef->nPoints,设置激活阈值currentMinActDist;
- 将所有的成熟点投影到当前帧,生成距离地图CoarseDistanceMap::makeDistanceMap(比如位置p有一个投影点了,那么位置p的值设为0,周围一圈像素设为1,再外面一圈设为2,以此类推,迭代进行);
- 遍历所有的未成熟点,如果满足一些条件比如上一次的投影轨迹长度(极线)小于8,quality(次最小误差比最小误差)大于3等,就将逆深度设为其最大值和最小值的平均,将其投影到当前帧,然后考虑其在距离地图上的值,如果该值足够大(用到了第一步中的激活阈值),可以认为该点附近没有成熟点,所以将其加入待优化序列里,反之,则删除该点;
- 对待优化序列里的未成熟点进行优化FullSystem::activatePointsMT_Reductor,然后激活;
未成熟点的优化是对其逆深度迭代优化。逆深度求导的过程和DSO初始化中的类似,额外加入了一个和点的梯度有关的系数wp,梯度越大,权重系数越小:
Jρ1=∂ρ1∂f(x)=wpwhρ1−1ρ2(∇Ixfx(tx−u2′tz)+∇Iyfy(ty−v2′tz))
2 滑动窗口法
求导的参数:相对的光度参数,相对位姿,主帧上点的逆深度,相机内参
对相机位姿求导:对应代码中:Vec6f Jpdxi[2]
∂δξ21∂rk=∂x2∂rk∗∂ξ21∂x2=[dx,dy]∗∂ξ21∂x2
∂ξ21∂x2=⎣⎡fxρ2000fyρ20−fxρ2u2′−fyρ2v2′0−fxu2′v2′−fy(1+v2′2)0fx(1+u2′2)fyu2′v2′0−fxv2′fyu2′0⎦⎤
对逆深度求导:对应代码中:Vec2f Jpdd;
[fxρ1−1ρ2(t21x−u2′t21z)fyρ1−1ρ2(t21y−v2′t21z)]
对相机内参求导:VecCf Jpdc[2];
∂C∂x2=[∂fx∂u2∂fx∂v2∂fy∂u2∂fy∂v2∂cx∂u2∂cx∂v2∂cy∂u2∂cy∂v2]
∂fx∂u2∂fy∂u2∂cx∂u2∂cy∂u2∂fx∂v2∂fy∂v2∂cx∂v2∂cy∂v2=u2′+fx∂fx∂u2′=u2′+ρ2ρ1−1(r31u2′−r11)fx−1(u1−cx)=fx∂fy∂u2′=fxfy−1ρ2ρ1−1(r32u2′−r12)fy−1(v1−cy)=fx∂cx∂u2′+1=ρ2ρ1−1(r31u2′−r11)+1=fx∂cy∂u2′=fxfy−1ρ2ρ1−1(r32u2′−r12)=fy∂fx∂v2′=fyfx−1ρ2ρ1−1(r31v2′−r21)fx−1(u1−cx)=v2′+fy∂fy∂v2′=v2′+ρ2ρ1−1(r32v2′−r22)fy−1(v1−cy)=fy∂cx∂v2′=fyfx−1ρ2ρ1−1(r31v2′−r21)=fy∂cy∂v2′+1=ρ2ρ1−1(r32v2′−r22)+1
对光度参数求导:对应代码VecNRf JabF[2];
δphoto Jphoto =[−eaji,−bji]=∂δphoto ∂rk=[∂−eaji∂rk∂−bji∂rk]=[Ii[pi]−bi1]
3.优化过程中零空间的处理
对于纯视觉SLAM,其零空间有7维:3 rotation+3 translation+1 scale;对于VIO系统而言,充分的加速度计激励可以为系统提供尺度信息,加速度计同时可以提供重力方向,使得pitch和roll可观,因此其不客观的维度为4维:3 translation+1 yaw。
对于位置的不可观可以这样理解,当地图整体移动,整个SLAM的优化问题是不变的。所以SLAM中的零空间其实是整个优化问题的零空间,而不是说是优化中某个节点的零空间。就是说整个优化问题存在不可观的维度,这个不客观的维度会通过优化问题进而影响到某个节点的优化,导致那个节点出现问题,常见的比如说纯视觉SLAM在转弯的时候,表现在地图中可能是地图的尺度突然缩小,相机移动量也对应缩小,同时不会对能量函数造成影响
在数学上: 对于正规方程: HΔx=b (1)
其中hession矩阵的零空间满足:HΔxns=0 (2)
结合两个式子,一定有:H(Δx+Δxns)=b
因此在零空间上的漂移不会违反系统的约束,但是会对结果产生影响
初始化一般进行归一化,相当于是人为设了一个尺度,为什么还会有尺度问题呢?这是因为DSO采用的滑动窗口法进行优化,当前面的关键帧离开滑动窗口后,最多只