# 3DGS 部署与训练指南（公开版）

> 面向读者：能在云算力平台开 Linux GPU 容器、会用 SSH 和命令行的个人开发者或AiAgent。
> 目标：在一台空载的 CUDA 12.8 GPU 容器上，从一段手机视频出发，训练出可在浏览器实时交互的 3D Gaussian Splatting（3DGS，三维高斯泼溅）模型并导出 PLY。
> 本文所有命令与耗时均来自 2026-10-14 在 RTX 5090 / 32GB 容器上的一次完整实测；换成其他 GPU 时耗时会等比变化，坑与解法不变。
> 采集阶段的拍摄规范（怎么拍才能建得好）见文末"采集速查"，原理性背景见配套博客文章。
>
> 来自 https://www.paddysun.top/ 

---

## 0. 环境侦察指南（拿到任何容器先跑这一步）

部署成败大半取决于"容器给的是什么"。先跑下面这段侦察脚本，把输出留档：

```bash
echo "== GPU ==";        nvidia-smi --query-gpu=name,memory.total,driver_version --format=csv,noheader
echo "== Python ==";     python -V
echo "== torch 栈 ==";   python -c "import torch; print(torch.__version__, torch.version.cuda, torch.cuda.get_device_capability(0))"
echo "== GPU 冒烟 ==";   python -c "import torch; a=torch.randn(4096,4096,device='cuda'); print((a@a).sum().item())"
echo "== toolkit ==";    ls -d /usr/local/cuda* 2>/dev/null; which nvcc || ls /usr/local/cuda*/bin/nvcc 2>/dev/null
echo "== 工具 ==";       which conda gcc git ffmpeg
echo "== 磁盘 ==";       df -h / | tail -1
echo "== 网络 =="
for u in https://pypi.org/simple/ https://github.com https://hf-mirror.com https://mirrors.bfsu.edu.cn/; do
  timeout 12 curl -s -o /dev/null -w "%{http_code} %{time_total}s $u\n" "$u"
done
```

**判读表**：

| 侦察项 | 合格线 | 不合格的处理 |
|---|---|---|
| GPU 显存 | 训练 ≥8GB 舒适，物体级 6GB 可挤 | 显存不足就降分辨率/降 splat 上限 |
| torch CUDA 版本 | 必须覆盖你显卡的算力代号（`get_device_capability` 输出）。**Blackwell 消费卡（50 系）必须 CUDA 12.8 起**，否则报 "no kernel image" | 按显卡代际重装匹配轮子 |
| `get_device_capability` 输出 | 记下来（如 `('12','0')`=sm_120），源码编译时可能要用 | — |
| nvcc | 能找到（PATH 或 /usr/local/cuda*/bin） | 装匹配版本的 CUDA toolkit |
| conda | 有最好（装 CUDA 版 COLMAP 用） | 无则装 miniconda（用可达的镜像源） |
| pypi / github / 镜像站可达性 | 至少一条快路 | GitHub 不通用镜像或"本机中转"；pip 慢换国内镜像 |

**两条铁律（本指南最重要的经验）**：
1. **不要动 torch 自带的 numpy 大版本**。NGC/官方 torch 轮子按特定 numpy ABI 编译，装其他包时若 numpy 被连带升级，`torch.from_numpy` 会直接报 "Numpy is not available"。每次装完包都重跑冒烟测试：
   ```bash
   python -c "import numpy,torch; torch.from_numpy(numpy.ones(2)); print('bridge OK')"
   ```
   装新包优先 `--no-deps`，确需指定版本时 `pip install --no-deps --force-reinstall numpy==<torch 适配版本>`。
2. **GitHub 不可达的容器**，代码资产走"本地机器从 jsdelivr/GitHub API 拉取 → scp 上传"的中转路线（本文 §4）。

## 1. 软件清单与角色

| 软件 | 角色 | 许可 |
|---|---|---|
| ffmpeg | 视频抽帧 | LGPL/GPL |
| COLMAP（CUDA 版） | SfM：解相机位姿+稀疏点云 | BSD，非商用需注意条款 |
| gsplat | 3DGS 训练/光栅化库（nerfstudio 团队） | Apache-2.0 商用免费 |
| fused-ssim | 训练加速依赖（simple_trainer 需要） | MIT |
| pycolmap（rmbrualla 版） | 纯 python 读 COLMAP 模型 | MIT |
| antimatter15/splat | 浏览器查看器（单 HTML+JS，无依赖） | MIT |

## 2. 装件（按序执行，每步附实测耗时）

### 2.1 ffmpeg
```bash
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq && apt-get install -y -qq ffmpeg        # 实测 1 分钟
```

### 2.2 gsplat（源码编译）
> 截至 gsplat v1.5.3（2025-07），官方预编译轮子最高只到 torch 2.4 + cu121。torch 2.5+ / cu122+ 组合必须源码编译。

