这是3D Gaussian系列的第4篇。前三篇把3DGS的核心原理、离线重建流程和渲染优化都过了一遍,这篇来聊一个更有现场感的话题:把3DGS直接塞进SLAM系统里。说白了,就是让“重建一个场景”从离线批处理变成一边移动一边建图,同时还要实时估算出相机自己的位置。这几年SplaTAM、GS-SLAM、MonoGS这些工作一个接一个冒出来,已经把“3DGS结合SLAM”从论文demo推到了可以拿出来跑真实数据的阶段,我也在室内RGB-D场景里完整跑过几套方案,这篇就把里面的门道和踩过的坑一起讲清楚。
适合读这篇的朋友大概是两类:一类是已经在做传统视觉SLAM,想了解3DGS这种显式辐射场表示到底能带来什么;另一类是玩过3DGS离线重建,想把重建做成实时增量式的。无论哪类,读完你至少能明白3DGS SLAM的系统架构、几个代表方案的差异,以及自己上手时该从哪里开始调参。
1. 为什么要做3DGS SLAM——从几何地图到辐射场地图
1.1 传统SLAM建图的局限
经典视觉SLAM比如ORB-SLAM,输出的地图是一堆稀疏路标点,这些点的作用是支撑定位,但你要拿它做可视化、做碰撞检测、做模拟仿真,基本是残废的。稠密SLAM这边,KinectFusion这类TSDF方案能重建出连续表面,但TSDF体素网格吃内存非常猛,而且重建出来的表面细节经常被平滑得像橡皮泥一样,缺乏真实纹理。
后来NeRF类的SLAM工作比如iMAP、NICE-SLAM尝试用隐式神经辐射场做地图,好处是渲染出来的图像质量比TSDF高了一个维度,但坏处也很明显:MLP推理和体渲染实在太慢,建图过程动不动要几分钟几千次迭代,很难达到“实时”这两个字的期望。
这个时候3DGS出现了。它的场景表示是几千到几百万个显式的高斯基元,每个基元带着位置、协方差、不透明度和球谐颜色。渲染时通过3D Gaussian Splatting做可微光栅化,一次前向就能拿到颜色和深度图,速度比NeRF的体渲染快几个数量级。这就是3DGS能切入SLAM赛道的直接原因:实时性、显式性、可微渲染,三样都占了。
1.2 3DGS如何匹配SLAM需求
要理解3DGS SLAM为什么能成立,得先知道3DGS离线重建和在线SLAM之间的核心差异。离线重建时,相机位姿已经用COLMAP之类的工具算好了,优化目标只有高斯参数本身;在线SLAM里,位姿和高斯参数都是未知的,要在运行过程中一起估计,这就是所谓的“跟踪与建图耦合”。
3DGS在这个耦合问题里表现好的原因有几个。第一,可微光栅化能让误差信号同时回传到高斯参数和相机位姿上,无需额外提取特征点,直接用像素级颜色误差就能迭代位姿。第二,显示基元天然适合增量更新,新来的关键帧只需要更新它看得到的那一小部分高斯,不像NeRF改一个区域要牵扯全局MLP权重。第三,通过α融合渲染出的深度图可以跟传感器深度直接做几何约束,这让RGB-D输入模式下的跟踪稳定性比纯光度约束高不少。
顺带提一句,3DGS社区里常讨论的mip-splatting这类抗锯齿工作,虽然主要解决多分辨率渲染的混叠问题,但它背后的思想——让高斯尺寸适配观察尺度——在做SLAM时也有参考价值。在线重建经常会遇到同一区域在不同距离被重复观测的情况,如果尺度初始化做不好,远处模糊近处发虚的问题会非常突出。
1.3 3DGS SLAM真正要解决的三件事
3DGS SLAM这个名字听起来很酷,但拆开来看,系统要解决的无非三件事。
第一件是实时位姿估计:没有真值轨迹,系统必须依赖可微渲染把当前帧和已有地图匹配起来,通过最小化渲染图与真实观测图的误差来反推相机位姿。第二件是增量地图管理:不能像离线训练那样一遍遍扫全量数据,必须设计关键帧机制,让高斯参数在滑动窗口内更新,同时控制高斯数量防止地图爆炸。第三件是漂移控制:由于位姿和地图会互相污染,位姿错了地图跟着错,地图错又让后续位姿崩,所以要有正规化手段、局部优化甚至回环检测来遏制误差累积。
这三件事互相耦合,排错时的思路也遵循同一逻辑:先看跟踪在哪个环节发散,再看地图更新是否引入错误高斯,最后检查关键帧窗口内容是否合理。
2. 3DGS SLAM整体架构拆解
2.1 核心系统框架
虽然不同论文的实现细节有差异,但3DGS SLAM系统基本都长成一个模板,分四个模块。
- 前端跟踪:接收新帧图像(可选深度),用当前高斯地图做可微渲染,把渲染结果与真实观测做代价计算,通过优化位姿参数得到当前帧的相机位姿。
- 建图模块:维护高斯地图的参数,在触发建图优化时,固定或联合优化窗口内关键帧的位姿和高斯参数,更新可见区域的基元。
- 关键帧管理:判断新帧是否值得成为关键帧,维护一个数量有限的滑动窗口,淘汰冗余帧同时保留有约束价值的历史帧。
- 可选回环模块:当前很多3DGS SLAM方案还没有完整的回环检测,LoopSplat这类工作在做这个方向。有回环的系统会在地图重新闭合后触发全局位姿图优化,再同步校正高斯地图。
模块联动上,跟踪和建图通常被设计成两个异步线程,或者同一个线程内交替执行。以我跑过的SplaTAM为例,新帧进来先做跟踪,跟踪损失低于阈值且视角偏移足够大时插入关键帧,然后进入局部建图循环,迭代几十次优化高斯参数。这样设计的好处是实时性和精度能两头兼顾。
2.2 高斯地图的数据组织与更新机制
3DGS的场景表示是大量3D高斯基元,每个基元核心参数有六个:位置μ、旋转四元数q、缩放向量s、不透明度α、球谐系数SH。Σ由q和s换算得来。渲染时把高斯基元投影到2D平面上,按深度排序做α混合,就是3D Gaussian Splatting的完整流程。
在线建图时,地图的初始化通常从第一帧的RGB-D图像开始:根据深度图反投影出点云,对点云做体素降采样,然后把每个点作为高斯的初始位置。这个初始化极其重要,如果第一帧深度噪声大,后续高斯位置就会错得离谱,跟踪很快发散。
更新机制上,在线系统不会对全部高斯做反向传播,而是只更新当前关键帧窗口内可见的高斯。做法是对每个高斯基元维护可见性信息,在优化前把窗口内所有关键帧的高斯投影进2D,统计哪些基元被观测到,只在这部分基元上计算梯度。对于长期不被看到的离群高斯,定期做一次修剪,用不透明度低于阈值、可见次数少这两个指标筛掉。
增量扩展方面,当新关键帧覆盖了地图未建模区域时,需要通过致密化添加新高斯。离线3DGS的adaptive densification筛选梯度大的基元做克隆或分裂,在线系统会沿用它,但阈值要调得更保守,避免因为位姿噪声造成虚假梯度导致高斯数量膨胀。
2.3 相机跟踪策略
相机跟踪本质上是一个姿态优化问题。给定当前地图M,我们希望找到位姿T让渲染函数R(M,T)输出的图像和当前观测I尽可能一致。目标函数简写就是:
loss(T) = p_loss(R_color(M,T), I_color) + λ * g_loss(R_depth(M,T), I_depth)这里的p_loss通常取L1加SSIM的组合,g_loss取深度图的L1或Huber损失。优化对象是SE(3)上的变换参数,一般用李代数方式做梯度下降,用上一帧位姿做初值。
纯RGB-D的3DGS SLAM在深度约束下跟踪相对稳,因为深度误差能提供很强的几何信号;单目版本则困难很多,没有真实深度只能靠光度误差,遇到低纹理区域容易滑走。实操里我发现,跟踪阶段的学习率一定要控制得比建图阶段小,否则一帧大梯度更新就能把位姿顶出局部最优,之后地图和位姿一起崩。
3. 关键实现细节与算法要点
3.1 高斯参数初始化与致密化(新手必看的关键参数)
高斯参数初始化是很多人第一次跑3DGS SLAM时忽略的重点。离线3DGS通常用SfM点云初始化,在线SLAM没有SfM,只能靠深度传感器或第一帧的估计深度。RGB-D模式下,直接把深度图反投影成点云,再做一次体素滤波,比如把点云体素下采样到2cm间隔,然后每个体素中心生成一个高斯位置,初始缩放向量设为一个各向同性的小值,比如每个方向0.05。
致密化策略是调参的第一个核心。离线3DGS的标准做法是根据高斯基元在视空间中的位置梯度来判断是否克隆或分裂:梯度大且基元尺寸小就分裂,梯度大且基元尺寸大就克隆。在线SLAM里,这个逻辑依然成立,但触发阈值需要根据实时性调整。阈值调高,高斯数量增长慢,地图稀疏,渲染细节差;阈值调低,高斯数量爆炸,显存压力大,还会在深度噪声区域产生大量错误基元。
SplaTAM的做法比较直观,它用渲染图和观测图之间的像素级残差来判定哪些区域需要“补点”,配合梯度信息做致密化。我在实际使用中会把致密化阈值设为离线训练默认值的1.5到2倍,先用跑通为主,再根据渲染质量逐步下调。
3.2 代价函数设计:几何约束与光度约束如何平衡
代价函数是3DGS SLAM的灵魂,它直接影响跟踪稳定性和地图质量。不同的传感器配置对应不同的代价函数组合。
RGB-D模式下,最常见组合是光度误差加几何深度误差。光度误差计算渲染颜色和输入颜色在L1和SSIM两个尺度上的差异,深度误差直接用渲染深度和传感器深度做Huber损失。整个建图优化目标可以写成:
loss_mapping = rgb_loss + depth_weight * depth_loss + ssim_weight * ssim_lossdepth_weight在不同方案里差异很大,SplaTAM将深度项设为可调权重,GS-SLAM则更强调深度和法线先验。我的经验是,当传感器深度噪声可控(比如Replica这类合成数据或高质量深度相机),深度权重可以给大一些;但遇到真实消费级深度传感器时,深度噪声往往集中在物体边缘,这时候把深度损失换成Huber并适当减小权重,反而能避免边缘区域被错误深度带偏。
单目版本没有深度传感器,代价函数只能靠光度误差,此时通常引入单目深度估计网络生成的伪深度作为监督,或者在几何正则上做文章。这里要提醒一句:伪深度质量方差很大,如果直接把伪深度当硬约束,地图会被约束到错误的几何上,表现就是渲染图像看着还行但点云弯曲变形,需要谨慎对待。
3.3 关键帧选择与滑动窗口策略
在线SLAM天然需要关键帧机制,因为不可能每一帧都做完整建图优化。关键帧选择的核心指标是“信息增益”。如果新帧和前一个关键帧的视角几乎没变,那它对地图的贡献就很小;如果视角转向一个新区域,就需要加入关键帧把新区域的高斯补全。
实现上常用三个信号做决策:一是跟踪损失,损失突然变大说明之前的地图覆盖不够,应该插入关键帧;二是可见高斯比例,如果当前帧能看到的高斯数量低于某个阈值,说明相机走到了未知区域;三是位姿变化量,平移和旋转超过设定阈值就触发关键帧插入。
滑动窗口的大小取决于算力。窗口太大,建图优化迭代慢,影响实时性;窗口太小,约束不足,位姿容易漂移。拿Replica数据集举例,跑通SplaTAM时用默认配置大约保持10到15个关键帧比较合适。当新的关键帧被插入时,窗口末尾的关键帧会被移出优化集合,但它的高斯参数不会被回滚,只是不再参与局部优化。这样离线重建里“全局一致”的追求和在线系统里“及时忘记”的约束之间达成了折中。
4. 主流方案对比与选型参考
4.1 SplaTAM/GS-SLAM/MonoGS等代表工作对比
3DGS SLAM方向2024年后涌现了一批代表性工作,这里选几个最常见的做对比。
- SplaTAM(CVPR 2024):RGB-D输入,首次提出完整的“跟踪+建图”高斯系统。它的关键是使用视觉-深度残差做跟踪和致密化,流程非常清晰。据说REplica、ScanNet上渲染质量和位姿精度都超过了之前的TSDF方案。
- GS-SLAM:也是RGB-D为主,更强调几何信息利用,扩展了高斯基元与几何一致性约束,建图时的深度和法线正则化比SplaTAM更精细。
- MonoGS:支持单目、双目、RGB-D三种输入,是少数把3DGS SLAM推广到单目场景的工作。它在跟踪时对比光度误差,同时用单目深度先验辅助建图,实用性很强,但单目模式下精度确实不如RGB-D稳定。
- Photo-SLAM:主打实时性和轻量,在单目和RGB-D下都能跑,渲染速度快,适合演示和AR场景。
- LoopSplat:3DGS加上回环检测与全局优化,解决大场景漂移问题,是3DGS SLAM里“完整形态”的代表。
这些方案在核心思路上没有本质区别,差异主要体现在几何先验的使用程度、关键帧窗口策略、致密化阈值设计以及回环是否覆盖。
4.2 不同应用场景怎么选方案
没有哪个方案是万能的,选型要看你的输入传感器和目标。
如果你的设备是RGB-D深度相机(比如RealSense、Azure Kinect),场景在室内,优先考虑SplaTAM或GS-SLAM。SplaTAM代码结构清晰,适合学习原理和二次开发;GS-SLAM在真实传感器深度噪声下表现更稳,推荐做机器人项目时直接试它。
如果只能用单目相机,MonoGS是目前最合适的选择之一,但要做好心理准备:没有真实深度,重建尺度和几何精度会明显打折,需要额外跑单目深度估计做正则化。如果是AR/VR效果展示,对渲染真实感要求高但对几何精度容忍度更高,Photo-SLAM这类轻量方案能快速出效果。
如果场景是室外或大场景,当前3DGS SLAM的稳定性还不够,以回环优化为主的方向会更有前途。总之,先用自己手里最成熟的数据模式跑通一个方案,后续再换其他方案对比效果,这是最稳妥的路径。
5. 实操:从跑通Demo到调出好效果
5.1 环境准备与数据集
3DGS SLAM的上手门槛主要在环境编译。硬件上至少需要一块支持CUDA的NVIDIA显卡,显存建议8GB以上,Replica一般场景8GB能跑通,但ScanNet等更复杂场景建议16GB。软件方面,以Ubuntu 20.04为例,常用依赖是Python 3.9、PyTorch 2.x、CUDA 11.7以上,另外核心组件diff-gaussian-rasterization需要本地编译。
数据集方面,Replica是最常用的测试床,合成室内RGB-D数据,带真值位姿,方便定量评估。TUM RGB-D是真实验证集,有真实深度和轨迹真值,但深度噪声更大。ScanNet带语义标签,适合做场景理解的扩展实验。
5.2 训练与评估流程
以SplaTAM为例,跑通一个demo的流程大致如下。先clone仓库,创建conda环境并安装依赖:
git clone https://github.com/graphdeco-inria/splatam.git cd splatam conda create -n splatam python=3.9 -y conda activate splatam pip install -r requirements.txt pip install ./submodules/diff-gaussian-rasterizationdiff-gaussian-rasterization编译步骤容易卡在CUDA版本匹配上,报错一般是“ninja: build stopped”或找不到cuda_runtime.h。我的处理办法是先用nvidia-smi确认驱动支持的CUDA版本,再在conda环境里装对应版本的cudatoolkit,避免用系统全局CUDA干扰。
数据准备好后,运行训练脚本,SplaTAM的命令大致是:
python scripts/run_splatam.py --dataset_path /path/to/replica --dataset_type replica --output_path ./output跑的过程中会实时打印跟踪损失、建图损失和渲染帧率。我习惯观察两个信号:跟踪损失如果长期高于某个阈值,说明关键帧的插入频率不够;建图损失下降缓慢,则说明高斯致密化过慢,可考虑降低致密化阈值。
评估轨迹用evo工具,它可以和TUM数据格式无缝衔接。关于evo的下载和使用,其实就是一个pip install evo,然后把保存的轨迹文件跟真值对齐,生成ATE和RPE指标。
5.3 调参经验
调参是3DGS SLAM最花时间的环节,但核心就那么几个参数。
图像分辨率是第一个决定性能的参数。Replica原始图像接近1080分辨率,如果显存吃紧或帧率上不去,把输入图像统一缩放到一半甚至四分之一分辨率,能换来数倍提速,代价是细节损失。我通常在调通流程后用原分辨率重训一遍出最终效果。
致密化阈值是第二个关键参数。它控制着高斯数量的增长速度。Replica这样的合成场景,高斯数量控制在几十万级别就能有相当不错的效果;真实场景如果噪声大,数量超过百万级后边际收益递减,反而容易过拟合深度噪声。实操时根据渲染PSNR是否还有提升空间来反向调节。
深度权重这个参数要按传感器类型微调。合成数据给0.5到1.0都可以,真实深度相机建议从0.2开始往上试,观察渲染图和几何图的一致性。还有位姿学习率和高斯参数学习率要分开设置,位姿学习率调到高斯参数学习率的十分之一左右比较稳。
6. 常见问题与排查技巧实录
6.1 典型故障速查表
3DGS SLAM的问题基本集中在环境、跟踪、内存三类,这里把高频问题整理成速查表。
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 编译diff-gaussian-rasterization报错 | CUDA版本或PyTorch版本不匹配 | conda重新安装cudatoolkit,确保PyTorch的CUDA版本和驱动兼容 |
| 渲染输出全黑或全是噪声点 | 光栅化器寄存器溢出或输入分辨率过低 | 降低图像分辨率并检查CUDA兼容性,查看输出日志是否有NaN |
| 跟踪漂移,误差曲线陡增 | 初始位姿估计失败或运动过快 | 降低运动速度,在离起始关键帧更近的位置重试,增大关键帧插入频率 |
| 显存OOM | 高斯数量过多或滑动窗口过大 | 提高致密化阈值,减少窗口大小,降低输入分辨率 |
| 地图出现重复残影 | 关键帧重叠过高导致局部优化不一致 | 增大关键帧间隔,增加深度代价权重 |
| 深孔、表面破碎 | 深度噪声大且几何权重不足 | 提升深度权重并开启Huber损失,必要时对深度图做预处理滤波 |
| 回环后地图错位 | 当前方案没有回环优化 | 换用LoopSplat或手动引入全局位姿图优化 |
6.2 几个值得记录的坑
我跑这套流程时踩过两个印象比较深的坑,写出来帮大家省时间。
第一个坑是diff-gaussian-rasterization编译成功后仍然渲染异常。查到最后是显存不足导致光栅化时开了低精度模式,大量高斯的深度值精度丢失,导致α混合顺序错乱。解决方法是把PyTorch的allow_tf32设置为False,让梯度计算保持全精度,虽然慢一点但稳定很多。
第二个坑是真实深度相机的深度边缘问题。消费级深度相机在物体边缘会产生大量飞点,这些飞点反投影成PointCloud后直接变成了错误的高斯位置,结果就是地图表面出现“毛刺”。处理办法不是调参数,而是在预处理阶段对深度图做双边滤波,并丢弃置信度低的深度像素。这一步对真实场景的效果提升比调任何网络参数都明显。
6.3 系统化排查思路
遇到3DGS SLAM跑不起来或效果差时,我建议按照“数据输入→初始化→跟踪→建图→渲染”的顺序逐步排查。先确认输入图像和深度图的语义对齐,再做第一帧初始化时查看生成的初始点云是否符合场景轮廓,然后看跟踪损失是否收敛,最后看建图窗口内的渲染指标。
这个顺序可以帮你快速定位问题层。比如渲染指标差但跟踪损失正常,问题多半在建图参数或高斯基元数量上;跟踪损失大但建图指标不错,问题多半出在关键帧策略和运动模型上。不要一上来就调全局参数,那样只会把系统调成一锅粥。
我个人在实际操作中的体会是,3DGS SLAM目前已经过了“只在论文里存在”的阶段,但离“开箱即用”还有距离。它最大的价值在于让“建图”这件事从纯几何走向了带真实感渲染的辐射场,这在AR、机器人仿真、数字孪生场景里是实打实的需求。如果你打算上手,我强烈建议第一步只做一件事:先跑通SplaTAM在Replica上的官方demo,在跑通之前别引入自己的数据,因为环境问题已经够喝一壶的了。
最后再分享一个小技巧:无论你最终选哪个方案,做任何实验之前先固定随机种子,用小规模序列跑一遍完整流程,把日志、超参数、数据集版本全部保存下来。3DGS SLAM的随机性和环境依赖比普通深度学习项目大得多,没有完整的实验记录,后面出了什么问题你根本说不清是自己调的参数不对,还是数据集版本变了。等你能稳定复现一个结果之后,再放开手去调致密化阈值和深度权重,那时候每一步改动会清晰很多。