使用Ceres求解非線性優化問題,一共分為三個部分:
- 第一部分:構建cost fuction,即代價函數,也就是尋優的目標式。參見《Ceres學習-1.CostFunction》https://www.cnblogs.com/vivian187/p/15393995.html
- 第二部分:通過代價函數構建待求解的優化問題。參見《Ceres學習-2.Problem》https://www.cnblogs.com/vivian187/p/15394000.html
- 第三部分:配置求解器參數並求解問題,這個步驟就是設置方程怎么求解、求解過程是否輸出等,然后調用一下Solve方法。
這節就講述第三個部分:
一個簡單的應用例子
// 來自於ceres-solver-1.14.0/examples/helloworld.cc
// 第三部分: 配置並運行求解器
// Run the solver!
Solver::Options options;
options.minimizer_progress_to_stdout = true;
options.linear_solver_type = ceres::DENSE_QR;
Solver::Summary summary; // 優化信息
Solve(options, &problem, &summary);// 求解!!!
std::cout << summary.BriefReport() << "\n"; // 輸出優化的簡要信息
求解最小二乘問題
ceres::Solve函數是Ceres求解最小二乘問題的核心函數,函數原型如下:
// 來自於ceres-solver-1.14.0/include/ceres/solver.h
void Solve(const Solver::Options& options, Problem* problem, Solver::Summary* summary);
參數:
Solver::Options 求解選項。是Ceres求解的核心,包括消元順序、分解方法、收斂精度等在內的求解器所有行為均由Solver::Options控制。
Problem 求解問題。參考《Ceres學習-2.Problem》
Solver::Summary 求解報告。用於存儲求解過程中的相關信息,並不影響求解器性能
參數詳解
Solver::Options
Solver::Options含有的參數種類繁多,API文檔中對於每個參數的作用和意義都給出了詳細的說明。由於在大多數情況下,絕大多數參數我們都會使用Ceres的默認設置。列舉了一些可能會改變的參數:
- linear_solver_type:信賴域方法中求解線性方程組所使用的求解器類型,默認為DENSE_QR,其他可選項如下:
DENSE_QR:QR分解,用於小規模最小二乘問題求解;
DENSE_NORMAL_CHOLESKY&SPARSE_NORMAL_CHOLESKY:Cholesky分解,用於具有稀疏性的大規模非線性最小二乘問題求解;
CGNR:使用共軛梯度法求解稀疏方程;
DENSE_SCHUR&SPARSE_SCHUR:SCHUR分解,用於BA問題求解;
ITERATIVE_SCHUR:使用共軛梯度SCHUR求解BA問題;
-
min_linear_solver_iteration/max_linear_solver_iteration:線性求解器的最小/最大迭代次數,默認為0/500,一般不需要更改;
-
max_num_iterations:求解器的最大迭代次數;
-
num_threads:Ceres求解時使用的線程數
-
linear_solver_ordering:線性方程求解器的消元順序,默認為NULL,即由Ceres自行決定消元順序;在以BA為典型代表的,對消元順序有特殊要求的應用中,可以通過成員函數reset設定消元順序,稍后將詳細說明;
linear_solver_ordering
Ceres消元順序的設置由linear_solver_ordering的reset函數完成,該函數接受參數為ParameterBlockOrdering對象。該對象將所有待優化參數存儲為帶標記(ID)的組(Group),
ID小的Group在求解線性方程的過程中會被首先消去。因此,我們需要做的第一個工作是調用其成員函數AddElementToGroup將參數添加到對應ID的Group中,函數原型為:
bool ParameterBlockOrdering::AddElementToGroup(const double *element, const int group)
接收的元素為變量數組的指針;組ID為非負整數,最小為0,如果該Id對應的Group不存在,則Ceres會自動創建。下面我們來看一個BA中的例子:
ceres::ParameterBlockOrdering* ordering = new ceres::ParameterBlockOrdering();
// set all points in ordering to 0
for(int i = 0; i < num_points; i++){
ordering->AddElementToGroup(points + i * point_block_size, 0);
}
// set all cameras in ordering to 1
for(int i = 0; i < num_cameras; i++){
ordering->AddElementToGroup(cameras + i * camera_block_size, 1);
}
該例子中,所有路標點被分到了ID = 0組,而所有相機位姿被分到了ID = 1組,因此在線性方程組的求解中,所有路標點會變首先SCHUR消元。
接下來,我們就可以使用reset函數制定線性求解器的消元順序了:
// set ordering in options
options->linear_solver_ordering.reset(ordering);
在實際應用中,對最終求解性能最大的就是線性方程求解器類型linear_solver_type和線程數,如果發現最后的求解精度或求解效率不能滿足要求,應首先嘗試更換這兩個參數。
Solver::Summary
Solver::Summary包含了求解器本身和求解中各變量的信息,許多成員函數與Solver::Options一致,詳細列表同樣請參閱API文檔,這里只給出另外兩個常用的成員函數:
- BriefReport():輸出單行的簡單總結;
- FullReport():輸出多行的完整總結。
實例Bundle Adjustment,參照下面兩個網站
https://www.cnblogs.com/vivian187/p/15331483.html 《5.Ceres官方教程-非線性最小二乘~Bundle Adjustment》