【DROID-W / WildGS-SLAM】动态场景SLAM:不确定性驱动BA、3DGS建图与10个核心优化点深度解析

摘要

DROID-W(WildGS-SLAM,CVPR 2026,ETH Zurich)在 DROID-SLAM 的密集 BA 框架基础上,引入可学习的逐像素不确定性掩码,并集成 3D Gaussian Splatting(3DGS)建图,专为动态场景设计。其核心突破在于:不确定性不是后处理掩码,而是与位姿、深度共同参与 BA 优化的状态量,仅用 385 个参数即可在线自适应动态场景。本文对 10 个关键工程优化点进行代码级梳理,每个优化点附具体文件路径,并总结对传统 SLAM 的借鉴价值。


一、整体架构

DROID-W 采用三进程并行架构 ,通过共享 GPU 内存(DepthVideo)实现高效的进程间通信,分工如下:
共享DepthVideo
共享DepthVideo
不确定性掩码反馈
全局BA结果
输入图像流
Process-1: Tracker
Process-2: Mapper
Process-3: Backend
Final Global BA
PoseTrajectoryFiller
ATE 评估

进程 职责 特点
Tracker 前端实时跟踪 + 局部 BA 每帧必执行,保证实时性
Mapper 3D Gaussian Splatting 建图 提供可靠不确定性掩码
Backend 全局 BA 优化 每 20 KF 触发一次,处理大规模关键帧

三进程通过 tensor.share_memory_() 共享 GPU 张量,避免进程间数据拷贝。


二、逐步流程解析

2.1 逐帧处理(MotionFilter.track)

每帧必执行,非关键帧跳过重计算操作:
||delta||_mean > 2.5px
每N帧强制
均不满足
新帧到来
DroidNet fnet 提取128维特征图 (1/8分辨率)
构建4层金字塔相关体 (7×7窗口)
1步GRU更新 → 光流delta
双重关键帧判决
运动显著 → 加入KF
非KF,跳过重计算
调用Metric3D估计度量深度先验 (~5 FPS)
调用DINOv2提取384维语义特征 (~22 FPS)

关键帧判决采用双重机制:运动显著判决保证快速运动帧被捕获,强制关键帧机制防止慢速场景下关键帧密度不足。

2.2 前端局部BA(Frontend.__update)

每新增一个关键帧触发,核心流程:
距离过近 AND 非强制 AND 未超连续丢帧上限
否则保留
新KF触发局部BA
清理生命周期>25的老边 → inactive缓存
add_proximity_factors: NMS选边
4次BA迭代 (不确定性加权)
距离二次验证
rm_keyframe: 撤销刚加入的KF
额外2次BA / loop_ba
用当前pose/disp初始化下一帧

2.3 两阶段初始化

阶段 触发条件 操作
第一阶段 达到 warmup=12 构建邻域边 (r=3),16次 BA,纯几何引导,快速得到初始位姿
第二阶段 Mapper 完成初始建图后 重新添加邻域边,8次 BA,不确定性掩码就绪,精度更高

两阶段设计的意义在于:第一阶段不依赖不确定性(此时 Mapper 还未就绪),第二阶段在有可靠不确定性掩码后做精细化初始化。

2.4 在线全局BA(每20个KF触发)

Backend.dense_ba 采用 update_lowmem 低内存方案,将全部关键帧分批(每批8帧)计算相关体,解决大规模关键帧显存溢出问题,详见优化 8。

2.5 终止阶段

终止时依次执行:Final Global BA(长序列耗时 27--103 s)→ PoseTrajectoryFiller 用 MoBA 插值全部非关键帧 → evo 工具计算 ATE(kf_traj_eval / full_traj_eval)。


三、10个核心优化点

优化1:不确定性驱动的Bundle Adjustment(UDBA)

代码位置src/lib/droid_kernels.cu:324

用软权重替代 RANSAC 的内外点硬分类,动态像素自动降低对 BA 的贡献:

cpp 复制代码
// CUDA kernel 内,每个像素的 BA 权重乘以不确定性倒数
float scale_uncer = max(45.0 * uncertainties[ix][i][j] - 35.0, 0.1f);
float w_uncer     = max(min(1.0 / scale_uncer, 1.0f), 0.0f);
wu = wu * w_uncer;   // u 方向加权
wv = wv * w_uncer;   // v 方向加权

// 双向不确定性检查:同时考虑目标帧不确定性
float uncer_target = bilinear_interp(uncertainties[jx], target_x, target_y);
wu = wu * (1.0 / max(45.0 * uncer_target - 35.0, 0.1f));

传统SLAM借鉴:将 RANSAC 内外点硬分类替换为软权重,在 LM 优化内部自动衰减动态像素贡献,且梯度可反向更新权重本身。


优化2:DINOv2特征 × 仿射变换 = 极轻量可学习不确定性

代码位置depth_video.py:154droid_kernels.cu:1022

不单独训练动态分割网络,用预计算的 DINOv2 特征加一个仅 385 参数的仿射头,实现在线自适应不确定性掩码:

python 复制代码
# 前向计算 (Python侧)
# dino_feats_resize: [H/8, W/8, 384],affine_weights: [385]
y_cdot = dino_feats_resize[t] @ affine_weights[:-1] + affine_weights[-1]
uncertainty = torch.log(1.1 + torch.exp(y_cdot))  # softplus,log(1.1)约束最小值

affine_weights(385个参数)与 pose(7 DoF)、disp 一起,在每次 BA 迭代中通过 CUDA kernel 反向传播更新(Adam,lr=1e-2)。反向传播链:

∂ L ∂ w = ∂ L ∂ u ⋅ ∂ u ∂ y ⋅ ∂ y ∂ w = J t o t a l ⋅ σ ( y c d o t ) ⋅ f d i n o \frac{\partial L}{\partial \mathbf{w}} = \frac{\partial L}{\partial u} \cdot \frac{\partial u}{\partial y} \cdot \frac{\partial y}{\partial \mathbf{w}} = J_{total} \cdot \sigma(y_{cdot}) \cdot \mathbf{f}_{dino} ∂w∂L=∂u∂L⋅∂y∂u⋅∂w∂y=Jtotal⋅σ(ycdot)⋅fdino

其中 σ ( ⋅ ) \sigma(\cdot) σ(⋅) 为 sigmoid 函数, f d i n o ∈ R 384 \mathbf{f}_{dino} \in \mathbb{R}^{384} fdino∈R384 为 DINOv2 特征向量。

传统SLAM借鉴:利用现成语义特征(DINOv2、CLIP 等)加线性头,参数极少,可在 BA 过程中在线自适应,无需离线训练。


优化3:DINOv2特征一致性作为不确定性的数据项

代码位置droid_kernels.cu:1446

让高不确定性像素的特征不一致代价贡献更小:

L d a t a = 1 − cosine_sim ( f i , f j r e p r o j ) u i ⋅ u j r e p r o j L_{data} = \frac{1 - \text{cosine\_sim}(\mathbf{f}_i, \mathbf{f}_j^{reproj})}{u_i \cdot u_j^{reproj}} Ldata=ui⋅ujreproj1−cosine_sim(fi,fjreproj)

其中 u i u_i ui、 u j r e p r o j u_j^{reproj} ujreproj 分别为源帧和目标帧(重投影后)的不确定性值。梯度计算解耦形式:

∂ L ∂ u i = − r ⋅ u j ( u i ⋅ u j ) 2 \frac{\partial L}{\partial u_i} = -\frac{r \cdot u_j}{(u_i \cdot u_j)^2} ∂ui∂L=−(ui⋅uj)2r⋅uj

额外约束:cosine_sim < 0.5 的像素对截为 0,排除纯噪声;仅计算帧距 ≥ 2 \geq 2 ≥2 的边,排除相邻帧重叠导致的冗余计算。

传统SLAM借鉴:将语义特征一致性引入位姿优化残差,代替或补充光度一致性,DINOv2 特征对遮挡、光照变化天然鲁棒。


优化4:先验正则化约束不确定性不发散

代码位置droid_kernels.cu:1688

通过 Log-barrier 先验与硬约束防止不确定性数值发散:

cpp 复制代码
// L_prior = log(u + 1),先验损失约束不确定性范围
// ∂L_prior/∂u = 1 / (u + 1)
J_prior[block_id][k] = 1.0 / (uncertainties[idx][i][j] + 1.0);

// 硬约束,限制不确定性在物理合理范围内
uncer = clamp(uncer, 0.1, 2.0);  // uncertainty_retr_kernel

系统总损失:

L t o t a l = γ d a t a ⋅ L d a t a + γ p r i o r ⋅ L p r i o r + γ d e p t h ⋅ L d e p t h L_{total} = \gamma_{data} \cdot L_{data} + \gamma_{prior} \cdot L_{prior} + \gamma_{depth} \cdot L_{depth} Ltotal=γdata⋅Ldata+γprior⋅Lprior+γdepth⋅Ldepth

其中 γ d a t a \gamma_{data} γdata、 γ p r i o r \gamma_{prior} γprior、 γ d e p t h \gamma_{depth} γdepth 为各项损失权重。

传统SLAM借鉴:对可学习参数加 log-barrier 先验,既防止数值不稳定,又给参数一个物理意义上合理的范围。


优化5:Scale-Shift联合BA(度量深度先验融合)

代码位置src/geom/ba.py:BA_with_scale_shift

解决单目深度先验的尺度和偏移未知问题,为每个关键帧维护独立的 scale/shift 参数,与深度、位姿联合优化:

d i = s i ⋅ d ^ i m o n o + t i d_i = s_i \cdot \hat{d}_i^{mono} + t_i di=si⋅d^imono+ti

其中 s i s_i si、 t i t_i ti 为第 i i i 个关键帧的尺度和偏移, d ^ i m o n o \hat{d}_i^{mono} d^imono 为 Metric3D 输出的视差。对应的额外 Jacobian:

J s = − d ^ i m o n o ⋅ α , J t = − α , H s t = J s t ⊤ J s t \mathbf{J}{s} = -\hat{d}i^{mono} \cdot \sqrt{\alpha}, \quad \mathbf{J}{t} = -\sqrt{\alpha}, \quad \mathbf{H}{st} = \mathbf{J}{st}^\top \mathbf{J}{st} Js=−d^imono⋅α ,Jt=−α ,Hst=Jst⊤Jst

valid_depth_mask(有效深度像素)权重放大 10×,确保可信深度对校准有更大拉力。

传统SLAM借鉴:将外部深度先验(IMU、激光、单目网络)通过 scale-shift 参数化融入滑窗 BA,自由度分配更合理。


优化6:关键帧撤销机制(含连续撤销上限)

代码位置frontend.py:78

BA 后二次确认关键帧有效性,避免前端预判误差,同时设置连续撤销上限防止退化场景下无限丢帧:

python 复制代码
if (d.item() < keyframe_thresh 
        and num_dropped < max_consecutive_drops 
        and not force):
    graph.rm_keyframe(t1 - 1)   # ← 撤销刚加入的 KF
    video.counter -= 1
    num_keyframes_dropped += 1
else:
    num_keyframes_dropped = 0   # ← 重置连续撤销计数

传统SLAM借鉴:关键帧选择放到 BA 后再确认,利用优化后的更精确估计做二次判断,提升关键帧选择准确率。


优化7:边的生命周期管理(Age + Inactive缓存)

代码位置factor_graph.py:85factor_graph.py:162

避免直接删除老边导致的长程约束丢失,采用"生命周期管理 + 缓存池"策略:

python 复制代码
# 老边不删除,移入 inactive 缓存
self.ii_inac = torch.cat([self.ii_inac, self.ii[mask]], 0)
self.jj_inac = torch.cat([self.jj_inac, self.jj[mask]], 0)
self.target_inac = torch.cat([self.target_inac, self.target[mask]], 0)

# BA 时按需激活近期 inactive 边
if use_inactive:
    m = (self.ii_inac >= t0 - 3) & (self.jj_inac >= t0 - 3)
    ii = torch.cat([self.ii_inac[m], self.ii], 0)
    jj = torch.cat([self.jj_inac[m], self.jj], 0)

传统SLAM借鉴:co-visibility 图的边不必立即丢弃,可用 age/score 管理池化,按需激活,尤其适合重访场景。


优化8:低内存全局BA(分块相关体)

代码位置factor_graph.py:update_lowmem

解决全局 BA 中"所有关键帧相关体无法同时放入显存"的问题,懒计算 + 分批处理:

python 复制代码
for i in range(0, jj.max() + 1, 8):
    v = (self.ii >= i) & (self.ii < i + 8)
    # 特征图 fmaps 存于共享 GPU 内存,按索引查询
    corr = AltCorrBlock(fmaps)(coords1[:, v], rig * iis, rig * jjs)
    # 更新这批边的 net/target/weight
    net[v], target[v], weight[v] = ...

将全部关键帧按批次(每批 8 帧)处理,节省 2--4 倍显存。

传统SLAM借鉴:大规模地图优化时,将特征/描述子以懒计算方式组织,按窗口分批做 Schur 补求解,节省显存。


优化9:基于NMS的近邻边选择

代码位置factor_graph.py:add_proximity_factorsnms_invalidate_ + precompute_offsets

替代传统 SLAM "选最近 k 帧构建边"的方式,用 NMS 保留几何视差显著的边:

  • 如果两帧距离很小,则在一定邻域内抑制其他近邻边(NMS 抑制半径内的冗余边);
  • 保留几何视差显著的边,确保约束的有效性与多样性。

