简介:本资源为在Visual Studio 2013 64位平台下编译完成的Ceres Solver数值优化库,面向从事计算机视觉、机器人、天文与地球科学等领域非线性最小二乘问题求解的开发者,尤其适合仍在使用VS2013旧工具链、需要稳定64位依赖库的工程人员。压缩包共约2000个文件,整体348.67MB,涵盖c、h、cpp、cc等源码文件,lib、dll、pdb等编译产物,以及vcxproj、sln、filters等VS工程文件,另含cmake脚本、mat与mtx测试数据、png文档图片和txt说明,完整保留了Ceres及其Eigen、glog、gflags、protobuf等依赖的构建痕迹。已有390人学习下载。借助该包,读者可直接获得可链接的库文件与头文件目录,省去自行配置CMake与依赖路径的繁琐过程,并可通过示例工程理解成本函数、损失函数与求解器选项的用法,为非线性优化项目的搭建与排错提供现成参考。
1. 为什么 2024 年还有人要在 vs2013 x64 下编译 ceres
如果你手上维护的是一套工业视觉、机器人标定或者老款医疗影像设备的上位机,大概率会遇到这种局面:主程序锁死在 Visual Studio 2013,平台必须是 x64,而算法侧又需要 ceres 做非线性最小二乘。这时候你去官网拉最新版 ceres,CMake 一跑就是满屏红字,Eigen、glog、gflags 版本全对不上,编译器 C++ 标准也顶不住。vs2013_x64 平台编译的 ceres 库,说的就是把这套组合在旧工具链上跑通:用 VS2013 的 x64 交叉工具集,配一套能编译的 Eigen、glog、gflags,最后产出 ceres 的静态库和头文件,塞进现有工程直接链接。它解决的不是“最新特性”,而是“老项目不能动、算法还得升级”的刚需。适合谁?适合那些不能换 IDE、不能升 Windows SDK、但必须用 ceres 做 BA 或曲线拟合的一线开发者。下面我按自己踩过的顺序,把这条路走一遍。
2. 工具链选型:VS2013 x64 下 ceres 的依赖版本怎么锁
2.1 为什么版本必须锁死,不能随手拉 master
VS2013 的 MSVC 编译器版本是 18.00,对 C++11 的支持是“部分可用”:auto、lambda、右值引用基本能用,但 constexpr、变参模板、std::make_unique 这些要么没有,要么残缺。ceres 从 1.14 之后大量使用 C++14 特性,直接编译必然报错。所以第一件事不是写代码,而是把 ceres 版本压到 1.13 或 1.14.0,这两个版本对 C++11 的依赖还在可控范围。Eigen 同理,3.3.x 是安全区,3.4 开始有 C++14 的 alignment 和模板写法,VS2013 容易在 Eigen::Matrix 的初始化上翻车。glog 和 gflags 是 ceres 的日志与命令行依赖,glog 0.4.0 之后用了 C++14 的 std::enable_if_t,必须退到 0.3.5。gflags 用 2.2.2 即可,它本身对编译器要求不高。
这里有个血泪经验:不要试图用 vcpkg 或 conda 装好的库直接链接。那些预编译包大多是用 VS2015 以上工具集编的,ABI 不兼容,链接时会报 LNK2038 的 RuntimeLibrary 不匹配。正确做法是全部源码编译,且 RuntimeLibrary 统一选 /MT 或 /MD,整个工程保持一致。
2.2 用 CMake 生成 VS2013 x64 工程的具体命令
先确认你机器上装了 VS2013 和 CMake。CMake 版本不要太高,3.10 到 3.20 之间都行,再高会默认用新策略,生成器可能找不到 VS2013。打开命令行,进入 ceres 源码目录,执行:
mkdir build_x64 cd build_x64 cmake -G "Visual Studio 12 2013 Win64" ^ -DCMAKE_BUILD_TYPE=Release ^ -DEIGEN_INCLUDE_DIR=D:/libs/eigen-3.3.7 ^ -DGLOG_INCLUDE_DIR=D:/libs/glog-0.3.5/src ^ -DGLOG_LIBRARY=D:/libs/glog-0.3.5/build_x64/Release/glog.lib ^ -DGFLAGS_INCLUDE_DIR=D:/libs/gflags-2.2.2/src ^ -DGFLAGS_LIBRARY=D:/libs/gflags-2.2.2/build_x64/Release/gflags.lib ^ -DBUILD_TESTING=OFF ^ -DBUILD_EXAMPLES=OFF ^ -DBUILD_SHARED_LIBS=OFF ^ -DCMAKE_USE_PTHREADS=OFF ^ ..这段命令的关键参数说明:-G "Visual Studio 12 2013 Win64"是生成 64 位工程的核心,少了 Win64 就是 32 位,后面链接全错。CMAKE_BUILD_TYPE=Release在 VS 生成器下其实不生效,但保留它不影响,真正决定配置的是后面用 msbuild 时指定的 Configuration。EIGEN_INCLUDE_DIR指向 Eigen 根目录,不是 Eigen/src。GLOG_LIBRARY和GFLAGS_LIBRARY必须指向你事先编好的 x64 Release 版 lib 文件,Debug 版会带_d后缀,别搞混。BUILD_SHARED_LIBS=OFF强制静态库,避免运行时还要拷 dll。CMAKE_USE_PTHREADS=OFF在 Windows 下必须关,否则会去找 pthread 头文件。
执行完如果 CMake 输出里Eigen、glog、gflags都显示 found,就可以进入下一步。如果某个显示 not found,检查路径里有没有中文或空格,VS2013 的 CMake 对空格路径处理很烂。
2.3 编译 glog 和 gflags 的 x64 版本
ceres 依赖的 glog 和 gflags 必须先编好。以 glog 0.3.5 为例,同样用 CMake 生成 VS2013 x64 工程:
cd glog-0.3.5 mkdir build_x64 cd build_x64 cmake -G "Visual Studio 12 2013 Win64" ^ -DCMAKE_BUILD_TYPE=Release ^ -DWITH_GFLAGS=OFF ^ -DBUILD_TESTING=OFF ^ ..WITH_GFLAGS=OFF是因为 glog 自己不需要 gflags,关掉能少一堆依赖。生成后用 msbuild 编译:
msbuild glog.sln /p:Configuration=Release /p:Platform=x64编译完在Release目录下会得到glog.lib和glog.dll(如果没设BUILD_SHARED_LIBS=OFF)。我一般会加-DBUILD_SHARED_LIBS=OFF直接出静态库,省得后面拷 dll。gflags 同理,生成时加-DBUILD_SHARED_LIBS=OFF -DBUILD_TESTING=OFF,编译出gflags.lib。
注意:glog 0.3.5 在 VS2013 下有一个已知问题,src/windows/glog/logging.h里用了std::min和std::max,但没包含<algorithm>,会报min不是std成员。解决办法是在该头文件开头手动加#include <algorithm>,或者直接在工程属性里预定义NOMINMAX并确保包含顺序。这个坑我踩过两次,每次都要愣一下才想起来。
3. 编译 ceres 本体:从 CMake 配置到 msbuild 出库
3.1 生成工程后必须改的三个工程属性
CMake 生成 ceres.sln 后,先别急着编译。用 VS2013 打开解决方案,找到ceres工程,右键属性,检查三处:
第一,C/C++ -> 代码生成 -> 运行库,Release 下必须是/MT或/MD,且和 glog、gflags、Eigen 以及你主工程的设置完全一致。我习惯用/MT,静态链接 CRT,发布时少依赖。如果 glog 编的是/MD,这里就必须改成/MD,否则链接报LNK2038: 检测到“RuntimeLibrary”的不匹配项。
第二,C/C++ -> 语言 -> 禁用特定警告,加上4996和4267。VS2013 对sprintf、fopen这些函数会报安全警告,ceres 源码里大量使用,不屏蔽的话输出窗口没法看。
第三,C/C++ -> 预处理器 -> 预处理器定义,确认有_CRT_SECURE_NO_WARNINGS和NOMINMAX。NOMINMAX防止 Windows 头文件里的min/max宏和std::min/std::max冲突,这个在 ceres 的array_utils里很容易触发。
改完这三处,再检查链接器 -> 输入 -> 附加依赖项,确保glog.lib和gflags.lib的路径正确。如果 CMake 配置时路径给对了,这里会自动填好。
3.2 用 msbuild 命令行编译并验证产物
属性改好后,可以用命令行编译,方便脚本化:
msbuild ceres.sln /p:Configuration=Release /p:Platform=x64 /m/m是多核并行编译,ceres 源码文件不少,能快一点。编译过程中如果报错,最常见的是两类:一类是Eigen的模板实例化错误,比如Eigen::internal::plain_array相关,这通常是 Eigen 版本高了,换 3.3.7 能解决;另一类是glog的CHECK宏展开问题,检查GLOG_INCLUDE_DIR是否指向了src目录而不是src/windows。
编译成功后,在build_x64/Release下会得到ceres.lib(静态库)或ceres.dll+ceres.lib(动态库)。同时include目录下会有 ceres 的头文件。验证产物是否可用,写一个最小测试:
#include <ceres/ceres.h> #include <iostream> 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() { double x = 5.0; ceres::Problem problem; ceres::CostFunction* cost_function = new ceres::AutoDiffCostFunction<CostFunctor, 1, 1>(new CostFunctor); problem.AddResidualBlock(cost_function, NULL, &x); 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() << std::endl; std::cout << "x = " << x << std::endl; return 0; }这段代码做的是最简单的常数拟合:让x逼近 10。编译时链接ceres.lib、glog.lib、gflags.lib,如果运行输出x = 10,说明库完全可用。注意AutoDiffCostFunction在 VS2013 下对 lambda 支持不好,必须用仿函数,这是 C++11 部分支持的限制。
3.3 静态库和动态库怎么选,以及 /MT 与 /MD 的取舍
静态库ceres.lib体积大,但部署简单,不需要带 dll。动态库ceres.dll体积小,但运行时必须把 dll 放到 exe 同目录或系统路径。对于老项目,我强烈建议静态库,因为 VS2013 时代的工程往往没有完善的部署脚本,少一个 dll 就少一个“找不到模块”的报错。
/MT和/MD的取舍更关键。/MT把 CRT 静态链接进每个 lib,最终 exe 不依赖msvcr120.dll,但如果你主工程用了/MD,链接时就会冲突。判断方法:看你主工程的C/C++ -> 代码生成 -> 运行库设置,它是什么,ceres 和 glog、gflags 就必须是什么。没有“哪个更好”,只有“必须一致”。我一般新项目用/MT,老项目跟着它原来的设置走。
4. 避坑与排查:VS2013 x64 编译 ceres 的 5 个典型翻车现场
4.1 现象:CMake 报 “Could NOT find Eigen3”
原因:EIGEN_INCLUDE_DIR指向了Eigen/src或者Eigen/Eigen,而 CMake 的FindEigen3.cmake需要的是包含Eigen/Core的根目录。解决:把路径改成D:/libs/eigen-3.3.7,确保该目录下直接有Eigen文件夹,里面是Core、Dense等头文件。
4.2 现象:编译 glog 时min不是std成员
原因:glog 0.3.5 的logging.h在 Windows 下没有包含<algorithm>,而 VS2013 的<windows.h>可能间接包含了min/max宏。解决:在logging.h开头加#include <algorithm>,并在工程预处理器里定义NOMINMAX。如果不想改源码,可以在包含glog/logging.h之前先包含<algorithm>并定义NOMINMAX。
4.3 现象:链接 ceres 时大量LNK2038不匹配
原因:ceres、glog、gflags 和主工程的RuntimeLibrary设置不一致,比如 ceres 是/MT,主工程是/MD。解决:统一所有库和主工程的运行库设置。检查每个工程的C/C++ -> 代码生成 -> 运行库,Release 下要么全是/MT,要么全是/MD。Debug 下对应/MTd和/MDd,不要混用 Release 和 Debug 的 lib。
4.4 现象:运行时报 “无法定位程序输入点” 或直接崩溃
原因:动态库版本不匹配,或者 Debug 版 exe 链接了 Release 版 lib。解决:确认 exe 和所有 dll 的编译配置一致。如果用的是静态库,检查是否误链接了glog_d.lib之类的 Debug 版。用 Dependency Walker 或 VS 的“模块”窗口看实际加载的 dll 版本。
4.5 现象:AutoDiffCostFunction编译报错,提示模板参数不对
原因:VS2013 对变参模板支持不完整,ceres 的AutoDiffCostFunction在 1.14 里用了可变参数模板来推导残差维度。解决:退到 ceres 1.13.0,或者改用DynamicAutoDiffCostFunction手动指定维度。另一个办法是显式写出模板参数,比如AutoDiffCostFunction<CostFunctor, 1, 1>而不是依赖自动推导。如果仿函数里用了 lambda,改成结构体仿函数。
5. 进阶技巧:把 ceres 集成进现有 VS2013 工程的属性表做法
5.1 用属性表统一管理包含目录和库目录
每次新建工程都手动配路径太蠢。我一般做一个ceres_x64.props属性表,在 VS2013 里通过“视图 -> 属性管理器”添加到所有需要 ceres 的工程。属性表内容如下:
<?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ImportGroup Label="PropertySheets" /> <PropertyGroup Label="UserMacros" /> <PropertyGroup> <IncludePath>D:\libs\ceres\include;D:\libs\eigen-3.3.7;D:\libs\glog-0.3.5\src;D:\libs\gflags-2.2.2\src;$(IncludePath)</IncludePath> <LibraryPath>D:\libs\ceres\build_x64\Release;D:\libs\glog-0.3.5\build_x64\Release;D:\libs\gflags-2.2.2\build_x64\Release;$(LibraryPath)</LibraryPath> </PropertyGroup> <ItemDefinitionGroup> <ClCompile> <PreprocessorDefinitions>NOMINMAX;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalOptions>/wd4996 /wd4267 %(AdditionalOptions)</AdditionalOptions> </ClCompile> <Link> <AdditionalDependencies>ceres.lib;glog.lib;gflags.lib;%(AdditionalDependencies)</AdditionalDependencies> </Link> </ItemDefinitionGroup> </Project>这个属性表把包含路径、库路径、预处理器定义、警告屏蔽和附加依赖一次性配好。注意ToolsVersion="12.0"对应 VS2013,别写成 14.0。IncludePath和LibraryPath用分号分隔,路径里不要有中文。AdditionalOptions里的/wd4996和/wd4267是屏蔽警告,和之前工程属性里设的效果一样。
5.2 验证属性表是否生效的检查清单
添加属性表后,新建一个空工程,写上面那个最小测试代码,编译链接。如果报错找不到头文件,检查属性表是否真的被导入(属性管理器里对应工程下有没有这个 props)。如果报链接错误,检查LibraryPath是否指向了正确的Release目录,以及AdditionalDependencies里的 lib 名字是否和实际文件名一致。还有一个隐蔽问题:属性表里的IncludePath会覆盖工程原有的包含路径,如果主工程还有其他第三方库,记得把$(IncludePath)放在末尾,保留原有路径。
5.3 一个我常用来快速判断库是否可用的习惯
每次编译完 ceres,我不会直接扔进主工程,而是先在一个独立的小工程里跑上面那个x = 10的测试。这个习惯帮我省了很多时间:如果小工程都跑不通,主工程里报的错只会更乱。测试通过后,我会把ceres.lib、glog.lib、gflags.lib和对应的头文件按目录结构拷到一个third_party/ceres_x64_vs2013文件夹里,连同属性表一起归档。下次换机器或者给同事,直接拷这个文件夹,属性表一挂就能用。VS2013 的 x64 工具链虽然老,但只要版本锁死、运行库统一、属性表配好,ceres 跑起来和在新 IDE 里没本质区别。希望帮到你。
本文还有配套的精品资源,点击获取