K2E-B-S15-1 · Paper Note
DSO — Direct Sparse Odometry
- Description:DSO 论文与系统详解 — 直接稀疏里程计,光度误差 + 稀疏点滑窗光度 BA、光度标定、未成熟点 trace、FEJ、关键帧/点选择策略、雅可比三分解
- My Notion Note ID:K2E-B-S15-1
- Created:2023-04-10
- Updated:2026-06-08
- License:转载欢迎:转载请注明作者 Yu Zhang 并附原文出处(yuzhang.io)
Table of Contents
- 1. Summary
- 2. Key Contributions
- 3. Method
- 4. Experiments & Results
- 5. Ablation & Discussion
- 6. Strengths / Limitations / Future Work
- 7. Code Walkthrough
- References
1. Summary
Title: Direct Sparse Odometry Authors: Jakob Engel, Vladlen Koltun, Daniel Cremers (TU Munich + Intel Labs) Paper: arXiv:1607.02565 (IEEE T-PAMI 2018,arXiv 2016) Github: JakobEngel/dso
DSO 是直接法视觉里程计的成熟之作。将 VO 表述为稀疏点上的光度误差滑窗 BA——不提取描述子、不做特征匹配,直接用像素灰度建立残差。与 LSD-SLAM(半稠密直接法)不同,DSO 是稀疏的——这里"稀疏"的本质不只是点少,而是点之间不加几何/平滑先验:给定相机位姿与内参后,各点的深度条件独立、互不耦合(半稠密直接法则有邻域平滑约束)。这正是 DSO 区别于 LSD-SLAM 的根本点。
系统涵盖完整的光度标定(曝光时间、暗角、响应函数),将光度参数纳入联合优化。每帧状态包含 6 DOF 位姿 + 2 个仿射光度参数,共 8 维;每个地图点用逆深度参数化。后端维护固定大小滑窗( 关键帧, 激活点),超出窗口的变量通过边缘化(Schur 补)压缩为先验。
实验表明,在 TUM monoVO 上 DSO 精度显著优于 ORB-SLAM;EuRoC 上 ORB-SLAM 精度略优(场景含小回环),但 DSO 鲁棒性更高。在弱纹理、重复纹理、边缘密集场景下鲁棒性更强。代价是不支持全局地图重用(无描述子、无回环)。

