尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Windows 11下编译支持CUDA的Ceres Solver 2.2.0完整指南

Windows 11下编译支持CUDA的Ceres Solver 2.2.0完整指南 1. 项目概述与背景最近在做一个三维重建相关的项目后端优化部分绕不开非线性最小二乘求解。Ceres Solver作为谷歌开源的利器自然是首选。但项目里有些模块用了CUDA加速想着要是Ceres也能用上GPU那整体流程的瓶颈说不定就能打通了。官方预编译的二进制包通常不包含CUDA支持在Windows上从头编译一个带CUDA的Ceres尤其是还要搞定其强依赖的SuiteSparse这个过程确实有点“劝退”。网上教程不少但要么年代久远要么步骤跳跃缺了关键细节就容易掉坑里。这次我成功在Windows 11上用Visual Studio 2022编译了Ceres Solver 2.2.0并集成了CUDA 12.x和SuiteSparse 5.13.0最后还把它做成了一个干净的CMake项目方便集成到自己的工程里。如果你也在Windows上被CeresCUDA的编译搞得头疼希望这篇从环境准备、依赖编译、问题排查到项目集成的完整记录能帮到你。2. 环境准备与工具链选型编译像Ceres这样依赖复杂的库一个清晰、一致的环境是成功的一半。在Windows上我们主要需要协调好编译器、CUDA工具链和数学库之间的关系。2.1 核心工具安装与版本确认我的基础环境是Windows 11 64位。以下是经过验证可以协同工作的工具版本Visual Studio 2022这是编译的“发动机”。必须安装“使用C的桌面开发”工作负载确保MSVC编译器如MSVC v143可用。社区版即可。CMake 3.28用于生成VS解决方案。建议从官网下载安装程序并勾选“将CMake添加到系统PATH”这样在命令行和VS中都能方便使用。CUDA Toolkit 12.x我使用的是12.4。安装时务必在自定义安装中勾选“Visual Studio Integration”。安装完成后在命令行输入nvcc --version和nvidia-smi来验证CUDA编译器驱动版本。一个常见坑点是nvcc版本编译器和nvidia-smi显示的驱动版本可能不一致但只要大版本号如12.x匹配通常问题不大。CUDA 12.x对NVIDIA显卡架构如Sm_89 for RTX 40系列支持更好。Git用于克隆源代码。注意路径中请尽量避免中文和空格。我将所有工具和源码都放在类似D:\Dev这样的纯英文路径下能减少很多不必要的麻烦。2.2 依赖库规划为何选择手动编译SuiteSparseCeres的依赖项很多例如Eigen、glog、gflags等。对于大多数依赖我们可以让Ceres在编译时自动下载通过FetchContent这非常方便。但SuiteSparse是一个例外。SuiteSparse是一套稀疏矩阵计算库Ceres用它来求解大型稀疏线性系统性能远超默认的稠密求解器。在Linux上通过包管理器安装libsuitesparse-dev很简单。但在Windows上没有官方预编译包。虽然有一些第三方编译好的二进制文件但版本、编译器运行时库MSVC Redistributable和我们的环境很难完全匹配极易引发运行时崩溃如“找不到VCRUNTIME140.dll”或“0xC0000005内存访问冲突”。因此最稳妥的方案是用自己的工具链从头编译SuiteSparse。这样生成的静态库.lib与我们的VS版本、运行时库完全一致确保最终链接和运行的稳定性。我们选择编译其核心组件AMD、CAMD、CCOLAMD、COLAMD、CHOLMOD和SPQR。其中CHOLMOD又依赖LAPACK和BLAS。对于LAPACK/BLAS我们选择Intel MKL。它性能强劲且提供了Windows预编译库兼容性好。可以从Intel官网下载并安装Intel oneAPI Base Toolkit其中包含MKL或者直接使用其在线安装器。安装后我们需要记住其库文件如mkl_intel_lp64.lib和头文件的路径。3. 编译SuiteSparse 5.13.0这是整个过程中最具挑战性的一环。SuiteSparse的构建系统原本是为Unix设计的在Windows上需要一些适配。3.1 源码获取与结构整理首先从Tim Davis教授的官网或GitHub仓库下载SuiteSparse 5.13.0的源码包。解压后你会看到一堆以组件命名的文件夹AMDCHOLMOD等和一个SuiteSparse_config文件夹。为了让CMake更容易管理我创建了一个清晰的目录结构D:\Dev\SuiteSparse_Source\ (源码解压至此) D:\Dev\SuiteSparse_Build\ (编译输出目录) D:\Dev\SuiteSparse_Install\ (最终库和头文件安装目录)将下载的SuiteSparse-5.13.0.tar.gz解压到SuiteSparse_Source内容应直接是各个组件文件夹。3.2 编写顶级CMakeLists.txt在SuiteSparse_Source目录下创建一个CMakeLists.txt文件。这个文件的作用是组织所有子组件并统一设置编译选项和查找MKL。cmake_minimum_required(VERSION 3.20) project(SuiteSparse LANGUAGES C) # 设置编译类型Release/Debug和运行时库MT/MTd set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreaded$$CONFIG:Debug:Debug) # 寻找Intel MKL。如果你安装在了默认位置可以这样设置。 # 请根据你的实际安装路径修改 set(INTEL_MKL_ROOT C:/Program Files (x86)/Intel/oneAPI/mkl/latest) set(ENV{MKLROOT} ${INTEL_MKL_ROOT}) find_package(MKL REQUIRED) if(MKL_FOUND) message(STATUS Found MKL: ${MKL_LIBRARIES}) include_directories(${MKL_INCLUDE_DIRS}) else() message(FATAL_ERROR MKL not found. Please install Intel oneAPI MKL.) endif() # 添加子目录注意依赖顺序。 # SuiteSparse_config 必须第一个它包含全局配置。 # CHOLMOD 依赖较多放在后面。 add_subdirectory(SuiteSparse_config) add_subdirectory(AMD) add_subdirectory(CAMD) add_subdirectory(CCOLAMD) add_subdirectory(COLAMD) # CHOLMOD 依赖之前的所有组件以及MKL add_subdirectory(CHOLMOD) add_subdirectory(SPQR) # 创建一个安装目标将所有库和头文件收集到指定目录 install(DIRECTORY include/ DESTINATION include) install(TARGETS suitesparseconfig amd camd ccolamd colamd cholmod spqr ARCHIVE DESTINATION lib LIBRARY DESTINATION lib RUNTIME DESTINATION bin)3.3 修改组件CMakeLists.txt与源码适配每个组件文件夹如AMD里可能没有CMakeLists.txt或者其Makefile不适用于Windows。我们需要为每个组件创建简单的CMakeLists.txt。以AMD为例在D:\Dev\SuiteSparse_Source\AMD中创建CMakeLists.txtproject(AMD LANGUAGES C) # 收集当前目录下的所有.c文件 file(GLOB_RECURSE SOURCES *.c) # 添加包含路径指向SuiteSparse_config的头文件 include_directories(../SuiteSparse_config) # 创建静态库 add_library(amd STATIC ${SOURCES}) # 设置库的属性避免警告 set_target_properties(amd PROPERTIES POSITION_INDEPENDENT_CODE ON ) # 安装头文件和库 install(FILES Include/amd.h DESTINATION include) install(TARGETS amd ARCHIVE DESTINATION lib)对于CHOLMOD情况更复杂因为它依赖MKL。在其CMakeLists.txt中需要链接MKLproject(CHOLMOD LANGUAGES C) file(GLOB_RECURSE SOURCES *.c) include_directories(../SuiteSparse_config ../AMD/Include ../CAMD/Include ../CCOLAMD/Include ../COLAMD/Include .) add_library(cholmod STATIC ${SOURCES}) set_target_properties(cholmod PROPERTIES POSITION_INDEPENDENT_CODE ON) # 关键链接依赖库和MKL target_link_libraries(cholmod PUBLIC amd camd ccolamd colamd suitesparseconfig ${MKL_LIBRARIES} # 链接MKL库 ) target_include_directories(cholmod PUBLIC $BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/Include ) install(FILES Include/cholmod.h Include/cholmod_*.h DESTINATION include) install(TARGETS cholmod ARCHIVE DESTINATION lib)一个关键的源码修改在SuiteSparse_config的SuiteSparse_config.h文件中需要确保它正确地定义了Windows下的符号。通常需要检查或添加#ifdef _WIN32 #define SuiteSparse_long __int64 #define SuiteSparse_long_max _I64_MAX // 确保使用符合C99标准的函数例如将round定义为_round #include math.h #ifndef round #define round(x) ((x) 0 ? (int)((x)0.5) : (int)((x)-0.5)) #endif #endif如果遇到round函数重定义警告可以注释掉自定义定义因为math.h通常已提供。3.4 生成、编译与安装生成VS解决方案 打开“x64 Native Tools Command Prompt for VS 2022”确保是64位环境。cd D:\Dev\SuiteSparse_Build cmake ../SuiteSparse_Source -G Visual Studio 17 2022 -A x64 -DCMAKE_INSTALL_PREFIX../SuiteSparse_Install参数解释-G指定生成器-A指定平台-DCMAKE_INSTALL_PREFIX指定安装路径。编译与安装cmake --build . --config Release --target INSTALL这个命令会编译Release配置并将库和头文件安装到D:\Dev\SuiteSparse_Install目录下。完成后该目录会有include和lib文件夹里面就是我们需要的所有东西。实操心得第一次编译可能会遇到各种未定义符号或链接错误。请务必按照依赖顺序添加子目录如先编译AMD等再编译依赖它们的CHOLMOD。错误信息通常会告诉你缺少哪个库根据提示调整target_link_libraries的顺序。编译SPQR如果报错找不到cholmod_l_*函数可能需要检查CHOLMOD的编译选项是否生成了这些带_l后缀的长整型版本函数。4. 编译Ceres Solver 2.2.0 (With CUDA)搞定SuiteSparse后编译Ceres本身反而相对直接。4.1 源码获取与CMake配置从Ceres Solver的GitHub仓库下载2.2.0版本源码解压到D:\Dev\Ceres_Source。创建构建目录D:\Dev\Ceres_Build。在CMake GUI中操作会更直观设置源码路径D:/Dev/Ceres_Source和构建路径D:/Dev/Ceres_Build。点击“Configure”选择“Visual Studio 17 2022”和“x64”。会出现一堆红色配置项。我们需要关键修改以下几项CMAKE_PREFIX_PATH: 添加我们的SuiteSparse安装路径如D:/Dev/SuiteSparse_Install。这能帮助CMake自动找到SuiteSparse。SUITESPARSE_INCLUDE_DIR_HINTS: 手动指向D:/Dev/SuiteSparse_Install/include。SUITESPARSE_LIBRARY_DIR_HINTS: 手动指向D:/Dev/SuiteSparse_Install/lib。Eigen3_DIR: 如果自动下载Eigen失败可以手动指定本地Eigen路径。BUILD_SHARED_LIBS:OFF我们编译静态库便于部署。BUILD_EXAMPLES:OFF非必须。CUDA:ON启用CUDA支持。CUDA_TOOLKIT_ROOT_DIR: 通常CMake能自动找到如果没找到手动指向CUDA安装目录如C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v12.4。CUDA_ARCHITECTURES: 根据你的GPU计算能力填写。例如RTX 4060 Ti是Ada Lovelace架构计算能力8.9可以填写native或89-real;89-virtual。填写native让CMake自动检测是最省事的。CXX11_ABI: 保持默认通常为ON。确保与后续使用Ceres的项目设置一致。MSVC_USE_STATIC_RUNTIME:ON使用静态运行时库/MT这样生成的库不依赖VC运行时DLL但会增大体积。如果选择OFF/MD则必须确保部署环境有对应运行时库。再次点击“Configure”直到没有红色错误。此时CMake应该能正确找到SuiteSparse的各组件库如SUITESPARSE_CHOLMOD_LIB等。点击“Generate”生成VS解决方案。4.2 编译与验证打开生成的Ceres.sln在解决方案配置中选择“Release”和“x64”然后生成“ALL_BUILD”目标。编译成功后在D:\Dev\Ceres_Build\lib\Release目录下会生成ceres.lib静态库和ceres.dll如果编译了动态库。头文件在源码目录的include里。验证CUDA是否启用编译一个使用了ceres::CUDADenseQR或ceres::CUDASparseCholesky等CUDA求解器的示例程序。或者在代码中检查#ifdef CERES_USE_CUDA std::cout CUDA support is enabled in Ceres! std::endl; #endif5. 创建集成的CMakeLists项目现在我们将编译好的Ceres和SuiteSparse集成到一个干净的CMake项目中方便在其他工程里引用。假设你的项目结构如下MyProject/ ├── CMakeLists.txt ├── src/ │ └── main.cpp ├── deps/ │ ├── ceres/ │ │ ├── include/ (从Ceres源码和SuiteSparse安装目录合并而来) │ │ └── lib/ (包含ceres.lib, suitesparse的各个.lib, 以及CUDA相关的.lib如cudart_static.lib) │ └── mkl/ │ └── (MKL的lib和include)5.1 依赖库的整理将以下文件拷贝到deps目录下Ceres:D:\Dev\Ceres_Source\include\ceres\*-MyProject\deps\ceres\include\ceres\D:\Dev\Ceres_Build\include\ceres\internal\config.h-MyProject\deps\ceres\include\ceres\internal\(这个生成的配置头文件很重要)D:\Dev\Ceres_Build\lib\Release\ceres.lib-MyProject\deps\ceres\lib\SuiteSparse:D:\Dev\SuiteSparse_Install\include\*.h-MyProject\deps\ceres\include\(与Ceres头文件放同级因为Ceres会直接包含suitesparse.h等)D:\Dev\SuiteSparse_Install\lib\*.lib-MyProject\deps\ceres\lib\CUDA:需要链接CUDA运行时库。通常使用cudart_static.lib静态链接。它位于CUDA_PATH\lib\x64如C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.4\lib\x64。将其也拷贝到MyProject\deps\ceres\lib\或者不拷贝在CMake中直接引用绝对路径。MKL:将MKL的库文件如来自%ONEAPI_ROOT%\mkl\latest\lib\intel64和头文件也组织到deps/mkl下或者直接在CMake中查找。5.2 编写项目CMakeLists.txtcmake_minimum_required(VERSION 3.20) project(MyCeresProject LANGUAGES CXX CUDA) # 注意这里添加了CUDA语言 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 设置依赖库路径 set(DEP_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/deps) set(CERES_DIR ${DEP_ROOT}/ceres) set(MKL_DIR ${DEP_ROOT}/mkl) # 假设MKL已整理至此 # 添加可执行文件 add_executable(my_ceres_app src/main.cpp) # 包含目录 target_include_directories(my_ceres_app PRIVATE ${CERES_DIR}/include # 如果MKL头文件不在默认路径也需要添加 ${MKL_DIR}/include ) # 链接库 - 这是一个需要仔细排序的列表 target_link_libraries(my_ceres_app PRIVATE # 1. Ceres Solver 主库 ${CERES_DIR}/lib/ceres.lib # 2. SuiteSparse 组件 (注意顺序被依赖的放后面) ${CERES_DIR}/lib/spqr.lib ${CERES_DIR}/lib/cholmod.lib ${CERES_DIR}/lib/amd.lib ${CERES_DIR}/lib/camd.lib ${CERES_DIR}/lib/ccolamd.lib ${CERES_DIR}/lib/colamd.lib ${CERES_DIR}/lib/suitesparseconfig.lib # 3. MKL 库 (需要根据MKL链接线指南调整这是一个简化示例) ${MKL_DIR}/lib/mkl_intel_lp64.lib ${MKL_DIR}/lib/mkl_sequential.lib ${MKL_DIR}/lib/mkl_core.lib # 4. CUDA 运行时库 cudart_static.lib # 如果路径不在系统库目录需要用完整路径或find_package(CUDA) # 5. Windows 系统库 ws2_32.lib # SuiteSparse可能需要的网络库 shlwapi.lib ) # 重要如果使用静态运行时库(/MT)需要定义宏 target_compile_definitions(my_ceres_app PRIVATE $$CXX_COMPILER_ID:MSVC:_STATIC_CPPLIB # 避免链接冲突 ) # 如果项目中有.cu文件需要启用CUDA编译并链接CUDA库 enable_language(CUDA) set_target_properties(my_ceres_app PROPERTIES CUDA_SEPARABLE_COMPILATION ON )5.3 一个简单的测试代码在src/main.cpp中编写一个简单的BA问题测试#include iostream #include ceres/ceres.h #include ceres/cuda_sparse_cholesky.h int main(int argc, char** argv) { google::InitGoogleLogging(argv[0]); // 检查CUDA是否可用 #ifdef CERES_USE_CUDA std::cout [INFO] Ceres CUDA support is enabled. std::endl; // 可以尝试设置线性求解器为CUDA版本 ceres::Solver::Options options; options.linear_solver_type ceres::SPARSE_NORMAL_CHOLESKY; options.sparse_linear_algebra_library_type ceres::CUDA_SPARSE; options.dense_linear_algebra_library_type ceres::CUDA; options.num_threads 1; // CUDA求解器通常单线程 options.minimizer_progress_to_stdout true; // 注意实际使用需要确保问题规模适合GPU且有可用的CUDA求解器 std::cout CUDA linear solver options configured. std::endl; #else std::cout [WARN] Ceres compiled without CUDA. std::endl; #endif // 这里可以构造一个简单的优化问题 // ... std::cout Ceres Solver with CUDA test environment setup successfully. std::endl; return 0; }6. 常见编译与链接问题排查即使按照步骤操作也可能会遇到各种错误。这里记录几个我踩过的坑和解决方法。6.1 SuiteSparse编译阶段“LINK : fatal error LNK1104: 无法打开文件 ‘mkl_intel_lp64.lib’”原因CMake没有正确找到MKL库路径。解决在顶级CMakeLists.txt中使用find_package(MKL REQUIRED)前通过set(MKLROOT “your_mkl_path”)或设置环境变量MKLROOT明确指定路径。确保路径中的斜杠使用/或双反斜杠\\。“未定义的外部符号 cholmod_l_*”原因SuiteSparse默认可能同时编译32位和64位整数版本int和long。Ceres通常使用long版本函数带_l后缀。解决在编译SuiteSparse时确保SuiteSparse_config.h中DLONG被定义通常默认就是这样就会编译cholmod_l_*函数。检查CHOLMOD的Makefile或CMake是否开启了-DDLONG选项。“error C2065: ‘round’: 未声明的标识符”原因Windows旧版本MSVC的math.h可能没有C99的round函数。解决在SuiteSparse_config.h的Windows部分使用前面提到的round宏定义或者包含cmath并确认使用标准兼容模式/std:c17或更高。6.2 Ceres编译阶段“Could not find CHOLMOD or CHOLMOD not compiled with SuiteSparse_config.h”原因CMake找不到SuiteSparse或找到的版本不匹配。解决确保CMAKE_PREFIX_PATH或SUITESPARSE*_DIR_HINTS指向的是你安装的目录SuiteSparse_Install而不是源码或构建目录。该目录下必须有include/suitesparse.h和lib/cholmod.lib等文件。“CUDA_nvcc_executable-NOTFOUND”原因CMake找不到nvcc编译器。解决检查CUDA是否安装正确并确保在“x64 Native Tools Command Prompt for VS 2022”中运行CMake这个命令行环境设置了VS和CUDA所需的路径。也可以手动设置CUDA_TOOLKIT_ROOT_DIR变量。“error : Unsupported GPU architecture ‘compute_89’”原因CUDA编译器版本太旧不支持你指定的新GPU架构如RTX 40系列的sm_89。解决升级CUDA Toolkit到支持该架构的版本如CUDA 11.8支持sm_89。或者在CUDA_ARCHITECTURES中指定一个旧一点的、支持的架构如75for Turing但这意味着无法充分利用新显卡性能。6.3 项目链接与运行时阶段“无法解析的外部符号 google::base::CheckOpMessageBuilder”原因缺少glog或gflags库。虽然Ceres静态链接了它们的一部分但有时仍需要显式链接。解决在你的项目CMakeLists.txt中链接glog.lib和gflags.lib。你可以让Ceres自动下载并编译它们然后将生成的库文件也拷贝到你的deps目录并链接。“0xC0000005: 读取位置 0xFFFFFFFFFFFFFFFF 时发生访问冲突”原因这是典型的运行时库不匹配或内存损坏。最常见的原因是SuiteSparse或Ceres是用动态运行时库/MD编译的而你的项目是用静态运行时库/MT编译的或者反之。解决统一所有库和你的项目的运行时库设置。在CMake中通过set(CMAKE_MSVC_RUNTIME_LIBRARY “MultiThreaded$$CONFIG:Debug:Debug”)来强制使用静态运行时库/MT或/MTd。确保你使用的所有第三方.lib文件都是用相同设置编译的。程序运行正常但CUDA求解器没有加速效果原因问题规模太小GPU启动和内存传输的开销超过了计算收益或者没有正确调用CUDA求解器。解决确保在ceres::Solver::Options中正确设置了linear_solver_type、sparse_linear_algebra_library_type和dense_linear_algebra_library_type为CUDA选项。对于稀疏Cholesky求解器使用options.linear_solver_type ceres::SPARSE_NORMAL_CHOLESKY; options.sparse_linear_algebra_library_type ceres::CUDA_SPARSE;。构造一个足够大的问题例如BA中成千上万个点和观测才能体现GPU优势。“CUSPARSE_STATUS_ALLOC_FAILED” 或 GPU内存不足原因问题数据太大超出了GPU显存。解决减小问题规模或者使用迭代求解器如CGNR配合CUDA的预处理子。监控GPU显存使用情况可以用nvidia-smi。整个流程走下来虽然步骤繁琐但一旦打通你就拥有了一个完全可控、深度定制的Ceres Solver开发环境。最大的体会是在Windows上处理这类源自Linux生态的库一致性是关键编译器版本、运行时库、依赖库的编译选项必须完全匹配。手动编译SuiteSparse虽然前期耗时但避免了后续无数诡异的运行时错误从长远看是值得的。最后将一切封装在清晰的CMake项目里无论是团队协作还是项目迁移都会轻松很多。
返回列表