
简介这是一份面向三维重建与视觉SLAM学习者的NeRF-SLAM复现文档适合已具备SLAM基础、希望深入理解NeRF与稠密建图结合方案的研究生、算法工程师和项目实践者参考。压缩包内为1个docx文件大小约11.01MB以文字讲解和流程记录为主便于按章节梳理复现思路。已有2327人学习下载说明该主题在相关学习群体中具有较高关注度。文档围绕追踪与映射两条主线展开追踪部分涉及连续单目图像输入、Instant-NGP深度监督、RAFT光流估计、Droid-SLAM更新机制以及DBA层中舒尔补、Cholesky分解和位姿逆深度优化映射部分介绍概率体素化NeRF、哈希编码加速、Mapping Loss与深度损失监督。文中还记录环境安装、数据准备、运行流程及复现中遇到的问题和解决思路可帮助读者理解系统结构、定位实现难点并对照排查重建效果与训练收敛问题。1. 从 clone 到第一行 cmake 报错nerf-slam 复现为什么总在环境这关卡住nerf-slam 的复现文档不长但真按它从头走一遍第一道坎几乎人人会撞上git clone --recurse-submodules没报错依赖也装完了然后cmake ./thirdparty/instant-ngp -B build_ngp这一行给你一句Could NOT find GLEW。修完 GLEW 继续往下gtsam 又抛Could NOT find BoostBoost 装好torch 和 CUDA 又对不上。这类基于 NeRF 的稠密三维重建项目环境比算法本身更容易把人劝退。下面按我实际踩坑的顺序写从编译失败到重建糊掉每一步都说清报错在讲什么、该改哪里。新手照命令能过熟手能看到版本约束背后的原因。2. Could NOT find GLEWinstant-ngp 编译链的三个前置条件2.1 先确认 cmake 版本别急着改 CMakeLists最直白的表现是 cmake 在读取配置阶段就退出报CMake 3.22 or higher is required. You are running version 3.16.3也有更隐晦的配置能过但生成阶段抛一堆 policy 警告后失败。根因不在依赖包而在 cmake 本体instant-ngp 的顶层配置声明了较高的最低版本并且用到了CMAKE_CUDA_ARCHITECTURES这类 3.18 才引入、3.22 才稳定的属性以及FetchContent的较新行为。低版本 cmake 在解析阶段就走不下去所以你会看到它连 GLEW、GLFW 都没来得及检查。Ubuntu 20.04 自带的 cmake 通常是 3.16Ubuntu 18.04 更低直接apt install cmake也升不上去。所以第一步不是装依赖是查版本# 看当前 shell 实际调用的 cmake 路径和版本 which -a cmake cmake --version # 如果系统里有 conda 环境conda 自带的 cmake 可能排在最前面 conda list | grep -i cmakewhich -a会按 PATH 顺序列出所有同名命令这一条能省掉后面大量我明明装了 3.22 为什么还是老版本的困惑。我遇到过 PATH 里 conda 的 cmake 3.19 排在/usr/local/bin/cmake前面编译时一直报版本不足查了半小时才发现调错了二进制。升级用源码编译最稳不依赖发行版源也不会和系统里其他软件打架# 下载 cmake 3.22 源码包 wget https://cmake.org/files/v3.22/cmake-3.22.0.tar.gz --no-check-certificate tar -zxvf cmake-3.22.0.tar.gz cd cmake-3.22.0 # bootstrap 阶段就把并行数带上否则这一步会跑很久 ./bootstrap --parallel$(nproc) make -j$(nproc) sudo make install # 关键清掉 shell 对 cmake 路径的缓存 hash -r cmake --version--parallel只影响 bootstrap 阶段的自举编译make -j影响正式编译两个都要给。hash -r是最容易漏的一步bash 会缓存命令路径你装完新版 cmake 后直接敲cmake --version它可能仍然指向旧的/usr/bin/cmake让人误以为安装失败。装完先hash -r再验证能少走一大段弯路。2.2 别覆盖系统 cmake装到独立目录的写法直接sudo make install会覆盖/usr/bin/cmake有些系统工具和别的项目构建脚本依赖老版本覆盖之后可能出别的问题。我一般装到独立前缀用的时候再切# 装到 /opt 下不动系统自带的 cmake ./bootstrap --prefix/opt/cmake-3.22 --parallel$(nproc) make -j$(nproc) sudo make install # 只对当前 shell 生效项目结束就切回去 export PATH/opt/cmake-3.22/bin:$PATH hash -r cmake --version注意sudo make install时 PATH 不一定继承你当前的设置--prefix指定的是安装目标路径和 PATH 无关所以 sudo 那一步不需要额外处理。真正要注意的是编译 gtsam 和 instant-ngp 的整个流程都必须在这个 PATH 下执行中途开新终端记得重新 export否则又回到 3.16。这类多版本共存的场景养成开新终端先cmake --version确认的习惯比什么技巧都管用。2.3 GLEW 报错的真身缺的是开发包外加 CMakeCache 在捣乱cmake 版本到位之后才会真正暴露依赖问题Could NOT find GLEW (missing: GLEW_INCLUDE_DIRS GLEW_LIBRARIES)。CMake 的FindGLEW模块要同时找到头文件GL/glew.h和库文件libGLEW.so只装运行时包是不够的——很多机器上已经装了libglew2.1这种运行时依赖但没有libglew-dev于是头文件缺失模块直接判失败。# 装开发包头文件和 .so 软链一起带进来 sudo apt install -y libglew-dev # 验证头文件和库是否都到位 dpkg -L libglew-dev | grep -E glew\.h|libGLEW # 删掉旧缓存再重新配置这一步不能省 rm -rf build_ngp cmake ./thirdparty/instant-ngp -B build_ngp -DCMAKE_BUILD_TYPERelWithDebInfo cmake --build build_ngp --config RelWithDebInfo -j$(nproc)rm -rf build_ngp是重点。CMake 会把GLEW_LIBRARIES-NOTFOUND这类结果缓存进CMakeCache.txt你装了包但不删缓存重新 cmake 仍然报一模一样的错很多人就是在这怀疑包是不是没装上。如果不想删整个目录至少删build_ngp/CMakeCache.txt和build_ngp/CMakeFiles。-DCMAKE_BUILD_TYPERelWithDebInfo与--config RelWithDebInfo是配套的前者给单配置生成器用后者给多配置生成器用写重复一点不冲突反而两边都能正确落到带调试符号的优化构建上。2.4 把 GLFW、X11、OpenMP 一次性补齐instant-ngp 的 GUI 和渲染部分还依赖一串系统库GLEW 修好之后大概率接着报 GLFW 或 X11 找不到。与其一个个试不如一次装齐sudo apt install -y \ libglew-dev libglfw3-dev libx11-dev libxi-dev \ libxxf86vm-dev libomp-dev libvulkan-dev # 重新配置并编译 rm -rf build_ngp cmake ./thirdparty/instant-ngp -B build_ngp -DCMAKE_BUILD_TYPERelWithDebInfo cmake --build build_ngp --config RelWithDebInfo -j$(nproc)libglfw3-dev对应find_package(glfw3)libx11-dev和libxi-dev对应窗口系统依赖libxxf86vm-dev是老式显示模式查询libomp-dev对应 OpenMP 并行libvulkan-dev在部分构建选项下才会用到。这套组合在 Ubuntu 20.04/22.04 上都验证过。编译阶段如果报 nvcc 相关错误说明问题已经不在系统库转而看第 4 章的 CUDA 版本对齐。3. Could NOT find Boostgtsam 与 python wrapper 的编译死结3.1 报错里那一长串组件名每个都是必需的gtsam 配置阶段的典型报错是Could NOT find Boost (missing: Boost_INCLUDE_DIR serialization system filesystem thread program_options date_time timer chrono regex) (Required is at least version 1.65)。这句话的信息量比看上去大括号里列的是 gtsam 实际链接的组件少任何一个都会导致后面的链接失败而不只是配置失败。Boost_INCLUDE_DIR单独列出来说明连头文件目录都没找到通常是压根没装或者装在 CMake 不认识的路径。这里有一个容易忽略的坑系统包管理器装的 Boost 和源码编译的 Boost 可能同时存在一个在/usr/include一个在/usr/local/include。CMake 的FindBoost按固定顺序搜索找到哪个用哪个但运行时的动态库搜索路径又是另一套逻辑最终可能出现编译通过、运行时报undefined reference to boost::...的情况。我一般先清掉包管理器版本再编源码版# 先看系统里到底有没有 boost装在哪 dpkg -l | grep -i libboost ls /usr/include/boost/version.hpp 2/dev/null ls /usr/local/include/boost/version.hpp 2/dev/null # 如果两边都有优先保留一个避免头文件和库版本错位确认清楚再动手比装完再排查链接错误省事得多。如果确定要用源码版把包管理器版本卸干净或者反过来只用包管理器版本并确保版本号 ≥ 1.65。3.2 源码编译 Boost 1.73 的完整命令与参数版本不够或者组件不全时源码编译是最直接的解法# 下载 1.73 源码包 wget https://boostorg.jfrog.io/artifactory/main/release/1.73.0/source/boost_1_73_0.tar.bz2 --no-check-certificate tar xvf boost_1_73_0.tar.bz2 cd boost_1_73_0 # 生成 b2显式指定 python3避免 bootstrap 猜错解释器 ./bootstrap.sh --with-pythonpython3 # complete 会编译所有构建变体避免 RelWithDebInfo 下找不到对应库 sudo ./b2 --buildtypecomplete install -j$(nproc) # 让动态链接器认识新装的库 sudo ldconfig--with-pythonpython3决定 boost.python 组件链接哪个解释器gtsam 的 python wrapper 全靠它机器上有多个 python 版本时不指定就可能链到系统自带的旧版本后面import gtsam直接报符号错误。--buildtypecomplete会把 debug、release、带和不带调试符号的各种变体都编出来代价是编译时间长好处是无论上层用什么构建类型都不会出现库文件找不到如果磁盘紧张可以只编 release 变体但要保证 gtsam 的构建类型一致。ldconfig必须执行/usr/local/lib不一定在动态库搜索路径里不刷新的话即使装好了运行时照样报找不到libboost_*.so。安装路径想换的话加--prefix/your/path装完需要手动把这个路径加进LD_LIBRARY_PATH并在 cmake 时传-DBOOST_ROOT。默认路径能省掉这些步骤我倾向默认安装。3.3 python wrapper 编不过的三种典型情况Boost 就位之后重新配置 gtsam还有几类问题会接着冒出来每一种的表现都不一样。第一类是找不到 Python 开发头文件报Could NOT find PythonLibs或Python.h: No such file。原因是缺python3-dev或者 cmake 挑中的解释器和你要用的不是同一个。解法是显式把解释器传进去# 装 python 开发头文件 sudo apt install -y python3-dev # 配置时锁定解释器避免 cmake 自己猜 cmake ./thirdparty/gtsam \ -DGTSAM_BUILD_PYTHON1 \ -DPYTHON_EXECUTABLE$(which python) \ -DGTSAM_INSTALL_MATLAB_TOOLBOXOFF \ -B build_gtsam cmake --build build_gtsam -j$(nproc)-DGTSAM_BUILD_PYTHON1是打开 wrapper 编译的总开关-DPYTHON_EXECUTABLE指定用哪个解释器在 venv 或 conda 环境下这一条尤其重要否则 cmake 会去找/usr/bin/python3编译出来的模块和你的环境不匹配-DGTSAM_INSTALL_MATLAB_TOOLBOXOFF关掉用不到的 MATLAB 部分能少一堆无关报错。第二类是 boost_python 库名对不上。Boost 1.73 编出来的库可能叫libboost_python39.so而 gtsam 的 cmake 脚本里硬编码找libboost_python3.so于是报Could NOT find Boost (missing: python3)。应急做法是建软链# 看清楚实际生成的库名 ls /usr/local/lib | grep boost_python # 按实际版本名建软链路径和版本号按你的机器改 sudo ln -s /usr/local/lib/libboost_python39.so /usr/local/lib/libboost_python3.so sudo ldconfig软链是权宜之计长期看还是得确认 gtsam 子模块的 cmake 脚本里找的到底是哪个名字两边对齐。第三类是编过了但import gtsam报undefined symbol这通常是PYTHONPATH指到了build_gtsam/python却忘了设LD_LIBRARY_PATH模块本身能找到它依赖的 boost 和 gtsam 动态库找不到。验证方式是# 编译产物一般在 build_gtsam/python 下 export PYTHONPATH$PWD/build_gtsam/python:$PYTHONPATH export LD_LIBRARY_PATH/usr/local/lib:$LD_LIBRARY_PATH python -c import gtsam; print(gtsam.__file__)能打印出.so的路径就算通了。报ImportError就把LD_LIBRARY_PATH再往上层目录加一层试试这类问题基本都能靠ldd定位——ldd那个.so一下哪一行是not found缺的就是哪个库。3.4 用 find_package 单独验证别反复重编每次改完依赖都重编整个项目时间成本太高。我一般写一个三行的CMakeLists.txt单独验证依赖是否能被找到mkdir -p /tmp/probe cd /tmp/probe cat CMakeLists.txt EOF cmake_minimum_required(VERSION 3.22) project(probe CXX) find_package(Boost 1.65 REQUIRED COMPONENTS serialization system filesystem thread program_options date_time timer chrono regex) find_package(GLEW REQUIRED) message(STATUS Boost_INCLUDE_DIRS ${Boost_INCLUDE_DIRS}) message(STATUS Boost_LIBRARIES ${Boost_LIBRARIES}) EOF cmake -B build . cmake --build build配置能过说明依赖层面没问题再回去编主项目就不会在同一个地方卡住。这套探针式验证在换机器、换容器的时候特别有用能快速区分是依赖没装好还是是项目自己的 cmake 逻辑有问题。4. torch 版本对不上CUDA extension 编译通过但 import 就崩4.1 三个 CUDA 版本号必须分清哪个是哪个instant-ngp 的 python 绑定会用 torch 的 C extension 机制编译torch 编译时用的 CUDA 版本和本机 nvcc 版本必须一致否则要么编译阶段报找不到 CUDA 头文件要么编译通过、import 时报undefined symbol: _ZN2at6detail...这类 ABI 错误。先跑三条命令把三个版本号摊开看# 驱动支持的 CUDA 运行时上限看右上角那行 nvidia-smi # 本机 CUDA toolkit 版本 nvcc --version # torch 编译时用的 CUDA 版本 python -c import torch; print(torch.__version__, torch.version.cuda, torch.cuda.is_available())三者的关系是nvidia-smi右上角显示的是驱动支持的最高 CUDA 运行时只要它不低于 torch 要求的版本就行驱动向下兼容nvcc --version是 toolkit 版本编译 extension 时用它torch.version.cuda是 torch 二进制包编译时用的 toolkit 版本。真正需要严格相等的是后两个nvcc和torch.version.cuda差一个 minor 版本就可能出问题。torch.cuda.is_available()返回 False 则是另一种情况多半是驱动太旧或者装了 CPU 版 torch和 ABI 无关。4.2 依赖安装顺序错了numpy 会背刺你在一个干净环境里安装顺序是有讲究的先建虚拟环境装死版本 torch再装项目 requirements最后装 gtsam 子模块的 requirements。反过来做的话项目 requirements 里的 numpy 可能把版本拉到较新而 gtsam 的 python 扩展是按较老 numpy 的 ABI 编的运行时直接报numpy.core.multiarray failed to import。# 建独立环境别用系统 python python -m venv .venv source .venv/bin/activate pip install --upgrade pip setuptools wheel # 先锁死 torch 版本cu113 对应 CUDA 11.3 pip install torch1.12.1cu113 torchvision0.13.1cu113 \ --extra-index-url https://download.pytorch.org/whl/cu113 # 再装项目依赖和 gtsam 的 python 依赖 pip install -r requirements.txt pip install -r ./thirdparty/gtsam/python/requirements.txt # 验证 torch 和 numpy 的实际版本 python -c import torch, numpy; print(torch.__version__, torch.version.cuda, numpy.__version__)--extra-index-url和--index-url的区别值得说清楚前者是追加一个源pip 会在官方源和这个源之间挑选版本更高的包所以如果官方源的 torch 更新可能会装成 CPU 版或者别的 CUDA 版本后者是替换源只从这个源找。要绝对锁死就用--index-url要省事就用--extra-index-url但装完必须验证torch.version.cuda。另外torchvision版本要跟着 torch 走0.13.1 对应 1.12.1错配会在 import 时报函数签名不匹配。4.3 pyngp 模块的加载验证instant-ngp 编译完成后python 绑定产物是build_ngp下的一个.so不在 site-packages 里需要手动加路径。验证脚本很短# check_ngp.py import sys sys.path.append(build_ngp) # pyngp 的 .so 产物目录 import pyngp as ngp # 建一个 NeRF 模式的 testbed能建出来说明绑定加载成功 testbed ngp.Testbed(ngp.TestbedMode.Nerf) print(pyngp loaded:, testbed)TestbedMode.Nerf对应 NeRF 训练模式构建时选错模式会走 GUI 渲染路径在没有显示设备的服务器上直接崩。如果 import 阶段报libcuda.so.1: cannot open shared object file说明容器里没挂驱动或者没装驱动和项目本身无关报undefined symbol且符号名带at::前缀基本可以断定是 torch 与 CUDA 的 ABI 不匹配回到 4.1 重新核对版本。还有一种少见情况是编译 ngp 时没开 python 绑定产物目录里根本没有.so需要检查 cmake 配置里-DNGP_BUILD_WITH_PYTHONON是否传了。5. 避坑清单重建糊掉、位姿颠倒、显存 OOM 的四个真实翻车现场5.1 子模块是空壳thirdparty 目录里什么都没有现象cmake ./thirdparty/instant-ngp直接报The source directory ... does not contain a CMakeLists.txt或者配置能过但编译到一半报找不到头文件。原因有两个一是 clone 时没加--recurse-submodules二是加了但中途网络中断留下一个空目录后续git submodule update认为已经初始化过而跳过。解决顺序是先补拉补拉无效再重来# 先尝试增量拉取 git submodule update --init --recursive # 如果目录仍然为空说明残留状态卡住了清掉重来 git submodule deinit -f . git submodule update --init --recursive # 验证子模块目录里确实有内容 ls thirdparty/instant-ngp/CMakeLists.txt ls thirdparty/gtsam/CMakeLists.txtdeinit -f会清掉子模块的本地状态再 update 就是全新拉取。这一步在磁盘空间不足或者用了浅克隆的场景下特别容易触发我一般 clone 完先du -sh thirdparty/*看一眼每个子模块的大小几百 KB 的基本都是空壳。5.2 位姿约定错重建形状对但整个场景是歪的现象NeRF 训练 loss 正常下降导出的点云能看出大概形状但整体镜像、上下颠倒或者相机轨迹明显偏离场景中心。原因是位姿矩阵的约定没对齐Droid SLAM 输出的位姿可能是相机到世界c2w而 NeRF 的transform.json期望世界到相机w2c坐标轴方面OpenCV 约定 y 轴向下、z 轴朝前OpenGL 约定 y 轴向上、z 轴朝后两套约定差一个绕 x 轴 180 度的翻转。这类问题不会报错只会让你在重建结果上抓瞎。定位方式是拿一个已知朝向的场景做单帧验证把第一帧的相机位姿打印出来看平移向量的方向和尺度是否和实际拍摄距离量级一致再检查旋转矩阵第三列相机光轴指向场景内还是场景外。修正时通常需要在读入位姿后左乘一个固定的翻转矩阵import numpy as np # OpenCV 到 OpenGL 的坐标轴翻转绕 x 轴 180 度 FLIP np.diag([1.0, -1.0, -1.0, 1.0]) def load_pose(raw_pose_w2c): # 先把 w2c 求逆得到 c2w再翻转坐标轴顺序不能反 c2w np.linalg.inv(raw_pose_w2c) return FLIP c2w pose load_pose(np.loadtxt(pose_0000.txt).reshape(4, 4)) print(相机中心:, pose[:3, 3]) # 应落在场景范围内 print(光轴方向:, pose[:3, 2]) # 应指向场景中心而不是背离FLIP的符号取决于你的数据源到底是哪套约定先用前 3 到 5 帧确认方向正确再批量转换。顺序很重要求逆和翻转写反了结果看起来也像那么回事但误差会在多帧优化里被放大最后点云糊成一团。5.3 深度尺度不一致λD 调大反而更糊现象训练能跑loss 也在降但重建结果边缘发毛、表面有噪声把深度损失权重 λD 从 1.0 往上调模糊反而更严重。原因是单目 SLAM 输出的深度是相对尺度每一段轨迹的尺度可能都不一样直接作为 NeRF 的深度监督信号等于在用错误的尺度拉模型再加上异常深度值没有按协方差加权少数错误点主导了梯度。处理思路是两条先做尺度归一再给协方差加下限。深度按中位数归一化到同一量级协方差过小的像素权重会被放大到无穷必须截断import numpy as np def normalize_depth(depth, cov, min_cov1e-3, max_cov1e2): # 用中位数做尺度归一比均值抗异常值 scale np.median(depth[depth 0]) depth_n depth / scale # 协方差截断避免极小值导致权重爆炸 cov_c np.clip(cov, min_cov, max_cov) # 权重取协方差倒数归一化到均值 1方便和颜色损失平衡 weight 1.0 / cov_c weight weight / weight.mean() return depth_n, weightmin_cov挡的是深度极准但数量很少的像素max_cov挡的是完全不可信的边缘区域两个值按你的数据噪声水平微调。λD 保持在 1.0 附近上下试不要一次调到 10权重不稳定的时候调大只会加速发散。这套改动加进去之后无纹理区域的表面应该明显变平整。5.4 显存 OOM4D correlation volume 是 W×H×W×H现象跑几十帧没事到几百帧突然CUDA out of memory报错栈指向 correlation 相关层。根因在 RAFT 的结构本身它要对两帧之间所有像素对做内积构造W×H×W×H的 4D 相关体分辨率翻倍显存占用是平方级增长。Droid SLAM 在这个基础上还要维护 frame graph 里每条边的相关体帧数一多就爆。解决优先级从高到低降输入分辨率、限制每个关键帧的邻接边数、开混合精度。第一条最有效W和H各降一半相关体显存降到四分之一第二条控制的是同时驻留的相关体数量在构图阶段限制每个关键帧只和前后各 N 帧建边长序列的显存占用就不会线性膨胀第三条要注意混合精度对位姿优化的数值稳定性有影响开了之后先看位姿误差有没有变大再用。提示OOM 报错里显示的总显存往往比你显卡标称的小因为 PyTorch 的缓存分配器会预留一部分判断是否真的不够用看torch.cuda.max_memory_allocated()更准。6. 快速定位拿到下一个复现仓库时先跑哪几条命令复现类项目的问题有个共同点报错信息指向的地方往往不是根因所在。Could NOT find GLEW的根因可能是 cmake 版本太低undefined symbol的根因可能是 numpy 版本被顶掉了。所以我的习惯是先做一轮环境体检再去看具体报错顺序如下。报错关键词先查什么命令CMake X.XX or higher is requiredPATH 里实际调用的 cmakewhich -a cmake cmake --versionCould NOT find XXX开发包是否安装、CMakeCache 是否残留dpkg -L libxxx-dev、rm -rf build_*undefined symbol动态库搜索路径与 ABI 版本ldd xxx.so | grep not foundCUDA out of memory分辨率与 batch 配置torch.cuda.max_memory_allocated()训练不收敛、重建糊位姿约定与深度尺度单帧位姿打印、深度中位数体检三件套我每次都先跑# 一条命令把关键版本全打出来粘到笔记里备查 cmake --version | head -1 nvcc --version | tail -2 python -c import torch, numpy; print(torch, torch.__version__, cuda, torch.version.cuda, numpy, numpy.__version__) nvidia-smi --query-gpuname,memory.total,driver_version --formatcsv这四行的输出基本能覆盖八成的版本类故障。装依赖的顺序也照这个走cmake → 系统库 → Boost/gtsam → torch → 项目 requirements每装完一层就跑一次对应验证不要一口气装完再排查出问题时根本分不清是哪一步引入的。另一条经验是善用最小复现。项目编不过时别在主目录里反复rm -rf build重来单独建一个空目录写三行find_package的 CMakeLists 验证依赖能不能被找到能过就说明问题在项目配置本身不能过就说明依赖没装好。这个判断能把排查范围立刻砍掉一半比读报错栈高效得多。从那次在 cmake 版本上耗掉一整个下午之后我每次拿到新的复现仓库都强制先跑一遍版本体检、确认which -a的输出只有一份再动手编译。这个习惯本身不解决任何技术问题但它能让你在真正遇到问题时确定自己不是在跟一个错误的环境较劲。希望帮到你。本文还有配套的精品资源点击获取