K2E-B-S15-1 · Paper Note

DSO — Direct Sparse Odometry

Created 2023-04-10Updated 2026-06-08slam / papers / s-slam-systems / s15-dso

  • 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

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 维;每个地图点用逆深度参数化。后端维护固定大小滑窗(Nf=7N_f = 7 关键帧,Np=2000N_p = 2000 激活点),超出窗口的变量通过边缘化(Schur 补)压缩为先验。

实验表明,在 TUM monoVO 上 DSO 精度显著优于 ORB-SLAM;EuRoC 上 ORB-SLAM 精度略优(场景含小回环),但 DSO 鲁棒性更高。在弱纹理、重复纹理、边缘密集场景下鲁棒性更强。代价是不支持全局地图重用(无描述子、无回环)。

DSO:建筑物场景的稀疏三维重建与轨迹(1分40秒视频)


2. Key Contributions

  • 直接稀疏联合优化:光度误差 + 几何参数(位姿、逆深度)+ 光度参数(仿射亮度)共同优化,无需显式特征匹配
  • 完整光度标定集成:首次将曝光时间、暗角、响应函数一起纳入优化,显著提升直接法鲁棒性
  • First Estimate Jacobians (FEJ):边缘化时固定线性化点,保证可观性一致性
  • 候选点 trace 机制:极线搜索跟踪未成熟点,在激活前给出可靠逆深度先验
  • 稀疏 Hessian 结构保持:边缘化时约 50% 残差被丢弃以维持稀疏性

3. Method

3.1 光度标定

相机成像模型:Ii(x)=G ⁣(tiV(x)Bi(x))I_i(\mathbf{x}) = G\!\left(t_i\,V(\mathbf{x})\,B_i(\mathbf{x})\right),其中 GG 为响应函数,VV 为暗角函数,tit_i 为曝光时间。光度校正图像:

Ii(x):=tiBi(x)=G1(Ii(x))V(x)(1)I'_i(\mathbf{x}) := t_i B_i(\mathbf{x}) = \frac{G^{-1}(I_i(\mathbf{x}))}{V(\mathbf{x})} \tag{1}

Bi(x)B_i(\mathbf{x}) 为辐照度(传感器接收到的辐射通量密度),消除设备非线性后直接法更稳定。

3.2 仿射亮度参数与光度误差

每帧始终估计仿射亮度参数 (ai,bi)(a_i, b_i);有光度标定时加正则化先验(λaai2+λbbi2\lambda_a a_i^2 + \lambda_b b_i^2),无标定时令 λa=λb=0\lambda_a=\lambda_b=0 去掉先验。host 帧 ii → target 帧 jj 的传递:

Ij(x)=tjeajtieai(Ii(x)bi)+bj(2)I_j(\mathbf{x}') = \frac{t_j e^{a_j}}{t_i e^{a_i}}\bigl(I_i(\mathbf{x}) - b_i\bigr) + b_j \tag{2}

ai,bia_i, b_i 为每关键帧的亮度偏移与增益(代码 affLL[0]affLL[1] 是两帧参数导出的相对系数)。

单点 pp 在目标帧 jj 的光度误差(NpN_p 邻域 8 点 SSD):

Epj:=pNpwp(Ij[p]bj)tjeajtieai(Ii[p]bi)γ(3)E_{pj} := \sum_{\mathbf{p}\in\mathcal{N}_p} w_\mathbf{p}\,\left\|\left(I_j[\mathbf{p}'] - b_j\right) - \frac{t_j e^{a_j}}{t_i e^{a_i}}\left(I_i[\mathbf{p}] - b_i\right)\right\|_\gamma \tag{3}

p=Πj(Tji,Πi1(p,dp))\mathbf{p}' = \Pi_j(\mathbf{T}_{ji},\,\Pi_i^{-1}(\mathbf{p}, d_p)) 为 host 点投影到 target 帧;γ\|\cdot\|_\gamma 为 Huber 范数;wpw_\mathbf{p} 为梯度依赖权重:

wp=c2c2+Ii(p)2(4)w_\mathbf{p} = \frac{c^2}{c^2 + \|\nabla I_i(\mathbf{p})\|^2} \tag{4}

cc 为常数,梯度越大权重越低(减弱噪声点影响)。

全局光度误差:Ephoto=iFpPijobs(p)EpjE_{\text{photo}} = \sum_{i\in\mathcal{F}}\sum_{p\in\mathcal{P}_i}\sum_{j\in\text{obs}(p)} E_{pj}

3.3 滑窗光度 BA

后端维护含 Nf=7N_f = 7 关键帧的滑窗,每帧状态 8 维(6DOF 位姿 + a,ba,b),每地图点 1 维(逆深度 dpd_p),全局内参也参与。GN 求解:

H=JTWJ,b=JTWr(5)\mathbf{H} = \mathbf{J}^T\mathbf{W}\mathbf{J}, \quad \mathbf{b} = -\mathbf{J}^T\mathbf{W}\mathbf{r} \tag{5}

J\mathbf{J} 为堆叠的光度雅可比矩阵,r\mathbf{r} 为残差向量,W\mathbf{W} 为 Huber 权重对角矩阵(在高残差处线性降权,对应 Huber 范数 γ\|\cdot\|_\gamma),H\mathbf{H} 为 GN 近似 Hessian,b\mathbf{b} 为梯度向量。

3.4 雅可比分解与 FEJ

DSO 雅可比三部分:图像梯度(不光滑)、几何雅可比(光滑)、光度雅可比(光滑)。

FEJ(First Estimate Jacobians)JgeoJ_\text{geo}JphotoJ_\text{photo} 在线性化点 x=0\mathbf{x}=0 处一次求值后固定,不随 GN 迭代更新——这就是 FEJ(论文 §2.3 明确如此命名)。作用:防止非线性零空间被破坏,保证可观性一致(见 VIO 滑窗优化)。仅图像梯度 JIJ_I 随迭代重算(依赖图像数据,不光滑)。

代码实现细节(与论文不同层次):isLinearized 门控(EnergyFunctionalPoints.cpp)区分已线性化/待边缘化残差与活跃残差,前者固定 evalPT 线性化点以保证边缘化一致性,活跃残差在每次 GN 迭代按当前状态重算——这是代码层面的分级处理,论文对 FEJ 的定义(JgeoJ_\text{geo}/JphotoJ_\text{photo} 固定于 x=0\mathbf{x}=0)适用于全部残差。

3.5 边缘化

滑窗满时通过 Schur 补将旧帧/点压缩为先验:

H^αα=HααHαβHββ1Hβα(6)\hat{\mathbf{H}}_{\alpha\alpha} = \mathbf{H}_{\alpha\alpha} - \mathbf{H}_{\alpha\beta}\mathbf{H}_{\beta\beta}^{-1}\mathbf{H}_{\beta\alpha} \tag{6}

α\alpha 为保留变量,β\beta 为被边缘化变量。边缘化时约 50% 残差丢弃以防止 fill-in 过稠密(见 边缘化)。

3.6 关键帧管理

总保留最新两帧 I1,I2I_1, I_2;可见点比 <5%< 5\% 的帧先边缘化;帧数 >Nf> N_f 时按距离分数选择:

s(Ii)=d(i,1)j[3,n],ji(d(i,j)+ϵ)1(7)s(I_i) = \sqrt{d(i,1)}\sum_{j\in[3,n],j\neq i}(d(i,j)+\epsilon)^{-1} \tag{7}

d(i,j)d(i,j) 为帧 ii 与帧 jj 之间的欧氏距离(位置距离,单位米);nn 为当前滑窗内关键帧总数(Nf=7\leq N_f = 7);ϵ\epsilon 为数值稳定常数(防除零);[3,n][3,n] 表示跳过最新两帧(第 1、2 帧始终保留,从第 3 帧开始参与选择)。分数 s(Ii)s(I_i) 大 → 帧 ii 与其他帧距离越均匀分散 → 保留;分数小 → 冗余,优先边缘化。

保留时空上分散的帧,边缘化冗余帧。

3.7 未成熟点与激活

三步:①高梯度候选选取(32×32 block 自适应阈值)→ ②trace(沿极线最小化光度误差,估计逆深度 + 范围,类似 depth filter)→ ③激活(投影到关键帧,取与已有点最大距离的候选,保持稀疏均匀)。


4. Experiments & Results

数据集与指标

数据集 序列数 评估指标
TUM monoVO 50 序列,105 min 对齐误差 ealigne_\text{align}(旋转漂移 + 尺度漂移)
EuRoC MAV 11 序列,19 min ATE(eatee_\text{ate})after Sim(3) 对齐
ICL-NUIM 8 序列,4.5 min eatee_\text{ate}

每序列正反方向各跑 5 次(共 10 次/序列),TUM 共 500 runs,EuRoC 共 110 runs(11×10),ICL-NUIM 共 80 runs。结果以累积误差曲线展示(x 轴阈值,y 轴低于该误差的序列数)。

EuRoC 结果:DSO(蓝)vs ORB-SLAM(红);ORB-SLAM 精度略占优,DSO 鲁棒性更高(曲线下方完成序列更多)

TUM monoVO 结果:DSO(蓝,~490 序列)显著优于 ORB-SLAM(红,~370 序列)

关键结论

  • 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 以 5×5\times 实时速度运行(低配:Np=800N_p=800Nf=6N_f=6424×320424\times320);实验默认单线程顺序执行,约为实时速度的 1/4;多线程 CPU 实现可达实时

5. Ablation & Discussion

光度标定影响(Fig 15)

  • 仅亮度恒定假设(无标定):最差
  • 去暗角:精度略降;反直觉:仅去暗角比同时去暗角+响应函数更差——说明缺暗角校正时响应函数校正反而带来一定补偿
  • 去响应函数(单独):边际影响
  • 加曝光时间:直接影响小,但配合完整标定有益

数据量(Fig 16)

  • Np>500N_p > 500 点后收益递减;Nf=7N_f = 7 帧最优,更多帧边际收益接近零
  • Fixed-lag 边缘化(无全局先验)性能显著下降 → 保留完整先验至关重要

数据选取(Fig 17)

  • 梯度阈值 gthg_\text{th} 影响有限;仅用 FAST 角点:精度下降 20–30%
  • 直接法用边缘 / 弱纹理区域的能力是真实收益(特征点法在此易失败)

关键帧频率(Fig 18)

  • <4< 4 fps:鲁棒性下降;>15> 15 fps:精度下降(冗余帧带来信息重叠);最优 ~88 kf/s

残差 pattern(Fig 19)

  • 3×33\times3 pattern 略差;8 点 pattern(默认)是 SSE 效率与覆盖的平衡点(见 §7)

6. Strengths / Limitations / Future Work

优点

  • 数据关联更整体:光度一致性代替显式匹配,弱纹理 / 重复纹理 / 边缘密集场景更强
  • TUM monoVO 精度与鲁棒性优于 ORB-SLAM(原版,2015,高翔实测)
  • 光度标定为直接法提供理论一致的框架

局限

  • 无回环:无描述子→无法全局匹配→地图不可重用(最大局限)
  • 几何噪声敏感:滚帘快门、相机标定误差 δg>1.5\delta_g > 1.5 像素时失效(图 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:每帧状态容器(位姿 + a,ba,b + 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] 由两帧 (ai,bi)(a_i, b_i)(aj,bj)(a_j, b_j) + 曝光比 tj/tit_j/t_i 导出的相对仿射系数,非单帧 eae^a
约 50% 残差边缘化时丢弃 EnergyFunctional::marginalizeFrame 中对 isLinearized 点的处理

7.5 关键参数(settings.cpp

参数 默认值 含义
maxFrames (NfN_f) 7 最大活跃关键帧数
desiredPointDensity (NpN_p) 2000 目标活跃点数
gradHistFrame 32×32 梯度阈值 block 大小
setting_minGradHistAdd (gthg_\text{th}) 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