```bash
export PATH=/usr/local/cuda/bin:$PATH     # nvcc 不在 PATH 时需要
export CUDA_HOME=/usr/local/cuda
pip install gsplat --no-build-isolation   # Ubuntu 24.04 系统Python 加 --break-system-packages
# 384 核容器 <2 分钟；8 核本机约 10-20 分钟
# 若报 unsupported arch：export TORCH_CUDA_ARCH_LIST="<你的算力代号，如 12.0>" 后重装
```

### 2.3 COLMAP CUDA 版（conda-forge）
```bash
# miniconda 用可达镜像（示例为北外 bfsu；按你容器实测选）
wget https://mirrors.bfsu.edu.cn/anaconda/miniconda/Miniconda3-latest-Linux-x86_64.sh -O /tmp/mc.sh
bash /tmp/mc.sh -b -p /root/miniconda3
cat > /root/.condarc <<'EOF'
channels: [defaults]
custom_channels:
  conda-forge: https://mirrors.bfsu.edu.cn/anaconda/cloud
show_channel_urls: true
EOF
/root/miniconda3/bin/conda create -n colmap --override-channels -c conda-forge "colmap=4.2.1=cuda_129*" -y
# ⚠️ build 串匹配必须带通配符 *（完整 build 形如 cuda_129ha585b08_0）
# ⚠️ 实测坑：依赖里的 openimageio 可能没被装上，报 libOpenImageIO.so 缺失时补：
/root/miniconda3/bin/conda install -n colmap --override-channels -c conda-forge openimageio -y
# ⚠️ 若补装后的 OIIO 小版本与 colmap 链接版本错位（如 colmap 要 3.1、装到 3.2），软链兜底（实测可用）：
# cd /root/miniconda3/envs/colmap/lib
# ln -sf libOpenImageIO.so.3.2.1 libOpenImageIO.so.3.1
# ln -sf libOpenImageIO_Util.so.3.2.1 libOpenImageIO_Util.so.3.1
# 验证：
export LD_LIBRARY_PATH=/root/miniconda3/envs/colmap/lib:$LD_LIBRARY_PATH
/root/miniconda3/envs/colmap/bin/colmap -h    # 出 Usage 即成功
```
⚠️ **COLMAP 4.x 相比 3.x 的变化**：`--SiftExtraction.use_gpu` 等选项已移除（GPU 默认开启）；`mapper` 要求 output_path 目录**预先存在**。

### 2.4 训练代码与依赖
训练脚本用 **gsplat 仓库 v1.5.3 tag 的 `examples/simple_trainer.py`** 及其配套模块（datasets/、gsplat_viewer.py、utils.py）。不要用 main 分支（要求更高版本 torch，会连带升级你的 torch/numpy 栈）。GitHub 可达就 `git clone` 后checkout tag；不可达就用"本地拉取+scp 中转"。

依赖（实测自洽组合）：
```bash
pip install imageio tqdm tyro viser pyyaml tensorboard torchmetrics \
  "opencv-python-headless==4.10.0.84" Pillow scikit-learn splines nerfview imageio-ffmpeg
pip install --no-deps --force-reinstall "numpy==1.26.4"    # 按铁律钉回 torch 适配版
cd fused-ssim && pip install --no-build-isolation .        # 必带 --no-build-isolation，否则隔离环境会重下 torch
```
⚠️ opencv 轮子有 numpy ABI 之分：numpy 1.x 栈配 4.10.0.84，numpy 2.x 栈配 4.12+。装完用 §0 冒烟命令复测。

## 3. 采集速查（决定上限的一步）

来自 COLMAP/Postshot 生态的公认规范，实测有效：
- **轨迹**：绕主体 2–5 圈、不同高度与俯仰；禁止单点原地摇移（三角化需要两个视点看同一点）；**禁止无结构漫游**——实测随机漫游 199 帧只配准 24 帧（12%），重建即乱码；
- **重叠**：相邻帧 30–50%；视频抽帧率 ≈ 200÷时长(秒)，夹在 1–6 fps；
- **静态**：移动物体/变化的光影都会成鬼影；拍摄总时长尽量压短（3 分钟级的光照漂移已足以毁掉远处匹配）；
- **对焦**：小光圈优先，"噪声优于模糊"；
- 数量级：物体级 70–150 帧足够；多数像样结果 ≥100 帧。

## 4. 视频处理四步（每场景）

### 4.1 抽帧
```bash
ffmpeg -y -loglevel error -i "视频.mp4" -vf "fps=5" -q:v 1 frames/sceneN_%04d.jpg
```
⚠️ 文件名/路径带空格时全程双引号；复杂命令写成脚本文件再 `bash` 执行，别在 ssh 命令行里嵌套引号。

