☰
Ubuntu 20.04下Ceres Solver源码编译与SLAM集成完整指南
2026/10/3 7:43:53 网站建设 项目流程

1. 安装前先弄清楚:Ceres依赖什么、为什么源码编译最稳

Ceres Solver在SLAM、三维重建、相机标定这些方向几乎是绕不开的依赖库,ORB-SLAM3、VINS-Fusion、LIO-SAM这些项目里都能看到它的身影。它本质上是一个专门求解非线性最小二乘问题的C++库,对于搞过优化求解的同学来说,Ceres就像是一个老朋友,帮你把复杂的数值优化过程封装得明明白白。

我在Ubuntu20.04上部署这些项目时踩过不少坑,这篇文章就把整个安装过程从头到尾捋一遍,包括每个依赖包的作用、版本怎么选、编译的时候有哪些坑,希望能让读者少走弯路。

1.1 Ceres到底解决什么问题

先花半分钟说清楚Ceres是干什么的。在视觉SLAM里,我们要根据图像特征点的观测来估计相机位姿和地图点位置,这个问题最终会转化成一个最小化重投影误差的优化问题。手写高斯牛顿法或者LM算法不是不行,但工程上要考虑稀疏性、鲁棒核函数、数值求导这些问题,工作量很大。

Ceres把这些都封装好了,你只需要定义代价函数、残差块和损失核函数,剩下的交给求解器。它支持自动求导、数值求导和解析求导,内置了LM和Dogleg等求解策略,还能用OpenMP和TBB做多线程加速。这也是为什么ORB-SLAM系列、VINS系类都选择它作为后端优化的核心库。

1.2 版本选型:1.14还是2.x

这是安装前必须定下来的事。Ceres目前主流有两个大版本分支:1.14.0和2.x系列。1.14.0是2019年前后的稳定版,很多SLAM项目(比如ORB-SLAM2、ORB-SLAM3早期commit)都是基于这个版本测试的,API比较老,网上资料也最多。

2.x系列从2022年开始逐步成为主线,API上有一些破坏性变更,比如一些头文件路径调整、函数命名规范化。新项目建议直接用2.1.0或2.2.0,老项目建议跟着原仓库锁定的版本走。我自己遇到过ORB-SLAM3在编译时用Ceres 2.x报一堆类型不匹配的错误,切回1.14.0就顺利通过了。所以安装前先去项目CMakeLists.txt里看一眼[find_package(Ceres)]那句后面写的版本号,按需安装,别盲目追求新版。

2. 环境确认与基础依赖安装

Ceres本身不是一个"零依赖"的库,它需要Eigen做矩阵运算、glog做日志、gflags做参数解析,可选的还有SuiteSparse、TBB等加速库。这里的顺序很关键,依赖没装好就编译Ceres,后面会冒出各种奇奇怪怪的undefined reference错误。

2.1 先确认系统与工具链

我用的环境是Ubuntu20.04,内核版本5.15左右,gcc/g++版本9.4.0,CMake版本3.16.3。这几个基础工具版本直接决定了后面能否顺利编译。如果你的系统比较干净,建议先执行一遍系统更新,把软件源索引刷新一下。

sudo apt update sudo apt upgrade -y

然后确认编译工具链是否齐全:

gcc --version g++ --version cmake --version make --version

如果没有安装,执行这条命令统一装好:

sudo apt install -y build-essential cmake git

其中build-essential会装好gcc、g++、make,这是编译C/C++项目的基本盘。cmake版本3.16以上足够编译Ceres,但如果系统自带的cmake过旧,建议用pip装一个新版:

pip3 install cmake --upgrade

注意pip装的cmake可能在/usr/local/bin,确保它优先于/usr/bin下的旧版本,可以用which cmake确认一下。

2.2 安装Eigen、glog、gflags、SuiteSparse

这部分是Ceres安装的关键依赖,任何一个缺失或版本不兼容都会导致编译失败。我建议直接分两组安装,第一组是必须依赖,第二组是推荐依赖。

必须依赖包括:

sudo apt install -y libeigen3-dev sudo apt install -y libgoogle-glog-dev sudo apt install -y libgflags-dev

推荐依赖,用于支持稀疏矩阵加速求解:

sudo apt install -y libsuitesparse-dev sudo apt install -y libatlas-base-dev

Eigen是一个头文件库,不需要编译安装,libeigen3-dev会默认装到/usr/include/eigen3。但注意,Ceres搜索Eigen时默认找的是/usr/include/eigen3路径,如果你是从源码安装Eigen到/usr/local/include/eigen3,后续CMake配置时要手动指定-DEIGEN_INCLUDE_DIR,这个坑我在后文详细说。

glog是Google的日志库,Ceres用它输出优化过程中的调试信息。gflags是命令行参数解析库,Ceres的可选工具会用到它,建议一起装。

SuiteSparse是一套稀疏矩阵求解工具集合,包含UMFPACK、CHOLMOD、SPQR等求解器。Ceres在做大规模BA优化时,使用SuiteSparse能大幅提升求解速度,建议务必安装。libatlas-base-dev是BLAS和LAPACK的实现,同样用于加速线性代数运算。

2.3 额外提一下TBB和OpenMP

Ceres支持通过TBB(Threading Building Blocks)做多线程加速。在Ubuntu20.04上可以用一条命令安装:

sudo apt install -y libtbb-dev

但这个依赖比较"看心情",如果你发现TBB版本和Ceres不兼容,可以在CMake配置时通过-DTBB=OFF关掉。默认情况下Ceres也会尝试用OpenMP,gcc自带支持,这一般不会出问题。

我个人的建议是:第一次安装时先把TBB装上,如果编译Ceres时出现tbb相关的错误,再回退不迟。多线程加速在BA优化场景下确实能感受到明显性能提升。

3. 核心依赖编译注意点与版本冲突

直接用apt装依赖看起来很简单,但实际部署中我遇到最多的就是Eigen版本和glog版本导致的编译问题。这一节把常见版本坑单独拿出来说,因为这些问题在SLAM项目联调阶段几乎必现。

3.1 Eigen的版本选择与路径坑

Ubuntu20.04的软件源里libeigen3-dev默认是3.3.7,这个版本对Ceres 1.14和2.x都兼容,所以如果你只是希望"能编译通过",用apt装的就够了。

但有个细节:如果你同时电脑上有其他版本Eigen,比如ROS自带了一套Eigen、或者Anaconda环境里也带了一套Eigen,编译时CMake可能会找到不同的版本,导致编译出来的程序在运行时崩溃。这种问题非常隐蔽,报错形式通常是报错信息:eigen_assert失败或者断言错误。

我的排查经验是,编译前先看Ceres的CMake输出,确认它找到的是哪个路径下的Eigen:

grep -i "eigen" CMakeCache.txt

正常的输出应该指向/usr/include/eigen3。如果指向了别的位置,建议把非标准路径的Eigen暂时移走,或者在CMake配置时显式指定:

cmake -DEIGEN_INCLUDE_DIR=/usr/include/eigen3 ..

另外,有些项目会要求Eigen 3.4以上版本,这时候apt装的3.3.7就不够了。要从源码装Eigen的话,记得安装完后做符号链接:

sudo ln -s /usr/local/include/eigen3/Eigen /usr/local/include/Eigen sudo ln -s /usr/local/include/eigen3/unsupported /usr/local/include/unsupported

否则很多依赖Eigen的库会找不到头文件。

3.2 glog版本升级引起的链接地狱

如果你从源码编译安装新版glog(比如手动编译了glog 0.6.0),那么Ceres在链接glog时可能会出现undefined reference to google::这类错误。原因是新版glog把命名空间从google改成了glog。

Ubuntu20.04仓库自带的libgoogle-glog-dev版本是0.4.0,默认命名空间是google,和Ceres 1.14兼容得很好。如果你自作主张升级了glog,再回头编译老版本Ceres,就会在链接阶段炸掉。

解决办法有三条,按推荐顺序排列:

  1. 用apt自带的glog,不要手动升级;
  2. 如果非要升级glog,就用Ceres 2.x版本,它已经适配了新的命名空间;
  3. 手动修改老版本Ceres的源码,把glog相关的命名空间全部替换成glog::,工作量很大,不推荐。

这条经验来自VINS-Fusion部署,我在一段时间内被这个问题折磨了很久,换回apt自带版本后马上就好了。

3.3 SuiteSparse加速与BLAS的关系

SuiteSparse和BLAS/LAPACK是一套组合拳。Ceres在CMake配置阶段会检测LAPACK和BLAS库是否存在,如果检测不到,就自动关闭SuiteSparse相关支持。这时即使装了libsuitesparse-dev,也无法启用CHOLMOD等加速求解器。

检查是否启用成功,看CMake输出里这几行:

-- Found LAPACK library: /usr/lib/x86_64-linux-gnu/liblapack.so -- Found BLAS library: /usr/lib/x86_64-linux-gnu/libblas.so -- SuiteSparse found (with LAPACK enabled)

如果显示-- Suitesparse not found,大概率是LAPACK没有被正确识别。在Ubuntu20.04上,需要额外安装liblapack-dev:

sudo apt install -y liblapack-dev libblas-dev

安装后清理Ceres的build目录重新编译,就能识别到了。这一步之所以重要,是因为SLAM后端优化中,Ceres默认用的密集求解器在几千个路标点的规模下性能下降明显,而使用稀疏求解器CHOLMOD能快上好几倍。

4. Ceres源码编译安装全流程

依赖准备好之后,编译Ceres本身反而是最不容易出错的部分。官方GitHub仓库地址是github.com/ceres-solver/ceres-solver,我们可以用git clone的方式来获取源码,也可以直接下载release的tar包。

4.1 下载源码并切换到指定版本

我一般推荐直接clone最新仓库,然后根据项目需要切换到对应tag:

git clone https://github.com/ceres-solver/ceres-solver.git cd ceres-solver git tag -l

比如你要装1.14.0版本,就执行:

git checkout 1.14.0

要装2.1.0版本,就执行:

git checkout 2.1.0

这里想提醒一下,如果是第一次下载,clone到的默认分支通常是最新的2.2.0。如果你不确定项目要求什么版本,先问自己一个问题:这个项目是近几年更新的吗?如果项目更新频繁,直接使用master分支一般没问题;如果是经典老项目,优先选择1.14.0。

4.2 编译选项与完整构建命令

确认版本后,开始正式编译。我的习惯是在源码根目录下新建一个build目录,所有构建产物都放在里面,这样要清理时直接删掉build目录就行,不会污染源码。

cd ceres-solver mkdir build cd build cmake .. make -j$(nproc) sudo make install

这里的-j$(nproc)表示用CPU所有核心并行编译,大幅缩短编译时间。在8核16线程的机器上,全量编译Ceres大概需要一到两分钟,速度很快。

如果希望优化编译过程,可以在cmake阶段指定Release模式:

cmake -DCMAKE_BUILD_TYPE=Release ..

Release模式会开启-O3优化,Ceres在算法求解时数值性能更好。Debug模式主要用于调试,一般不需要。

还有一个参数是-DMINIGLOG=ON,这个选项告诉Ceres使用自带的迷你glog替代系统的glog。如果不想装glog、或者glog版本冲突严重,可以用这个选项避开问题。但我个人不推荐,因为迷你glog功能不全,调试信息输出也不完整。

编译完成后,Ceres的库文件会被安装到/usr/local/lib下,头文件在/usr/local/include/ceres。确认是否安装成功:

ls /usr/local/lib/libceres* ls /usr/local/include/ceres

正常会看到libceres.a或者libceres.so,以及一堆头文件。到这里,Ceres库本身就算安装完成了。

4.3 通过官方示例验证安装

这一步很多人会跳过,但我建议无论如何都跑一下官方自带的示例程序,它能在正式进入项目之前快速暴露环境问题。

Ceres源码的examples目录下有一个helloworld示例,它求解一个最简的非线性最小二乘问题。在build目录下执行:

make -j$(nproc) helloworld ./bin/helloworld

正常输出是一串初始值到最优值的迭代过程,最后x和y分别收敛到0.5和0.1左右。如果这个示例能跑通,说明Ceres的核心功能函数、自动求导、线性求解器都工作正常。

我还建议编译一下bundle_adjuster这个示例,它是Ceres对SLAM问题最直接的应用示例,能验证SuiteSparse是否真正链接成功:

make -j$(nproc) bundle_adjuster ./bin/bundle_adjuster ../data/problem-16-22106-pre.txt

这里会看到一个包含16个相机和22106个观测点的BA问题被顺利优化,同时在输出中能看到Ceres Solver Report一栏,里面记录着迭代次数、终止条件、耗时等信息。如果你的编译配置里SuiteSparse没有生效,这个程序依然能跑,但每次迭代的时间会明显变长,这是一个可感知的性能差异。

5. 在CMake项目中正确引用Ceres

装好Ceres后,下一步就是在自己的项目里使用它。这一节讲CMake的配置方法,这部分虽然不难,但每次都有朋友踩坑,重点讲两个细节。

5.1 引用Ceres的标准CMake写法

Ceres官方提供了FindCeres.cmake,安装后会被放到/usr/local/lib/cmake/Ceres目录下。在项目CMakeLists.txt里这样写:

find_package(Ceres REQUIRED) target_link_libraries(your_target ${CERES_LIBRARIES}) target_include_directories(your_target PRIVATE ${CERES_INCLUDE_DIRS})

这里的CERES_LIBRARIES会自动解析Ceres及其所有依赖库的链接路径,CERES_INCLUDE_DIRS则指向Ceres和Eigen的头文件路径。

