Ceres Solver是谷歌2010就开始用于解决优化问题的C++库,2014年开源.在Google地图,Tango项目,以及著名的SLAM系统OKVIS和Cartographer的优化模块中均使用了Ceres Solver.html
有关为什么SLAM问题能够建模为最小二乘问题,进而使用最优化方法来求解,能够理解这一段话:git
在SLAM领域优化问题还可使用g2o来求解.不过Ceres提供了自动求导功能,虽然是数值求导,但能够避免复杂的雅克比计算,目前来看Ceres相对于g2o的缺点仅仅是依赖的库多一些(g2o仅依赖Eigen).可是提供了能够直接对数据进行操做的能力,相对于g2o应用在视觉SLAM中,更加倾向于通用的数值优化,最重要的是提供的官方资料比较全(看g2o简直受罪...).详细的介绍能够参考google的文档:http://ceres-solver.org/features.htmlgithub
优化问题的本质是调整优化变量,使得优化变量建模得出的估计值不断接近观测数据(使得目标函数降低),是最大似然框架下对优化变量的不断调整,获得优化变量建模得出的估计值在观测条件下的无偏估计过程.多线程
这里已知的是固定的观测数据z,以及须要调整的初始估计值x0.一般会建模成观测数据和估计值之间的最小二乘问题(但并不必定是最好的建模方式):app
$\mathop{\arg\min}_{x_{i,j}} \ \ \frac{1}{2}\sum_{i=1}^{m}\sum_{j=1}^{n}\left \|z_{i,j}-h(x{_{i,j}})) \right \| ^{2}$框架
一些概念:函数
求解步骤:性能
以寻找y=(10-x)2的最小值为例测试
1.定义一个Functor(拟函数/函数对象)类,其中定义的是CostFunction. 须要重载函数调用运算符,从而能够像使用函数同样使用该类对象.(与普通函数相比,可以在类中存储状态,更加灵活)优化
operator()的形参,前面几个对应 problem.AddResidualBlock(cost_function, NULL, &x);中最后一部分优化变量,最后一个对应残差
struct CostFunctor { template <typename T> bool operator()(const T* const x, T* residual) const { residual[0] = T(10.0) - x[0]; return true; } };
这里模板参数T一般为double,在须要求residual的雅克比时,T=Jet
2. 创建非线性最小二乘问题,并使用Ceres Solver求解
int main(int argc, char** argv) { google::InitGoogleLogging(argv[0]); // The variable to solve for with its initial value. double initial_x = 5.0; double x = initial_x; // Build the problem. Problem problem; // Set up the only cost function (also known as residual). This uses // auto-differentiation to obtain the derivative (jacobian). CostFunction* cost_function = new AutoDiffCostFunction<CostFunctor, 1, 1>(new CostFunctor); problem.AddResidualBlock(cost_function, NULL, &x); // Run the solver! Solver::Options options; options.linear_solver_type = ceres::DENSE_QR; options.minimizer_progress_to_stdout = true; Solver::Summary summary; Solve(options, &problem, &summary); std::cout << summary.BriefReport() << "\n"; std::cout << "x : " << initial_x << " -> " << x << "\n"; return 0; }
3. 参数选择
在作Bundle Adjustment过程当中,创建好优化问题后,须要对优化求解器进行一些参数设置:
Solver::Options options; options.gradient_tolerance = 1e-16; options.function_tolerance = 1e-16;
...
总结:
设置options细节较多,能够参考官方文档去设定:
http://ceres-solver.org/nnls_solving.html#solver-options.
经过实验发现除了多线程以及linear_solver_type,别的对优化性能和结果影响不是很大:
实验代码在:https://github.com/xushangnjlh/xushang_SLAM;
对于93个相机位姿 61203个地图点 287451个观测数据的SFM优化问题:
测试数据集http://grail.cs.washington.edu/projects/bal/final.html
边缘化的影响:
线程影响(在四核CPU上测试):
运行结果截图: