ARTICLE DETAIL

资讯详情

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

Windows下Ceres Solver 2.2.0(CUDA版)编译与CMake项目集成全攻略

Windows下Ceres Solver 2.2.0(CUDA版)编译与CMake项目集成全攻略 1. 项目概述与核心价值在计算机视觉、机器人SLAM同步定位与地图构建以及各类优化问题中非线性最小二乘求解器扮演着核心角色。Ceres Solver正是这个领域的佼佼者它是一个由Google开发的开源C库专门用于建模和求解大规模、复杂的非线性最小二乘问题。从BABundle Adjustment优化相机位姿和三维点到拟合复杂的曲线曲面Ceres因其高效、稳定和灵活的API而备受青睐。然而对于许多在Windows平台上进行开发的工程师和研究者来说将Ceres Solver尤其是其GPU加速版本成功集成到自己的项目中常常是一个令人头疼的“拦路虎”。官方文档和社区讨论多以Linux环境为主Windows下的编译特别是涉及到CUDA和其强依赖的线性代数库Suitesparse时各种路径问题、库版本冲突、编译选项配置错误层出不穷。网络上零散的教程要么步骤不全要么版本过时导致新手在“环境配置”这一步就可能耗费数天时间。这个项目的核心价值就是提供一个在Windows 10/11系统下从零开始、手把手编译Ceres Solver 2.2.0支持CUDA并集成Suitesparse的完整、可复现的解决方案。我们不仅会完成库本身的编译更重要的是会详细讲解如何将编译好的库文件、头文件以及CMake配置无缝集成到你自己的CMakeLists.txt项目中让你能真正用上这个强大的工具而不是止步于“编译成功”。整个过程会涵盖工具链准备VS、CMake、CUDA、依赖库编译Eigen、glog、gflags、Suitesparse、Ceres本身的配置与编译以及最终的集成实战。我会分享我踩过的所有坑和对应的填坑技巧确保你能一次成功。2. 环境准备与工具链详解工欲善其事必先利其器。在Windows上编译一个复杂的C科学计算库清晰的环境准备是成功的一半。这里我们需要的不是最新版本而是经过验证、彼此兼容的版本组合。2.1 核心工具安装与版本选择Visual Studio 2019/2022这是Windows下C编译的基石。我强烈推荐使用Visual Studio 2019因为其与CUDA工具包的兼容性经过更长时间的考验。安装时务必勾选“使用C的桌面开发”工作负载确保MSVC编译器、Windows SDK等组件齐全。VS2022也可行但可能需要更注意CUDA版本匹配。CMake 3.18用于生成VS的解决方案文件。建议从官网下载安装程序安装时勾选“将CMake添加到系统PATH”方便在命令行使用。版本不低于3.18即可。CUDA Toolkit 11.x这是启用GPU加速的关键。Ceres 2.2.0对CUDA版本有要求经过测试CUDA 11.6或11.8是兼容性较好的选择。安装时选择“自定义安装”可以只安装必要的组件CUDA Development、CUDA Runtime等但务必确保安装路径不含中文和空格默认路径即可。安装完成后需要验证打开命令行输入nvcc --version应能正确显示版本信息。Git用于克隆源代码。同样建议安装并添加到PATH。注意安装路径的“纯洁性”至关重要。所有工具的安装路径包括后续源码的下载和编译目录都应使用全英文路径避免任何空格和特殊字符如Program Files。我习惯在D:\Dev或C:\Dev下创建所有开发相关的目录一劳永逸地避免路径问题。2.2 依赖库源码获取我们将采用源码编译的方式安装所有依赖以确保版本和编译选项的绝对可控。请在一个统一的目录下例如D:\Dev\CeresDeps进行以下操作。EigenCeres的核心矩阵运算依赖。它是一个纯头文件库无需编译。git clone https://gitlab.com/libeigen/eigen.git # 切换到稳定分支例如3.4.0 cd eigen git checkout tags/3.4.0glog gflagsGoogle的日志和命令行参数解析库。# 在CeresDeps目录下分别克隆 git clone https://github.com/google/glog.git git clone https://github.com/gflags/gflags.git对于glog和gflags我们需要编译为静态库.lib。稍后详细说明编译步骤。Suitesparse这是最复杂的一环。Ceres的SPARSE_NORMAL_CHOLESKY等稀疏求解器需要它。我们需要其子集主要是CHOLMOD、AMD、CAMD、COLAMD、CCOLAMD等。访问Tim Davis教授的官网例如搜索SuiteSparse下载最新稳定版的源码包如SuiteSparse-7.0.1.tar.gz。解压到CeresDeps目录。关键点Suitesparse的编译在Windows上非常棘手因为它原本为Unix设计。我们需要使用CMake来构建。后面会专门用一节来攻克它。3. 依赖库的编译实战有了源码下一步就是将它们变成Windows能识别的.lib文件。3.1 编译gflags与glog这两个库的编译流程相似我们以x64 Release静态库为目标。编译gflags在CeresDeps\gflags目录下新建一个build文件夹并进入。打开“x64 Native Tools Command Prompt for VS 2019”或2022这是一个配置好VS编译环境命令行。执行以下CMake命令注意替换你的实际路径cmake .. -G Visual Studio 16 2019 -A x64 -DCMAKE_INSTALL_PREFIXD:\Dev\CeresDeps\gflags_install -DBUILD_SHARED_LIBSOFF -DBUILD_STATIC_LIBSON -DCMAKE_BUILD_TYPERelease-G指定生成器对应你的VS版本。-A x64指定目标架构。-DCMAKE_INSTALL_PREFIX指定安装目录编译好的库和头文件会复制到这里。-DBUILD_SHARED_LIBSOFF和-DBUILD_STATIC_LIBSON强制构建静态库。生成解决方案后执行编译和安装cmake --build . --config Release --target install完成后在gflags_install目录下你会看到lib\gflags_static.lib和include\头文件。编译glog流程几乎相同但glog依赖于gflags。我们需要在CMake命令中指定gflags的路径。# 在glog源码目录下创建build目录并进入 cmake .. -G Visual Studio 16 2019 -A x64 -DCMAKE_INSTALL_PREFIXD:\Dev\CeresDeps\glog_install -DBUILD_SHARED_LIBSOFF -DCMAKE_BUILD_TYPERelease -DGFLAGS_ROOTD:\Dev\CeresDeps\gflags_install关键参数是-DGFLAGS_ROOT它告诉CMake去哪里找我们刚刚编译好的gflags。同样执行编译安装命令cmake --build . --config Release --target install3.2 攻克堡垒Suitesparse的Windows编译这是整个过程中最具挑战性的部分。Suitesparse包含多个组件我们需要的是其中几个。手动配置非常复杂幸运的是社区有维护较好的CMake脚本来简化这个过程。获取CMake化脚本推荐使用suitesparse-metis-for-windows或SuiteSparse_cmake这类项目。这里以其中一个为例你可以搜索并克隆对应的仓库到本地例如D:\Dev\CeresDeps\SuiteSparse_cmake。准备源码将你下载的官方SuiteSparse源码包如SuiteSparse-7.0.1解压并将其中的所有文件夹AMD,CAMD,COLAMD,CCOLAMD,CHOLMOD等复制到CMake脚本项目的根目录下覆盖或合并原有文件。关键配置在CMake脚本目录下创建build文件夹并进入。使用CMake GUI或命令行进行配置。命令行示例cmake .. -G Visual Studio 16 2019 -A x64 -DCMAKE_INSTALL_PREFIXD:\Dev\CeresDeps\suitesparse_install -DBUILD_METISOFF -DCMAKE_BUILD_TYPERelease将BUILD_METIS设为OFF除非你的问题明确需要它这可以避免额外的依赖。确保你的系统已安装Intel MKL或OpenBLAS因为CHOLMOD需要BLAS/LAPACK库。如果没有可以下载OpenBLAS的Windows预编译库并在CMake中指定BLA_VENDOROpenBLAS和BLAS_LIBRARIES等路径。编译与安装同样执行cmake --build . --config Release --target install。这个过程可能会遇到一些错误常见的是头文件路径问题或库链接问题。通常需要根据编译错误手动修改对应组件的CMakeLists.txt或源文件中的#include路径将../../SuiteSparse_config/SuiteSparse_config.h改为SuiteSparse_config.h等相对路径。这是一个需要耐心调试的过程。验证成果安装完成后在suitesparse_install目录下你应该能找到lib\目录下包含libcholmod.lib,libamd.lib等库文件以及include\目录下的所有头文件。实操心得编译Suitesparse时如果遇到无法解决的链接错误一个备选方案是使用vcpkg来安装它vcpkg install suitesparse:x64-windows。vcpkg会自动处理依赖但生成的库可能是动态链接的并且版本可能不是最新。对于追求极致控制和版本匹配的项目还是推荐源码编译。4. Ceres Solver的配置与编译万事俱备只欠东风。现在我们来编译主角——Ceres Solver。获取源码在CeresDeps目录下克隆Ceres。git clone https://github.com/ceres-solver/ceres-solver.git cd ceres-solver git checkout tags/2.2.0 # 切换到2.2.0版本创建构建目录在ceres-solver目录下创建build并进入。关键CMake配置这是最核心的一步。我们需要通过CMake GUI或命令行设置所有依赖的路径和编译选项。打开CMake GUI设置源码路径为D:\Dev\CeresDeps\ceres-solver构建路径为D:\Dev\CeresDeps\ceres-solver\build。点击“Configure”选择你的VS版本和x64。配置完成后会出现一系列红色选项。我们需要修改以下关键项CMAKE_INSTALL_PREFIX: 设置为D:\Dev\CeresDeps\ceres_install或其他你喜欢的安装路径。BUILD_SHARED_LIBS:OFF我们编译静态库便于部署。BUILD_EXAMPLES:OFF非必须节省时间。BUILD_TESTING:OFF非必须。EIGEN_INCLUDE_DIR: 设置为D:\Dev\CeresDeps\eigenEigen头文件路径。GLOG_INCLUDE_DIR: 设置为D:\Dev\CeresDeps\glog_install\include。GLOG_LIBRARY: 设置为D:\Dev\CeresDeps\glog_install\lib\glog.lib或glog_static.lib根据实际生成的名字。GFLAGS_INCLUDE_DIR: 设置为D:\Dev\CeresDeps\gflags_install\include。GFLAGS_LIBRARY: 设置为D:\Dev\CeresDeps\gflags_install\lib\gflags_static.lib。SUITESPARSE_INCLUDE_DIR_HINTS: 设置为D:\Dev\CeresDeps\suitesparse_install\include。SUITESPARSE_LIBRARY_DIR_HINTS: 设置为D:\Dev\CeresDeps\suitesparse_install\lib。启用CUDA找到CUDA相关的选项确保CERES_USE_CUDA被勾选为ON。CMake会自动检测你的CUDA Toolkit路径。如果检测不到手动设置CUDA_TOOLKIT_ROOT_DIR为你的CUDA安装路径如C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.6。线性代数后端确保CERES_USE_EIGEN_SPARSE为ON。对于Suitesparse如果CMake正确找到了头文件和库CERES_USE_SUITESPARSE也会自动变为ON。再次点击“Configure”直到没有红色条目出现。然后点击“Generate”。编译与安装在CMake GUI中点击“Open Project”会在VS中打开生成的解决方案。在VS中将解决方案配置设置为“Release”和“x64”。然后在“解决方案资源管理器”中右键点击“INSTALL”项目选择“生成”。这将编译整个Ceres库并将其头文件、库文件复制到之前设置的CMAKE_INSTALL_PREFIX目录下。或者在命令行x64 Native Tools Command Prompt中进入build目录执行cmake --build . --config Release --target install编译成功后在ceres_install目录下你会得到include\ceres和lib\目录。lib目录下应包含ceres.lib静态库以及可能的一些依赖库。5. 集成到CMakeLists.txt项目库编译好了最终目的是要在自己的项目里用它。假设你有一个简单的CMake项目目录结构如下MyProject/ ├── CMakeLists.txt ├── include/ └── src/ └── main.cpp5.1 项目CMakeLists.txt配置你需要修改MyProject/CMakeLists.txt关键是要正确找到find_package我们编译的Ceres库。由于我们不是通过系统包管理器安装的标准的find_package(Ceres)很可能失败。因此我们采用直接指定路径的方式。cmake_minimum_required(VERSION 3.18) project(MyCeresProject) set(CMAKE_CXX_STANDARD 17) # 1. 设置Ceres的安装路径 set(CERES_DIR D:/Dev/CeresDeps/ceres_install/lib/cmake/Ceres CACHE PATH Path to Ceres config.cmake) # 注意Ceres在安装时会在其lib/cmake下生成CeresConfig.cmake等文件这个路径指向包含这些文件的目录。 # 2. 寻找Ceres包 find_package(Ceres REQUIRED PATHS ${CERES_DIR} NO_DEFAULT_PATH) # 3. 添加你的可执行文件 add_executable(my_ceres_app src/main.cpp) # 4. 链接Ceres库 target_link_libraries(my_ceres_app Ceres::ceres) # 5. 包含目录通常find_package已处理好但可显式添加 target_include_directories(my_ceres_app PRIVATE ${CERES_INCLUDE_DIRS}) # 如果你的代码还需要Eigen等头文件也需要添加 target_include_directories(my_ceres_app PRIVATE D:/Dev/CeresDeps/eigen)关键解释set(CERES_DIR ...): 这行代码设置一个变量指向Ceres安装目录下的lib/cmake/Ceres文件夹。这个文件夹里的.cmake文件包含了库路径、依赖关系等所有信息。find_package(... PATHS ... NO_DEFAULT_PATH): 告诉CMake只在指定的CERES_DIR路径下寻找Ceres的配置文件不去系统默认路径找这样就能精准定位到我们自己编译的版本。target_link_libraries(... Ceres::ceres): 链接名为Ceres::ceres的目标。这个目标是在CeresConfig.cmake中定义的它自动包含了Ceres库本身以及其所有依赖如glog, gflags, Eigen, Suitesparse的库。这是现代CMake的最佳实践比手动写一堆-l链接选项要清晰和可靠得多。5.2 一个简单的测试代码在src/main.cpp中你可以写一个简单的程序来测试集成是否成功#include iostream #include ceres/ceres.h // 一个简单的代价函数示例最小化 (10 - x)^2 struct CostFunctor { template typename T bool operator()(const T* const x, T* residual) const { residual[0] T(10.0) - x[0]; return true; } }; int main(int argc, char** argv) { google::InitGoogleLogging(argv[0]); // 初始化glog double initial_x 5.0; double x initial_x; ceres::Problem problem; ceres::CostFunction* cost_function new ceres::AutoDiffCostFunctionCostFunctor, 1, 1(new CostFunctor); problem.AddResidualBlock(cost_function, nullptr, x); ceres::Solver::Options options; options.linear_solver_type ceres::DENSE_QR; // 使用稠密QR求解器 options.minimizer_progress_to_stdout true; ceres::Solver::Summary summary; ceres::Solve(options, problem, summary); std::cout summary.BriefReport() \n; std::cout x : initial_x - x std::endl; return 0; }使用CMake配置并构建你的项目在MyProject下新建build目录执行cmake ..和cmake --build .。如果一切顺利运行生成的可执行文件你会看到优化过程输出并且x的值应该非常接近10。6. 常见问题与深度排查指南即使按照步骤操作也可能会遇到各种问题。这里记录了几个最常见且棘手的坑及其解决方案。6.1 CUDA相关编译错误错误“error: identifier “__forceinline__” is undefined”或类似与__host__、__device__相关的错误。原因这通常是因为在包含Ceres或CUDA头文件之前包含了某些Windows系统头文件如windows.h这些头文件定义了宏与CUDA的关键字冲突。解决在你的源代码中确保在任何地方#include ceres/ceres.h或任何CUDA头文件如cuda_runtime.h都放在最前面。如果必须包含windows.h可以尝试在包含它之前定义NOMINMAX和WIN32_LEAN_AND_MEAN宏来减少冲突。#define NOMINMAX #define WIN32_LEAN_AND_MEAN #include windows.h // ... 其他头文件 #include ceres/ceres.h错误“no kernel image is available for execution on the device”(CUDA error 209)原因你编译的CUDA代码Ceres中启用CUDA后部分算法由CUDA实现与当前显卡的架构不兼容。CUDA代码需要针对特定的显卡计算能力如sm_61, sm_75, sm_86等进行编译。解决在编译Ceres时通过CMake指定正确的GPU架构。在CMake GUI中找到CMAKE_CUDA_ARCHITECTURES变量将其设置为你的显卡架构。例如RTX 3060是sm_86RTX 2080 Ti是sm_75。如果不确定可以设置为“61;75;80;86”等一组常见架构但这会增加编译时间。最准确的方法是查询你的显卡型号对应的计算能力。6.2 链接错误LNK2001, LNK2019错误“unresolved external symbol ... gflags...”或“unresolved external symbol ... google::...”原因你的项目没有正确链接gflags或glog库。虽然你通过find_package(Ceres)链接了Ceres::ceres但有时CMake的传递依赖关系可能没有完全设置好特别是当这些依赖库也是静态库时。解决在你的CMakeLists.txt中在find_package(Ceres)之后显式地链接这些库。首先尝试用find_package找到它们find_package(gflags REQUIRED) find_package(glog REQUIRED) target_link_libraries(my_ceres_app Ceres::ceres gflags::gflags glog::glog)如果find_package找不到你可能需要手动指定路径类似于我们之前设置CERES_DIR的方式。错误“unresolved external symbol cholmod_...”原因Suitesparse库特别是CHOLMOD没有正确链接。同样可能是传递依赖问题。解决确保在编译Ceres时CMake成功检测并启用了CERES_USE_SUITESPARSE。在你的项目CMakeLists中可能需要手动添加Suitesparse的库目录和库文件。这比较繁琐更推荐确保Ceres的CeresConfig.cmake文件能正确导出这些依赖。检查ceres_install/lib/cmake/Ceres/CeresTargets-release.cmake文件看其中INTERFACE_LINK_LIBRARIES字段是否包含了Suitesparse的库如cholmod。如果没有你可能需要回退到手动指定所有库的“老办法”。6.3 运行时错误程序崩溃或输出乱码特别是在使用glog输出日志时。原因静态库的运行时库Runtime Library设置不匹配。这是Windows下混合编译静态库时的一个经典问题。你的项目、Ceres、glog、gflags等所有静态库必须使用相同的运行时库设置如/MT或/MD。解决你需要统一所有库的编译设置。我们之前编译依赖库和Ceres时使用的是CMake的默认设置在Windows上VS的默认设置通常是/MD动态链接运行时库。确保你的项目也使用相同的设置。在CMake中可以通过设置CMAKE_MSVC_RUNTIME_LIBRARY变量为MultiThreadedDLL对应/MD来强制指定。或者在VS项目属性中将“C/C” - “代码生成” - “运行时库”设置为“多线程DLL (/MD)”。必须保证所有环节一致。6.4 CMake找不到包find_package(Ceres)失败即使CERES_DIR设置正确。原因Ceres安装目录下的CMake配置文件可能因为路径问题无法正确加载其依赖。解决最稳妥但稍显笨拙的方法是放弃find_package采用直接引用库文件和头文件的“传统”方式。在你的项目CMakeLists.txt中# 直接包含头文件路径 target_include_directories(my_ceres_app PRIVATE D:/Dev/CeresDeps/ceres_install/include D:/Dev/CeresDeps/eigen D:/Dev/CeresDeps/glog_install/include D:/Dev/CeresDeps/gflags_install/include D:/Dev/CeresDeps/suitesparse_install/include ) # 直接链接库文件 target_link_libraries(my_ceres_app D:/Dev/CeresDeps/ceres_install/lib/ceres.lib D:/Dev/CeresDeps/glog_install/lib/glog.lib D:/Dev/CeresDeps/gflags_install/lib/gflags_static.lib D:/Dev/CeresDeps/suitesparse_install/lib/libcholmod.lib D:/Dev/CeresDeps/suitesparse_install/lib/libamd.lib # ... 链接其他必要的Suitesparse库和BLAS库如libopenblas.lib )这种方式虽然不够优雅但胜在直接可控能有效绕过CMake包查找机制的复杂性。你需要根据实际生成的库文件名可能带或不带lib前缀来调整路径。整个流程走下来从环境准备到最终项目集成确实是一次对耐心和细心的考验。尤其是在Windows平台上各种工具链的差异和依赖库的编译每一步都可能遇到意想不到的问题。我个人的体会是做好笔记记录下每一个成功的配置参数和解决错误的方法这些经验会成为你宝贵的财富。一旦环境搭建成功Ceres Solver强大的优化能力将会极大地提升你解决复杂非线性问题的效率。最后一个小技巧是可以考虑将编译好的所有依赖库Eigen, glog, gflags, Suitesparse, Ceres打包成一个“超级开发包”以后在新电脑或新项目中使用时只需配置一次CMake路径即可能节省大量重复劳动的时间。
返回列表