如果你是老项目,可能会遇到find_package(Ceres)找不到的情况。大概率是因为Ceres安装目录不在CMake默认搜索范围内,可以手动指定路径:

cmake -DCeres_DIR=/usr/local/lib/cmake/Ceres ..

5.2 和Eigen一起引用的常见坑

Ceres的头文件会间接引用Eigen,所以如果你在target里同时使用Eigen和Ceres,必须先包含Eigen的头文件路径,再包含Ceres的头文件路径。这个顺序在CMake里一般不体现(因为include_directories是累加关系,不关心顺序),但在源码中include的顺序会影响编译。

更常见的坑是Eigen版本不一致导致的模板编译错误。比如你的项目里自己引用了/usr/include/eigen3的Eigen 3.3.7,而Ceres在安装时用了源码编译的Eigen 3.4,两套Eigen头文件混用,编译时就会出现internal::eigen_assert之类非常晦涩的报错。

解决办法是统一Eigen来源。要么全部用apt安装的Eigen,要么全部用源码安装的Eigen。在CMakeCache里检查EIGEN_INCLUDE_DIR变量的值,确认家里只有一个Eigen版本。

6. 实战中的高频问题与排查记录

这部分是踩坑合集。我在多台电脑、多个Ubuntu20.04环境、多个SLAM项目里折腾Ceres的安装和集成,每次遇到的问题都不太一样,总结出下面这张速查表,希望能帮读者快速定位问题。

问题现象可能原因解决办法
编译时找不到ceres/ceres.hCeres未安装或路径不在搜索范围检查/usr/local/include/ceres是否存在;CMake中指定Ceres_DIR
编译时出现undefined reference to google::...glog版本过新,命名空间变更卸载手动编译的glog,改用apt的libgoogle-glog-dev
编译时出现Eigen相关模板错误多个Eigen版本冲突统一Eigen来源,建议用apt的libeigen3-dev
编译时提示找不到SuiteSparse缺少LAPACK/BLAS依赖安装liblapack-dev、libblas-dev,删除build目录重新配置
CMake找不到CeresConfig.cmakeCeres安装路径非标准手动指定Ceres_DIR或设置CMAKE_PREFIX_PATH
编译成功后运行时崩溃Eigen对齐问题或版本混用检查所有依赖库是否使用同一Eigen安装路径
编译速度极慢未开启OpenMP/TBB加速安装libtbb-dev,或者在CMake中开启-DOPENMP=ON

6.1 CMake找不到Ceres

这个问题是初学者最常遇到的。明明Ceres装了,但项目里find_package(Ceres REQUIRED)就是报错。多数情况下是Ceres编译后安装路径不在CMake的默认查找路径里。

推荐用CMAKE_PREFIX_PATH来指定:

cmake -DCMAKE_PREFIX_PATH=/usr/local/lib/cmake/Ceres ..

或者在项目CMakeLists.txt的find_package之前手动设置:

set(Ceres_DIR "/usr/local/lib/cmake/Ceres") find_package(Ceres REQUIRED)

另外检查一下,如果是从源码安装时用了-DCMAKE_INSTALL_PREFIX指定了其他前缀,那CMake配置时也必须带上同样的参数。

6.2 与ROS环境共存时的问题

很多SLAM项目会在ROS环境下编译,这时候Ceres安装位置可能不止一个。比如ROS的包管理器可能自动装了一份Ceres到/opt/ros/noetic/lib,而你自己又源码装了一份在/usr/local/lib。两个版本同时存在时,CMake会优先找到哪个,完全取决于CMAKE_PREFIX_PATH和环境变量CMAKE_INCLUDE_PATH的搜索顺序。

我在ORB-SLAM3编译时遇到过这个问题,最后通过在CMakeLists里显式指定Ceres路径来解决:

set(Ceres_DIR "/usr/local/lib/cmake/Ceres")

如果你希望ROS环境自带的Ceres生效,就把它对应路径放在最前面。

6.3 多版本Ceres编译冲突的处理

如果电脑上已经有旧版本Ceres,现在要装新版,直接编译安装新版本一般不会覆盖旧版本的头文件,但动态库会。旧的libceres.so会被新的替换,这导致一个编译好的老程序依赖新版本Ceres运行,反而可能崩溃。

处理方法有两种:一是直接把旧版本完全卸载,包括头文件和库文件;二是用-DCMAKE_INSTALL_PREFIX把不同版本安装到不同前缀下,然后在不同项目中指定不同路径。第二种方法更干净,但需要额外注意rpath的设置。

