LingBot-Map 复现与原理剖析:基于 Geometric Context Transformer 的流式 3D 重建

项目地址github.com/robbyant/lingbot-map

论文:Geometric Context Transformer for Streaming 3D Reconstruction

复现环境:Ubuntu 24.04 + RTX 4090 24GB + PyTorch 2.6.0


1. 项目概述

LingBot-Map 是一个基于 Vision Transformer 的流式 3D 重建系统。输入单目 RGB 图片序列(或视频),逐帧输出相机位姿(6-DoF)、深度图和 3D 点云,无需传统的 SfM 初始化或全局 BA 优化。

核心特点:

  • 流式推理(Streaming Inference):逐帧处理 + KV Cache 注意力,在 RTX 4090 上可达 ~20 FPS(518×378 分辨率)

  • 长序列支持:通过滑动窗口 KV Cache + Keyframe Interval 策略,支持超 10,000 帧

  • 端到端:从 RGB 直接到 3D 点云,无后处理 BA

  • SOTA 重建质量:在 Tanks and Temples、ScanNet 等基准上优于现有流式和离线方法

与 SLAM 的区别:LingBot-Map 是离线 3D 重建系统,输入是已录制的视频文件/图片序列,不是实时摄像头流。它追求的是重建质量而非实时性,"~20 FPS"指的是处理速度而非实时吞吐。


2. 环境搭建

2.1 硬件要求

LingBot-Map 的显存需求主要由三部分组成:

复制代码
显存 = 模型权重 + ViT 特征 + KV Cache + 中间激活 + 输出
帧数 预估 KV Cache 最低显存建议
≤100 帧 ~2.5 GB 8 GB
≤320 帧 ~5 GB 12 GB
≤1000 帧 ~12 GB 16 GB
>3000 帧 ~20+ GB 24 GB(推荐 RTX 4090 / A6000)

关键约束 :必须使用 SM 7.0+(Volta 及更新)架构的 NVIDIA GPU 。Pascal 架构(如 P40、P100)因 SM 6.1 不被 CUDA 12.x 预编译 kernel 支持,运行时会报 GET was unable to find an engine

2.2 创建 Python 环境

复制代码
conda create -n lingbot-map python=3.10 -y
conda activate lingbot-map

2.3 安装 PyTorch

根据 CUDA 版本选择。CUDA 12.4 对应:

复制代码
pip install torch==2.6.0 torchvision==0.21.0 --index-url https://download.pytorch.org/whl/cu124

验证 GPU 可用:

复制代码
python -c "import torch; print(torch.cuda.is_available()); print(torch.cuda.get_device_name(0))"

2.4 安装依赖

复制代码
# 基础依赖
pip install einops safetensors opencv-python tqdm scipy
​
# 3D 可视化(viser 提供浏览器端 3D 点云查看)
pip install viser trimesh matplotlib requests
​
# 模型下载工具(国内用 ModelScope 更快)
pip install huggingface_hub modelscope
​
# LingBot-Map 本体(editable 模式)
git clone https://github.com/robbyant/lingbot-map.git ~/lingbot-map
pip install -e ~/lingbot-map

2.5 下载模型权重

模型文件 4.4GB。HuggingFace 直连国内可能被墙,使用 ModelScope 国内源:

复制代码
python -c "
from modelscope import snapshot_download
snapshot_download('Robbyant/lingbot-map', cache_dir='models', allow_patterns='lingbot-map*.pt')
"

2.6 下载天空分割模型(室外场景)

skyseg.onnx(168MB)用于过滤点云中的天空噪点:

复制代码
python -c "
import os
os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'
from huggingface_hub import hf_hub_download
hf_hub_download('JianyuanWang/skyseg', 'skyseg.onnx', local_dir='.')
"

室内场景可跳过此步,运行时去掉 --mask_sky 参数。

2.7 FlashInfer 与 SDPA

LingBot-Map 支持两种注意力后端:

后端 性能 安装难度 适用场景
FlashInfer 高(论文 ~20 FPS) 需从源码编译 CUDA kernel Linux + 完整 CUDA 工具链
SDPA 略低 零安装(PyTorch 内置) Windows / 无 FlashInfer

如果未安装 FlashInfer,运行时加 --use_sdpa 即可降级为 PyTorch 内置的 F.scaled_dot_product_attention


3. 核心架构

3.1 总体数据流

复制代码
输入: RGB 图片序列 [S, 3, H, W]
  │
  ├── 1. Patch Embedding (DINOv2 ViT-L/14)
  │     图片 → ImageNet 标准化 → Conv → Patch Tokens [S, N_patches, 1024]
  │
  ├── 2. Special Tokens
  │     [Camera Token] [Register×4] [Scale Token] + Patch Tokens
  │     → [S, 6 + N_patches, 1024]
  │
  ├── 3. Alternating Attention Blocks (24 层)
  │     ├── Frame Attention: 每帧独立 self-attention(空间特征)
  │     └── Global Attention: 跨帧 causal attention + KV Cache(时序一致性)
  │     → Multi-scale token outputs
  │
  ├── 4. Prediction Heads
  │     ├── Camera Head: camera token → 4 次迭代 → pose [S, 9]
  │     ├── DPT Head (Depth): tokens + image → depth [S, H, W, 1]
  │     └── DPT Head (Points): tokens + image → world_points [S, H, W, 3]
  │
  └── 5. Depth Unprojection
       depth + pose → Camera Coords → World Coords → 3D Point Cloud

3.2 Backbone:DINOv2 ViT-L/14

参数
image_size 518
patch_size 14
embed_dim 1024
depth 24
num_heads 16
num_register_tokens 4

每帧 518×294 图片 → 37×21 = 777 个 patch tokens,每个 1024 维。虽然代码支持加载 DINOv2 预训练权重,但发布 checkpoint 中已包含全部权重,pretrained_path 为空不影响使用。

3.3 Special Tokens 体系

每帧在 patch tokens 前添加 6 个可学习的特殊 token:

复制代码
Token 序列: [Camera] [Register×4] [Scale] [Patch_0] ... [Patch_776]
               0        1-4        5       6           782
Token 数量 作用
Camera Token 1 聚合帧级信息,输入 Camera Head 预测位姿
Register Tokens 4 继承自 DINOv2,存储全局图像信息
Scale Token 1 核心创新:承载前几帧的双向尺度信息,解决单目尺度不确定性

Scale Token 的工作机制

num_scale_frames(默认 8)帧共享一个 scale token,在这些帧之间做双向注意力(非因果),让模型在尺度估计阶段汇聚全局尺度/深度信息。后续流式帧使用另一个 scale token,仅做因果注意力。这个设计是 LingBot-Map 解决单目尺度歧义性的关键。

3.4 Alternating Attention(交替注意力)

这是整个模型最核心的设计。24 层 Transformer 按 aa_block_size=1 组织成 Frame Attention 和 Global Attention 交替执行:

复制代码
Layer 0:  Frame Attention   ← 每帧独立 spatial self-attention
Layer 1:  Global Attention  ← 跨帧 temporal causal attention + KV Cache
Layer 2:  Frame Attention
Layer 3:  Global Attention
...
Layer 22: Frame Attention
Layer 23: Global Attention

Frame Attention(ViT Block):

  • 每帧内部标准 self-attention,所有帧并行

  • 2D RoPE 位置编码

  • LayerScale + DropPath

Global Attention(SDPA / FlashInfer Block):

  • 因果注意力(Causal Attention):每帧只能看到当前帧及过去帧,不能看到未来

  • 跨帧交互,让 camera token 访问其他帧的信息

  • KV Cache 存储过去帧的 Key/Value,避免重复计算

  • 3D RoPE 位置编码(t, y, x)

3.5 KV Cache 机制

这是 streaming 推理的核心,避免对历史帧的重复编码。

存储结构(SDPA 模式):

