搞三维重建的朋友,对 COLMAP 这套开源流程应该不陌生。采集图像、特征提取、稀疏重建、稠密重建,每一步都有现成命令行,跑起来确实方便。真正让人头疼的是从源码编译,尤其是想让核心优化库 Ceres Solver 把 CUDA 能力打开的时候——很多人卡在这一步,少则半天,多则两三天。网上搜到的教程不少,但好多还停留在老版本参数上,照着敲完照样报错。
这篇文章我直接从实操角度出发,把“Ceres 开 CUDA → COLMAP 识别 CUDA → 验证生效”这条链路完整捋一遍。不管你是自己从源码编译 COLMAP,还是被 “ceres-solver has no CUDA support” 这种提示折磨的人,下面这套步骤和避坑清单应该都能帮上忙。
先说一个常见误区:很多人以为装了 NVIDIA 驱动,再装个 CUDA Toolkit,COLMAP 就自动用上 GPU 了。实际上 COLMAP 的核心优化库 Ceres 如果没有在编译阶段开启 CUDA 后端,那么后面的一切都白搭,COLMAP 即使编过了,跑 Bundle Adjustment 时还是 CPU 在硬扛,GPU 占用率基本是零。所以这个问题的关键,不在 COLMAP 本身,而是在 Ceres 的编译配置上。
1. 先搞清楚:COLMAP 卡脖子到底卡在哪
1.1 Ceres 在 COLMAP 里扮演什么角色
COLMAP 做三维重建,核心是不断求解非线性最小二乘问题。图像之间匹配出来的特征点动辄几万几十万,要把相机位姿、三角化点坐标尽可能优化到和观测一致,这个优化过程就是 Bundle Adjustment,业内一般直接叫 BA。BA 求解的效率和稳定性,直接决定了重建能不能收敛、能不能跑得快。
而 COLMAP 的 BA 求解器就是 Ceres Solver。Ceres 本身是一个通用的非线性最小二乘库,Google 出品,COLMAP 只是它的一个重量级用户。Ceres 在编译的时候,可以选择用不同的线性代数后端:比如 LAPACK、BLAS、SuiteSparse,以及 CUDA。如果你在编译 Ceres 时没有把 CUDA 打开,那 COLMAP 在 BA 阶段就只能用 CPU 求解器。小数据集看不太出来,一旦场景变大、特征点变多,CPU 和 GPU 的求解耗时差距能到数倍乃至一个数量级。
1.2 Ceres 的 CUDA 后端到底加速了什么
Ceres 从 2.1 版本开始正式支持 CUDA 后端,主要用于稠密 Cholesky 分解、稠密 QR 分解等线性代数运算,这些运算在求解 BA 的 Hessian 矩阵时会被高频调用。Ceres 的官方文档里明确写过,CUDA 后端依赖 CUDA 10.1+、Eigen 3.3.7+、CMake 3.18+,以及算力 3.5 以上的 NVIDIA 显卡。
需要说明的是,这不是说 Ceres 里所有计算都能甩给 GPU。稀疏部分很多时候还是要靠 CPU 上的 SuiteSparse 来完成。但光是稠密求解那一段的 GPU 加速,就已经能带来非常明显的体感差异了。我自己的直观感受是:同一个数据集,Ceres 不带 CUDA 时 BA 一轮迭代要等十几秒,开了 CUDA 之后基本两三秒就过。这种差距不是玄学,真正跑过的人都有感觉。
所以回到题目:解决 COLMAP 编译中的 Ceres CUDA 支持问题,本质上就是两步——第一步把 Ceres 的 CUDA 后端编出来,第二步让 COLMAP 在查找依赖时认准这个带 CUDA 的 Ceres。
2. 编译前的环境体检:这些基础没打牢,后面全是坑
2.1 驱动、CUDA Toolkit、编译器版本必须一起看
很多人一上来就 git clone 然后 cmake,结果报错报得莫名其妙,其实就是环境没对齐。编译带 CUDA 的 Ceres,至少有三样东西要确认:NVIDIA 驱动、CUDA Toolkit、以及 C++ 编译器版本。
先看驱动:
nvidia-smi如果这条命令能正常输出显卡信息,说明驱动已经装好。右上角会显示一个 CUDA Version 字样,比如 12.4。这代表你的驱动支持的 CUDA 最高版本,它并不代表你已经安装了对应版本的 CUDA Toolkit。
再看 Toolkit:
nvcc --version如果显示 command not found,说明你只装了驱动,还没装 CUDA Toolkit。那就需要去 NVIDIA 官网下载对应版本的 CUDA Toolkit 安装包。常见做法是选择 runfile 或 deb 方式,这里不展开,装完之后把环境变量配上:
export CUDA_HOME=/usr/local/cuda export PATH=$CUDA_HOME/bin:$PATH export LD_LIBRARY_PATH=$CUDA_HOME/lib64:$LD_LIBRARY_PATH然后还有一个很容易被忽略的版本匹配问题:nvcc 对 GCC 的版本有上限要求。比如 CUDA 11.x 高版本和 CUDA 12.0/12.1 一般最高支持 GCC 12;如果用 Ubuntu 22.04 默认的 GCC 11,配合 CUDA 12.1 通常没问题。但如果你的系统默认 GCC 是 13,而 CUDA 是 11.x,nvcc 在编译阶段会直接报 “unsupported GNU version”,这时候就得装一个兼容的 GCC 版本,或者换更新的 CUDA。
为了省事,我给一个常见组合参考:
| 系统环境 | GCC 版本 | 推荐 CUDA 版本 | 注意事项 |
|---|---|---|---|
| Ubuntu 20.04 | 9 | CUDA 11.0~11.8 | 很稳,Ceres 2.1+ 没问题 |
| Ubuntu 22.04 | 11 | CUDA 11.8~12.1 | 最常见组合,不建议用 GCC 13 |
| Ubuntu 22.04 | 12 | CUDA 12.2+ | 需要手动确认 nvcc 兼容 |
| Ubuntu 24.04 | 13 | CUDA 12.4+ | 老 CUDA 容易报 GCC 版本错误 |
2.2 系统依赖安装和源码准备
Ceres 编译还依赖一堆数值库。为了避免后面缺这个缺那个,先把系统依赖一次装齐:
sudo apt update sudo apt install -y cmake libeigen3-dev libsuitesparse-dev libtbb-dev \ libboost-all-dev libgoogle-glog-dev libgflags-dev libatlas-base-dev这里有几个关键点。libeigen3-dev 是 Ceres 的模板头文件库,几乎必须。libsuitesparse-dev 提供稀疏 Cholesky 分解相关的能力,比如 CHOLMOD、CXSparse,对 Ceres 的稀疏求解很重要。如果你不装它,Ceres 也会编过去,但配置阶段会显示 SuiteSparse 没找到,COLMAP 后面某些功能会受到限制。
源码建议直接从官方拉:
git clone https://ceres-solver.googlesource.com/ceres-solver git clone https://github.com/colmap/colmap.git这里要特别提醒:不要图省事直接 apt install libceres-dev。Ubuntu 源里的 Ceres 版本通常比较老,而且一般不带 CUDA 支持。你后面就算费尽力气把 COLMAP 编出来,它找到的还是那个没开 CUDA 的 Ceres,等于白忙。
3. Ceres 编译:CUDA 开关和架构参数是灵魂
3.1 关键 CMake 选项逐项拆解
进入 Ceres 源码目录后,核心操作就是 CMake 配置。这里最关键的几个选项我一个个说清楚。
第一个是-DCERES_CUDA=ON。这是 Ceres 2.1 之后专门控制 CUDA 后端的开关。有些老教程写的是-DCERES_USE_CUDA=ON,那是旧版参数,新版 Ceres 根本不认。如果你照抄老教程,CMake 配置阶段不会报错,但 CUDA 实际没开,等 COLMAP 那边提示 “Ceres has no CUDA support” 的时候才知道被坑了。
第二个是-DCMAKE_CUDA_ARCHITECTURES=80这类架构参数。这里的数字必须和你的显卡算力对应。RTX 30 系列是 Ampere 架构,算力 8.6,所以写 86。RTX 40 系列是 Ada 架构,算力 8.9,写 89。V100 是 Volta,算力 7.0,写 70。A100 是 Ampere 数据中心卡,算力 8.0,写 80。
很多人喜欢直接写-DCMAKE_CUDA_ARCHITECTURES=all,意思是把能支持的架构全部编一遍。这样确实能保证兼容性,但代价是编译时间会变得很长,而且容易在某些环境上因为其他架构的设备代码生成问题而失败。除非你就是想在一台机器上编出到处能跑的版本,否则我还是建议明确指定你自己的架构。
第三个是-DCUDA_TOOLKIT_ROOT_DIR=/usr/local/cuda。如果你的 CUDA 装在了非标准路径,这个选项能帮 CMake 准确找到 Toolkit。一般 /usr/local/cuda 是个软链接,指向你实际安装的 CUDA 版本目录。
查显卡算力可以用这条命令:
nvidia-smi --query-gpu=compute_cap --format=csv输出类似 8.6,那你 CMake 里就写 86。如果是多卡异构环境,可以用分号分隔,比如-DCMAKE_CUDA_ARCHITECTURES=86;75,但非必要不建议这么搞,因为会增加编译耗时。
3.2 完整编译命令与验证
下面是一套我在 Ubuntu 22.04 + CUDA 12.1 + RTX 4090 环境下验证过的完整命令:
cd ceres-solver mkdir build && cd build cmake .. \ -DCMAKE_BUILD_TYPE=Release \ -DCERES_CUDA=ON \ -DCMAKE_CUDA_ARCHITECTURES=89 \ -DCUDA_TOOLKIT_ROOT_DIR=/usr/local/cuda \ -DCMAKE_PREFIX_PATH=/usr/local make -j$(nproc)make 完成之后,安装:
sudo make install安装完成后,建议检查一下 CMake 缓存,确认 CUDA 真的开了:
grep -i cuda CMakeCache.txt正常应该能看到类似CERES_CUDA:BOOL=ON的输出。如果这里显示 OFF,或者没找到相关条目,说明你的 Ceres 版本不对或者配置有问题,先别急着往下走。
另外注意 CMake 配置阶段的输出。你会看到类似:
SuiteSparse: YES CUDA: YES如果 CUDA 显示 NO,那就先不要 make,回头检查 CMake 选项和环境变量。如果 SuiteSparse 显示 NO,建议先补齐依赖再继续,否则即使 Ceres 编过了,COLMAP 后面的稀疏求解能力也会缩水。
3.3 为什么编译位置和安装路径这么重要
COLMAP 在编译时通过 find_package(Ceres) 来找 Ceres,而这个查找过程依赖 Ceres 的 CMake 配置文件 CeresConfig.cmake。如果你把 Ceres 装到了 /usr/local,COLMAP 默认也能找到。但如果你自定义了安装路径,比如装到 /opt/ceres,那就必须在编译 COLMAP 时通过 CMAKE_PREFIX_PATH 告诉它去哪个目录找。
我在实际中遇到不少人是把 Ceres 源码放某个目录,直接 build,没有 make install,然后就跑去编 COLMAP。COLMAP 的 find_package 不一定能定位到那个 build 目录里的 CeresConfig.cmake。所以稳妥做法就是先 install 到系统路径,或者至少把 build 目录里的 CeresConfig.cmake 路径通过 Ceres_DIR 显式传给 COLMAP 的 cmake 配置。
4. COLMAP 编译:让它认准带 CUDA 的 Ceres
4.1 COLMAP 的 CMake 配置与 GUI 开关
Ceres 搞定之后,COLMAP 这边就轻松多了。进入 COLMAP 源码目录:
cd colmap mkdir build && cd build cmake .. \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_PREFIX_PATH=/usr/local \ -DCUDA_ENABLED=ON这里的-DCUDA_ENABLED=ON是 COLMAP 自己的选项,控制是否启用 COLMAP 自带流程里的 GPU 加速,比如特征提取里的 SIFT 部分。注意它和 Ceres 的 CUDA 是两码事。如果你的 Ceres 没有开 CUDA,那 COLMAP 配置时就会出现红色警告或者提示找不到带 CUDA 的 Ceres,但不会立刻中断,等运行的时候才会发现 BA 完全不吃 GPU。
如果你不需要 COLMAP 的 GUI 界面,可以不装 Qt,CMake 会自动把 GUI 相关部分关掉,只编命令行工具。这样能省不少编译时间。如果要 GUI,提前装:
sudo apt install -y qtbase5-dev libqt5opengl5-dev我在实际使用中其实很少开 GUI,大部分情况都是直接用 colmap feature_extractor、colmap mapper、colmap model_converter 这些命令行工具。所以没有 GUI 并不影响正常使用。
4.2 确认 CMake 配置摘要里的关键项
COLMAP 的 CMake 配置阶段会打印一个摘要,重点看几行:
- CUDA 是否 ON
- Ceres 的路径和版本
- 是否找到 SuiteSparse
如果摘要里 Ceres 版本显示的是老版本,或者路径指向了你压根不记得的某个目录,那就要小心了。可以显式指定 Ceres 的位置:
cmake .. -DCeres_DIR=/usr/local/lib/cmake/Ceres或者如果你没有 install Ceres,直接指向 Ceres 的 build 目录:
cmake .. -DCeres_DIR=/path/to/ceres-solver/build这样能最大程度避免 find_package 找到了系统里其他位置的 Ceres。
配置没问题后,执行编译:
make -j$(nproc) sudo make installCOLMAP 编译量不小,如果是第一次编译,建议先确认内存和磁盘空间够用。我见过有人 make 到一半进程被杀,就是因为内存不足。加 -j 参数的时候可以适当保守一些,比如 -j8。
5. 编译报错与排查实录
5.1 高频报错速查表
实际折腾过程中,报错基本集中在下面几个场景。我把它们整理成表格,方便你对照着处理。
| 报错特征 | 根本原因 | 解决方法 |
|---|---|---|
| Could NOT find Ceres / Package Ceres not found | Ceres 没安装,或 CMake 找不到配置文件 | 检查 Ceres 是否 make install;通过 Ceres_DIR 显式指定路径 |
| ceres-solver has no CUDA support | Ceres 版本太老或编译时没开 CERES_CUDA | 确认 Ceres 在 2.1 以上;重新编译 Ceres 并检查 CMakeCache |
| Unsupported gpu architecture 'compute_xx' | CMAKE_CUDA_ARCHITECTURES 填错 | 用 nvidia-smi --query-gpu=compute_cap 查实际算力 |
| nvcc fatal: unsupported gnu version | GCC 版本超出当前 CUDA 支持范围 | 安装兼容的 GCC,或升级 CUDA Toolkit |
| CUDA_cublas_LIBRARY / CUDA_cusolver_LIBRARY not found | CUDA Toolkit 安装不完整或路径错误 | 重新安装 CUDA Toolkit,核对 CUDA_TOOLKIT_ROOT_DIR |
| SuiteSparse not found | 缺少 libsuitesparse-dev | apt install libsuitesparse-dev 后重新配置 Ceres |
| CMake Error: no known features for CUDAToolkit | CMake 版本过低 | 升级 CMake 到 3.18 以上(建议 3.21+) |
5.2 几个容易误判的隐蔽场景
这里有几个我踩过坑后总结的经验,藏得比较深,值得单独拿出来说。
第一个是 CMake 缓存残留。如果你之前用默认配置编过一次 Ceres,后来又加了 CUDA 相关选项重新 cmake,有时候会因为 CMakeCache.txt 里的旧变量没清干净,导致 CUDA 相关的选项没有真正生效。遇到这种情况,最省心的方式是把 build 目录整个删掉,重新 mkdir build,重新 cmake。我后来几乎都是这么干的,比在旧缓存上反复试探要靠谱得多。
第二个是显卡算力写得太高。比如你的显卡明明是 RTX 3060,算力 8.6,但你参考网上别人的教程写成了 89(那是 RTX 40 系列的算力),编译时 nvcc 会报不认识的 gpu architecture。反过来,如果你写低了,比如 70,虽然能编过,但运行时可能因为找不到对应的 SASS 或者 PTX 代码而无法加载内核,表现就是 COLMAP 好像没报错,但 GPU 利用率死活上不去。所以架构参数一定不能拍脑袋,必须查准确。
第三个是 CUDA Toolkit 装了两份。有时候系统里既有通过 deb 方式装的 CUDA,又有 runfile 方式装的另一套,导致 /usr/local/cuda 这个软链接指向混乱。现象是 nvcc --version 和 CMake 找到的版本不一致。我建议检查一下 /usr/local/cuda 到底指向哪个目录,必要时手动修改软链接,确保 CMake 找到的就是你预期的那一套。
第四个是 COLMAP 和 Ceres 的 CUDA 概念被混淆。COLMAP 配置界面里 CUDA_ENABLED 只是它自己业务的开关,不代表 Ceres 的 CUDA 状态。哪怕 COLMAP 这边 CUDA_ENABLED=ON,如果 Ceres 没开 CUDA,BA 还是 CPU 求解。反之亦然,Ceres 开了 CUDA,但 COLMAP 自己的特征提取没开 CUDA,那么 SIFT 匹配那部分也会走 CPU。最理想的情况当然是两端都打开。
6. 安装后的验证:怎么确认 CUDA 真的在干活
6.1 从 CMake 输出和文件入手
Ceres 编译完,装好之后,有几个最直接的检查点。
第一,看 Ceres 装出来的 CMake 配置文件。比如 /usr/local/lib/cmake/Ceres/CeresConfig.cmake 里,会有和 CUDA 相关的变量。你可以搜索里面是否有 CUDA 目录。如果全部是 OFF 或空,说明这个 Ceres 没有启用 CUDA。
第二,编译 COLMAP 时看配置摘要。前面提到了,摘要里如果 CUDA 是 ON、Ceres 路径也正确,说明链接关系大概率没问题。这一步虽然简单,但很多人不看,等编译完跑起来才发现不对。
第三,看编译产物。如果你用 ldd 查看 COLMAP 的可执行文件动态库依赖,理论上能看到链接了 ceres 相关的库。虽然 ceres 很多时候是被静态链接的,但如果能看到 libcuda、libcublas 之类的依赖,也能侧面说明链路确实接通了。
6.2 从运行状态来验证
上面的检查都通过之后,最直观的验证方式是跑一次实际重建流程。拿一个小场景,比如二三十张图片,执行:
colmap feature_extractor --database_path db.db --image_path images colmap mapper --database_path db.db --image_path images --output_path out在 mapper 阶段,另开一个终端执行 nvidia-smi,观察是否有 colmap 进程,以及 GPU 利用率是否波动。如果 BA 真的用上了 GPU,你会看到显存有占用,GPU-Util 会跳动。如果跑完整个流程 GPU 利用率都是 0%,那大概率 Ceres 的 CUDA 后端没有真正生效。
还有一个更细的观察角度是看日志耗时。开启详细日志后,BA 迭代过程会有迭代次数和耗时信息。如果你对比过 CPU 和 GPU 版本,你会发现每轮迭代的耗时差距明显。我自己在测一个一万多张图片的数据集时,CPU 版本 BA 单轮迭代要二十多秒,GPU 版本只要几秒,整体重建流程时间差距立竿见影。
最后分享一个我自己的使用习惯:后来每次在干净服务器上编译这套流程,我都会先在 Ceres 的 CMake 配置阶段停一下,确认 SuiteSparse、CUDA 都是 YES,然后再往下走。这个前置检查花不了两分钟,但能避开后面一大堆莫名其妙的连锁报错。编译这种依赖链路很长的项目,前期多花一点时间把环境确认清楚,永远比后期排错省时间。