ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Ceres Solver 与 COLMAP 的 CUDA 加速编译实战指南

Ceres Solver 与 COLMAP 的 CUDA 加速编译实战指南 搞过三维重建的人大概都躲不开 COLMAP 和 Ceres Solver 这一对组合。COLMAP 是目前最主流的增量式 SfM 工具而 Ceres 是它背后的核心优化库Bundle Adjustment、位姿估计、三角化这些环节全都要靠它。问题在于Ceres 如果不开启 CUDA 支持COLMAP 的 BA 就只能跑 CPU 版本模型一大、图片一多那个速度能让人等到怀疑人生。这篇博文就手把手讲清楚怎么解决 Ceres 编译时 CUDA 支持打不开、COLMAP 编译时识别不到 CUDA 的问题里面给的配置步骤和参数都是我自己在 Ubuntu 上实际踩过坑之后验证过的适合刚接触 COLMAP 源码编译、或者编译了几次都没能把 CUDA 加速真正用起来的朋友。1. 问题概述与前置准备1.1 这个编译问题的根源在哪里COLMAP 的 C 源码里凡是涉及大规模优化求解的地方都在调用 Ceres Solver 的接口。而 Ceres 在 2.1.0 版本之前对 CUDA 的支持是非常有限的甚至可以说基本没有从 2.1.0 开始Ceres 才正式加入了基于 CUDA 的稠密线性代数加速模块用于加速 BA 中的 Schur 补求解和稠密矩阵运算。这就带来一个很典型的局面很多人用系统包管理器直接装了libceres-dev那个版本往往比较老或者编译时根本没有开 CUDA 选项于是 COLMAP 编译出来后虽然能跑但 GPU 利用率极低BA 阶段完全走 CPU。另一个容易踩的坑是Ceres 编译时即使加了 CUDA 相关的 CMake 变量也可能因为找不到 CUDA toolkit、nvcc 版本和 GCC 版本不匹配、compute capability 设置不对等原因最后编译出一个没有 CUDA 支持的库。COLMAP 的 CMake 在查询 Ceres 时会读取 Ceres 编译时生成的CeresConfig.cmake如果 Ceres 当时没开 CUDACOLMAP 这边自然也不会认为 CUDA 可用。1.2 动手之前先确认硬件和系统环境我在这篇文章里默认的系统环境是 Ubuntu 20.04 或 22.04这也是 COLMAP 和 Ceres 最常用的构建平台。Windows 用户建议直接用官方预编译版本或者 WSL因为在 Windows 上用 MSVC 编译这三个东西的组合坑更多尤其是 CUDA 的gencode参数和 Visual Studio 的版本对应关系非常让人头疼。开始之前先做三件事nvidia-smi这一步看驱动版本和显卡型号确认你的 GPU 还能不能用以及驱动是否支持当前的 CUDA 版本。nvcc -V这一步确认 CUDA Toolkit 是否已经安装。如果提示找不到 nvcc说明你还没有装 CUDA toolkit后面要装。gcc --version cmake --version确认编译器和 CMake 版本。注意 CUDA 对 GCC 版本有严格上限比如 CUDA 11.x 最多支持 GCC 11CUDA 12.x 支持到 GCC 12。如果系统默认 GCC 版本过高后面编译 CUDA 代码时会报unsupported GNU version的错误。这三项确认完之后再花一分钟查一下显卡算力后面设置 CUDA architecture 要用nvidia-smi --query-gpucompute_cap --formatcsv比如我的 4060 Ti 输出的是8.9说明是 Ada Lovelace 架构compute capability 是 8.9sm_89。知道这个数字之后编译参数就不会瞎填了。2. 编译前的环境准备与依赖安装2.1 CUDA Toolkit 安装方式的选择很多人在这一步就分心了老想着是不是一定要用.run方式安装。我个人的建议是用 NVIDIA 官方提供的.run文件安装不要用apt install cuda那种方式。原因有两点第一apt 版本往往和显卡驱动的版本产生耦合在 Linux 仓库里容易出现驱动不匹配第二.run安装方式可以让你只装 toolkit不覆盖已有的显卡驱动对做开发来说更可控。.run安装的过程大致是wget https://developer.download.nvidia.com/compute/cuda/12.4.0/local_installers/cuda_12.4.0_550.54.14_linux.run sudo sh cuda_12.4.0_550.54.14_linux.run执行之后会进入一个交互式界面。注意这里有个细节如果你已经装好了显卡驱动在安装选项里要取消 Driver 那一项只保留 CUDA Toolkit 相关的组件。因为这个.run文件里自带了一个建议版本的显卡驱动如果你选上它它会覆盖你当前的驱动运气不好直接黑屏。安装完成后默认路径是/usr/local/cuda-12.4同时系统会产生一个/usr/local/cuda的软链接指向你安装的那个版本。这一步很关键因为后续编译 Ceres 和 COLMAP 时CMake 会优先找/usr/local/cuda这个固定路径。2.2 环境变量与基础依赖安装装好 CUDA 之后需要把 CUDA 相关的 bin 和 lib 路径加进环境变量。这里要强调一下nvcc -V和nvidia-smi显示的版本号经常不一致很多人因此以为装错了。其实nvidia-smi显示的是显卡驱动支持的最高 CUDA 版本而nvcc -V显示的是当前 toolkit 的版本两者不需要完全一致只要驱动版本不低于 toolkit 要求的版本就能正常工作。export PATH/usr/local/cuda/bin:$PATH export LD_LIBRARY_PATH/usr/local/cuda/lib64:$LD_LIBRARY_PATH为了让这些环境变量永久生效最好写进~/.bashrc否则每次打开新的终端都要重新 export 一遍。接着安装编译 COLMAP 和 Ceres 需要的系统依赖sudo apt-get update sudo apt-get install -y git cmake build-essential \ libboost-program-options-dev libboost-filesystem-dev \ libboost-graph-dev libboost-system-dev \ libeigen3-dev libflann-dev libfreeimage-dev \ libmetis-dev libgoogle-glog-dev libgtest-dev \ libsqlite3-dev libglew-dev qtbase5-dev \ libqt5opengl5-dev libcgal-dev这里特别注意libeigen3-dev和libgoogle-glog-dev这两个包Ceres 依赖它们。如果你之前手动编译过别的版本 Eigen建议先清理干净否则 CMake 可能会找到两个版本导致编译期头文件冲突。2.3 编译器版本与 CUDA 兼容性检查前面提到 GCC 版本有上限这里补充一个实操经验CUDA 12.4 官方支持的最高 GCC 是 12Ubuntu 22.04 默认的 GCC 就是 11.4所以一般没问题。但如果你是 Ubuntu 24.04默认 GCC 可能是 13 甚至 14编译 CUDA 代码时就会报错。这种情况下不要硬刚直接装一个兼容版本的 GCC 并切换sudo apt-get install -y gcc-12 g-12 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 120 sudo update-alternatives --install /usr/bin/g g /usr/bin/g-12 120这样切换之后nvcc 编译时就会使用 gcc-12不会再说unsupported GNU version。这个坑我踩过好几次每次都是换编译器解决的折腾 CUDA 版本反而浪费时间。3. 源码编译 Ceres SolverCUDA 支持的关键3.1 为什么一定要自己编译 CeresCOLMAP 官方文档明确建议从源码编译 COLMAP 时Ceres 也要从源码编译而且最好是用启用 CUDA 的配置。原因不只是系统包管理器里的 Ceres 版本太老更关键的是COLMAP 在编译时会通过CeresConfig.cmake检测 Ceres 的配置选项。如果 Ceres 在编译时没有把 CUDA 支持编进去COLMAP 的这个宏CERES_USE_CUDA就不会被定义即使 COLMAP 自己编译时开了 CUDA 选项BA 阶段也无法使用 CUDA。说白了Ceres 是下游库它要先把 CUDA 支持编进去COLMAP 才能吃到这个红利。另外注意一点有些发行版会在libceres-dev里启用 CUDA但绝大多数不会因为开启 CUDA 会让这个包的依赖和体积急剧膨胀对镜像友好度极差。所以不要赌运气直接源码编译是确定性的做法。3.2 获取 Ceres 源码并配置 CMake我这边使用的是 Ceres 2.2.0 版本。实际上 2.1.0 和 2.2.0 在编译配置上差别不大关键是版本号不能太低至少要 2.1.0 以上否则根本没有 CUDA 选项。git clone https://ceres-solver.googlesource.com/ceres-solver cd ceres-solver git checkout 2.2.0 mkdir build cd build然后执行 CMake 配置。这里给出一个经过验证的完整命令cmake .. \ -DCMAKE_BUILD_TYPERelease \ -DBUILD_SHARED_LIBSON \ -DCERES_CUDAON \ -DCUDA_USE_STATIC_CUDA_RUNTIMEON \ -DCUDA_NVCC_FLAGS-O3 -gencode archcompute_89,codesm_89 \ -DCUDA_TOOLKIT_ROOT_DIR/usr/local/cuda \ -DCMAKE_INSTALL_PREFIX/usr/local几个参数逐个说明-DCERES_CUDAON这是 Ceres 开启 CUDA 的总开关如果这个不加后面全是白费。-DCUDA_USE_STATIC_CUDA_RUNTIMEON静态链接 CUDA runtime避免编译出的 Ceres 在运行时找不到libcudart.so。-DCUDA_NVCC_FLAGS这里把archcompute_89,codesm_89替换成你自己显卡的算力可以从前面nvidia-smi --query-gpucompute_cap --formatcsv的结果得知。如果你不想指定得这么精确也可以用-DCMAKE_CUDA_ARCHITECTURESnative让 CMake 自动检测但有时检测不到多卡环境下的目标卡所以我还是建议手动指定。-DCUDA_TOOLKIT_ROOT_DIR/usr/local/cuda显式指定 CUDA 安装路径防止 CMake 找错版本。如果你用的是 Ceres 2.1.0还有一个细节要注意CUDA_NVCC_FLAGS里的 compute capability 不能写错否则会在编译时出现ptxas fatal的错误。如果真出现了回显会告诉你错误的 compute capability照着修正就行。3.3 编译安装并确认 CUDA 是否生效配置完成之后直接编译安装make -j$(nproc) sudo make install这里我建议先不加-j$(nproc)如果你是第一次编译机器内存不大可以用make -j4甚至make -j2来避免 OOM。Ceres 本身不算重但是 CUDA 部分一编译起来内存占用会突然飙高。编译完成后最重要的一个步骤是检查 Ceres 到底有没有把 CUDA 编进去。很多人编译完就认为成功了其实没检查最后 COLMAP 还是跑 CPU。检查方式有两个grep -i cuda CMakeCache.txt这个文件里应该能看到CERES_CUDA:BOOLON且CUDA_FOUND:BOOLTRUE。更直接的方式是看编译过程中有没有出现-- Using CUDA: TRUE之类的输出或者看/usr/local/lib/cmake/Ceres/CeresConfig.cmake里面是否定义了CERES_USE_CUDA。还可以用 nm 命令检查 Ceres 的库里有没有 CUDA 相关符号nm -D /usr/local/lib/libceres.so | grep -i cuda | head有输出的话说明 CUDA 符号真的链接进去了可以放心进行下一步。4. 编译 COLMAP让 Ceres CUDA 真正生效4.1 拉取 COLMAP 源码与依赖COLMAP 的源码托管在 GitHub 上建议直接用 git clone 拉取不要下载 zip 包那样不好后续切版本。git clone https://github.com/colmap/colmap.git cd colmap git checkout 3.9.1 mkdir build cd build我这里以 3.9.1 为例。COLMAP 的不同版本对构建的要求有那么一点点差别但大方向一致。需要注意从 3.8 版本开始COLMAP 默认要求 Ceres 版本不低于某个特定版本并且要求 Eigen 版本也不能太低所以前面安装依赖时最好选较新的版本。4.2 CMake 配置 COLMAP 的关键参数COLMAP 的 CMake 配置相对直观但有一个极易踩的坑如果你在 CMake 命令行里写了-DCUDA_ENABLEDONCOLMAP 就会去寻找 CUDA toolkit并同时检查 Ceres 是否支持 CUDA。只有这两个条件都满足最终才会把 COLMAP 自身的 CUDA 内核启用。一个经过验证的关键配置如下cmake .. \ -DCMAKE_BUILD_TYPERelease \ -DCMAKE_CUDA_ARCHITECTURESnative \ -DCUDA_ENABLEDON \ -DCERES_DIR/usr/local/lib/cmake/Ceres \ -DGUI_ENABLEDON-DCMAKE_CUDA_ARCHITECTURESnative这个是 COLMAP 3.9 以后非常关键的参数。很多人在这一步报错No supported CUDA architecture was detected就是因为没有设置这个变量CMake 默认的 CUDA architecture 列表为空。-DCERES_DIR显式指向 Ceres 安装后生成的 CMake 配置文件目录。如果这个不指定CMake 可能会找到系统自带的 Ceres。-DGUI_ENABLEDON如果你不需要图形界面可以把这个关掉减少编译时间但默认开起来更方便后面验证。配置完成后仔细看 CMake 的输出信息。正常情况下会出现-- Found Ceres Solver: /usr/local/lib/cmake/Ceres/CeresConfig.cmake -- Ceres Solver version: 2.2.0 -- Ceres Solver CUDA support: enabled如果这里显示Ceres Solver CUDA support: disabled那问题一定出在 Ceres 这边回到第 3 章检查你的 Ceres 编译配置别往下走否则 COLMAP 编译出来也是个没有 BA 加速的版本。4.3 编译安装与最终验证确认 CMake 输出无误后开始编译make -j$(nproc) sudo make installCOLMAP 的编译量比 Ceres 大不少尤其当 GUI 和 CUDA 同时开启时几十个 CUDA 源文件逐个通过 nvcc 编译时间从十几分钟到半小时不等。这里又有一个经验编译 COLMAP 时如果使用-j$(nproc)把 CPU 全部拉满偶发 OOM 会导致 nvcc 进程被系统杀掉然后编译中断。建议 16 核以上的机器用-j8或-j12更稳妥。编译完成后确认安装结果colmap -h如果能看到帮助信息说明安装成功。还可以跑一下colmap photogrammetry -h看到几个子命令正常显示说明核心库没问题。更严苛的验证是运行一个小的三维重建流程在日志中观察是否出现了 CUDA 设备信息colmap feature_extractor \ --database_path ./test.db \ --image_path ./images \ --SiftExtraction.use_gpu 1如果输出日志里有类似Using SiftGPU或Number of CUDA devices: 1的内容说明 CUDA 链路是通的Ceres 和 COLMAP 的 CUDA 支持都生效了。5. 常见问题排查与实操心得5.1 常见编译错误速查表我整理了几个高频问题基本把大家会遇到的编译期异常覆盖到了建议先收藏错误现象可能原因解决方法nvcc fatal : Unsupported gpu architecture compute_XX算力数字写错或 CUDA 版本太老不支持新算力用nvidia-smi --query-gpucompute_cap --formatcsv确认实际算力再修改CUDA_NVCC_FLAGS或CMAKE_CUDA_ARCHITECTURESunsupported GNU version! gcc versions later than X are not supportedGCC 版本超出 CUDA 支持范围安装特定版本 GCC或用update-alternatives切换默认编译器Could not find CUDA/CUDA_TOOLKIT_ROOT_DIR not set没装 CUDA toolkit 或者路径没写对检查/usr/local/cuda是否存在在 CMake 命令行里显式传入-DCUDA_TOOLKIT_ROOT_DIRCeres Solver CUDA support: disabledCeres 编译时没开CERES_CUDA或者 system 版本的 Ceres 没有 CUDA回到源码编译 Ceres确认grep CERES_CUDA CMakeCache.txt是 ONld: cannot find -lcudart链接器找不到 CUDA runtime确认LD_LIBRARY_PATH里包含/usr/local/cuda/lib64并静态链接 runtimeptxas fatal : Ptx assembly aborted due to errorscompute capability 写错或增量编译缓存损坏清空 build 目录重新配置或修改 architecture这些错误里最让人摸不到头脑的是最后一种ptxas fatal很多时候其实只是因为之前编译时用的gencode参数和当前环境不匹配残留的 CMake 缓存把错误的参数带进去了。所以一般在 CUDA 编译相关报错时我的第一反应不是改命令而是直接把 build 目录删掉重新执行 cmake 和 make。这个操作能在 80% 的场景下解决玄学问题。5.2 几处能够显著减少返工的实操细节第一Ceres 的CeresConfig.cmake是编译 COLMAP 时的“裁判”。如果你发现 COLMAP 配置时检测不到 Ceres但明明已经装了 Ceres请用-DCERES_DIR指向具体路径。特别是用make install后如果安装前缀不是默认的/usr/local这个步骤几乎是必须的。第二关于CUDA_USE_STATIC_CUDA_RUNTIME我在多台机器上对比过建议开。因为 COLMAP 编译后拿到别的机器上运行的概率并不低如果 Ceres 动态链接了 CUDA runtime而目标机器没装 CUDA就会在运行时崩溃静态链接后就没有这个困扰。第三善用ccache加速重复编译。CUDA 的nvcc本身有自己的缓存机制但配合ccache能更明显地缩短重复编译时间。装好之后在 CMake 前设置环境变量sudo apt-get install ccache export CUDA_NVCC_FLAGS-D__CUDA_NO_BC__1后面这个宏是避免 nvcc 缓存冲突的实测对重复编译的提升很有帮助。第四如果你在一个已经编译过 COLMAP 的目录里切换了 Ceres 或 CUDA 版本强烈建议删掉 build 目录重新配置。CMake 缓存对 CUDA 的检测结果往往不会主动刷新你改了环境变量它也感知不到这就是很多人在Could not find CUDA里反复横跳的根本原因。5.3 我个人的经验补充最后说一个我自己的习惯。我这个编译流程基本一年会走好几遍每次在不同机器上部署时都懒得重新敲那些 CMake 参数所以我会把整个过程写成一个脚本放到项目仓库的docker/目录下这样再编译新环境时一条命令就搞定。具体脚本逻辑不复杂就是按顺序执行 apt 安装、克隆源码、cmake 配置、make 和 make install。唯一要注意的是脚本里不要写死 compute capability而是用nvidia-smi --query-gpucompute_cap --formatcsv动态获取再拼进 CMake 变量里。还有一个容易忽略的小坑当你设置了/usr/local/cuda软链接之后如果系统里残留着之前用 apt 装的 CUDA可能会在/usr/lib/x86_64-linux-gnu/cmake/CUDA里也生成一份 CMake 配置文件导致 CMake 优先找到旧版本。这种情况下可以在 CMake 命令行里加-DCMAKE_IGNORE_PATH/usr/lib/x86_64-linux-gnu/cmake强制让 CMake 忽略系统路径彻底避免版本错乱。我还有个经验如果编译时出现cublas相关的链接错误尤其是cannot find -lcublas绝大多数情况是 CUDA 版本切换后软链接/usr/local/cuda还指在旧版本上。检查一下软链接指向然后根据实际情况重新ln -sfn一次问题就没了。说到底Ceres 的 CUDA 支持编译并不难难的是把整个链路上每一层的开关都打开显卡算力匹配、CUDA toolkit 安装、Ceres 的CERES_CUDA、COLMAP 的CUDA_ENABLED、CMake 的 architecture 参数。哪一个环节断了最后都是白折腾。按照这篇文章的顺序走完你的 COLMAP 就能在 BA 阶段真正吃到 GPU 加速的红利跑大数据集时的体感完全是两个级别。
返回列表