复制代码
kv_cache = {
    "k_0": tensor[B, H, S_cached, P, D],   # 第 0 层 global block 的 Key
    "v_0": tensor[B, H, S_cached, P, D],   # 第 0 层 global block 的 Value
    "k_1": ..., "v_1": ...,                 # 后续层
    # 被淘汰帧的 special tokens 保留在 _special cache
    "k_0_special": tensor[B, H, S_evicted, N_special, D],
}

滑动窗口淘汰

当缓存帧数超过 scale_frames + sliding_window 时触发:

复制代码
cache: [S0...S7 | ...被淘汰的中间帧... | W1...W64]
        ← scale_frames=8 →              ← sliding_window=64 →
  • 保留前 scale_frames 帧(尺度参考帧,不可丢弃)

  • 保留最后 sliding_window 帧(最近帧)

  • 被淘汰中间帧的 special tokens(camera/scale/register token)转入 _special cache,patch tokens 丢弃

Keyframe Interval

复制代码
is_keyframe = (keyframe_interval <= 1) or \
              ((i - scale_frames) % keyframe_interval == 0)
  • keyframe_interval=1:每帧都是 keyframe,cache 全存(默认,≤320 帧时自动启用)

  • keyframe_interval=4:每 4 帧存 1 个,cache 缩减 75%

  • 非 keyframe 帧仍产生完整预测(pose + depth + points),只是 K/V 不持久化

3.6 Camera Head:迭代位姿优化

Camera Head 从 Camera Token 预测 9 维位姿编码 absT_quaR_FoV

维度 含义
0:3 绝对平移 T (x, y, z)
3:7 四元数旋转 R (w, x, y, z)
7:9 视场角 (FOV_h, FOV_w)

采用 4 次迭代优化--camera_num_iterations 可调):

复制代码
for iter in range(num_iterations):
    # 1. 用当前位姿估计生成 AdaLN 调制参数
    shift, scale, gate = poseLN_modulation(embed_pose(current_pose))
    # 2. Adaptive LayerNorm: 用位姿信息调制 token 特征
    tokens = gate * (adaln_norm(tokens) * (1+scale) + shift)
    # 3. 4 层 Transformer trunk
    tokens = trunk(tokens)
    # 4. MLP 预测位姿残差并累加
    current_pose = current_pose + delta_pose

AdaLN(Adaptive Layer Normalization) 是 Camera Head 的核心机制:将当前预测的位姿编码投影为 LN 的 shift/scale 参数,让位姿估计指导 token 的特征调制,使下一次迭代能更准确地预测残差。

3.7 DPT Head:密集预测

DPT Head(出自 Vision Transformers for Dense Prediction, Ranftl et al. 2021)从多尺度 token 重建密集空间输出:

复制代码
Layer 0 tokens → Conv1×1 → 256C → Resize×4  ┐
Layer 1 tokens → Conv1×1 → 512C → Resize×2  ├→ Concat → Fusion → Output
Layer 2 tokens → Conv1×1 → 1024C             │
Layer 3 tokens → Conv1×1 → 1024C             ┘

两个 DPT Head 实例共享结构,输出不同:

Head 输出维度 激活函数
Depth Head 2 (depth + confidence) exp / expp1
Point Head 4 (world_xyz + confidence) inv_log / expp1

3.8 两阶段推理流程

Phase 1:尺度初始化(Scale Frames)

前 8 帧一起处理,通过 scale token 做双向注意力,汇聚全局尺度信息。8 帧的 K/V 全部存入 cache。

Phase 2:流式逐帧处理

复制代码
for i in range(8, S):
    is_keyframe = ((i-8) % keyframe_interval == 0)
    if not is_keyframe:
        skip_append_to_cache()
    output = forward(frame_i)  # 因果注意力 + KV Cache

每帧的 camera token 通过 causal attention 看到:所有 8 个 scale 帧(双向)+ 过去所有 keyframe(因果),实现全局轨迹一致性约束。

3.9 关键技术总结

技术 出处 作用
DINOv2 ViT Backbone Oquab et al. 2024 自监督视觉特征提取
RoPE (2D + 3D) Su et al. 2024 空间 + 时空位置编码
KV Cache + Sliding Window LLM 推理 / StreamingLLM 避免重复计算历史帧
FlashInfer Paged Cache Ye et al. 2024 类 vLLM 分页 KV Cache,减少碎片
Scale Token LingBot-Map 原创 解决单目尺度歧义性
Camera Token + AdaLN LingBot-Map 原创 迭代相机位姿优化
DPT Head Ranftl et al. 2021 ViT → 密集预测

4. 运行

4.1 跑 courthouse 示例

复制代码
cd ~/lingbot-map && python demo.py \
    --model_path /path/to/lingbot-map.pt \
    --image_folder example/courthouse \
    --mask_sky \
    --use_sdpa

286 帧,RTX 4090 上推理约 39 秒(~7.2 FPS),峰值显存 13 GB。

4.2 跑自己的视频

复制代码
python demo.py \
    --model_path /path/to/lingbot-map.pt \
    --video_path /path/to/video.mp4 \
    --fps 10 \
    --mask_sky \
    --use_sdpa

--fps 10 表示从视频中每秒抽 10 帧。视频越长,建议 fps 越低(>1 分钟可设 --fps 5)。

4.3 关键参数速查

参数 默认值 作用
--image_size 518 预处理宽度
--mode streaming 推理模式(streaming / windowed)
--fps 10 视频抽帧率
--mask_sky False 天空分割过滤
--use_sdpa False 使用 SDPA 替代 FlashInfer
--keyframe_interval 1(≤320帧自动) Keyframe 间隔
--kv_cache_sliding_window 64 KV Cache 窗口大小
--num_scale_frames 8 尺度初始化帧数
--camera_num_iterations 4 位姿迭代次数
--offload_to_cpu False 每帧输出移到 CPU
--conf_threshold 1.5 点云置信度阈值
--downsample_factor 10 点云下采样率

4.4 显存优化方案

当显存不足时,按优先级尝试:

方案 预期节省 代价
--offload_to_cpu 0.5-1 GB 小幅传输延迟
--keyframe_interval 4 KV Cache 减 75% 跳帧略微降低精度
--kv_cache_sliding_window 32 限制 cache 上限 旧帧信息被丢弃
--camera_num_iterations 1 减 3 次迭代 位姿精度小幅下降
--mode windowed 分窗重置 cache 需调节 window_size/overlap

推荐的内存友好命令:

复制代码
python demo.py \
    --model_path /path/to/lingbot-map.pt \
    --image_folder example/courthouse \
    --mask_sky --use_sdpa \
    --keyframe_interval 4 \
    --kv_cache_sliding_window 32 \
    --offload_to_cpu

5. 查看结果

推理完成后自动启动 viser 服务器(默认端口 8080),浏览器打开 http://localhost:8080

操作 方式
旋转 鼠标左键拖动
平移 鼠标中键拖动(或 Shift+左键)
缩放 鼠标滚轮

可视化内容包括:

  • 3D 彩色点云(天空点已过滤)

  • 相机轨迹连线

  • 当前帧点云高亮

  • 置信度过滤(低于 conf_threshold 的点被剔除)


6. 常见问题

6.1 CUDA out of memory

原因 :KV Cache 随帧数线性增长,keyframe_interval=1 时每帧都存 cache。

解决 :增大 --keyframe_interval,减小 --kv_cache_sliding_window,启用 --offload_to_cpu,或切换到 --mode windowed

6.2 FlashInfer is not available

原因:FlashInfer 未安装。

解决 :加 --use_sdpa,使用 PyTorch 内置 SDPA 注意力。性能略降但不影响结果。

6.3 GET was unable to find an engine(P40/P100 等老卡)

原因:Pascal 架构(SM 6.1)不被 CUDA 12.x 预编译 kernel 支持。

解决:换用 SM 7.0+ 的 GPU(RTX 20 系及以上,V100 等)。

6.4 HuggingFace 下载卡住

原因:国内网络限制。

