免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

Ceres Solver VS2019预编译库配置:依赖链解析与避坑指南

Ceres Solver VS2019预编译库配置:依赖链解析与避坑指南 简介面向Windows平台上配置Ceres求解器的开发者这份资源是使用Visual Studio 2019预先编译好的库文件集合同时提供debug与release版本可直接配合配套教程完成环境搭建免去从源码编译的繁琐流程。包内共432个文件以328个头文件、40个dll动态库和35个lib静态库为主体头文件提供调用接口动态库用于运行时链接静态库则支持更便捷的嵌入方式同时覆盖Eigen、Cholmod等关键依赖模块整体压缩包约17.35MB目录结构清晰便于按需检索。其中debug版附带调试符号便于定位异常release版则针对性能优化适配发布环境。已有824人学习下载适合从事视觉SLAM、三维重建、非线性优化等项目的工程师和研究人员。获取后只需在项目中正确配置包含目录与库目录即可快速获得可调试、可发布的Ceres链接环境免去自行编译版本不匹配的困扰大幅缩短集成时间并降低入门门槛。1. 配置 Ceres 的第一站这套 VS2019 预编译库能省多少事干视觉 SLAM 或者相机标定的开发者第一次在 Windows 上接触 Ceres Solver 时十有八九会卡在依赖库编译上。Eigen 还好说头文件拖过来就能用glog、gflags、suitesparse、lapack 这些库从源码编译稍不留神就是一连串“无法解析的外部符号”报错能看花眼。这套资源等于把 VS2019 环境下编译好的 lib 文件、dll 文件直接备好解压后按照网上那篇配置教程把包含目录和库目录填进工程就能在 VS2019 平台上把 Ceres 配好。包内同时覆盖 Debug 与 Release 两套文件适合已经拿到 Ceres 源码或者准备做非线性优化但不想在 Windows 上消耗几个晚上逐库编译的从业者。不管你要做相机标定、SLAM 后端优化还是曲线拟合先把环境跑通再谈算法本身。2. Ceres 的 Windows 依赖链这套预编译包到底装了什么2.1 为什么自己源码编译容易翻车Ceres Solver 本身是一个非线性最小二乘求解库核心算法和接口设计得相当干净但它不像某些单文件库那样可以直接拖进工程。CMake 配置阶段就会要求你提供 Eigen、glog、gflags以及可选的 suitesparse、lapack。这意味着一行 Ceres 代码都还没写就得先为这一串依赖库准备环境。Eigen 是模板库头文件拷进包含目录就能用属于最省心的一个glog 和 gflags 负责日志和命令行解析要编成 lib 才能链suitesparse 里的 CHOLMOD 负责稀疏 Cholesky 分解是 Ceres 处理大规模稀疏问题的关键依赖lapack/blas 处理稠密线性代数。这四个依赖里任何一个版本没对上Ceres 的 CMake 探测阶段就会直接提示找不到对应包。在 Windows 上问题会更具体。大部分开发者习惯用 VS2019 编译 C 工程但这些数值库的源码往往有 Fortran 代码需要额外安装 Fortran 编译器。常见的情况是 CMake 找不到合适的 BLAS 实现或者 Fortran 运行时缺某个 dll折腾一晚上连 Ceres 的 configure 都没跑过去。预编译包把这条依赖链直接趟平了。拿到手以后不需要关心 lapack 是怎么编出来的也不需要手工安装 gfortran 运行时只要头文件路径指对、lib 按 Debug/Release 分开链、dll 放到运行目录Ceres 就能被工程正常调用。所以我向来建议第一次用 Ceres 的开发者先跑预编译包别一上来就挑战源码编译。2.2 解压后你会看到的文件lib、dll 与模块符号打开压缩包一堆带 d 后缀或者不带 d 后缀的 dll很容易让人发怵。我一般会按“本体库、线性代数依赖、运行时依赖”三类拆开看。文件/模块标识归属实际作用ceres-debug.dllCeres 本体调试版非线性优化求解器主运行库liblapack.dllLAPACK 依赖稠密线性代数底层计算libcholmodd.dllCHOLMOD 调试版稀疏矩阵 Cholesky 分解libgfortran-3.dllFortran 运行时数值编译产物运行时的底层支撑Cholesky/CholmodSupport/Core/DenseEigen 模块标识表示预编译时启用了对应矩阵模块需要留意的是列表里的 Cholesky、CholmodSupport、Core、Dense 这几个名字不是单独的可执行文件而是包内文件或符号中出现的模块标识。看到它们等于给了你一个信息预编译时 Ceres 已经启用了 Eigen 的稀疏 Cholesky 和稠密模块在调用 SPARSE_NORMAL_CHOLESKY 这类求解器时不会遇到“模块没编译进来”的问题。为什么 lapack 和 cholmod 会同时出现在包里因为 Ceres 的线性求解器分两大流派稠密矩阵走 DENSE_QR / DENSE_SCHUR底层落到 LAPACK稀疏大规模问题走 SPARSE_NORMAL_CHOLESKY 或 SPARSE_SCHUR底层落到 CHOLMOD。预编译包同时打包了 liblapack.dll 和 libcholmodd.dll说明编译时两侧的 support 都开了。实际使用中如果你只做小型曲线拟合CHOLMOD 不会被用到一旦切到 BA 问题Ceres 会自动加载稀疏求解器这时缺了 CHOLMOD 就会运行期崩溃不是编译期能发现的。配置时我把这些模块标识理解为“包含目录里必须有对应 Eigen 头文件”的信号。如果解压包里带了 Eigen 的头文件目录直接指过去如果没带就从自己工程现有的 Eigen 拷贝一份版本尽量和编译时一致。Eigen 3.3 和 3.4 的接口有小幅调整虽然不至于链接失败但自动微分在个别模板实例化上可能报编译错误。还有一个容易忽略的细节lib 和 dll 是配合使用的。链接期编译器通过 .lib 里的导入符号确认函数存在运行期系统加载 .dll 解析具体地址。所以只配置了“库目录”而没把 dll 放到 exe 身边程序会在启动时崩溃后面第 4 章专门说这个搬运问题。2.3 Debug 和 Release 差异不只是后缀名预编译包里调试版产品习惯在文件名后加小写字母 d 来区分比如 ceres-debug.dll 对应 ceres-debug.lib。很多新手以为这只是改名实际差异在运行库设置。VS2019 的 C/C 运行库分 /MD多线程 DLL、/MDd多线程调试 DLL、/MT多线程静态、/MTd多线程调试静态几种。预编译库编译时用的如果是 /MDd你的工程在 Debug 配置下也必须保持一致否则会在链接阶段看到 LNK2038 mismatch detected for RuntimeLibrary 的报错。Release 同理需要切到 /MD。这也是为什么我坚持把 Debug 和 Release 的库路径分开设置而不是在属性面板里写死一个路径。后面会演示用 $(Configuration) 宏动态切换这也是避免混用最省心的办法。提示有些压缩包解压后根本没按 Debug/Release 分子目录文件名又只有 ceres-debug.lib 这种后缀能区分。拿到这种包第一件事就是自己动手补出两个目录把文件对号入座。3. VS2019 工程配置全流程从属性面板到第一个求解器3.1 把压缩包整理成 third_party 标准结构配置过程真正花时间的不是填路径而是把目录结构理清楚。我习惯把解压内容整理成下面这个形态放在解决方案目录下的 third_party/ceres 里。third_party/ceres/ ├── include/ │ ├── ceres/ │ ├── eigen3/ │ ├── glog/ │ └── gflags/ ├── lib/ │ ├── Debug/ │ │ └── ceres-debug.lib │ └── Release/ │ └── ceres.lib └── bin/ ├── Debug/ │ ├── ceres-debug.dll │ ├── libcholmodd.dll │ └── liblapack.dll └── Release/ ├── ceres.dll └── ...这里的文件名只是示意具体以压缩包内实际文件为准。Release 下如果也带了 libgfortran-3.dll就一并放进 bin/Release。之所以把 include、lib、bin 拆开是因为 VS 属性面板只认绝对或相对路径不支持通配符目录一旦散落在桌面或下载文件夹里路径极其不稳定。用 $(SolutionDir) 引用解决方案目录是最省事的方式。这样工程文件只要放在解决方案的子目录里换电脑或者同步到仓库路径依然能解析。我见过不少人直接把解压路径填到“附加包含目录”编译没问题但一旦把工程发给别人立刻满屏找不到头文件。3.2 建工程与改属性面板在 VS2019 里新建一个空 C 控制台工程然后在“属性管理器”里为 Debug|x64 和 Release|x64 分别打开属性页。这里给出我常用的一套属性面板配置直接粘到 .vcxproj 或者属性表里都可以。PropertyGroup LabelUserMacros CeresRoot$(SolutionDir)third_party\ceres/CeresRoot /PropertyGroup PropertyGroup Condition$(Configuration)Debug IncludePath$(CeresRoot)\include;$(CeresRoot)\include\eigen3;$(IncludePath)/IncludePath LibraryPath$(CeresRoot)\lib\Debug;$(LibraryPath)/LibraryPath /PropertyGroup PropertyGroup Condition$(Configuration)Release IncludePath$(CeresRoot)\include;$(CeresRoot)\include\eigen3;$(IncludePath)/IncludePath LibraryPath$(CeresRoot)\lib\Release;$(LibraryPath)/LibraryPath /PropertyGroup这条配置逻辑很简单Debug 和 Release 条件属性分开写IncludePath 指向同一组头文件LibraryPath 指向各自版本的 lib 目录。include 和 include\eigen3 两条路径都要有因为 Ceres 头文件里写的是 #include ceres/ceres.h而 Eigen 的头文件被引用成 #include Eigen/Core包含目录需要落在 eigen3 这一层才能让尖括号形式命中。如果不用 XML在属性面板的“VC 目录”里手动填也是一样的结果。重点是 LibraryPath 一定要区分配置别图省事把 Debug 和 Release 都指到同一个目录。3.3 附加依赖项与预处理定义路径配好后链接器还不知道该链接哪个库。打开“链接器 → 输入 → 附加依赖项”按配置分别填写库名。Debug: ceres-debug.lib glogd.lib gflagsd.lib Release: ceres.lib glog.lib gflags.libglog 和 gflags 这两行不一定每个包都附带了对应 lib 文件。如果包内在 lib 目录里给出了就直接写上去如果没给而编译期需要用到 glog 符号链接器会明确报缺少 glog.lib这时候再想办法补一个对应版本即可。不要因为包内没带就跳过Ceres 的头文件里对日志库的依赖是写死的。同时在“C/C → 预处理器 → 预处理定义”里加一个宏GLOG_NO_ABBREVIATED_SEVERITIES。这个宏的作用是让 glog 使用完整的日志级别名避免它定义的 ERROR 宏和 Windows SDK 里的同名宏打架。不加的话编译时经常冒出几百行莫名其妙的宏展开报错排查起来非常浪费时间。3.4 第一个最小求解器验证链接是否正确配置完成后用一个最小例程确认整个环境是通的。这段代码拟合一条直线两个虚拟点分别是 (1, 3) 和 (2, 5)直线方程是 y a * x b。#include ceres/ceres.h #include glog/logging.h struct LineResidual { template typename T bool operator()(const T* const a, const T* const b, T* residual) const { residual[0] T(3.0) - a[0] * T(1.0) - b[0]; // 点 (1, 3) residual[1] T(5.0) - a[0] * T(2.0) - b[0]; // 点 (2, 5) return true; } }; int main() { google::InitGoogleLogging(ceres_demo); double a 0.0, b 0.0; ceres::Problem problem; ceres::CostFunction* cost new ceres::AutoDiffCostFunctionLineResidual, 2, 1, 1( new LineResidual()); problem.AddResidualBlock(cost, nullptr, a, b); ceres::Solver::Options options; options.linear_solver_type ceres::DENSE_QR; options.minimizer_progress_to_stdout true; ceres::Solver::Summary summary; ceres::Solve(options, problem, summary); std::cout summary.BriefReport() \n; std::cout a a , b b \n; return 0; }AutoDiffCostFunction 的模板参数依次是残差类型、残差块维度这里是 2因为一次算两个残差、第一个参数块维度、第二个参数块维度。AddResidualBlock 把残差块挂到 problem 上nullptr 表示不需要独立的损失函数。Options 里把线性求解器设为 DENSE_QR适合这种小规模的稠密问题换成 SPARSE_NORMAL_CHOLESKY 也可以但没必要。如果链接配置正确Debug 下运行输出里 a 应该接近 2b 接近 1迭代两三次就会收敛。看到这个结果说明 lib、dll、头文件三者已经对上了。4. 衔接教程细节预处理宏、日志库与 DLL 搬运规则4.1 预处理宏GLOG_NO_ABBREVIATED_SEVERITIES 必须写网上的教程配置步骤一般会把“预处理定义”这一栏写得比较简单有的甚至直接忽略。这里我把宏的部分单独拎出来因为它引发的编译错误非常隐蔽。如果工程里同时引用了 windows.h 或者某些第三方 SDK而 glog 又把 ERROR 这种短名字宏释放出来预处理阶段就会互相覆盖最终报错出现在一个完全无关的头文件里。宏名称作用典型场景GLOG_NO_ABBREVIATED_SEVERITIES停用 glog 的短日志级别宏工程中包含 Windows SDK 头文件时必备CERES_EXPORTCeres 符号导出声明一般不需要手动定义把这个宏加进去以后glog 的日志级别会变成 INFO、WARNING、ERROR、FATAL 这种完整写法调用方式变成 LOG(ERROR)而不是 LOG(ER)。对老用户来说可能不习惯但它能换掉一个非常隐蔽的冲突源。如果你在配置后仍然遇到宏相关报错可以在代码开头临时加一段 #ifdef 打印确认 ERROR 到底是被哪个头文件改写的不过大多数情况下加上 GLOG_NO_ABBREVIATED_SEVERITIES 就安静了。4.2 把 dll 搬到 exe 身边生成后事件写法链接过了、编译也过了第一次按 F5 却弹出“找不到 ceres-debug.dll”这是最常见的运行期事故。原因很简单lib 让链接器找到了函数地址dll 却还躺在 third_party/ceres/bin 里Windows 加载器只在 exe 目录和系统目录里找依赖。解决手段有两种。第一种是把 bin 目录里的 dll 全部拷到 $(OutDir)第二种是把 bin 目录加入系统 PATH。我推荐第一种干净且不污染系统环境。在工程属性“生成事件 → 生成后事件”里写一行命令即可xcopy /y /d $(CeresRoot)\bin\$(Configuration)\*.dll $(OutDir) if errorlevel 1 exit 1这里的 $(CeresRoot) 来自前面定义的宏$(Configuration) 自动展开成 Debug 或 Release。xcopy 的 /y 表示遇到同名文件直接覆盖/d 表示只复制比目标新的文件避免每次都全量拷贝拖慢编译速度。如果你用的是 CMake 工程等价的做法是在 CMakeLists.txt 里写 add_custom_command(TARGET ceres_demo POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ...)效果一样。目的永远是同一个让 exe 启动时能在自己的目录里找到所有依赖 dll。还有一点要注意如果输出路径带空格xcopy 两边的引号必须写全少一个引号就会出现“文件名、目录名或卷标语法不正确”的报错。4.3 Debug/Release 切换规则有些工程只配了一套路径切换解决方案配置后就出现“无法解析的外部符号”。原因往往是库目录或附加依赖项没有随配置切换。正确做法是让路径随 $(Configuration) 变化同时附加依赖项也按配置分开。Debug 链 ceres-debug.libRelease 链 ceres.lib不能反过来。文件后缀 d 不是装饰它对应了不同的导入库和运行期依赖。配置附加依赖项库目录Debugceres-debug.lib, glogd.lib, gflagsd.liblib\DebugReleaseceres.lib, glog.lib, gflags.liblib\Release如果你用的是我前面给的 XML 属性配置这些都已经按条件分开。手动配置的话每次切换配置后检查一下“链接器 → 输入”确认没有同时出现 debug 和 release 两类库名。另一个常见习惯是把两个 lib 全写上去让链接器自己挑这在某些库上是可行的在 Ceres 上不建议因为 debug 和 release 的 CRT 依赖不同混写大概率触发 LNK2038。5. 避坑记录配置 Ceres 过程中我踩过的五个地雷5.1 链接报“无法解析的外部符号”名字还怪怪的现象代码编译全过链接阶段突然报 unresolved external symbol符号名里有时带 d 后缀有时不带。原因Debug 工程链了 Release 库或者反过来。Ceres 的调试版导入库符号和 Release 版本不是同一套混用必然找不到。解决去“链接器 → 输入 → 附加依赖项”确认当前配置对应的是 ceres-debug.lib 还是 ceres.lib。Debug 只留前者Release 只留后者。改完重新生成问题基本消失。如果还报就把“生成”改成“重新生成”让链接器把旧的 .obj 缓存清掉再试。5.2 运行时报“找不到 libgfortran-3.dll”现象exe 编译成功双击运行弹窗提示缺 libgfortran-3.dll点确定后程序退出。原因Ceres 的数值依赖链里有 Fortran 编译产物运行时需要 libgfortran-3.dll。这个 dll 通常已经躺在压缩包的 bin 目录里只是没被复制到 exe 目录。解决不要手忙脚乱去下载什么运行库先把包内所有 dll 一次性复制到输出目录。用 4.2 的生成后事件以后每次编译自动带上。复制完以后可以用工具打开 exe 看一眼依赖树确认除了系统 dll 之外剩下几个都来自 bin 目录。5.3 LNK2038 mismatch detected for RuntimeLibrary现象链接阶段报 LNK2038提示 MDd_DynamicRelease 和 MTd_StaticRelease 不匹配。原因VS 工程的“运行时库”选成了“多线程 (/MT)”而预编译 Ceres 库是用“多线程调试 DLL (/MDd)”编译的。两者使用的 CRT 不同链接器直接拒绝混用。解决打开工程属性C/C → 代码生成 → 运行时库Debug 选“多线程调试 DLL (/MDd)”Release 选“多线程 DLL (/MD)”。这个设置是整个配置里最容易忽略的一项。如果你在同一个解决方案里还有静态库工程记得同步修改否则静态库和 exe 之间又会出现同样的 mismatch。5.4 Eigen 的 unsupported 头文件找不到现象编译 Ceres 自带头文件时报错 Cannot open include file: unsupported/Eigen/MatrixFunctions或者类似路径。原因包含目录只加到了 eigen3/Eigen 这一层而 Eigen 的 unsupported 目录在 eigen3/unsupported 下。Ceres 的矩阵函数实现会引用 unsupported 子目录里的头文件路径不完整就找不到。解决在包含目录里加 $(CeresRoot)\include\eigen3 这一层而不是 eigen3\Eigen。这样 Eigen/Core 和 unsupported/Eigen/MatrixFunctions 两种写法都能命中。这个坑很容易被忽略因为项目自身的代码可能只用到了 Eigen/Core编译不报错直到 Ceres 的某个内部头文件被实例化才炸出来。5.5 新工程能跑旧工程一换就报错现象同一个 lib、同一套 dll在新建的测试工程里一切正常挪到旧工程里却出现各种宏冲突和头文件找不到。原因旧工程可能设置了全局预处理定义或者用了不同的平台工具集。属性管理器里的“继承值”被覆盖导致 include 路径失效。解决把 Ceres 相关配置固化成属性表在属性管理器里同时加载到新老工程。属性表的优先级高于工程设置加一次以后整条配置链保持一致不用每个工程都重新手工填一遍。从那以后我再也不相信“手动配一次就行”这种说法所有第三方库都走属性表。6. 验活用一段最小曲线拟合把库跑通并确认版本边界6.1 先跑最小例程再谈算法配置完 Ceres 以后不要急着把 SLAM 后端或者标定算法搬进工程。先拿第 3.4 节那段最小例程分别在 Debug 和 Release 下各跑一次确认两件事两边都能编译链接两边的输出里 a 和 b 都收敛到 2 和 1。Debug 和 Release 的迭代次数、浮点舍入结果可能略有差异但最终结果应当一致。如果 Release 下正常而 Debug 下报错优先检查运行时库设置是不是又在 /MD 和 /MDd 之间摇摆如果 Debug 下正常而 Release 下无法解析优先检查附加依赖项是不是还残留着 debug 库名。6.2 用 dumpbin 检查 dll 依赖是否齐备最小例程跑通以后我习惯再用工具确认一遍依赖链完整。打开 VS2019 的“开发者命令提示符”进入输出目录执行dumpbin /dependents ceres_demo.exe输出会列出 ceres-debug.dll、liblapack.dll、libgfortran-3.dll 等运行期依赖文件。和包内 bin 目录逐一比对如果有某个 dll 没出现在 exe 目录里程序在其他机器上运行时就会缺东西。检查项预期结果Debug 链接库ceres-debug.libRelease 链接库ceres.libdll 完整性exe 目录里有 ceres、lapack、gfortran 等全部运行库预处理定义包含 GLOG_NO_ABBREVIATED_SEVERITIES运行时库Debug 为 /MDdRelease 为 /MD整套流程走完后Ceres 在 VS2019 下的配置才算真正收尾。从那以后我每下一次预编译包第一件事都是先跑通最小例程再动上层逻辑。这样做的好处是把“库的问题”和“算法的问题”彻底切开后面再报错基本可以直接定位到自己的代码。希望这些细节能帮到你。本文还有配套的精品资源点击获取
返回列表