传统SLAM借鉴:构建稀疏但多样的约束图,降低计算量的同时保证优化精度。


优化10:Mapper侧不确定性掩码闭环

代码位置mapper.py:637mapper.py:876

Tracker 优化的不确定性直接传递给 3DGS Mapper,形成"优化-建模-反馈"闭环:

python 复制代码
def _get_video_uncertainty_mask(self, kf_idx):
    uncertainty = self.video.uncertainty[kf_idx]  # Tracker 维护的不确定性图
    uncer_rescaled = torch.clamp(45 * uncertainty - 35, min=0.1)
    mask = torch.clamp(1.0 / uncer_rescaled, 0, 1)
    # 高不确定性(动态)→ mask≈0 → 3DGS 损失中权重趋零
    return mask

传统SLAM借鉴:前端优化的动态场景置信度可直接指导建图侧(3DGS/NeRF/点云)的损失权重,提升建图一致性。


四、借鉴价值汇总

借鉴点 DROID-W 做法 传统方案改进方向
动态点处理 软权重 1 / u 1/u 1/u,梯度可反传 替代 RANSAC 硬分类,提升鲁棒性
深度先验融合 per-KF scale-shift + Schur 补 优化 IMU/激光深度约束参数化方式
语义特征利用 DINOv2 + 线性层 → 动态掩码 无需训练专用分割网络,降低开发成本
双向一致性检查 源帧 × 目标帧不确定性乘积 提升动态遮挡场景鲁棒性
参数极简设计 385 参数仿射头,在优化中自适应 轻量可学习模块嵌入几何优化
Log-barrier 先验 L p r i o r = log ⁡ ( u + 1 ) L_{prior} = \log(u+1) Lprior=log(u+1) 约束辅助变量范围,避免数值发散
关键帧二次确认 BA 后再做距离判断 比前端预判更可靠的关键帧管理
边生命周期管理 Age + inactive 缓存池 滑窗外约束不直接丢弃,提升轨迹一致性
NMS 边选择 在距离矩阵上做 NMS 构建稀疏但多样的约束图,降低计算量
分块低内存 BA AltCorrBlock + 分批处理 大规模地图 BA 的显存优化范式

小结

DROID-W 最值得借鉴的设计核心在于:把不确定性当作可优化的状态量,而非后处理滤波器 。传统 SLAM 里,动态物体的处理往往是在外部做分割或 RANSAC 剔除,再喂给优化器------这条链路的问题在于分割误差会直接污染 BA。DROID-W 的方案是把动态像素的"可信度"直接参数化成 u i u_i ui,让它在 BA 的内层迭代里和位姿、深度一起被梯度更新。385 个参数的仿射头保证了这个机制的轻量性,DINOv2 特征的语义鲁棒性保证了初始估计的质量。

局限性上,Metric3D 的 5 FPS 瓶颈是当前实时性的主要制约,Final Global BA 在长序列下耗时可达 100s 以上,限制了离线后处理以外的应用场景。如果目标是嵌入实时系统,深度先验推理的轻量化替换(MiDaS-small、DepthFM 等)和增量式全局 BA 是两个值得探索的方向。

相关推荐
淡海水10 小时前
【AI模型】常见问题与解决方案
人工智能·深度学习·机器学习
β添砖java11 小时前
深度学习(13)PyTorch神经网络基础
人工智能·深度学习
victory043112 小时前
论文设计和撰写1
人工智能·深度学习·机器学习
沪漂阿龙14 小时前
OpenAI Agents SDK 深度解析(三):执行层——Agent 的“幕后指挥部”
人工智能·深度学习
数智工坊14 小时前
【SAM-DETR论文阅读】:基于语义对齐匹配的DETR极速收敛检测框架
网络·论文阅读·人工智能·深度学习·transformer
童园管理札记14 小时前
【续】数字时代:学前教育的新改革
经验分享·深度学习·职场和发展·微信公众平台
AI医影跨模态组学16 小时前
如何将纵向CT影像组学特征与局部晚期胃癌化疗时空异质性及耐药演化建立关联,并进一步解释其与化疗响应、淋巴结转移及生存预后的机制联系
人工智能·深度学习·论文·医学·医学影像·影像组学
硅谷秋水18 小时前
ClawVM:有状态工具LLM智体的Harness管理型虚拟内存
人工智能·深度学习·语言模型
春风有信18 小时前
【DM】DDPM与DDIM的数学原理
人工智能·深度学习·机器学习