Ceres入门——Ceres的基本使用方法
Ceres入門——Ceres的基本使用方法
- 1.使用流程
- 2.實例分析——HelloWorld
- 2.1 構(gòu)建代價函數(shù)(cost function)
- 2.2 構(gòu)建尋優(yōu)問題
- 2.3 配置并運行求解器
- 2.4 測試結(jié)果
- 3.實例分析——非線性擬合
- 3.1 構(gòu)建代價函數(shù)
- 3.2 構(gòu)建尋優(yōu)問題
- 3.3 配置并運行求解器
- 3.4 測試結(jié)果
Ceres solver 是谷歌開發(fā)的一款用于非線性優(yōu)化的庫,在谷歌的開源激光雷達slam項目cartographer中被大量使用。Ceres官網(wǎng)上的文檔非常詳細(xì)地介紹了其具體使用方法,相比于另外一個在slam中被廣泛使用的圖優(yōu)化庫G2O,ceres的文檔可謂相當(dāng)豐富詳細(xì)。本篇博客介紹一下Ceres庫的基本使用方法。
Ceres可以解決邊界約束魯棒非線性最小二乘法優(yōu)化問題。這個概念可以用以下表達式表示:
這一表達式在工程和科學(xué)領(lǐng)域有非常廣泛的應(yīng)用。比如統(tǒng)計學(xué)中的曲線擬合,或者在計算機視覺中依據(jù)圖像進行三維模型的構(gòu)建等等。
ρi(∣∣fi(xi1,...,xik)∣∣2)\rho_i(||f_i(x_{i_1},...,x_{i_k}) ||^2)ρi?(∣∣fi?(xi1??,...,xik??)∣∣2)這一部分被成為殘差塊(ResidualBlock),其中的 fi(?)f_i (\cdot)fi?(?) 叫做代價函數(shù)(CostFunction)。代價函數(shù)依賴于一系列參數(shù)[xi1][x_{i_1}][xi1??] ,這一系列參數(shù)(均為標(biāo)量)稱為參數(shù)塊(ParameterBlock)。當(dāng)然參數(shù)塊中也可以只含有一個變量。ljl_jlj?和 uju_juj?分別是變量塊xjx_jxj?的上下邊界。
ρi\rho_iρi?是損失函數(shù)(LossFunction) 。按照損失函數(shù)的是一個標(biāo)量函數(shù),其作用是減少異常值(Outliers)對優(yōu)化結(jié)果的影響。其效果類似于對函數(shù)的過濾。
1.使用流程
使用Ceres求解非線性優(yōu)化問題,可以分為三個步驟:
1. 構(gòu)建代價函數(shù)(cost function)
代價函數(shù),也就是尋優(yōu)的目標(biāo)式。這個部分需要使用仿函數(shù)(functor)這一技巧來實現(xiàn),做法是定義一個cost function的結(jié)構(gòu)體,在結(jié)構(gòu)體內(nèi)重載()運算符。關(guān)于仿函數(shù)的內(nèi)容可以參考這篇博客:c++仿函數(shù) functor
2.通過代價函數(shù)構(gòu)建待求解的優(yōu)化問題
3.配置求解器參數(shù)并求解問題
這個步驟就是設(shè)置方程怎么求解、求解過程是否輸出等
2.實例分析——HelloWorld
以Ceres安裝文件中example文件夾下的helloworld.cc示例程序來對Ceres庫的基本使用過程進行分析。在Hello World這個例子中,待優(yōu)化的函數(shù)是 f(x)=10?xf(x)=10?xf(x)=10?x 。
先看源碼:
#include <iostream>
#include <ceres/ceres.h>using namespace std;
using namespace ceres;
//第一部分:構(gòu)建代價函數(shù)
struct CostFunctor {template <typename T>bool operator()(const T* const x, T* residual) const {residual[0] = T(10.0) - x[0];return true;}
};//主函數(shù)
int main(int argc, char** argv) {google::InitGoogleLogging(argv[0]);// 尋優(yōu)參數(shù)x的初始值,為5double initial_x = 5.0;double x = initial_x;// 第二部分:構(gòu)建尋優(yōu)問題Problem problem;CostFunction* cost_function =new AutoDiffCostFunction<CostFunctor, 1, 1>(new CostFunctor); //使用自動求導(dǎo),將之前的代價函數(shù)結(jié)構(gòu)體傳入,第一個1是輸出維度,即殘差的維度,第二個1是輸入維度,即待尋優(yōu)參數(shù)x的維度。problem.AddResidualBlock(cost_function, NULL, &x); //向問題中添加誤差項,本問題比較簡單,添加一個就行。//第三部分: 配置并運行求解器Solver::Options options;options.linear_solver_type = ceres::DENSE_QR; //配置增量方程的解法options.minimizer_progress_to_stdout = true;//輸出到coutSolver::Summary summary;//優(yōu)化信息Solve(options, &problem, &summary);//求解!!!std::cout << summary.BriefReport() << "\n";//輸出優(yōu)化的簡要信息//最終結(jié)果std::cout << "x : " << initial_x<< " -> " << x << "\n";return 0;
}
2.1 構(gòu)建代價函數(shù)(cost function)
待優(yōu)化函數(shù)為f(x)=10?xf(x)=10?xf(x)=10?x,也就是說我們要尋找最優(yōu)的xxx值使 f(x)f(x)f(x)最小。所以我們的誤差項為10.0?x[0]10.0-x[0]10.0?x[0]。
struct CostFunctor {template <typename T> bool operator()(const T* const x, T* residual) const {residual[0] = 10.0 - x[0];//誤差項return true;}
};
2.2 構(gòu)建尋優(yōu)問題
首先,定義Problem類型的變量,然后將構(gòu)建的代價函數(shù)添加到尋優(yōu)問題中。AutoDiffCostFunction將剛剛建立的CostFunctor 結(jié)構(gòu)的一個實例作為輸入,自動生成其微分并且賦予其一個CostFunction 類型的接口。
Problem problem;CostFunction* cost_function = new AutoDiffCostFunction<CostFunctor, 1, 1>(new CostFunctor); //使用自動求導(dǎo),將之前的代價函數(shù)結(jié)構(gòu)體傳入,第一個1是輸出維度,即殘差的維度,第二個1是輸入維度,即待尋優(yōu)參數(shù)x的維度。problem.AddResidualBlock(cost_function, NULL, &x); //向問題中添加誤差項,本問題比較簡單,添加一個就行。
2.3 配置并運行求解器
為求解這個優(yōu)化問題,我們需要做一些配置。這一部分很好理解,創(chuàng)建一個Option,配置一下求解器的配置,創(chuàng)建一個Summary。最后調(diào)用Solve方法,求解。
Solver::Options options;options.linear_solver_type = ceres::DENSE_QR; //配置增量方程的解法options.minimizer_progress_to_stdout = true;//輸出到coutSolver::Summary summary;//優(yōu)化信息Solve(options, &problem, &summary);//求解!!!
2.4 測試結(jié)果
測試結(jié)果如下,經(jīng)過3次迭代之后求出x的最優(yōu)解為10.
iter cost cost_change |gradient| |step| tr_ratio tr_radius ls_iter iter_time total_time0 1.250000e+01 0.00e+00 5.00e+00 0.00e+00 0.00e+00 1.00e+04 0 1.91e-05 6.60e-051 1.249750e-07 1.25e+01 5.00e-04 5.00e+00 1.00e+00 3.00e+04 1 3.00e-05 1.31e-042 1.388518e-16 1.25e-07 1.67e-08 5.00e-04 1.00e+00 9.00e+04 1 7.87e-06 1.46e-04
Ceres Solver Report: Iterations: 3, Initial cost: 1.250000e+01, Final cost: 1.388518e-16, Termination: CONVERGENCE
x : 5 -> 10
3.實例分析——非線性擬合
以Ceres安裝文件中另一個示例是使用Ceres來進行非線性擬合。它指定一系列的點對來擬合一個曲線的系數(shù)。這一系列點對是通過曲線y=e0.3x+0.1y=e^{0.3x+0.1}y=e0.3x+0.1插值的點然后添加了標(biāo)準(zhǔn)差的高斯噪聲。我們要擬合的曲線形式為:y=emx+cy = e^{mx+c}y=emx+c
先看代碼:
#include <iostream>
#include "ceres/ceres.h"using ceres::AutoDiffCostFunction;
using ceres::CostFunction;
using ceres::Problem;
using ceres::Solver;
using ceres::Solve;const int kNumObservations = 67;
//觀測值
const double data[] = {0.000000e+00, 1.133898e+00,7.500000e-02, 1.334902e+00,1.500000e-01, 1.213546e+00,2.250000e-01, 1.252016e+00,3.000000e-01, 1.392265e+00,3.750000e-01, 1.314458e+00,4.500000e-01, 1.472541e+00,5.250000e-01, 1.536218e+00,6.000000e-01, 1.355679e+00,6.750000e-01, 1.463566e+00,7.500000e-01, 1.490201e+00,8.250000e-01, 1.658699e+00,9.000000e-01, 1.067574e+00,9.750000e-01, 1.464629e+00,1.050000e+00, 1.402653e+00,1.125000e+00, 1.713141e+00,1.200000e+00, 1.527021e+00,1.275000e+00, 1.702632e+00,1.350000e+00, 1.423899e+00,1.425000e+00, 1.543078e+00,1.500000e+00, 1.664015e+00,1.575000e+00, 1.732484e+00,1.650000e+00, 1.543296e+00,1.725000e+00, 1.959523e+00,1.800000e+00, 1.685132e+00,1.875000e+00, 1.951791e+00,1.950000e+00, 2.095346e+00,2.025000e+00, 2.361460e+00,2.100000e+00, 2.169119e+00,2.175000e+00, 2.061745e+00,2.250000e+00, 2.178641e+00,2.325000e+00, 2.104346e+00,2.400000e+00, 2.584470e+00,2.475000e+00, 1.914158e+00,2.550000e+00, 2.368375e+00,2.625000e+00, 2.686125e+00,2.700000e+00, 2.712395e+00,2.775000e+00, 2.499511e+00,2.850000e+00, 2.558897e+00,2.925000e+00, 2.309154e+00,3.000000e+00, 2.869503e+00,3.075000e+00, 3.116645e+00,3.150000e+00, 3.094907e+00,3.225000e+00, 2.471759e+00,3.300000e+00, 3.017131e+00,3.375000e+00, 3.232381e+00,3.450000e+00, 2.944596e+00,3.525000e+00, 3.385343e+00,3.600000e+00, 3.199826e+00,3.675000e+00, 3.423039e+00,3.750000e+00, 3.621552e+00,3.825000e+00, 3.559255e+00,3.900000e+00, 3.530713e+00,3.975000e+00, 3.561766e+00,4.050000e+00, 3.544574e+00,4.125000e+00, 3.867945e+00,4.200000e+00, 4.049776e+00,4.275000e+00, 3.885601e+00,4.350000e+00, 4.110505e+00,4.425000e+00, 4.345320e+00,4.500000e+00, 4.161241e+00,4.575000e+00, 4.363407e+00,4.650000e+00, 4.161576e+00,4.725000e+00, 4.619728e+00,4.800000e+00, 4.737410e+00,4.875000e+00, 4.727863e+00,4.950000e+00, 4.669206e+00,
};
//1.代價函數(shù)結(jié)構(gòu)體
struct ExponentialResidual {ExponentialResidual(double x, double y): x_(x), y_(y) {}template <typename T> bool operator()(const T* const m, const T* const c, T* residual) const {residual[0] = y_ - exp(m[0] * x_ + c[0]);return true;}private:const double x_;const double y_;
};int main(int argc, char** argv) {double m = 0.0;double c = 0.0;//構(gòu)建尋優(yōu)問題Problem problem;for (int i = 0; i < kNumObservations; ++i) {problem.AddResidualBlock(new AutoDiffCostFunction<ExponentialResidual, 1, 1, 1>(new ExponentialResidual(data[2 * i], data[2 * i + 1])),NULL,&m, &c);}//配置并運行求解器Solver::Options options;options.max_num_iterations = 25;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 << "Initial m: " << 0.0 << " c: " << 0.0 << "\n";std::cout << "Final m: " << m << " c: " << c << "\n";return 0;
}
3.1 構(gòu)建代價函數(shù)
這里的代價函數(shù)結(jié)構(gòu)體和第二節(jié)中的代價函數(shù)結(jié)構(gòu)體有些區(qū)別。在第二節(jié)中,我們待求的參數(shù)是xxx,所以殘差項是y=10?xy=10-xy=10?x。但是在此處,我們待求解的參數(shù)是mmm和ccc,相應(yīng)的殘差項為res=y?em?x?cres = y - e^{m*x-c}res=y?em?x?c。相對來說,上一節(jié)的代價函數(shù)結(jié)構(gòu)體中并沒有真正意義上利用仿函數(shù)的優(yōu)勢,而此節(jié)中的代價函數(shù)結(jié)構(gòu)體則充分體現(xiàn)了放函數(shù)的優(yōu)勢。
ExponentialResidual結(jié)構(gòu)體定義了兩個私有成員變量 x_ 和 y_ ,并且在構(gòu)造函數(shù)中將構(gòu)造函數(shù)的參數(shù)賦值給這兩個變量。這樣就可以在重定義 () 操作符時,僅通過輸入兩個參數(shù)mmm和ccc就能計算殘差。
struct ExponentialResidual {ExponentialResidual(double x, double y): x_(x), y_(y) {}//重定義()操作符template <typename T> bool operator()(const T* const m, const T* const c, T* residual) const {residual[0] = y_ - exp(m[0] * x_ + c[0]); //殘差項 res = y - exp(m[0]*x-c[0])return true;}private:const double x_;const double y_;
};
3.2 構(gòu)建尋優(yōu)問題
我們要擬合的曲線是y=emx+cy=e^{mx+c}y=emx+c 此時我們有67組觀測值。想要求得參數(shù)mmm和ccc的值,我們需要構(gòu)建如下目標(biāo)函數(shù):min12∑i∣∣yi?emxi+c∣∣2min\frac{1}{2}\sum_i{||y_i-e^{mx_i+c}||^2}min21?i∑?∣∣yi??emxi?+c∣∣2
因此我們需要用 forforfor 循環(huán)將殘差項加入到我們定義的 problemproblemproblem 當(dāng)中。
Problem problem;for (int i = 0; i < kNumObservations; ++i) {problem.AddResidualBlock(new AutoDiffCostFunction<ExponentialResidual, 1, 1, 1>(new ExponentialResidual(data[2 * i], data[2 * i + 1])),NULL,&m, &c);}
3.3 配置并運行求解器
在這部分中與第二節(jié)不同的是我們設(shè)定了最大迭代次數(shù),其他無異。
//配置并運行求解器Solver::Options options;options.max_num_iterations = 25;options.linear_solver_type = ceres::DENSE_QR;options.minimizer_progress_to_stdout = true;Solver::Summary summary;Solve(options, &problem, &summary);
3.4 測試結(jié)果
iter cost cost_change |gradient| |step| tr_ratio tr_radius ls_iter iter_time total_time0 1.211734e+02 0.00e+00 3.61e+02 0.00e+00 0.00e+00 1.00e+04 0 2.91e-04 3.44e-041 2.334822e+03 -2.21e+03 0.00e+00 7.52e-01 -1.87e+01 5.00e+03 1 3.31e-05 4.15e-042 2.331438e+03 -2.21e+03 0.00e+00 7.51e-01 -1.86e+01 1.25e+03 1 1.10e-05 4.34e-043 2.311313e+03 -2.19e+03 0.00e+00 7.48e-01 -1.85e+01 1.56e+02 1 1.00e-05 4.49e-044 2.137268e+03 -2.02e+03 0.00e+00 7.22e-01 -1.70e+01 9.77e+00 1 9.06e-06 4.62e-045 8.553131e+02 -7.34e+02 0.00e+00 5.78e-01 -6.32e+00 3.05e-01 1 1.41e-05 4.80e-046 3.306595e+01 8.81e+01 4.10e+02 3.18e-01 1.37e+00 9.16e-01 1 2.83e-04 7.67e-047 6.426770e+00 2.66e+01 1.81e+02 1.29e-01 1.10e+00 2.75e+00 1 2.93e-04 1.07e-038 3.344546e+00 3.08e+00 5.51e+01 3.05e-02 1.03e+00 8.24e+00 1 2.82e-04 1.36e-039 1.987485e+00 1.36e+00 2.33e+01 8.87e-02 9.94e-01 2.47e+01 1 2.81e-04 1.64e-0310 1.211585e+00 7.76e-01 8.22e+00 1.05e-01 9.89e-01 7.42e+01 1 2.81e-04 1.93e-0311 1.063265e+00 1.48e-01 1.44e+00 6.06e-02 9.97e-01 2.22e+02 1 2.82e-04 2.22e-0312 1.056795e+00 6.47e-03 1.18e-01 1.47e-02 1.00e+00 6.67e+02 1 2.80e-04 2.50e-0313 1.056751e+00 4.39e-05 3.79e-03 1.28e-03 1.00e+00 2.00e+03 1 2.83e-04 2.79e-03
Ceres Solver Report: Iterations: 14, Initial cost: 1.211734e+02, Final cost: 1.056751e+00, Termination: CONVERGENCE
Initial m: 0 c: 0
Final m: 0.291861 c: 0.131439
可以看出,經(jīng)過14次迭代之后,計算出m=0.291861m=0.291861m=0.291861,c=0.131439c = 0.131439c=0.131439,與真實值差異不大。
參考博客:
https://blog.csdn.net/wzheng92/article/details/79634069
https://blog.csdn.net/cqrtxwd/article/details/78956227
總結(jié)
以上是生活随笔為你收集整理的Ceres入门——Ceres的基本使用方法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Cartographer 数据集国内地址
- 下一篇: 求一个学习的个性签名!