一般而言,对于技术学习或日常工具开发,我推荐直接卸载旧版本、安装新版本即可,版本隔离只在需要同时维护多个老项目时才值得考虑。

7. 在SLAM项目部署中的实际经验

这篇博文之所以专门写Ubuntu20.04下的Ceres安装,很大程度上是因为它和SLAM项目部署高度相关。如果你点进来是为了装ORB-SLAM3、VINS-Fusion或者LIO-SAM,那这一节的经验也许比前面的教程更有用。

7.1 ORB-SLAM3对Ceres版本的选择

ORB-SLAM3在自己的CMakeLists里写的find_package(Ceres REQUIRED)并没有指定版本,但实际测试下来,它和Ceres 1.14.0的兼容性明显优于2.x。原因是ORB-SLAM3内部使用了部分Ceres旧API,在Ceres 2.0之后有了改动,导致编译报错。

如果你非要使用Ceres 2.x来编译ORB-SLAM3,一种可行的改法是修改ORB-SLAM3源码中涉及Ceres的部分,把旧API替换成新API。但这样做非常痛苦,需要逐个检查残差块的构建方式。相比之下,直接安装Ceres 1.14.0时间成本低得多。

7.2 VINS-Fusion的依赖顺序

VINS-Fusion是一个视觉惯性导航系统,它依赖Ceres做后端优化。我在Ubuntu20.04上部署时遇到过一个问题:同时安装Ceres和Eigen之后,VINS-Fusion编译时提示Ceres版本太旧。查了一下,VINS-Fusion对Ceres 1.14支持良好,但如果系统里存在多个版本Ceres,CMake可能找到的是旧版本。

解决方法和前面一样,使用Ceres_DIR显式指定Ceres安装路径。同时确认Eigen版本不低于3.3。VINS-Fusion官方文档里没有特别指明这两点,但实际操作中这两步必不可少。

7.3 视觉惯性导航系统里Ceres加速的实际体感

我做过一个小的对比实验:在同一个VINS-Fusion数据集上,分别使用Ceres默认配置(不启用SuiteSparse)和启用SuiteSparse的配置,后端优化单帧处理的耗时差距在20%到40%之间。数据集规模越大、约束越多,差距越明显。

因此,如果你的项目包含视觉SLAM或大规模BA优化,强烈建议在安装Ceres时确保SuiteSparse被正确识别。判断方法很简单,编译Ceres时看CMake输出是否包含以下内容:

-- Found SuiteSparse: ... (with LAPACK enabled)

如果只有SuiteSparse found而没有with LAPACK字样,说明LAPACK可能没有链接上,需要补装liblapack-dev,然后重新编译Ceres。这一步值得花时间。

8. 卸载与重装Ceres的正确方式

最后聊一下卸载和重装。Ceres安装教程很多,但卸载教程很少。将来如果升级Ceres或者装坏了要重来,知道怎么彻底卸载能避免很多烦恼。

8.1 从系统中移除Ceres

如果你是通过make install安装在默认路径下的,可以用以下方式卸载:

cd ceres-solver/build sudo make uninstall

注意,make uninstall只在CMake生成了uninstall目标时才有效。CMake默认不生成这个目标,需要提前用cmake -DUNINSTALL_TARGET=ON配置。如果你当初没有开启,就只能手动删除文件:

sudo rm -rf /usr/local/lib/libceres* sudo rm -rf /usr/local/include/ceres sudo rm -f /usr/local/lib/cmake/Ceres/CeresConfig.cmake sudo rm -f /usr/local/lib/cmake/Ceres/CeresTargets.cmake sudo rm -f /usr/local/lib/pkgconfig/ceres.pc

如果当初指定了CMAKE_INSTALL_PREFIX,则把上面路径换成对应前缀下的路径。

8.2 重新安装时的注意事项

重新安装前,记得清理旧的build目录,否则CMake可能会有缓存残留:

cd ceres-solver rm -rf build

如果之前使用过-D参数指定依赖库路径,重新配置时务必带上同样的参数,或者直接清空build目录让它重新检测。

我自己在Ubuntu20.04上多次重装Ceres之后,养成了一个小习惯:把所有自定义安装记录在一个笔记里,包括使用了哪些cmake参数、依赖装到了哪个路径下。这样下次换机器或者重装系统,照着笔记执行一遍就能还原环境,不会丢三落四。

在多个版本的切换实践中,我后来比较喜欢用Docker来隔离开发环境,在容器里编译SLAM项目就不需要反复折腾宿主机上的库版本了。当然这是后话,如果只是单机开发,上面这套手动安装流程已经足够可靠。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询