ceres 是干什么的 非线性优化 ,求函数的最小值,一般都是求 $\sum(*)^2$
比如最小二乘迭代求解
ceres 怎么用 对于一个优化问题,通常是求能使得一些平方和达到最小值的参数为多少,利用ceres求解一个问题,通常分为以下几步
构建问题器对象 1 2 3 4 5 6 7 using ceres::AutoDiffCostFunction;using ceres::CostFunction;using ceres::Problem;using ceres::Solver;using ceres::Solve;Problem problem;
构建参数块(可选),告诉ceres参数如何更新 1 2 3 4 problem.AddParameterBlock (x, 2 , (new ceres::AutoDiffLocalParameterization <TestLocalParamterization, 2 , 2 >()));
TestLocalParamterization的声明和定义如下, 必须要返回true,
结构体中必须重载()运算符,第一个参数为变量,第二个参数为delta,第三个参数为更新结果
1 2 3 4 5 6 7 8 struct TestLocalParamterization { template <typename T> bool operator () (const T* x, const T* delta, T* x_plus_data) const { x_plus_data[0 ] = x[0 ] + delta[0 ]; x_plus_data[1 ] = x[1 ] + delta[1 ]; return true ; } };
构建残差块 AddResidualBlock 的参数,按照顺序
代价函数:定义残差的位置
核函数:暂时不用,使用Null。
变量
AutoDiffCostFunction 的参数模板参数第一个为输出的维度,第二个输入的维度
1 2 3 CostFunction* cost_function = new AutoDiffCostFunction<CostFunctor, 2 , 2 >(new CostFunctor); problem.AddResidualBlock (cost_function, NULL , x);
要添加参加块,首先是要声明定义好残差块
1 2 3 4 5 6 7 8 9 struct CostFunctor { template <typename T> bool operator () (const T* const x, T* residual) const { residual[0 ] = 10.0 - x[0 ]; residual[1 ] = 10.0 - x[1 ]; return true ; } };
计算优化器 1 2 3 4 Solver::Options options; Solver::Summary summary; Solve (options, &problem, &summary);
此时传入的x 已经被优化完毕
举例 前两个例子来自于 https://blog.csdn.net/cqrtxwd/article/details/78956227
例一 求
当给一个初值,比如$x_0=2$,最后可以迭代出结果,x=10;
ceres 使用 例一的问题输入其实就俩
输出就是迭代结果,10。下面重点关注如何定义这两部
定义优化函数 最关心的一步为residual[0] = T(10.0) - x[0];
ceres会自动加上平方
1 2 3 4 5 6 7 8 struct CostFunctor { template <typename T> bool operator () (const T* const x, T* residual) const { residual[0 ] = T (10.0 ) - x[0 ]; return true ; } };
定义初始 1 2 double initial_x = 5.0 ;
代码模板 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 #include <iostream> #include <ceres/ceres.h> using namespace std;using namespace ceres;struct CostFunctor { template <typename T> bool operator () (const T* const x, T* residual) const { residual[0 ] = T (10.0 ) - x[0 ]; return true ; } }; int main (int argc, char ** argv) { google::InitGoogleLogging (argv[0 ]); double initial_x = 5.0 ; double x = initial_x; Problem problem; CostFunction* cost_function = new AutoDiffCostFunction<CostFunctor, 1 , 1 >(new CostFunctor); problem.AddResidualBlock (cost_function, NULL , &x); 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 ; } ———————————————— 版权声明:本文为CSDN博主「福尔摩睿」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https:
需要构建的代价函数(cost function,然后添加到problem里面,这里添加了一次
1 problem.AddResidualBlock (cost_function, NULL , &x);
很明显,这里x就是需要求的变量
CMakeLists.txt 1 2 3 4 5 6 7 8 9 10 11 12 //CMakeLists.txt: cmake_minimum_required(VERSION 2.8) project(ceres) find_package(Ceres REQUIRED) include_directories(${CERES_INCLUDE_DIRS}) add_executable(use_ceres use_ceres.cpp) target_link_libraries(use_ceres ${CERES_LIBRARIES}) ———————————————— 版权声明:本文为CSDN博主「福尔摩睿」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/cqrtxwd/article/details/78956227
例二 例二和例一的不同在于,自变量变成了两维的,这里还添加了如何增加参数块,更新的方法
代码模板 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 #include "ceres/ceres.h" #include "glog/logging.h" #include <iostream> using ceres::AutoDiffCostFunction;using ceres::CostFunction;using ceres::Problem;using ceres::Solver;using ceres::Solve;struct CostFunctor { template <typename T> bool operator () (const T* const x, T* residual) const { residual[0 ] = 10.0 - x[0 ]; residual[1 ] = 10.0 - x[1 ]; return true ; } }; struct TestLocalParamterization { template <typename T> bool operator () (const T* x, const T* delta, T* x_plus_data) const { x_plus_data[0 ] = x[0 ] + delta[0 ]; x_plus_data[1 ] = x[1 ] + delta[1 ]; return true ; } }; int main (int argc, char ** argv) { google::InitGoogleLogging (argv[0 ]); double x[] = {0.5 , 2.0 }; for (auto &i: x) { std::cout << i << " " ; } std::cout << "\n" << std::endl; Problem problem; problem.AddParameterBlock (x, 2 , (new ceres::AutoDiffLocalParameterization <TestLocalParamterization, 2 , 2 >())); CostFunction* cost_function = new AutoDiffCostFunction<CostFunctor, 2 , 2 >(new CostFunctor); problem.AddResidualBlock (cost_function, NULL , x); Solver::Options options; options.minimizer_progress_to_stdout = true ; Solver::Summary summary; Solve (options, &problem, &summary); std::cout << summary.BriefReport () << "\n" ; for (auto &i: x) { std::cout << i << " " ; } std::cout << "\n" << std::endl; return 0 ; }
例三 拟合一个非线性函数曲线
假设现在有1000个点$(x_i,y_i)$,那么误差函数,或者代价函数(cost function)为
该案例和上面不同的地点
待求变量:从例一的一维x变成了例二的三维[a,b,c]
求和的项:从原来的一项,变成现在的1000项
定义优化函数 这里,我们定义好$y_i-y(x_i)$,以便后续计算1000次,不同的$x_i,y_i$只需要通过构造函数传入就行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 struct CURVE_FITTING_COST { CURVE_FITTING_COST (double x,double y):_x(x),_y(y){} template <typename T> bool operator () (const T* const abc,T* residual) const { residual[0 ]=_y-ceres::exp (abc[0 ]*_x*_x+abc[1 ]*_x+abc[2 ]); return true ; } const double _x,_y; }; ———————————————— 版权声明:本文为CSDN博主「福尔摩睿」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https:
把函数放入问题中problem.AddResidualBlock 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ceres::Problem problem; for (int i=0 ;i<1000 ;i++) { problem.AddResidualBlock ( new ceres::AutoDiffCostFunction<CURVE_FITTING_COST,1 ,3 >( new CURVE_FITTING_COST (x_data[i],y_data[i]) ), nullptr , abc ); } ———————————————— 版权声明:本文为CSDN博主「福尔摩睿」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https:
代码模板 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 #include <iostream> #include <opencv2/core/core.hpp> #include <ceres/ceres.h> using namespace std;using namespace cv;struct CURVE_FITTING_COST { CURVE_FITTING_COST (double x,double y):_x(x),_y(y){} template <typename T> bool operator () (const T* const abc,T* residual) const { residual[0 ]=_y-ceres::exp (abc[0 ]*_x*_x+abc[1 ]*_x+abc[2 ]); return true ; } const double _x,_y; }; int main () { double a=3 ,b=2 ,c=1 ; double w=1 ; RNG rng; double abc[3 ]={0 ,0 ,0 }; vector<double > x_data,y_data; for (int i=0 ;i<1000 ;i++) { double x=i/1000.0 ; x_data.push_back (x); y_data.push_back (exp (a*x*x+b*x+c)+rng.gaussian (w)); } ceres::Problem problem; for (int i=0 ;i<1000 ;i++) { problem.AddResidualBlock ( new ceres::AutoDiffCostFunction<CURVE_FITTING_COST,1 ,3 >( new CURVE_FITTING_COST (x_data[i],y_data[i]) ), nullptr , abc ); } ceres::Solver::Options options; options.linear_solver_type=ceres::DENSE_QR; options.minimizer_progress_to_stdout=true ; ceres::Solver::Summary summary; ceres::Solve (options,&problem,&summary); cout<<"a= " <<abc[0 ]<<endl; cout<<"b= " <<abc[1 ]<<endl; cout<<"c= " <<abc[2 ]<<endl; return 0 ;} } ———————————————— 版权声明:本文为CSDN博主「福尔摩睿」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https:
核函数 求解优化问题中(比如拟合曲线),数据中往往会有离群点、错误值什么的,最终得到的寻优结果很容易受到影响,此时就可以使用一些损失核函数来对离群点的影响加以消除。要使用核函数,只需要把上述代码中的NULL或nullptr换成损失核函数结构体的实例。 Ceres库中提供的核函数主要有:TrivialLoss 、HuberLoss、 SoftLOneLoss 、 CauchyLoss。 比如此时要使用CauchyLoss,只需要将nullptr换成new CauchyLoss(0.5)就行(0.5为参数)。 下面两图别为Ceres官网上的例程的结果,可以明显看出使用损失核函数之后的曲线收离群点的影响更小。