DSOL(Direct Sparse Odometry Lite)是宾夕法尼亚大学重构DSO(直接稀疏里程计)的一个版本,众所周知DSO的代码自身耦合比较复杂,而DSOL主要是重构了DSO的代码。增加了gtest测试、glog和fmt格式化输出、absl多线程并行,交互使用ROS一定程度影响了效率。
DSOL可适用双目和单目的SLAM系统。
概述
DSOL结构非常清晰,几乎和DSO算法流程图一样一一对应,看到名字就知道对应的意义。主要都是放到 DirectOdometry::Estimate 下,有以下几个大块对应那三个线程
- Track(image_l, image_r, dT)
- Map(bool add_kf, const cv::Mat& depth)
- BundleAdjust 被Map调用
- BundleAdjuster::Marginalize 被RemoveKeyframe调用
在前端跟踪时使用 FrameAligner,完成相对KeyFrame的直接图像对齐,计算AlignCost;后端优化时使用 BundleAdjuster,计算光度BA问题,主要使用Hessian计算舒尔补边缘化并求解优化。
命名
其中基本命名不同包括:
- aligner 对应于直接图像对齐的实例
- adjuster 对应于后端PBA的实例
- refine 主要指的是双目直接匹配的视差精化
- ooc, oob 遮挡或运动目标引起的bad、out,需要丢弃的patch
- patch 原为pattern
- Forward 正向wrap投影、Backward 反向投影
- FrameHessian1 用于aligner、FrameHessian2 用于adjuster
数据结构
主要数据结构和DSO一样,可能有改名字的,更清晰了。除了其他SLAM中都有的,还包含下面这些主要的基本数据结构
- AffineModel 光度仿射模型
- ImagePyramid 图像金字塔
- PixelSelector 用于找到图像中具有大梯度的像素
- Patch 像素周围的块,也就是pattern
- PatchHessian 分块Hessian的图像梯度块的基类,用于aligner和adjuster
- PointHessian 这个不是常用的,只用于refiner
- Frame 不包含FramePoint,仅用于tracking
- Keyframe 包含FramePointGrid 等,是维护的主要内容
- FramePoint 关键帧上的iDepth点,存储Jacobian
- FramePointGrid 每个格子 大小 32*32, 包含FramePoint
- FrameState 包含SE3和affine
- FrameHessian 一个基类,包含大小信息,不包含Hessian矩阵。用于实现 1.aligner中使用的三角化程度比较低的单帧Hessian 2. adjuster中使用的帧间关联Hessian 3. FrameHessianX 包含动态大小Hessian数据
- KeyframeWindow 包含多个KF的滑窗,放在window.h中
- FrameAligner 直接图像对齐
- DepthPointGrid 每个格子,包含逆深度和像素点
- BundleAdjuster 计算光度BA问题
- FramePointHessian 存储求解出的点数据,
- SchurFrameHessian 存储舒尔补之后的数据,
- StereoMatcher 用于双目的稀疏立体匹配器
主干算法逻辑
按照调用顺序,主要流程按三个大块,所用到的用于SLAM功能的函数如下:
NodeData::Run
DirectOdometry::Estimate
DirectOdometry::Track{ // 跟踪
DirectOdometry::TrackFrame{
FrameAligner::Align
FrameAligner::AlignLevel 金字塔一层的图像对齐
FrameAligner::Solve
SolveCholeskyScaled
FrameAligner::BuildLevel 为当前层构建AlignCost,返回FrameHessian1
AlignCost::Build 为每个patch构造FrameHessian
AlignCost::WarpPatch 输入patch0、point0和FrameHessian1,返回DepthPoint
AlignCost::UpdateHess 为单独一个patch 更新Hessian
AlignCost::CalcJacGeo 计算du_dx类型为JacGeo
FrameHessian1::AddPatchHess
}
DirectOdometry::ShouldAddKeyframe
}
DirectOdometry::Map{ // 建图
DirectOdometry::AddKeyframe(cv::Mat& depth)
DirectOdometry::AddKeyframe(Frame& frame)
PixelSelector::SetOccMask
PixelSelector::Select
Keyframe::InitPoints
StereoMatcher::Match
StereoMatcher::MatchCoarse
StereoMatcher::MatchRefine
StereoMatcher::BestMatch
Keyframe::InitFromDisp
Camera::Disp2Idepth 视差到深度图
Keyframe::InitFromAlign(cv::Mat& idepth) 初始化
Keyframe::InitPatches 按金字塔层级初始化patch
Keyframe::InitPatchesLevel
Patch::ExtractAround3
}
DirectOdometry::BundleAdjust{ // PBA
BundleAdjuster::Adjust
BundleAdjuster::AdjustLevel
BundleAdjuster::BuildLevel 为所有关键帧单个level构建Hessian
AdjustCost::Build 返回FrameHessian2
AdjustCost::WarpPatch
Patch::ExtractFast
cv::Point3d GradValAtD
AdjustCost::UpdateHess
AdjustCost::CalcJacGeo 计算j_geo类型为JacGeo
MatrixMNd<2, 3> DprojDpoint
FrameHessian2::AddPatchHess
FramePointHessian::AddPatchHess
}
DirectOdometry::Map{
DirectOdometry::RemoveKeyframe{ 删除关键帧
BundleAdjuster::Marginalize // 边缘化
BundleAdjuster::BuildLevelKf 1. 为一个关键帧的单个level构建Hessian
AdjustCost::Build 返回FrameHessian2,下面是并行调用,(前面TrackFrame已经出现过)
AlignCost::WarpPatch 输入patch0、point0和FrameHessian1,返回DepthPoint
AlignCost::UpdateHess 为单独一个patch 更新Hessian
AlignCost::CalcJacGeo 计算du_dx类型为JacGeo
FrameHessian1::AddPatchHess
FramePointHessian::AddFrameHess
FramePointHessian::MargPointsRange 2. 边缘化帧kf_ind中的所有点
FillLowerTriangular
SafeCwiseInverse
MargPointsToFrames 将点边缘化到帧上,并行化计算新的Hessian
FillUpperTriangular
SchurFrameHessian::MargFrame 4. 边缘化帧kf_ind以形成新的先验
StableRotateBlockTopLeft
MargTopLeftBlock
}}
类基本位于它们的.cpp文件中,直观的可以找到。