这半年我几乎把业余时间都砸进了3DGS里。从Ubuntu 22.04上第一次编译3DGS原版代码开始到后来在Ubuntu 20.04上复现某个3DGS-SLAM项目再到现在能完整跑通从手机拍照、COLMAP重建、训练高斯模型到最后渲染导出的整条3DGS重建流程中间踩过的坑足够写成长篇连载了。这篇博客就是我梳理出来的学习笔记3DGS是什么、代码怎么复现、重建流程里哪些参数真正影响效果以及为什么SLAM方向最近集体转向3DGS。如果你正准备折腾3dgs或者在ubuntu22、ubuntu20上卡在环境配置、代码复现阶段这篇内容可以直接拿来当参考。1. 先弄清楚3DGS到底在解决什么问题1.1 3DGS的本质用一团软球表示场景3D Gaussian Splatting中文一般叫三维高斯泼溅名字听着唬人拆开看其实不复杂。它没有延续NeRF那套用神经网络编码连续场的思路而是直接把场景表示成一堆带体积的3D高斯函数。你可以把这上万个高斯基元想象成一个个有渐变的彩色软球每个软球都有自己的位置、朝向、胖瘦、颜色和透明度。它们叠在一起经过投影和颜色混合就能形成一张锐利的照片。代码层面每个3D高斯需要维护的参数其实非常明确一个三维位置均值一个3x3的协方差矩阵代码里为保正定通常用四元数加三个缩放系数来表示一组球谐系数用来表达随视角变化的颜色还有一个标量不透明度。整个训练过程你可以理解成在拼拼图最开始用SFM稀疏重建出来的点云铺下第一层小高斯之后不断调整所有参数让渲染出的画面越来越接近输入照片。优化过程中还有一步自适应密度控制它不会傻乎乎维持固定数量而是哪里细节不够就多克隆分裂出一些高斯基元哪里冗余就把透明度过低的高斯删掉。理解这一点对后面看训练日志很有帮助。1.2 和NeRF、传统重建方式相比优势到底在哪学习3DGS绕不开NeRF。NeRF的思路是用MLP网络去编码场景的连续辐射场渲染每条光线都要沿射线采样几十个点每个点都过一遍网络推理。效果虽然好渲染速度却很难让人满意早期一张1080P图动辄几秒甚至更久。3DGS换了个完全不同的玩法场景信息直接住在显式的高斯基元里渲染时不需要问神经网络这个点颜色是什么只要把高斯投影、按深度排序、逐像素做alpha混合就行。一个复杂的静态场景在高端显卡上能跑到上百FPS体验完全是两代东西。再对比传统Mesh网格重建。Mesh需要维护顶点、边、面的拓扑关系复杂表面重建时很容易出现漏面、塌陷、纹理贴不上。3DGS没有网格拓扑的包袱细节表达能力很突出能还原出树叶缝隙、发丝这类精细结构。代价是存储量偏大但后续可以用量化、剪枝、Hash索引来压。在我看来3DGS刚好踩在神经隐式表达和传统显式表达的中间地带显式基元让它天然支持可控编辑和快速渲染连续高斯分布又让它保留了不错的几何表达能力。当然它也有明显短板。第一训练阶段对GPU显存和算力的要求不低8G显卡只能跑小场景中低分辨率第二对输入照片的质量和位姿精度敏感前期稀疏重建做不好后面再怎么调都难救第三场景一旦变大高斯基元数量会急剧膨胀几十万甚至上百万个高斯的管理本身就是新问题。带着这些前提去看代码你会发现很多设计都是为了解决上述某一类不足。2. 环境搭建与代码复现Ubuntu 20/22上的全过程2.1 硬件、系统和依赖版本怎么选先说我的实测配置一台i7加RTX 3060 Laptop 8G显存另一台台式机是RTX 4090。原版仓库在这种档位上都能跑只是分辨率和速度差别明显。如果你的目标是复现原版并调通流程8G显存入门够用但建议分辨率控制在1600x1200以内迭代次数也可以从30000降到10000先验证效果。真想认真做实验16G以上显存会舒服很多。操作系统方面Ubuntu 20.04和22.04我都试过。20.04默认GCC 922.04默认GCC 11原版仓库里的diff-gaussian-rasterization对GCC版本并不挑真正挑剔的是CUDA和PyTorch版本之间的匹配。我最终稳定的组合是组件我验证过的版本Ubuntu20.04 / 22.04GCC9.x / 11.xCUDA Toolkit11.8 或 12.1PyTorch1.13 / 2.0.x / 2.1.x 均可用Python3.8 / 3.10安装顺序建议是先创建conda环境再装PyTorch注意用和CUDA配套的版本然后装plyfile、tqdm、scipy、tensorboard这些基础依赖最后再编译两个子模块。官方仓库用了submodule管理所以clone的时候不能直接git clone完事必须先git clone主仓库再执行git submodule update --init --recursive把diff-gaussian-rasterization和simple-knn拉下来否则后面编译一定报错。2.2 编译原版仓库时最容易踩的几个坑环境配置是劝退率最高的一关我把自己和周围人踩过的问题汇总一下基本覆盖了大部分编译失败场景。第一个坑是找不到CUDA头文件。报错通常是fatal error: CUDASomething.h: No such file or directory。原因很简单CUDA_HOME环境变量没设置或者默认路径下没有对应文件。解决方式是在~/.bashrc里加一行export CUDA_HOME/usr/local/cuda-11.8再source ~/.bashrc。记得把nvcc和cuda头文件所在目录都确认一遍。第二个坑是PyTorch的CUDA版本和本机nvcc版本对齐问题。比如PyTorch是用CUDA 12.1编译的你系统里只有CUDA 11.8此时PyTorch的扩展接口和diff-gaussian-rasterization的编译环境对不上会出现一堆莫名其妙的链接错误。最稳妥的办法是查清楚PyTorch对应哪个CUDA版本再用conda安装指定版本。这里有个小技巧python -c import torch; print(torch.version.cuda)能直接看到PyTorch内置的CUDA版本。第三个坑是显卡算力编译参数。diff-gaussian-rasterization的setup.py里其实有根据TORCH_CUDA_ARCH_LIST来选算力的逻辑。如果你用的老显卡比如10系、20系默认编译参数可能没给你卡对应的compute capability运行时就会出现Invalid device function之类的问题。这时候要在环境变量里声明export TORCH_CUDA_ARCH_LIST7.530系是8.640系是8.9。别问为什么编译时不报错这种错一般要到实际训练渲染时才暴露。第四个坑是Python版本导致的三方库冲突。Python 3.7以下对torch和CUDA生态支持很差我建议直接用Python 3.8或3.10。另外pip install -r requirements.txt之前先确认tqdm、scipy、plyfile版本正常不然训练到一半因为scipy接口变化报错排查起来很费时间。2.3 验证环境是否可用的最小测试环境配好后先别急着上大数据集跑一个最小验证流程能省很多麻烦。我一般习惯用一个只有几十张图片的小场景甚至可以用官方仓库自带的样例数据。命令很直观python train.py -s 数据目录 -m 输出目录 --iterations 3000如果3000次迭代能顺利跑完输出目录里出现point_cloud和conf文件夹说明整个链路已经通了。这时候再开TensorBoard看loss曲线如果loss稳定下降说明diff-gaussian-rasterization的前向和反向都正常工作。别小看这一步很多人直接拿大数据集开跑结果跑了半天才发现是优化器发散回头再排查环境就浪费了很多时间。3. 完整重建流程拆解从图片到可交互场景3.1 数据准备你自己拍的素材能不能直接训练重建流程的起点不是训练而是数据准备。原版3DGS依赖COLMAP做Structure from Motion也就是先估计每张图像的相机内外参同时生成稀疏点云。这个稀疏点云是初始化高斯基元的种子非常重要。你可以直接用官方的convert.py脚本它会自动调用COLMAP完成特征提取、匹配、稀疏重建然后把结果整理成sparse/0、images等目录结构。自己拍数据时我总结了几条硬经验照片重叠度要高相邻两张画面重合至少60%-70%不然配对找不到足够的特征点避免大范围光照突变比如一会在强阳光下、一会在树荫里会让曝光估计和颜色一致性问题放大场景里不要有明显动态物体行人、车流、飘动的旗帜都会让优化模型努力同时满足多个矛盾约束而导致重影如果场景里有大片玻璃、镜面、纯色墙面这类低纹理区域COLMAP很容易在那些地方稀疏重建失败后续3DGS也会跟着缺细节。如果你想跳过自己采集的麻烦可以用一些公开数据集比如Mip-NeRF 360、Tanks Temples主要看场景覆盖度和相机轨迹质量。3.2 训练阶段参数怎么理解效果怎么调训练入口是train.py核心参数没几个但理解它们背后的逻辑很重要。--iterations总迭代步数默认30000。小数据集或资源有限时降到10000-20000也能出不错效果大场景建议保持30000以上。--sh_degree球谐系数最高阶数默认3。这个值控制颜色随视角变化的表达能力降低到2甚至1会明显损失视角依赖的高光效果。--densify_grad_threshold自适应密度控制里分裂/克隆高斯的梯度阈值。默认0.0002调高会让密度增长更保守调低会更快填充细节但也要付出显存和训练时间。学习率衰减位置均值的初始学习率在代码里是按position_lr_init和position_lr_final插值衰减的。很多人手动把初始学习率调很大想去加速结果就是训练后期高斯的抖动特别厉害重建质量反而下降。我看过太多这类案例基本结论是先保持默认跑一遍再考虑调。训练过程中要继续监控loss曲线。如果loss在前几千步降得很快后面进入平台期这是正常现象不必焦虑。真正要警惕的是loss突然跳涨那大概率是学习率调太高或者某个关键帧的位姿估计有问题。原版每1000步会保存一个ckpt文件并且会导出当前的点云方便中途查看效果。我一般在3000、7000、15000步各停一次看一眼可视化的中间结果确认没有明显的烟雾状伪影或大面积空洞。3.3 渲染与评估结果文件怎么用PSNR怎么算训练完成后散落的高斯基元还只是一堆二进制格式的点云需要用render.py把它们变成图像。命令是python render.py -m 输出目录渲染结果默认放在输出目录下的test_preds和train_preds里。前者对应测试视角后者对应训练视角。看train_preds通常会比test_preds清晰因为模型见过这些视角这不是作弊只是用来确认模型是否过拟合。真正评估泛化能力要看test_preds。然后运行python metrics.py -m 输出目录会计算每个测试视角的PSNR、SSIM、LPIPS并给出平均值。我举个例子在Mip-NeRF 360这类数据集上一个训练良好的3DGS模型PSNR可以到27-30 dBSSIM在0.85-0.95之间LPIPS通常在0.1以下。如果你复现出来明显低于这个范围先检查数据质量其次检查是否过拟合到了训练视角。损失函数默认是L1加D-SSIM组合两个权重分别为0.8和0.2物理含义上L1约束亮度接近SSIM约束结构感知质量。我很少动这个权重因为默认值在大量场景上已经验证过自己调反而容易顾此失彼。3.4 实操心得三个显著影响重建质量的细节第一个细节初始点云数量直接影响最终细节。COLMAP稀疏重建的点如果太少比如只有几千个点那第一代高斯基元根本铺不满场景。这时候有两个办法一是把COLMAP的--Mapper.ba_global_function_tolerance改小让稀疏重建更激进二是在train.py之后允许更早的分裂也就是把--densify_from_iter从默认的500提前到200。我实测提前分裂能有效弥补初始点不足。第二个细节白色天空和纯色区域很容易生成漂浮雾。因为约束不足优化器会在没有特征的地方洒一团低不透明度的高斯来平均颜色。如果数据里有大段天空建议在预处理时做mask或者干脆像很多公开数据集那样把天空裁剪掉。纯色或者重复纹理的墙面也会出现类似问题但一般影响较小。第三个细节sh_degree不要一上来就拉满配。原版在训练过程中会按照speedup逻辑从0阶逐渐升到3阶这让颜色表达由低到高渐进增加。很多人为了省事直接让模型从头到尾用3阶高光区域容易在早期就收敛到错误结果。我倾向于保留默认。如果你发现某个视角的高光闪烁特别明显可能是球谐阶数过高过拟合了某个训练视角适度降低到2可以缓解。4. 从静态场景到在线建图3DGS与SLAM的结合4.1 为什么SLAM社区突然盯上了3DGS传统SLAM系统通常维护稀疏路标点或稠密点云地图。稀疏地图足够定位但对用户来说可读性很差没法拿来做导航、编辑、展示。稠密点云地图能看但缺少连续表面和完整的辐射信息。基于TSDF或八叉树的稠密重建可以做到物体级重建不过对传感器要求高、更新策略复杂、全局一致性和分辨率也受限。NeRF曾经被尝试用来做SLAM后端建图但NeRF的隐式表达让增量更新比较麻烦渲染开销太大实时跟踪很难扛住。3DGS的出现等于提供了一条中间路线显式的高斯基元方便增量插入和局部更新渲染速度快到可以支撑实时重投影和位姿优化而且密度控制机制天然适配在线建图——在不确定区域多加点高斯在冗余区域删一点。所以3DGS-SLAM近一年爆发式增长本质上是把3DGS从离线拍照重建搬进了在线追踪与建图的循环里。4.2 几类代表性方案的设计思路我大概把接触过的方案分成三类。第一类以SplaTAM为代表主打RGB-D输入。它维护一个全局高斯地图在跟踪阶段通过最小化当前帧和渲染帧之间的颜色误差与深度误差来估计相机位姿在建图阶段用滑动窗口里的关键帧去优化新增的高斯基元。它可以看作把原来COLMAP初始化全局训练换成每来一帧增量更新核心难点是如何避免旧帧被新帧的信息覆盖导致地图漂移。第二类以Gaussian Splatting SLAM为代表支持单目、双目、RGB-D多种输入。它把3DGS和传统SLAM的前端特征跟踪、关键帧选择、共视关系结合起来利用共视约束来维护一个局部活动窗口窗口外的高斯会被冻结从而控制优化规模。这个设计在回环检测上比纯增量更新更有优势因为关键帧的几何约束可以让漂移限制在局部。第三类以MonoGS等为代表强调纯单目或轻量深度条件下的鲁棒性。单目情况下没有深度真值建图模块的几何损失要非常小心不然尺度漂移会直接毁掉高斯地图。它们通常会增加一些正则化项比如限制高斯椭球的扁率、加入平滑项约束相邻高斯之间的连续性也会用mapping和tracking双线程交替执行来分摊计算压力。4.3 自己跑3DGS-SLAM类项目的一点实测体会第一次跑这类项目时我犯了个错误拿原版3DGS的调试经验去套结果完全不适用。刚才说过原版是离线全局优化的你可以从头到尾盯着一条loss曲线3DGS-SLAM是渐进式的前期帧对高斯地图的影响权重很大一旦前几帧位姿没卡稳整个地图就歪了。我后来总结出三个实用习惯一是先跑小场景、短轨迹把整个在线流程跑通再上大数据集二是重点关注关键帧策略尽量不要让连续帧之间出现较大视角跳变这会让高斯地图在前端还没收敛时就被撕裂三是多看渲染出的深度图和边界。高斯地图在线构建的深度虽然能给前端提供信息但边界处很毛糙直接在cost里用大权重会导致位姿优化被错误深度带偏。跑完一个方案之后再对比论文里的效果你会发现论文指标很好看但复现时往往会因为传感器噪声、场景尺度差异、参数默认值不匹配导致指标缩水。这不是你代码抄错了而是这些方案在发布时通常绑定其数据集做了参数调优。我的建议是自己动手做一个不同场景的实验记录下关键参数对结果的影响再回头读实现细节会理解得更深。5. 学习路径复盘给正准备入坑的朋友5.1 入门阶段的优先级建议我见过不少朋友一上来就跳到3DGS-SLAM方向代码还没跑通就想着改模块、加回环。结果卡在环境里一个礼拜最后连原始高斯的原理都没弄清楚。如果让我重新学一次优先级应该是这样的第一步先把原版3DGS复现跑通。环境、训练、渲染、评估全流程走一遍确保小场景效果过得去。第二步精读train.py和render.py的代码流程搞清楚数据流照片怎么变成初始点云点云怎么变成高斯参数参数怎么通过可微光栅化器反向传播更新。第三步去看构建在3DGS之上的各类应用从静态重建到场景编辑、压缩、动态重建再到SLAM。第四步选一个有代码的SLAM项目从头跑一遍加调试再去读它的核心模块改动。我自己花了大概两周时间完成第一步到第二步期间大部分时间耗在环境上。后来发现市面上已经有不少社区封装好的Docker镜像如果你不想折腾环境可以直接找一个现成的、带原版仓库依赖的镜像作为起点。但我仍然建议至少手动编译一次diff-gaussian-rasterization只有亲手踩过那个坑后面自己扩展CUDA代码时心里才有底。5.2 论文阅读的顺序与工具论文只看摘要容易高估自己的能力要建立系统阅读路线。我的阅读顺序是原版论文3D Gaussian Splatting for Real-Time Radiance Field Rendering必须精读配合附录看公式推导至少搞清楚协方差矩阵的数学表达和投影过程。然后读高斯基元加速与压缩方向比如用稀疏结构或Hash索引改数据结构的论文明白3DGS在工程落地时的瓶颈在哪。再读抗锯齿和场景理解像Mip-Splatting是用多尺度各向异性高斯来缓解缩放锯齿这类工作能加深你对高斯频率特性的理解。最后读SLAM相关论文比如SplaTAM、Gaussian Splatting SLAM、MonoGS等重点对比它们的跟踪损失、关键帧滑动窗口、高斯增量管理。工具上我强烈建议配合源码读论文。第一次看SplaTAM时我同时在三个文件之间跳原始论文、train.py的损失函数、外参优化模块。论文写的是总览源码才是完整细节。我会在论文边际写下对应的源码文件和函数名后续回看能省很多时间。5.3 接下来我还想继续探索的方向3DGS目前还在高速演进中如果说有什么方向值得继续投入精力我会挑四个一是大场景管理如何让千万级高斯基元在有限显存里被高效调度二是动态场景重建把时间维度编码进高斯参数或者用变形场方案让单个高斯基元在不同帧间运动这是个很有意思的系统设计问题三是语义与可控编辑让高斯能理解这是一张桌子从而支持交互式场景修改四是移动端部署让低功耗设备也能渲染和局部更新高斯地图这件事和AR场景强相关。当然我不会要求每个人都去啃CUDA源码但至少应该保持每周读一到两篇新论文的习惯。走到那里的时候你对3DGS的理解已经跟最初完全不同了。最后再分享一个小技巧训练过程中如果发现重建结果有明显块状或者条状伪影先用SIBR Viewer加载输出目录里的point_cloud.ply在视图里旋转几圈大部分问题一眼就能看出来是某个区域高斯分布异常还是整体相机位姿出错。我有一半的调参决策是靠这个viewer做的比起反复看PSNR数字直接看三维效果要直观得多。如果你也在3DGS这条路上摸索希望这篇记录能帮你少踩几个坑。