2. Key Contributions
- 直接稀疏联合优化:光度误差 + 几何参数(位姿、逆深度)+ 光度参数(仿射亮度)共同优化,无需显式特征匹配
- 完整光度标定集成:首次将曝光时间、暗角、响应函数一起纳入优化,显著提升直接法鲁棒性
- First Estimate Jacobians (FEJ):边缘化时固定线性化点,保证可观性一致性
- 候选点 trace 机制:极线搜索跟踪未成熟点,在激活前给出可靠逆深度先验
- 稀疏 Hessian 结构保持:边缘化时约 50% 残差被丢弃以维持稀疏性
3. Method
3.1 光度标定
相机成像模型:,其中 为响应函数, 为暗角函数, 为曝光时间。光度校正图像:
为辐照度(传感器接收到的辐射通量密度),消除设备非线性后直接法更稳定。
3.2 仿射亮度参数与光度误差
每帧始终估计仿射亮度参数 ;有光度标定时加正则化先验(),无标定时令 去掉先验。host 帧 → target 帧 的传递:
为每关键帧的亮度偏移与增益(代码 affLL[0]、affLL[1] 是两帧参数导出的相对系数)。
单点 在目标帧 的光度误差( 邻域 8 点 SSD):
为 host 点投影到 target 帧; 为 Huber 范数; 为梯度依赖权重:
为常数,梯度越大权重越低(减弱噪声点影响)。
全局光度误差:
3.3 滑窗光度 BA
后端维护含 关键帧的滑窗,每帧状态 8 维(6DOF 位姿 + ),每地图点 1 维(逆深度 ),全局内参也参与。GN 求解:
为堆叠的光度雅可比矩阵, 为残差向量, 为 Huber 权重对角矩阵(在高残差处线性降权,对应 Huber 范数 ), 为 GN 近似 Hessian, 为梯度向量。
3.4 雅可比分解与 FEJ
DSO 雅可比三部分:图像梯度(不光滑)、几何雅可比(光滑)、光度雅可比(光滑)。
FEJ(First Estimate Jacobians): 和 在线性化点 处一次求值后固定,不随 GN 迭代更新——这就是 FEJ(论文 §2.3 明确如此命名)。作用:防止非线性零空间被破坏,保证可观性一致(见 VIO 滑窗优化)。仅图像梯度 随迭代重算(依赖图像数据,不光滑)。
代码实现细节(与论文不同层次):isLinearized 门控(EnergyFunctionalPoints.cpp)区分已线性化/待边缘化残差与活跃残差,前者固定 evalPT 线性化点以保证边缘化一致性,活跃残差在每次 GN 迭代按当前状态重算——这是代码层面的分级处理,论文对 FEJ 的定义(/ 固定于 )适用于全部残差。
3.5 边缘化
滑窗满时通过 Schur 补将旧帧/点压缩为先验:
为保留变量, 为被边缘化变量。边缘化时约 50% 残差丢弃以防止 fill-in 过稠密(见 边缘化)。
3.6 关键帧管理
总保留最新两帧 ;可见点比 的帧先边缘化;帧数 时按距离分数选择:
为帧 与帧 之间的欧氏距离(位置距离,单位米); 为当前滑窗内关键帧总数(); 为数值稳定常数(防除零); 表示跳过最新两帧(第 1、2 帧始终保留,从第 3 帧开始参与选择)。分数 大 → 帧 与其他帧距离越均匀分散 → 保留;分数小 → 冗余,优先边缘化。
保留时空上分散的帧,边缘化冗余帧。
3.7 未成熟点与激活
三步:①高梯度候选选取(32×32 block 自适应阈值)→ ②trace(沿极线最小化光度误差,估计逆深度 + 范围,类似 depth filter)→ ③激活(投影到关键帧,取与已有点最大距离的候选,保持稀疏均匀)。
4. Experiments & Results
数据集与指标:
| 数据集 | 序列数 | 评估指标 |
|---|---|---|
| TUM monoVO | 50 序列,105 min | 对齐误差 (旋转漂移 + 尺度漂移) |
| EuRoC MAV | 11 序列,19 min | ATE()after Sim(3) 对齐 |
| ICL-NUIM | 8 序列,4.5 min |
每序列正反方向各跑 5 次(共 10 次/序列),TUM 共 500 runs,EuRoC 共 110 runs(11×10),ICL-NUIM 共 80 runs。结果以累积误差曲线展示(x 轴阈值,y 轴低于该误差的序列数)。


