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上測試):
運行結果截圖: