NW' Blog
2026年8月13日 · 内容页面   |   📝编辑于2026年8月13日

3DGS 训练全流程记录
(NVIDIA P104-100)

RealityScan 2.2 手机摄影测量 → 导出 CSV / COLMAP 格式 → 3D Gaussian Splatting(3DGS)建模。完整记录为什么 NerfStudio 路线在 P104 上走不通、如何搭建原版 3DGS(INRIA)环境,以及从数据准备到训练验证的完整操作教程与实测结果。

3DGS 训练预览

1. 前言

项目背景:RealityScan 2.2 手机摄影测量 → 导出 CSV / COLMAP 格式 → 3D Gaussian Splatting(3DGS)建模。本文涵盖:为什么 NerfStudio 路线在 P104 上走不通、如何搭建原版 3DGS(INRIA)环境、从数据准备到训练验证的完整操作教程,以及 P104 实测结果。

配套文档:《RealityScan2.2-NerfStudio-CSV调试记录》(数据转换阶段排错)。
配套脚本:fix_rc_csv.pycolmap_txt2bin.pyprepare_gs_data.pybuild_all.batbuild_dgr.bat

1.1 数据链路

数据流
RealityScan 2.2 对齐(手机照片 16 张)
  ├─ 导出 3.csv          → 相机位姿/内参/畸变(Internal/External camera parameters)
  ├─ 导出 images/        → COLMAP 配套图像(00000.png,已重命名)
  └─ 导出 sparse/0/      → COLMAP 稀疏重建(文本格式 txt)

本流程最终产物:3DGS 训练好的高斯点云(point_cloud.ply)+ 渲染图像

1.2 环境

项目
GPUNVIDIA P104-100(8 GB,Pascal 架构 sm_61)
驱动 / CUDA582.53 / 系统 CUDA Toolkit 11.8(nvcc 11.8.89)
编译器Visual Studio 2022 Community(MSVC 14.44)
Python3.10.11
torch / torchvision2.4.1+cu118 / 0.19.1+cu118
训练框架gaussian-splatting(INRIA,main + diff-gaussian-rasterization dr_aa 分支)
训练数据16 张,0.5x 分辨率(≈2250×1000)

1.3 文档结构

  • 第 2 章:问题(为什么 NerfStudio 路线受阻、训练环节全部问题)
  • 第 3 章:解决思路与方案选型
  • 第 4 章:逐步操作教程(可直接照做)
  • 第 5 章:P104 实测训练结果
  • 第 6 章:后续操作;第 7 章:踩坑清单;第 8 章:附录

2. 问题

2.1 核心问题:NerfStudio 的 splatfacto 无法在 P104 上运行

3D Gaussian Splatting 的核心是一个自定义 CUDA 光栅化器,NerfStudio 1.1.5 的 splatfacto 使用开源库 gsplat。排查确认:

  • gsplat 源码构建配置明确注释:build against architectures >= 7.0 (required by cooperative_groups::labeled_partition in gsplat);
  • cooperative_groups::labeled_partition 是 Volta(sm_70)引入的独立线程调度特性,Pascal(sm_60/61)硬件上不存在;
  • 官方预编译 wheel 仅含 sm_70/75/80/86/90 的 cubin,cuobjdump -elf gsplat/csrc.pyd 可验证;即使自行编译,代码级依赖也无法绕过

结论:splatfacto 需要 GPU 架构 ≥ sm_70(如 T4、V100、RTX 20 系+);P104-100(sm_61)只能换方案。

2.2 训练环节遇到的问题(概览)

即使换用原版 3DGS,训练环境搭建也是一串连锁问题(详见第 7 章踩坑清单):

#问题一句话原因
1torch.cuda.is_available()=False装的是 CPU 版 torch
2gsplat no kernel image available见 2.1,硬件不支持
3CUDA version (11.8) mismatches PyTorch (12.4)torch 的 CUDA 版本与系统 nvcc 不一致
4STL1002: expected CUDA 12.4 or newerMSVC 14.44 的新 STL 拒绝旧 CUDA
5unsupported Microsoft Visual Studio versionCUDA 11.8 不认 MSVC 14.44
6cv2 _ARRAY_API not foundnumpy 被误升到 2.x
7colmap images.bin 解析乱码qvec/tvec 误用 float32,应为 float64
8antialiasing 参数报错rasterizer 分支与主仓库代码不匹配
9路径反斜杠丢失bash 转义问题

3. 解决方法(方案选型)

方案可行性结论
splatfacto(NerfStudio)+ P104硬件不支持 sm_61
splatfacto + 云 GPU(T4 等)可行,但需要额外资源备选
原版 3DGS(INRIA gaussian-splatting)CUDA 光栅化代码兼容 Pascal,社区在 GTX 10 系上大量验证✅ 采用

选择原版 3DGS 的理由:

  1. diff-gaussian-rasterization 光栅化器编写于 2023 年(CUDA 11 时代),对 Pascal 支持良好;
  2. 我们的数据已经具备原版 3DGS 需要的 COLMAP 格式(RealityScan 导出),无需重新做特征匹配/重建;
  3. 稀疏点云(23513 点)可直接作为高斯初始化,训练更快更稳。

3.1 整体思路

流程
环境对齐(torch cu118 ↔ nvcc 11.8, numpy<2)
   → 获取源码(main + submodules,dr_aa 分支 + glm)
   → 编译三个 CUDA 扩展(MSVC + nvcc,绕过双向版本检查)
   → 数据准备(colmap txt→bin;图像与内参 0.5x 缩放)
   → 训练(7000 步验证)→ 渲染验证(PSNR)

4. 具体操作教程

4.0 环境检查

bash
# 显卡与架构
nvidia-smi --query-gpu=name,compute_cap,memory.total --format=csv
# 期望输出:NVIDIA P104-100, 6.1, 8192 MiB

# CUDA 工具链(nvcc 必须存在)
nvcc --version

# Python 依赖状态
python -c "import torch; print(torch.__version__, torch.cuda.is_available())"
python -c "import numpy; print(numpy.__version__)"

# MSVC 工具链(确认 cl.exe 存在即可,路径在 VS 安装目录)
ls "/c/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/"

4.1 Python 环境对齐

关键原则:torch 的 CUDA 版本必须与 nvcc 版本一致,否则 torch cpp_extension 直接拒绝编译。

bash
# 系统 nvcc 是 11.8 → 安装 cu118 版 torch(必须用 PyTorch 官方源,默认源装的是 CPU 版!)
python -m pip install torch==2.4.1 torchvision==0.19.1 \
  --index-url https://download.pytorch.org/whl/cu118

# numpy 必须 <2(open-cv 等二进制扩展依赖 numpy 1.x)
python -m pip install "numpy==1.26.4"

# 验证
python -c "import torch; print(torch.__version__, torch.cuda.is_available())"   # 2.4.1+cu118 True
python -c "import numpy; print(numpy.__version__)"                              # 1.26.4

⚠️ pip install --force-reinstall 会从默认源连带升级依赖(numpy→2.x、torch→CPU 版),曾两次踩坑。装完后务必复核版本。

4.2 获取源码

GitHub 直连(尤其 git 协议)在本环境极不稳定,推荐 ghfast.top 镜像下载 zip:

bash
cd /d/temp

# 主仓库(main 分支)
curl -L --retry 10 -o gs.zip "https://ghfast.top/https://github.com/graphdeco-inria/gaussian-splatting/archive/refs/heads/main.zip"
unzip -q gs.zip && mv gaussian-splatting-main gaussian-splatting

# rasterizer:必须用 dr_aa 分支(主仓库代码调用 antialiasing 参数,main 分支无此接口)
curl -L --retry 10 -o dgr.zip "https://ghfast.top/https://github.com/graphdeco-inria/diff-gaussian-rasterization/archive/refs/heads/dr_aa.zip"
unzip -q dgr.zip
mkdir -p gaussian-splatting/submodules/diff-gaussian-rasterization
mv diff-gaussian-rasterization-dr_aa/* gaussian-splatting/submodules/diff-gaussian-rasterization/

# simple-knn 在 INRIA GitLab(不在 GitHub!)
curl -L --retry 10 -o sknn.zip "https://gitlab.inria.fr/bkerbl/simple-knn/-/archive/main/simple-knn-main.zip"
unzip -q sknn.zip
mkdir -p gaussian-splatting/submodules/simple-knn
mv simple-knn-main/* gaussian-splatting/submodules/simple-knn/

# fused-ssim(metrics 用)
curl -L --retry 10 -o fssim.zip "https://ghfast.top/https://github.com/rahul-goel/fused-ssim/archive/refs/heads/main.zip"
unzip -q fssim.zip
mkdir -p gaussian-splatting/submodules/fused-ssim
mv fused-ssim-main/* gaussian-splatting/submodules/fused-ssim/

# glm(rasterizer 的数学库依赖)
curl -L --retry 10 -o glm.zip "https://ghfast.top/https://github.com/g-truc/glm/archive/refs/tags/1.0.1.zip"
unzip -q glm.zip
mkdir -p gaussian-splatting/submodules/diff-gaussian-rasterization/third_party
cp -r glm-1.0.1/* gaussian-splatting/submodules/diff-gaussian-rasterization/third_party/glm/

4.3 编译三个 CUDA 扩展

编译前置条件(已在 4.0/4.1 满足):vcvars 环境、nvcc 11.8、torch cu118、numpy<2。

修改三个 setup.py:在 extra_compile_args["nvcc"] 列表开头追加两个参数(MSVC 14.44 过新、CUDA 11.8 过旧,分别绕过两侧版本检查):

python
"-allow-unsupported-compiler", "-D_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH"

已修改好的脚本见 readme/build_all.bat(对应三个 submodule 的 setup.py 均已加参数):

build_all.bat
@echo off
call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat" >nul 2>&1
set PYTHONIOENCODING=utf-8
set DISTUTILS_USE_SDK=1
cd /d D:\temp\gaussian-splatting\submodules\diff-gaussian-rasterization
python setup.py install
cd /d D:\temp\gaussian-splatting\submodules\simple-knn
python setup.py install
cd /d D:\temp\gaussian-splatting\submodules\fused-ssim
python setup.py install

执行与验证:

bash
cd /d/temp/ReasonixChat/3/readme && cmd //c build_all.bat
cd /d/temp/gaussian-splatting
python -c "from diff_gaussian_rasterization import GaussianRasterizationSettings, GaussianRasterizer; print('dgr OK')"
python -c "import simple_knn; print('simple_knn OK')"
python -c "import fused_ssim; print('fused_ssim OK')"

编译时间:三个扩展合计约 5~10 分钟(单核 ninja 编译)。

4.4 数据准备

RealityScan 导出的 sparse/0colmap 文本格式,而原版 3DGS 只读二进制格式,需要转换:

bash
# 1) txt → bin(16 相机 / 16 图像 / 23513 点)
cd /d/temp/ReasonixChat/3
python readme/colmap_txt2bin.py

# 2) 图像与内参同步缩放到 0.5x(适配 8GB 显存与训练速度)
#    产物:D:\temp\ReasonixChat\3\gs_data(images/ + sparse/0/*.bin)
python readme/prepare_gs_data.py 0.5

colmap_txt2bin.py 要点:

  • cameras.bin:[id(int32), model(int32), w(int64), h(int64), params(double...)],PINHOLE=1 共 4 个参数(fx, fy, cx, cy);
  • images.bin:qvec/tvec 是 float64(double),这是最易写错的地方(colmap 官方 ReadImagesBinary 用 8 字节 double);随后是 camera_id(int32)name(ASCII+\0)num_points(uint64)(x,y double, point3D_id int64)...;
  • points3D.bin:[id(int64), xyz(3×double), rgb(3×uint8), error(double), track_len(int64), (image_id int32, point2D_idx int32)...]

prepare_gs_data.py 要点:3DGS 读取图像原始分辨率,没有内置缩放;缩放图像的同时必须按同一比例缩放 fx/fy/cx/cyw/h,否则相机几何错乱。

4.5 训练

bash
cd /d/temp/gaussian-splatting
PYTHONIOENCODING=utf-8 python train.py \
  -s "D:/temp/ReasonixChat/3/gs_data" \
  -m "D:/temp/ReasonixChat/3/gs_output" \
  --iterations 7000
参数含义
-s数据目录(含 images/sparse/0/),路径用正斜杠(bash 下反斜杠会被吞)
-m输出目录(模型、checkpoint、日志)
--iterations迭代数,默认 30000;验证流程先用 7000
--eval可选,按 1/8 划分测试集并输出测试指标

训练日志节选(首次运行会提示把 points3D.bin 转成 points3D.ply,属正常):

log
Loading Training Cameras
Number of points at initialisation :  23513
Training progress: 100%|██████████| 7000/7000 [11:21<00:00, 9.49it/s, Loss=0.0155550]
[ITER 7000] Evaluating train: L1 0.009710725769400597 PSNR 36.59599494934082
[ITER 7000] Saving Gaussians
Training complete.

4.6 渲染验证

bash
cd /d/temp/gaussian-splatting
PYTHONIOENCODING=utf-8 python render.py \
  -m "D:/temp/ReasonixChat/3/gs_output" \
  -s "D:/temp/ReasonixChat/3/gs_data" \
  --iteration 7000

输出:gs_output/train/ours_7000/{renders,gt}/gs_output/test/ours_7000/

渲染结果与真值对比
渲染结果(左)与真值(右)对比 —— 16 帧平均 PSNR 37.24 dB

用脚本对比渲染图与 GT 计算 PSNR:

python
import numpy as np
from PIL import Image
from pathlib import Path

r = Path("D:/temp/ReasonixChat/3/gs_output/train/ours_7000")
psnrs = []
for p in sorted((r / "renders").glob("*.png")):
    gt = np.asarray(Image.open(r / "gt" / p.name).convert("RGB"), dtype=np.float32) / 255.0
    pred = np.asarray(Image.open(p).convert("RGB"), dtype=np.float32) / 255.0
    mse = np.mean((pred - gt) ** 2)
    psnrs.append(10 * np.log10(1.0 / (mse + 1e-12)))
print(f"PSNR: mean={np.mean(psnrs):.2f} min={np.min(psnrs):.2f} max={np.max(psnrs):.2f}")

5. P104 实测训练结果

5.1 训练数据

项目
图像16 张,0.5x ≈ 2250×1000(原始 4500×2000)
稀疏点初始化23513 点
迭代数7000
训练耗时11 分 21 秒(约 9.5 it/s)
Loss 收敛0.1606 → 0.0156
训练集评估PSNR 36.6 dB,L1 0.0097

5.2 渲染验证(16 帧,与输入图像对比)

指标数值
平均 PSNR37.24 dB
最低 / 最高30.17 / 39.92 dB

说明:此为训练集视角的渲染对比(验证流程),数值已属良好水平;要评估泛化能力需 --eval 划分测试集。

训练输入照片
训练输入:RealityScan 2.2 对齐的手机照片(16 张之一)

5.3 产物

gs_output/
gs_output/
├── point_cloud/iteration_7000/point_cloud.ply   ← 训练好的高斯泼溅点云(核心产物)
├── input.ply                                     ← 稀疏点初始化
├── cameras.json / exposure.json                  ← 相机与曝光参数
├── train/ours_7000/{renders,gt}/                 ← 渲染图与真值
├── test/ours_7000/
└── events.out.tfevents.*                         ← TensorBoard 日志

6. 后续操作

6.1 完整训练(推荐,约 50 分钟)

bash
cd /d/temp/gaussian-splatting
PYTHONIOENCODING=utf-8 python train.py \
  -s "D:/temp/ReasonixChat/3/gs_data" \
  -m "D:/temp/ReasonixChat/3/gs_output_full" \
  --iterations 30000

产物:gs_output_full/point_cloud/iteration_30000/point_cloud.ply

6.2 查看与导出

  • SIBR viewer(官方实时查看器,需另行编译,见仓库 SIBR_viewers/);
  • 直接渲染视频:render.py 支持 --skip_train/--skip_test 与自定义轨迹;
  • 点云 ply 可用 CloudCompare / MeshLab 打开检查质量。

6.3 换用更合适的 GPU(如需要 NerfStudio 生态)

gsplat 要求 sm_70+;租用 T4/RTX 20 系+ 后,output/transforms.json 已就绪,直接:

bash
PYTHONIOENCODING=utf-8 ns-train splatfacto --data output --output-dir runs \
  --experiment-name myscan --max-num-iterations 30000 --vis tensorboard \
  nerfstudio-data --downscale-factor 2

7. 踩坑清单(按出现顺序)

#现象原因处理
1torch.cuda.is_available()=False默认源装了 CPU 版 torch用 PyTorch 官方源装 cu118/cu124 wheel
2gsplat no kernel image is available for execution on the devicegsplat 不支持 sm_61(见 2.1)换原版 3DGS
3DLL load failed while importing gsplat.csrctorch 版本与 gsplat 预编译 wheel(pt24)不匹配此路已弃用(gsplat 不再需要);若坚持,需 torch 降到与 wheel 一致的版本
4The detected CUDA version (11.8) mismatches ... PyTorch (12.4)torch 的 CUDA 与 nvcc 不一致torch 换 cu118 与 nvcc 11.8 对齐
5unsupported Microsoft Visual Studio versionCUDA 11.8 的 host_config.h 拒绝 MSVC 14.44nvcc 加 -allow-unsupported-compiler
6STL1002: Unexpected compiler version, expected CUDA 12.4 or newerMSVC 14.44 新 STL 反向拒绝旧 CUDAnvcc 加 -D_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH
7cv2 _ARRAY_API not found / numpy.core.multiarray failed to importnumpy 被 pip 连带升到 2.xpip install "numpy==1.26.4"
8glm/glm.hpp: No such file or directoryrasterizer 的 third_party/glm 子依赖缺失下载 glm 放入 third_party/glm/
9colmap 二进制读取 UnicodeDecodeErrorimages.bin 的 qvec/tvec 误用 float32修正为 float64(<4d/<3d)
10GaussianRasterizationSettings ... unexpected keyword 'antialiasing'rasterizer 用了 main 分支,主仓库代码需要 dr_aa换 dr_aa 分支重新编译
11Could not recognize scene type路径反斜杠被 bash 吞掉,sparse 检测失败传参用正斜杠 D:/temp/...
12DISTUTILS_USE_SDK 警告导致编译中止torch cpp_extension 要求显式声明 SDK 环境bat 中 set DISTUTILS_USE_SDK=1
13--force-reinstall 后 torch 变 CPU 版 / numpy 变 2.xpip 从默认源重装依赖--no-deps 或装完复核版本

8. 附录

8.1 文件清单

文件/目录说明
readme/3DGS训练全流程记录.md本文档
readme/RealityScan2.2-NerfStudio-CSV调试记录.md数据转换阶段排错文档
readme/fix_rc_csv.pyRealityScan 2.2 CSV 列名映射(含 --zero-k4)
readme/colmap_txt2bin.pycolmap 文本 → 二进制转换
readme/prepare_gs_data.py图像+内参同步缩放,生成 3DGS 数据目录
readme/build_all.bat一键编译三个 CUDA 扩展
readme/build_dgr.bat单独编译 rasterizer
gs_data/3DGS 输入数据(0.5x)
gs_output/训练产物(7000 步)
output/NerfStudio 转换产物(transforms.json,备用)

8.2 原理补充

为什么原版 3DGS 支持 Pascal 而 gsplat 不支持?

3D Gaussian Splatting 的光栅化核心是按 tile 组织高斯并做并行排序/混色。原版 diff-gaussian-rasterization(2023)使用经典的 warp 级同步(__syncwarp / shared memory)实现,这些原语 Pascal 就有;而 gsplat 引入 cooperative_groups::labeled_partition(Volta 独立线程调度)实现更高效的动态并行,代价是放弃 Pascal。这是"新库更快但更挑硬件"的典型取舍。

RealityScan 的 COLMAP 坐标系说明

  • CSV 导出:(E, N, U) 右手系(x 东、y 北、z 上/alt);
  • COLMAP 导出:(E, -U, N) 右手系(相机中心 = (x, -alt, y)),两者差一个固定旋转,经 PnP 重投影验证几何一致;
  • 原版 3DGS 直接消费 COLMAP 姿态,无需关心绝对坐标系(训练时自行归一化)。

8.3 参考链接


Happy Reconstructing! 🎉

评论区 留下您的想法
教程