解决

  • 方式 A:用 ModelScope 国内源下载(推荐)

  • 方式 B:设置 HF 镜像 export HF_ENDPOINT=https://hf-mirror.com

  • 方式 C:手动下载后指定路径

6.5 pretrained_path 为空的警告

原因:模型初始化时尝试加载 ViT backbone 预训练权重(DINOv2),但路径为空。

影响 :无。全部权重已包含在 lingbot-map.pt checkpoint 中,可忽略此警告。

6.6 推理速度随帧数递减

原因:KV Cache 线性增长 → Self-Attention 计算量 O(n²),加上显存溢出时触发共享内存(Unified Memory),带宽从 ~500 GB/s(GPU 本地)骤降到 ~16 GB/s(PCIe)。

缓解 :使用 --kv_cache_sliding_window 限制 cache 大小,或使用 --keyframe_interval 稀疏化。


7. 代码结构

复制代码
lingbot_map/
├── models/
│   ├── gct_base.py              # GCT 基类
│   ├── gct_stream.py            # 流式推理(≤320 帧)
│   ├── gct_stream_window.py     # 窗式推理(>3000 帧)
│   └── gct_stream_window_v2.py  # 窗式推理 v2
├── aggregator/
│   ├── base.py                  # Patch Embedding + Token + Block 构建
│   └── stream.py                # 因果注意力 + KV Cache + 3D RoPE
├── layers/
│   ├── attention.py             # 4 种注意力实现
│   ├── block.py                 # 4 种 Transformer Block
│   ├── rope.py                  # 2D/3D RoPE
│   ├── vision_transformer.py    # DINOv2 ViT 变体
│   └── flashinfer_cache.py      # FlashInfer 分页 KV Cache
├── heads/
│   ├── camera_head.py           # 位姿预测(迭代优化 + AdaLN)
│   ├── dpt_head.py              # 深度/点云密集预测
│   └── head_act.py              # 输出激活函数
├── utils/
│   ├── pose_enc.py              # 位姿编解码
│   ├── geometry.py              # 深度→世界坐标反投影
│   └── load_fn.py               # Checkpoint 加载
└── vis/
    ├── point_cloud_viewer.py    # viser 3D 可视化
    ├── sky_segmentation.py      # 天空分割
    └── glb_export.py            # GLB 模型导出

参考

  • 论文:Geometric Context Transformer for Streaming 3D Reconstruction

  • 代码:https://github.com/robbyant/lingbot-map

  • DINOv2: Oquab et al., "DINOv2: Learning Robust Visual Features without Supervision", 2024

  • DPT: Ranftl et al., "Vision Transformers for Dense Prediction", 2021

  • FlashInfer: Ye et al., "FlashInfer: Kernel Library for LLM Serving", 2024

相关推荐
梦想不只是梦与想1 小时前
Python 中的 match-case(模式匹配)
python·match-case
郝同学今天有进步吗1 小时前
构建 LangGraph Code Review Agent(四):文件过滤与 AnalysisPackage 分包
git·python·ai·code review
俊哥V1 小时前
每日 AI 研究简报 · 2026-07-23
人工智能·ai
a1117761 小时前
基于PyTorch的动物图像识别系统 开源
人工智能·pytorch·python
qetfw1 小时前
MWU:Vue 3 + FastAPI 的 MaaFramework 跨平台 WebUI 源码
前端·vue.js·python·fastapi·开源项目·效率工具
Xzaveir_7771 小时前
企业号码负面标记治理:认证、申诉与合规事件的三轨模型
大数据·网络·人工智能·科技·产品经理
武子康1 小时前
1.2GB 离线语音 Agent 真正值得复用的不是 908ms:四阶段职责 + 状态感知 Tool Schema + 可观测时间锚点
人工智能·后端·agent
测试老哥1 小时前
接口自动化测试分层设计与实践总结
自动化测试·软件测试·python·测试工具·职场和发展·测试用例·接口测试
水如烟1 小时前
孤能子视角:中西医合璧系列·05 收束篇——同一关系场的四次显影:术法道伦理的完整循环
人工智能