关键结论:
- TUM monoVO:DSO 大幅优于 ORB-SLAM(约 490 vs 370 序列低于误差阈值)
- EuRoC:ORB-SLAM 精度略优于 DSO(场景含小回环,ORB-SLAM 可隐式利用);但 DSO 鲁棒性更高(完成序列更多);限时间窗 t_\max = 10\text{s} 后两者精度相近
- ICL-NUIM(合成,光度一致):DSO 优于 ORB-SLAM(直接法在理想光度一致下发挥充分)
- DSO 以 实时速度运行(低配:、、);实验默认单线程顺序执行,约为实时速度的 1/4;多线程 CPU 实现可达实时
5. Ablation & Discussion
光度标定影响(Fig 15):
- 仅亮度恒定假设(无标定):最差
- 去暗角:精度略降;反直觉:仅去暗角比同时去暗角+响应函数更差——说明缺暗角校正时响应函数校正反而带来一定补偿
- 去响应函数(单独):边际影响
- 加曝光时间:直接影响小,但配合完整标定有益
数据量(Fig 16):
- 点后收益递减; 帧最优,更多帧边际收益接近零
- Fixed-lag 边缘化(无全局先验)性能显著下降 → 保留完整先验至关重要
数据选取(Fig 17):
- 梯度阈值 影响有限;仅用 FAST 角点:精度下降 20–30%
- 直接法用边缘 / 弱纹理区域的能力是真实收益(特征点法在此易失败)
关键帧频率(Fig 18):
- fps:鲁棒性下降; fps:精度下降(冗余帧带来信息重叠);最优 ~ kf/s
残差 pattern(Fig 19):
- pattern 略差;8 点 pattern(默认)是 SSE 效率与覆盖的平衡点(见 §7)
6. Strengths / Limitations / Future Work
优点:
- 数据关联更整体:光度一致性代替显式匹配,弱纹理 / 重复纹理 / 边缘密集场景更强
- TUM monoVO 精度与鲁棒性优于 ORB-SLAM(原版,2015,高翔实测)
- 光度标定为直接法提供理论一致的框架
局限:
- 无回环:无描述子→无法全局匹配→地图不可重用(最大局限)
- 几何噪声敏感:滚帘快门、相机标定误差 像素时失效(图 20)
- 光照要求:需要全局曝光,明显运动模糊会丢失跟踪
- 长时间运行有尺度漂移(单目固有)
后续工作:
- LDSO(DSO + loop closure,Gao et al. 2018)
- Stereo DSO(双目消去尺度歧义)
- 视觉+IMU 紧耦合扩展
7. Code Walkthrough
代码库:JakobEngel/dso
7.1 目录结构
src/
FullSystem/ ← 系统集成:前端、关键帧管理、点管理
OptimizationBackend/ ← 后端:Hessian 累加、GN 求解、边缘化
utils/ ← 去畸变、数据集读写
IOWrapper/ ← 可视化(Pangolin、ROS)
7.2 核心数据结构
FrameHessian:每帧状态容器(位姿 + + Hessian 信息)。含四类点:
| 字段 | 含义 |
|---|---|
pointHessians |
活跃点(参与当前优化) |
pointHessiansMarginalized |
已边缘化点 |
pointHessiansOut |
判为外点 |
immaturePoints |
未成熟点(正在 trace) |
7.3 整体流程
- 非关键帧:仅计算位姿(
trackNewestCoarse);同时更新所有未成熟点深度(traceNewCoarse) - 关键帧:前端判断(光流 + 曝光变化超阈值)→ 后端激活新点、增加残差、优化(
optimize)、边缘化(marginalizeFrame)
7.4 光度雅可比与 FEJ 实现
// OptimizationBackend/EnergyFunctionalPoints.cpp
// isLinearized 为 true 时 Jacobians 固定为 evalPT 处的值 (FEJ)
// 活跃残差每次 GN 迭代在当前状态重线性化
paper-vs-code 差异:
| 论文描述 | 代码实现 |
|---|---|
| 8 点邻域 pattern | settings.cpp staticPattern[8],自定义对称散布形状(注释仅标 "SSE efficiency") |
affLL[0], affLL[1] |
由两帧 、 + 曝光比 导出的相对仿射系数,非单帧 |
| 约 50% 残差边缘化时丢弃 | EnergyFunctional::marginalizeFrame 中对 isLinearized 点的处理 |
7.5 关键参数(settings.cpp)
| 参数 | 默认值 | 含义 |
|---|---|---|
maxFrames () |
7 | 最大活跃关键帧数 |
desiredPointDensity () |
2000 | 目标活跃点数 |
gradHistFrame |
32×32 | 梯度阈值 block 大小 |
setting_minGradHistAdd () |
7 | 梯度阈值常数项 |
setting_keyframesPerSecond |
~8 | 关键帧频率目标 |
References
| 类别 | 链接 |
|---|---|
| 论文 | Engel J., Koltun V., Cremers D. (2018). Direct Sparse Odometry. IEEE T-PAMI, 40(3). arXiv:1607.02565 |
| 代码 | github.com/JakobEngel/dso |
| 前作 | Engel J., Schöps T., Cremers D. (2014). LSD-SLAM. ECCV. — 半稠密直接法 |
| 解读 | 高翔: DSO 详解 |
| 解读 | 任乾: DSO 求导 / 源码解读 一 / 二 / 三 |
| 相关 | FEJ/可观性: OpenVINS 文档:也见 VIO 滑窗优化 |
| 相关 | 光度标定: cnblogs.com/luyb/p/6077478 |