### 4.2 COLMAP SfM
```bash
export LD_LIBRARY_PATH=/root/miniconda3/envs/colmap/lib:$LD_LIBRARY_PATH
C=/root/miniconda3/envs/colmap/bin/colmap
D=/root/data/datasets/<场景>;  mkdir -p $D/images $D/sparse
cp frames/sceneN_*.jpg $D/images/
$C feature_extractor  --database_path $D/database.db --image_path $D/images --ImageReader.camera_model SIMPLE_RADIAL
$C sequential_matcher --database_path $D/database.db --SequentialMatching.overlap 10 --SequentialMatching.quadratic_overlap 1
$C mapper             --database_path $D/database.db --image_path $D/images --output_path $D/sparse
```
成功判据：`sparse/0/` 出现 cameras.bin / images.bin / points3D.bin。
**验收指标：配准率**。实测 3 个结构化拍摄场景均 100% 配准；1 个无结构漫游场景仅 12% 配准且重建完全不可用。配准率 <80% 时优先重拍/重抽帧，不要硬训。
实测耗时（5090，72–197 帧）：40 秒～10 分钟。

### 4.3 gsplat 训练
```bash
cd gsplat_work     # 含 simple_trainer.py + datasets/ + pycolmap/ + fused-ssim(已装)
python simple_trainer.py default --data_dir $D --data_factor 1 --save_ply --disable_viewer \
  --result_dir /root/data/results/<场景>
```
- 产物：`results/<场景>/ply/point_cloud_{6999,29999}.ply`、评估轨迹视频、stats；
- 实测（5090）：72 帧/1.42M 高斯 **9 分 34 秒**（52 it/s）；197 帧/2.55M 高斯约 10 分钟；显存峰值 3–4GB；
- ⚠️ pycolmap（rmbrualla 纯 python 版）在 numpy≥2 下 `np.uint64(-1)` 抛 OverflowError，改为 `np.uint64(2**64 - 1)`；
- PLY 头应含 x/y/z + f_dc_* + f_rest_* + scale_* + rot_* + opacity（标准 3DGS 属性）。

### 4.4 坐标系适配浏览器查看器
COLMAP 坐标系 Y 轴向下，直接转 .splat 后在 antimatter15 等查看器里场景会翻转/竖立。转换时绕 X 轴转 90°：
```bash
npx splat-transform -w point_cloud_29999.ply -r 90,0,0 sceneN.splat
```

## 5. 回传与展示

1. PLY 回传本机（scp）；2. `npx splat-transform` 转 .splat（336MB→45MB）；3. 本地目录放 [antimatter15/splat](https://github.com/antimatter15/splat) 的 index.html+main.js，`python -m http.server 8777` 起服务，浏览器 `index.html?url=xxx.splat` 查看；
⚠️ 该 viewer 的 main.js 把 `?url=` 拼在 HuggingFace 前缀后面——把 `new URL(..., "https://huggingface.co/...")` 的第二个参数改成 `location.href`；改完给 `<script src="main.js?v=2">` 加版本号破浏览器缓存；
4. 或把 PLY 拖入 [superspl.at/editor](https://superspl.at/editor)（免费网页编辑器）：清浮点、裁剪、压 SPZ（约为 PLY 的 1/10）、一键发布成 URL。

## 6. 踩坑速查（全部实测）

| # | 坑 | 解法 |
|---|---|---|
| 1 | numpy 被连带升级破坏 torch | §0 铁律 1 |
| 2 | gsplat 无新组合预编译轮 | 源码编译 + TORCH_CUDA_ARCH_LIST |
| 3 | fused-ssim 装卡死 | `--no-build-isolation`（否则偷下 torch） |
| 4 | COLMAP 4.x 选项变化/OIIO 缺失 | §2.3 |
| 5 | 路径空格+ssh 嵌套引号 | 脚本文件执行 |
| 6 | `pgrep -f`/`pkill -f` 匹配到自身命令行 | 用精确 PID |
| 7 | nohup 后台任务随 SSH 会话被清 | `setsid nohup ... < /dev/null` |
| 8 | 装件与训练并行污染环境 | 串行；装完必测冒烟 |
| 9 | pip 依赖树打架卡死 | `--no-deps` 直装 |

## 7. 实测成绩单（RTX 5090 / 32GB，2026-10-14）

| 场景 | 输入 | SfM | 训练30k步 | 高斯数 | 配准率 |
|---|---|---|---|---|---|
| 室外茶台烟灰缸 | 72帧 | 40秒 | 9分34秒 | 1.42M | 100% |
| 透明地球仪（关灯） | 141帧 | 1.5分 | ~7分 | 1.36M | 100% |
| 透明地球仪（开灯） | 197帧 | 10分 | ~10分 | 2.55M | 100% |
| 阳台大场景（无结构漫游） | 199帧 | 4.5分 | — | — | **12%（弃用）** |
| 单图×3（SHARP 前馈） | 1图 | — | 5秒/张 | 1.18M | 不适用 |
