安装
ceres是google库,首先安装相关依赖
1 | sudo apt-get install -y libatlas-base-dev |
如果使用Ubuntu18.04,安装
libcxsparse3.1.2
可能出错,ubuntu从18.04版本开始,libcxsparse这个包的名称改为libcxsparse3
。具体方法参考安装Ceres相关依赖时libcxsparse3.1.2报错
如果安装时找不到 cxsparse 或者其他的lib,需要添加下面的源
1 | sudo vim /etc/apt/sources.list |
把下面的源粘贴到source.list的最上方:
deb http://cz.archive.ubuntu.com/ubuntu trusty main universe
更新一下:
sudo apt-get update
, 然后再进行第一步的安装。
从github上下载,这里要注意ceres的版本和Eigen是搭配的,ceres版本越新,对Eigen的版本要求也越新,它的CMakeLists
里有提示,所以不要安装最新的。 安装2.0.0 即可
下载解压后执行老一套命令:
1 | mkdir build |
配置 CMake
安装官方的说明配置是错误的,应该是这样:
1 | find_package(Ceres REQUIRED) |
如果这样报错,找不到ceres,就加上
set(Ceres_DIR "/usr/local/lib/cmake/ceres")
, 也可能是 /usr/local/lib/cmake/ceres
ceres使用LM或狗腿算法,求解参数的数据类型只支持 double*
。Ceres相对于g2o的缺点仅仅是依赖的库多一些(g2o仅依赖Eigen).但是可以直接对数据进行操作
ceres是求解给定函数的最小值
Solver::Options
Solver::Options::trust_region_strategy_type
可以取LEVENBERG_MARQUARDT
或DOGLEG
Solver::Options::use_inner_iterations
设为True,可以启用 Ruhe & Wedin的非线性推广的算法II。这个版本的Ceres具有更高的迭代复杂度,但是每次迭代都显示更好的收敛行为。把Solver::Options::num_threads
设为最大值是非常值得推荐的minimizer_progress_to_stdout
num_threads: 设置使用的线程,但有时线程少反而用时更少,不明白为什么
linear_solver_type
对于非线性最小二乘问题,最终转化为求解方程
Ceres提供了很多计算的方法,这就涉及Solver::Options::linear_solver_type
的取值问题
- DENSE_QR
默认值。 适合使用稠密雅可比矩阵的小规模问题(几百个参数和几千个残差),也就是使用Eigen库的稠密矩阵QR分解。如果ceres优化问题不是SLAM的大型后端,不是稀疏问题,使用DENSE_QR
- DENSE_NORMAL_CHOLESKY & SPARSE_NORMAL_CHOLESKY
大规模的非线性最小二乘问题通常是稀疏的。对于这种情况使用稠密QR分解是低效率的,改用Cholesky因式分解,它有两种变体 - 稀疏和密集。
DENSE_NORMAL_CHOLESKY
是执行正规方程的稠密Cholesky分解。 Ceres使用Eigen稠密的LDLT
因式分解算法。
SPARSE_NORMAL_CHOLESKY
是执行正规方程的稀疏Cholesky分解,这为大量稀疏问题节省了大量的时间和内存消耗。Ceres使用SuiteSparse
或 CXSparse
库中的稀疏Cholesky分解算法 或 Eigen中的稀疏Cholesky分解算法。
如果Ceres编译时支持了这三个库,那么linear_solver_type
默认值是SPARSE_NORMAL_CHOLESKY
,否则就是DENSE_QR
。使用最多的是DENSE_QR
,cartographer前端的ceres scan mather用的也是DENSE_QR
CGNR : 使用共轭梯度法求解稀疏方程
DENSE_SCHUR & SPARSE_SCHUR : 适用于BA问题
ceres::Solver::Summary 的常用函数
ceres::Solver::Summary summary;
- double Solver::Summary::total_time_in_seconds. Time (in seconds) spent in the solver
直接使用summary.total_time_in_seconds
int Solver::Summary::num_threads_given: Number of threads specified by the user for Jacobian and residual evaluation.
int Solver::Summary::num_threads_used
Number of threads actually used by the solver for Jacobian and residual evaluation. This number is not equal to Solver::Summary::num_threads_given
if none of OpenMP
or CXX_THREADS
is available.
以上两个线程的参数是由Options::num_threads
决定的,如果设置太大,这两个参数就会不同,只会用最大线程数。
min_linear_solver_iteration 和 max_linear_solver_iteration:线性求解器的最小/最大迭代次数,默认为0/500,一般不需要更改
max_num_iterations : 默认是50。求解器的最大迭代次数,并不是越大越好。对于SLAM前端的实时性有要求,所以
max_num_iterations
不能太大,ALOAM里设置为4bool Solver::Summary::IsSolutionUsable() const
算法返回的结果是否数值可靠。 也就是Solver::Summary:termination_type
是否是CONVERGENCE
, USER_SUCCESS
或者 NO_CONVERGENCE
,也就是说求解器满足以下条件之一:
- 达到了收敛误差
- 达到最大迭代次数和时间
- user indicated that it had converged
通过实验发现除了多线程以及 linear_solver_type
,别的对优化性能和结果影响不是很大
BriefReport
FullReport
例子
1 | iter cost cost_change |gradient| |step| tr_ratio tr_radius ls_iter iter_time total_time |
Initial Cost 从 1.259266e+05 变为 Final Cost的 2.467825e+04,变化不够大。 如果 Initial cost 和 Final Cost 差别很小,说明优化之前的数据已经足够好
参考:
官方教程学习笔记(十三)