ARTICLE DETAIL

资讯详情

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

VS2019编译Ceres库与DLL:Windows下SLAM非线性优化环境配置指南

VS2019编译Ceres库与DLL:Windows下SLAM非线性优化环境配置指南 简介这份资源是使用VS2019编译完成的Ceres依赖库文件集合面向需要在Windows平台配置Ceres Solver的C开发者与视觉SLAM、三维重建方向的学习者可省去自行编译Eigen、gflags、glog、SuiteSparse等依赖的繁琐过程。压缩包共432个文件包含328个头文件、40个动态库与35个静态库另有umfpack、spqr、cholmod、metis、pardiso等稀疏矩阵求解相关模块整体约17.35MB并同时提供Debug与Release两套版本便于按工程配置灵活选用。目前已有824人学习下载。配合作者给出的配置教程读者可快速完成VS2019环境下的库路径、附加依赖项与链接设置规避版本不匹配、符号缺失等常见编译链接问题适合作为Ceres工程搭建的现成依赖包直接复用。1. 为什么我宁愿用别人编译好的 ceres lib 和 dll也不自己从头编如果你在 Windows 上做 SLAM、VIO、BA 或者任何需要非线性优化的 C 项目ceres 基本绕不开。但真正让人头疼的不是写代码而是编译。Eigen、glog、gflags、SuiteSparse、CXSparse、lapack、blas……光是依赖链就能劝退一批人。我自己第一次编 ceres 花了整整两天最后卡在libgfortran-3.dll找不到程序一跑就崩。这份资源就是 VS2019 编译好的 ceres 相关 lib 和 dll 文件包含 debug 和 release 两套核心模块覆盖 Cholesky、CholmodSupport、Core、Dense配套的ceres-debug.dll、liblapack.dll、libgfortran-3.dll、libcholmodd.dll都在。拿到手之后你不需要再装 CMake、不需要配 SuiteSparse 源码、不需要折腾 Fortran 编译器直接在 VS2019 里把库路径和附加依赖项配好就能用。适合正在做视觉 SLAM、三维重建、标定优化又不想在环境上耗时间的同学。2. 先搞清楚这些 lib 和 dll 分别管什么选型与依赖关系2.1 ceres 在 Windows 下的依赖链到底长什么样ceres 本身是一个 C 库但它不是孤立存在的。它依赖 Eigen 做矩阵运算依赖 glog 做日志依赖 gflags 做命令行参数解析依赖 SuiteSparse 里的 CHOLMOD 做稀疏 Cholesky 分解而 CHOLMOD 又依赖 BLAS 和 LAPACKLAPACK 在 Windows 上通常用 MinGW 编译出来的版本于是又带出了libgfortran-3.dll这个运行时。所以你在资源里看到的文件不是随便凑的它们对应的是这样一条链文件作用是否必须ceres-debug.dllceres 核心动态库debug 版本debug 模式必须liblapack.dll线性代数底层库CHOLMOD 调用使用 CholmodSupport 时必须libgfortran-3.dllFortran 运行时lapack 依赖跟 liblapack 一起必须libcholmodd.dllSuiteSparse 的 CHOLMOD 模块debug 版使用稀疏求解时必须ceres.libceres 导入库链接用必须cholmod.libCHOLMOD 导入库使用 CholmodSupport 时必须如果你只是用Dense模块做小规模优化理论上不需要 CHOLMOD 那一套。但实际项目里只要涉及 BA矩阵维度一上来就必须走稀疏求解所以 CholmodSupport 基本是标配。2.2 为什么选别人编译好的而不是自己编自己编 ceres 最大的问题不是 ceres 本身而是 SuiteSparse。SuiteSparse 在 Windows 上原生编译非常麻烦官方推荐用 Cygwin 或者 MSYS2但那样编出来的库跟 VS2019 的 ABI 不一定兼容。另一个坑是 LAPACK如果你用 Intel MKL 替代配置复杂不说还容易跟 CHOLMOD 的接口对不上。这份资源是用 VS2019 工具链编出来的lib 和 dll 的 ABI 跟 VS2019 一致不会出现“链接过了但运行时报堆损坏”这种玄学问题。而且 debug 和 release 分开提供debug 版带调试符号release 版体积小、优化充分。我一般会在开发阶段用 debug 版定位问题发布时切到 release 版。提示如果你用的是 VS2022理论上可以兼容 VS2019 编译的库因为 MSVC 的 ABI 从 VS2015 到 VS2022 保持向后兼容。但保险起见建议还是用 VS2019 打开项目避免工具集差异带来的意外。2.3 目录怎么放我习惯的工程结构拿到文件之后不要乱丢建议按下面的结构组织your_project/ ├── third_party/ │ └── ceres/ │ ├── include/ # ceres 头文件目录 │ ├── lib/ │ │ ├── debug/ # debug 版 lib 和 dll │ │ └── release/ # release 版 lib 和 dll │ └── bin/ │ ├── debug/ # debug 版 dll │ └── release/ # release 版 dll ├── src/ └── your_project.sln把 dll 单独放bin目录是为了后面配“生成后事件”方便lib 按 debug/release 分开放VS 里切换配置时只需要改一个宏就能自动指向对应目录。头文件目录如果你手里没有需要从 ceres 官方仓库单独下载对应版本的头文件lib 和 dll 只解决链接和运行编译还是需要头文件。3. 在 VS2019 里一步步配上去从附加包含目录到生成后事件3.1 配置附加包含目录和库目录打开你的 VS2019 项目右键项目 → 属性。先把平台切到x64因为这份资源是 64 位的Win32 用不了。在“C/C → 常规 → 附加包含目录”里加入 ceres 头文件路径比如$(SolutionDir)third_party\ceres\include然后在“链接器 → 常规 → 附加库目录”里加入 lib 所在目录。这里要用宏区分 debug 和 release$(SolutionDir)third_party\ceres\lib\$(Configuration)$(Configuration)在 VS 里会自动展开成Debug或Release这样你切换配置时不用手动改路径。注意目录名大小写要跟你实际文件夹一致Windows 虽然不区分大小写但团队协作时统一命名能少很多事。3.2 附加依赖项怎么写debug 和 release 的区别在“链接器 → 输入 → 附加依赖项”里填 lib 文件名。debug 配置下填ceres-debug.lib cholmod.lib lapack.librelease 配置下填ceres.lib cholmod.lib lapack.lib注意 debug 版的 ceres 库名字带-debug后缀这是 ceres 官方 CMake 脚本的命名习惯。如果你把 debug 和 release 的 lib 搞混了链接阶段可能不报错但运行时会崩因为 debug 版用了不同的 STL 迭代器调试级别。注意libgfortran-3.dll和liblapack.dll是运行时依赖不需要写进附加依赖项它们只需要在 exe 旁边能被找到就行。3.3 把 dll 放到正确位置生成后事件自动拷贝dll 如果不在 exe 同目录或者系统 PATH 里程序启动时会直接报“找不到 xxx.dll”。手动拷贝容易忘我一般用生成后事件自动处理。右键项目 → 属性 → 生成事件 → 生成后事件命令行填xcopy /Y /I $(SolutionDir)third_party\ceres\bin\$(Configuration)\*.dll $(OutDir)这条命令的意思是把bin\Debug或bin\Release下所有 dll 拷贝到输出目录。/Y表示覆盖时不询问/I表示如果目标不存在就当目录创建。这样每次编译完 dll 自动就位省得手动拖。如果你不想用生成后事件也可以把 dll 所在目录加到系统 PATH 里但那样换一台机器就要重新配不推荐。3.4 写一段最小验证代码确认 ceres 真的能跑配置完之后别急着上大项目先写个最小例子验证。下面这段代码做一次简单的曲线拟合#include ceres/ceres.h #include iostream // 残差函数y exp(m * x c) struct ExponentialResidual { ExponentialResidual(double x, double y) : x_(x), y_(y) {} template typename T bool operator()(const T* const m, const T* const c, T* residual) const { residual[0] T(y_) - ceres::exp(m[0] * T(x_) c[0]); return true; } private: const double x_; const double y_; }; int main() { // 生成带噪声的观测数据 const int kNumObservations 10; double data[2 * kNumObservations]; for (int i 0; i kNumObservations; i) { double x i * 0.1; double y exp(0.3 * x 0.1); // 真实参数 m0.3, c0.1 data[2 * i] x; data[2 * i 1] y; } double m 0.0; double c 0.0; ceres::Problem problem; for (int i 0; i kNumObservations; i) { problem.AddResidualBlock( new ceres::AutoDiffCostFunctionExponentialResidual, 1, 1, 1( new ExponentialResidual(data[2 * i], data[2 * i 1])), nullptr, m, c); } ceres::Solver::Options options; options.linear_solver_type ceres::DENSE_QR; // 小规模问题用 DENSE_QR options.minimizer_progress_to_stdout true; ceres::Solver::Summary summary; ceres::Solve(options, problem, summary); std::cout summary.BriefReport() std::endl; std::cout m m , c c std::endl; return 0; }这段代码里AutoDiffCostFunctionExponentialResidual, 1, 1, 1的三个数字分别表示残差维度 1、第一个参数块维度 1、第二个参数块维度 1。DENSE_QR适合变量少的情况如果变量超过几百个换成SPARSE_NORMAL_CHOLESKY并确保 CHOLMOD 链接正确。编译运行后如果输出m接近 0.3、c接近 0.1说明 ceres 配置成功。如果报链接错误先检查附加依赖项里 lib 名字对不对如果运行时报 dll 缺失检查生成后事件有没有生效。4. 踩坑记录lib 和 dll 配置中最容易翻车的五个地方4.1 现象链接时报 LNK2019 无法解析的外部符号原因通常是附加依赖项里漏了某个 lib或者 debug/release 用混了。比如你在 Debug 配置下填了ceres.lib而不是ceres-debug.lib链接器能找到符号但运行时 STL 版本不匹配。解决先确认“附加库目录”指向的文件夹里确实有对应的 lib 文件再检查“附加依赖项”里的名字跟文件名完全一致。VS 的“链接器 → 输入”里可以写多行每行一个 lib不要用逗号分隔。4.2 现象程序启动时报“找不到 libgfortran-3.dll”原因是liblapack.dll依赖libgfortran-3.dll但后者没有被拷贝到 exe 目录。很多人只拷贝了liblapack.dll忽略了它背后的 Fortran 运行时。解决把libgfortran-3.dll跟liblapack.dll放在同一目录确保生成后事件的通配符*.dll能覆盖到它。如果还是报错用 Dependency Walker 或者dumpbin /dependents看一下liblapack.dll到底依赖哪些 dll。4.3 现象Debug 下能跑Release 下崩溃原因通常是 Release 配置下链接了 debug 版的 lib或者 debug 版 dll 被拷贝到了 Release 输出目录。debug 版的 ceres 用了_ITERATOR_DEBUG_LEVEL2而 release 版是 0混用会导致堆结构不一致。解决严格按配置分离。在“附加库目录”里用$(Configuration)宏在生成后事件里也用$(Configuration)确保 Debug 只拿 debug 的文件Release 只拿 release 的文件。4.4 现象使用 CholmodSupport 时提示 cholmod 相关符号找不到原因是只链接了ceres.lib没有链接cholmod.lib。ceres 的 CholmodSupport 是一个独立模块它需要显式链接 CHOLMOD 的导入库。解决在附加依赖项里加上cholmod.lib并确保libcholmodd.dlldebug或对应的 release 版 dll 在输出目录。如果你不用稀疏求解可以在代码里避免包含ceres/ceres.h中的 CholmodSupport 头但通常没必要。4.5 现象换了一台机器后所有 dll 都找不到原因是 dll 没有跟 exe 放在一起而是依赖了开发机上的 PATH 环境变量。换机器后 PATH 不一样自然找不到。解决永远用生成后事件把 dll 拷贝到$(OutDir)发布时直接把整个输出目录打包。如果项目要求单文件发布可以考虑用静态库版本但那份资源是动态库静态库需要另外编译。5. 进阶用法用 CMake 管理 ceres 依赖和版本切换5.1 为什么我后来改用 CMake 而不是纯 VS 属性页纯 VS 属性页配置在单人单项目时没问题但一旦项目多起来、或者需要跟别人协作属性页的配置很难复用。CMake 可以把 ceres 的路径、lib 名字、dll 拷贝全部写成脚本换机器只需要改一个变量。下面是我常用的CMakeLists.txt片段cmake_minimum_required(VERSION 3.15) project(ceres_demo LANGUAGES CXX) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) # ceres 根目录换机器只改这一行 set(CERES_ROOT ${CMAKE_SOURCE_DIR}/third_party/ceres) # 根据构建类型选择 debug 或 release 目录 if(CMAKE_BUILD_TYPE STREQUAL Debug) set(CERES_LIB_DIR ${CERES_ROOT}/lib/Debug) set(CERES_BIN_DIR ${CERES_ROOT}/bin/Debug) set(CERES_LIB_NAME ceres-debug) else() set(CERES_LIB_DIR ${CERES_ROOT}/lib/Release) set(CERES_BIN_DIR ${CERES_ROOT}/bin/Release) set(CERES_LIB_NAME ceres) endif() # 头文件路径 include_directories(${CERES_ROOT}/include) # 查找 ceres 库 find_library(CERES_LIBRARY NAMES ${CERES_LIB_NAME} PATHS ${CERES_LIB_DIR} NO_DEFAULT_PATH ) find_library(CHOLMOD_LIBRARY NAMES cholmod PATHS ${CERES_LIB_DIR} NO_DEFAULT_PATH ) add_executable(ceres_demo src/main.cpp) target_link_libraries(ceres_demo ${CERES_LIBRARY} ${CHOLMOD_LIBRARY}) # 编译后自动拷贝 dll add_custom_command(TARGET ceres_demo POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CERES_BIN_DIR} $TARGET_FILE_DIR:ceres_demo COMMENT Copying ceres dlls to output directory )这段脚本的关键点是find_library用了NO_DEFAULT_PATH强制只在指定目录找避免找到系统里其他版本的 ceres。add_custom_command在每次编译后把 dll 目录整个拷贝到 exe 旁边比逐个文件拷贝更省心。5.2 怎么验证 dll 版本跟 lib 版本匹配有时候你拿到一份 lib 和 dll但不确定它们是不是同一次编译出来的。可以用dumpbin看导入表dumpbin /headers ceres-debug.dll | findstr machine dumpbin /headers ceres-debug.lib | findstr machine两条命令输出的 machine 类型应该一致都是x64。如果一个是x86一个是x64链接阶段就会报错。另外可以用dumpbin /exports ceres-debug.dll看导出的符号跟 lib 里的符号对比确认没有缺失。5.3 一个我踩过的坑ceres 版本跟 Eigen 版本不匹配ceres 对 Eigen 版本有要求太老的 Eigen 缺少某些模板特化编译会报一堆看不懂的模板错误。我一般用 Eigen 3.3.7 或 3.4.0这两个版本跟 ceres 1.14 和 2.x 都兼容。如果你换了 Eigen 版本后编译报错先看错误信息里有没有Eigen::internal相关的符号有的话大概率是版本问题。从那以后我每次配 ceres 环境都会先跑一遍最小验证代码确认DENSE_QR和SPARSE_NORMAL_CHOLESKY两种求解器都能正常工作再开始写业务代码。这个习惯帮我省了很多“以为是业务逻辑问题、其实是环境问题”的排查时间。希望帮到你。本文还有配套的精品资源点击获取
返回列表