Ceres学习笔记002--使用Ceres求解Powell方程

使用Ceres求解Powell方程

1 Powell方程

Powell 方程如下:
F ( x ) = [ f 1 ( x ) , f 2 ( x ) , f 3 ( x ) , f 4 ( x ) ] (1) F(x) = [f_1(x),f_2(x),f_3(x),f_4(x)]\tag{1} F(x)=[f1(x),f2(x),f3(x),f4(x)](1)
其中:
{ f 1 ( x ) = x 1 + 10 ∗ x 2 f 2 ( x ) = 5 ∗ ( x 3 − x 4 ) f 3 ( x ) = ( x 2 − 2 ∗ x 3 ) 2 f 4 ( x ) = 10 ∗ ( x 1 − x 4 ) 2 (2) \left\{\begin{matrix} f_1(x) = x_1 + 10 * x_2 \\ f_2(x) = \sqrt{5} * (x_3 - x_4) \\ f_3(x) = (x_2 - 2*x_3)^2 \\ f_4(x) = \sqrt{10} * (x_1 - x_4)^2 \end{matrix}\right.\tag{2} f1(x)=x1+10x2f2(x)=5 (x3x4)f3(x)=(x22x3)2f4(x)=10 (x1x4)2(2)
Powell方程中,含有4个参数,4个残差项,目标是找到参数块 [ x 1 , x 2 , x 3 , x 4 ] [x_1,x_2,x_3,x_4] [x1,x2,x3,x4],使得下式能够取得最小值(实际上一般是极小值):
1 2 ∗ ∣ ∣ F ( x ) ∣ ∣ 2 = 1 2 ∗ ( ∣ ∣ f 1 ( x ) ∣ ∣ 2 + ∣ ∣ f 2 ( x ) ∣ ∣ 2 + ∣ ∣ f 3 ( x ) ∣ ∣ 2 + ∣ ∣ f 4 ( x ) ∣ ∣ 2 ) (3) \frac{1}{2} * ||F(x)||^2 = \frac{1}{2} * (||f_1(x)||^2 + ||f_2(x)||^2 + ||f_3(x)||^2 + ||f_4(x)||^2) \tag{3} 21∣∣F(x)2=21(∣∣f1(x)2+∣∣f2(x)2+∣∣f3(x)2+∣∣f4(x)2)(3)

2 Ceres使用基本步骤

Ceres求解过程主要有两大步骤,构建最小二乘问题和求解最小二乘问题,具体步骤如下:

2.1 构建最小二乘问题
  1. 用户自定义残差计算模型,可能存在多个;
  2. 构建Ceres代价函数(CostFunction),将用户自定义残差计算模型添加至CostFunction,可能存在多个CostFunction,为每个CostFunction添加用户自定义残差计算模型,并指定用户自定义残差计算模型的导数计算方法;
  3. 构建Ceres问题(Problem),并在Problem中添加残差块(ResidualBlock),可能存在多个ResidualBlock,为每个ResidualBlock指定CostFunction,LossFunction以及参数块(ParameterBlock)。
2.2 求解最小二乘问题
  1. 配置求解器参数Options,即设置Problem求解方法及参数。例如迭代次数、步长等等;
  2. 输出日志内容Summary;
  3. 优化求解Solve。
构建最小二乘问题时的一些思考
  1. 目标函数有4个维度,可以拆成4个代价函数,每个代价函数包含1项残差;也可以合并成1个代价函数,包含4项残差;
  2. 有4个参数,可以拆成4个参数块,每个参数块包含1个参数;也可以合并成1个参数块,包含4个参数;
  3. 计算导数时,可以使用自动微分法、数值微分法、解析解法。

接下来将通过多种方式实现。

3 代码实现

3.1 cost4_x4_auto
  1. 4个代价函数(CostFunction),每个代价函数的输出残差维度为1;
  2. 4个参数块(ParametersBlock),每个参数块的输入维度为1
  3. 导数计算使用自动微分。

完整代码如下:

#include  "ceres/ceres.h"
#include "gflags/gflags.h"
#include "glog/logging.h"

//	用户自定义第1个维度的残差计算模型
//	输入2个参数块,每个参数块的维度为1
//	输出1个残差块,残差块的维度为1
struct F1
{
	template <typename  T>
	bool operator()(const T* const x1, const T* const x2, T* residual) const
	{
		residual[0] = x1[0] + 10.0 * x2[0];
		return true;
	}
};

//	用户自定义第1个维度的残差计算模型
struct F2
{
	template<typename T>
	bool operator()(const T* const x3, const T*const x4, T* residual) const
	{
		residual[0] = sqrt(5.0) * (x3[0] - x4[0]);
		return true;
	}
};

//	用户自定义第1个维度的残差计算模型
struct F3
{
	template<typename T>
	bool operator()(const T* const x2, const T* const x3, T* residual) const
	{
		residual[0] = (x2[0] - 2.0 * x3[0]) * (x2[0] - 2.0 * x3[0]);
		return true;
	}
};

//	用户自定义第1个维度的残差计算模型
struct F4
{
	template<typename T>
	bool operator()(const T* const x1, const T* const x4, T* residual) const
	{
		residual[0] = sqrt(10.0) * ((x1[0] - x4[0]) * (x1[0] - x4[0]));
		return true;
	}
};

int main(int argc, char** argv)
{
	GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, true);
	google::InitGoogleLogging(argv[0]);

	//	设置参数初始值,输入4个参数块,每个参数块的维度为1
	double x1 = 3.0;
	double x2 = -1.0;
	double x3 = 0.0;
	double x4 = 1.0;

	//	构建非线性最小二乘问题
	ceres::Problem problem;
	//	添加残差块,需要依次指定代价函数、损失函数、参数块
	//	本例中损失函数为单位函数,每个成本函数需要输入2个参数块
	problem.AddResidualBlock(new ceres::AutoDiffCostFunction<F1, 1, 1, 1>(new F1), NULL, &x1, &x2);
	problem.AddResidualBlock(new ceres::AutoDiffCostFunction<F2, 1, 1, 1>(new F2), NULL, &x3, &x4);
	problem.AddResidualBlock(new ceres::AutoDiffCostFunction<F3, 1, 1, 1>(new F3), NULL, &x2, &x3);
	problem.AddResidualBlock(new ceres::AutoDiffCostFunction<F4, 1, 1, 1>(new F4), NULL, &x1, &x4);

	//	配置求解器参数
	ceres::Solver::Options options;
	//	设置最大迭代次数
	options.max_num_iterations = 100;
	//	指定线性求解器来求解问题
	options.linear_solver_type = ceres::DENSE_QR;
	//	输出每次迭代的信息
	options.minimizer_progress_to_stdout = true;

	//	输出日志内容
	ceres::Solver::Summary summary;

	std::cout << "Initial x1 = " << x1
		<< ", x2 = " << x2
		<< ", x3 = " << x3
		<< ", x4 = " << x4
		<< "\n";

	//	开始优化求解
	ceres::Solve(options, &problem, &summary);

	//	输出优化过程及结果
	std::cout << summary.FullReport() << "\n";
	std::cout << "Initial x1 = " << x1
		<< ", x2 = " << x2
		<< ", x3 = " << x3
		<< ", x4 = " << x4
		<< "\n";

	std::system("pause");
	return 0;
}

结果如下:

Initial x1 = 3, x2 = -1, x3 = 0, x4 = 1
iter      cost      cost_change  |gradient|   |step|    tr_ratio  tr_radius  ls_iter  iter_time  total_time
   0  1.075000e+02    0.00e+00    1.55e+02   0.00e+00   0.00e+00  1.00e+04        0    6.16e-04    1.19e-03
   1  5.036190e+00    1.02e+02    2.00e+01   2.16e+00   9.53e-01  3.00e+04        1    1.40e-03    2.77e-03
   2  3.148168e-01    4.72e+00    2.50e+00   6.23e-01   9.37e-01  9.00e+04        1    7.74e-04    3.67e-03
   3  1.967760e-02    2.95e-01    3.13e-01   3.08e-01   9.37e-01  2.70e+05        1    7.62e-04    4.56e-03
   4  1.229900e-03    1.84e-02    3.91e-02   1.54e-01   9.37e-01  8.10e+05        1    5.29e-04    5.20e-03
   5  7.687123e-05    1.15e-03    4.89e-03   7.69e-02   9.37e-01  2.43e+06        1    5.19e-04    5.82e-03
   6  4.804625e-06    7.21e-05    6.11e-04   3.85e-02   9.37e-01  7.29e+06        1    7.59e-04    6.71e-03
   7  3.003028e-07    4.50e-06    7.64e-05   1.92e-02   9.37e-01  2.19e+07        1    7.49e-04    7.58e-03
   8  1.877006e-08    2.82e-07    9.54e-06   9.62e-03   9.37e-01  6.56e+07        1    7.62e-04    8.46e-03
   9  1.173223e-09    1.76e-08    1.19e-06   4.81e-03   9.37e-01  1.97e+08        1    6.04e-04    9.19e-03
  10  7.333425e-11    1.10e-09    1.49e-07   2.40e-03   9.37e-01  5.90e+08        1    5.17e-04    9.82e-03
  11  4.584044e-12    6.88e-11    1.86e-08   1.20e-03   9.37e-01  1.77e+09        1    5.39e-04    1.05e-02
  12  2.865573e-13    4.30e-12    2.33e-09   6.02e-04   9.37e-01  5.31e+09        1    5.59e-04    1.11e-02
  13  1.791438e-14    2.69e-13    2.91e-10   3.01e-04   9.37e-01  1.59e+10        1    5.30e-04    1.17e-02
  14  1.120029e-15    1.68e-14    3.64e-11   1.51e-04   9.37e-01  4.78e+10        1    9.31e-04    1.28e-02

Solver Summary (v 2.0.0-eigen-(3.3.8)-no_lapack-eigensparse-no_openmp)

                                     Original                  Reduced
Parameter blocks                            4                        4
Parameters                                  4                        4
Residual blocks                             4                        4
Residuals                                   4                        4

Minimizer                        TRUST_REGION

Dense linear algebra library            EIGEN
Trust region strategy     LEVENBERG_MARQUARDT

                                        Given                     Used
Linear solver                        DENSE_QR                 DENSE_QR
Threads                                     1                        1
Linear solver ordering              AUTOMATIC                        4

Cost:
Initial                          1.075000e+02
Final                            1.120029e-15
Change                           1.075000e+02

Minimizer iterations                       15
Successful steps                           15
Unsuccessful steps                          0

Time (in seconds):
Preprocessor                         0.000576

  Residual only evaluation           0.000475 (14)
  Jacobian & residual evaluation     0.002427 (15)
  Linear solver                      0.005271 (14)
Minimizer                            0.013570

Postprocessor                        0.000089
Total                                0.014236

Termination:                      CONVERGENCE (Gradient tolerance reached. Gradient max norm: 3.642190e-11 <= 1.000000e-10)

Initial x1 = 0.000146222, x2 = -1.46222e-05, x3 = 2.40957e-05, x4 = 2.40957e-05

根据优化结果,当 x 1 = x 2 = x 3 = x 4 = 0 x_1 = x_2 = x_3 = x_4 = 0 x1=x2=x3=x4=0 时,能够得到最小值。

3.2 cost1_x1_auto
  1. 1个代价函数(CostFunction),输出残差维度为4;
  2. 1个参数块(ParametersBlock),输入参数维度为4;
  3. 导数计算采用自动微分。

完整代码如下:

#include "ceres/ceres.h"
#include "gflags/gflags.h"
#include "glog/logging.h"

//	用户自定义残差计算模型
//	输入1个参数块,参数块维度为4
//	输出1个残差块,残差块维度为4
struct F1234
{
	template<typename T>
	bool operator()(const T* const x, T* residual) const
	{
		residual[0] = x[0] + 10.0 * x[1];
		residual[1] = sqrt(5.0) * (x[2] - x[3]);
		residual[2] = (x[1] - 2.0 * x[2]) * (x[1] - 2.0 * x[2]);
		residual[3] = sqrt(10.0) * (x[0] - x[3]) * (x[0] - x[3]);
		return true;
	}
};

int main(int argc, char** argv)
{
	GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, true);
	google::InitGoogleLogging(argv[0]);

	//	设置参数初始值,输入1个参数块,参数块的维度为4
	double x1234[4] = { 3, -1, 0, 1 };

	//	构建非线性最小二乘问题
	ceres::Problem problem;
	//	添加残差块,需要依次指定代价函数、损失函数、参数块
	//	本例中损失函数为单位函数,代价函数需要输入1个参数块
	problem.AddResidualBlock(new ceres::AutoDiffCostFunction<F1234, 4, 4>(new F1234), NULL, x1234);

	//	配置求解器参数
	ceres::Solver::Options options;
	//	设置最大迭代次数
	options.max_num_iterations = 100;
	//	指定线性求解器来求解问题
	options.linear_solver_type = ceres::DENSE_QR;
	//	输出每次迭代的信息
	options.minimizer_progress_to_stdout = true;

	//	输出日志内容
	ceres::Solver::Summary summary;

	std::cout << "Initial x1 = " << x1234[0]
		<< ", x2 = " << x1234[1]
		<< ", x3 = " << x1234[2]
		<< ", x4 = " << x1234[3]
		<< "\n";

	//	开始优化求解
	ceres::Solve(options, &problem, &summary);

	//	输出优化过程及结果
	std::cout << summary.FullReport() << "\n";
	std::cout << "Initial x1 = " << x1234[0]
		<< ", x2 = " << x1234[1]
		<< ", x3 = " << x1234[2]
		<< ", x4 = " << x1234[3]
		<< "\n";

	std::system("pause");
	return 0;
}

结果如下:

Initial x1 = 3, x2 = -1, x3 = 0, x4 = 1
iter      cost      cost_change  |gradient|   |step|    tr_ratio  tr_radius  ls_iter  iter_time  total_time
   0  1.075000e+02    0.00e+00    1.55e+02   0.00e+00   0.00e+00  1.00e+04        0    1.01e-03    1.54e-03
   1  5.036190e+00    1.02e+02    2.00e+01   2.16e+00   9.53e-01  3.00e+04        1    1.61e-03    3.42e-03
   2  3.148168e-01    4.72e+00    2.50e+00   6.23e-01   9.37e-01  9.00e+04        1    6.19e-04    4.22e-03
   3  1.967760e-02    2.95e-01    3.13e-01   3.08e-01   9.37e-01  2.70e+05        1    7.00e-04    5.03e-03
   4  1.229900e-03    1.84e-02    3.91e-02   1.54e-01   9.37e-01  8.10e+05        1    5.11e-04    5.69e-03
   5  7.687123e-05    1.15e-03    4.89e-03   7.69e-02   9.37e-01  2.43e+06        1    4.93e-04    6.29e-03
   6  4.804625e-06    7.21e-05    6.11e-04   3.85e-02   9.37e-01  7.29e+06        1    4.78e-04    6.88e-03
   7  3.003028e-07    4.50e-06    7.64e-05   1.92e-02   9.37e-01  2.19e+07        1    5.01e-04    7.49e-03
   8  1.877006e-08    2.82e-07    9.54e-06   9.62e-03   9.37e-01  6.56e+07        1    7.43e-04    8.35e-03
   9  1.173223e-09    1.76e-08    1.19e-06   4.81e-03   9.37e-01  1.97e+08        1    7.21e-04    9.21e-03
  10  7.333425e-11    1.10e-09    1.49e-07   2.40e-03   9.37e-01  5.90e+08        1    7.08e-04    1.00e-02
  11  4.584044e-12    6.88e-11    1.86e-08   1.20e-03   9.37e-01  1.77e+09        1    5.00e-04    1.07e-02
  12  2.865573e-13    4.30e-12    2.33e-09   6.02e-04   9.37e-01  5.31e+09        1    4.85e-04    1.13e-02
  13  1.791438e-14    2.69e-13    2.91e-10   3.01e-04   9.37e-01  1.59e+10        1    4.82e-04    1.19e-02
  14  1.120029e-15    1.68e-14    3.64e-11   1.51e-04   9.37e-01  4.78e+10        1    6.79e-04    1.27e-02

Solver Summary (v 2.0.0-eigen-(3.3.8)-no_lapack-eigensparse-no_openmp)

                                     Original                  Reduced
Parameter blocks                            1                        1
Parameters                                  4                        4
Residual blocks                             1                        1
Residuals                                   4                        4

Minimizer                        TRUST_REGION

Dense linear algebra library            EIGEN
Trust region strategy     LEVENBERG_MARQUARDT

                                        Given                     Used
Linear solver                        DENSE_QR                 DENSE_QR
Threads                                     1                        1
Linear solver ordering              AUTOMATIC                        1

Cost:
Initial                          1.075000e+02
Final                            1.120029e-15
Change                           1.075000e+02

Minimizer iterations                       15
Successful steps                           15
Unsuccessful steps                          0

Time (in seconds):
Preprocessor                         0.000531

  Residual only evaluation           0.000361 (14)
  Jacobian & residual evaluation     0.001866 (15)
  Linear solver                      0.005511 (14)
Minimizer                            0.014814

Postprocessor                        0.000128
Total                                0.015473

Termination:                      CONVERGENCE (Gradient tolerance reached. Gradient max norm: 3.642190e-11 <= 1.000000e-10)

Initial x1 = 0.000146222, x2 = -1.46222e-05, x3 = 2.40957e-05, x4 = 2.40957e-05
3.3 cost1_x4_auto
  1. 1个代价函数(CostFunction),输出残差维度为4;
  2. 4个参数块(ParametersBlock),每个参数块的输入参数维度为1;
  3. 导数计算采用自动微分。

完整代码如下:

#include "ceres/ceres.h"
#include "gflags/gflags.h"
#include "glog/logging.h"

//	用户自定义残差计算模型
//	输入4个参数块,参数块维度为1
//	输出1个残差块,残差块维度为4
struct F1234
{
	template<typename T>
	bool operator()(const T* const x1, const T* const x2, const T* const x3, const T* const x4, T* residual) const
	{
		residual[0] = x1[0] + 10.0 * x2[0];
		residual[1] = sqrt(5.0) * (x3[0] - x4[0]);
		residual[2] = (x2[0] - 2.0 * x3[0]) * (x2[0] - 2.0 * x3[0]);
		residual[3] = sqrt(10.0) * (x1[0] - x4[0]) * (x1[0] - x4[0]);
		return true;
	}
};

int main(int argc, char** argv)
{
	GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, true);
	google::InitGoogleLogging(argv[0]);

	//	设置参数初始值,输入4个参数块,每个参数块的维度为1
	double x1 = 3.0;
	double x2 = -1.0;
	double x3 = 0.0;
	double x4 = 1.0;

	//	构建非线性最小二乘问题
	ceres::Problem problem;
	//	添加残差块,需要依次指定代价函数、损失函数、参数块
	//	本例中损失函数为单位函数,成本函数需要输入4个参数块
	problem.AddResidualBlock(new ceres::AutoDiffCostFunction<F1234, 4, 1, 1, 1, 1>(new F1234), NULL, &x1, &x2, &x3, &x4);

	//	配置求解器参数
	ceres::Solver::Options options;
	//	设置最大迭代次数
	options.max_num_iterations = 100;
	//	指定线性求解器来求解问题
	options.linear_solver_type = ceres::DENSE_QR;
	//	输出每次迭代的信息
	options.minimizer_progress_to_stdout = true;

	//	输出日志内容
	ceres::Solver::Summary summary;

	std::cout << "Initial x1 = " << x1
		<< ", x2 = " << x2
		<< ", x3 = " << x3
		<< ", x4 = " << x4
		<< "\n";

	//	开始优化求解
	ceres::Solve(options, &problem, &summary);

	//	输出优化过程及结果
	std::cout << summary.FullReport() << "\n";
	std::cout << "Final x1 = " << x1
		<< ", x2 = " << x2
		<< ", x3 = " << x3
		<< ", x4 = " << x4
		<< "\n";

	std::system("pause");
	return 0;
}

结果如下:

Initial x1 = 3, x2 = -1, x3 = 0, x4 = 1
iter      cost      cost_change  |gradient|   |step|    tr_ratio  tr_radius  ls_iter  iter_time  total_time
   0  1.075000e+02    0.00e+00    1.55e+02   0.00e+00   0.00e+00  1.00e+04        0    5.29e-04    9.91e-04
   1  5.036190e+00    1.02e+02    2.00e+01   2.16e+00   9.53e-01  3.00e+04        1    1.15e-03    2.31e-03
   2  3.148168e-01    4.72e+00    2.50e+00   6.23e-01   9.37e-01  9.00e+04        1    4.61e-04    2.87e-03
   3  1.967760e-02    2.95e-01    3.13e-01   3.08e-01   9.37e-01  2.70e+05        1    4.50e-04    3.43e-03
   4  1.229900e-03    1.84e-02    3.91e-02   1.54e-01   9.37e-01  8.10e+05        1    4.57e-04    3.98e-03
   5  7.687123e-05    1.15e-03    4.89e-03   7.69e-02   9.37e-01  2.43e+06        1    5.65e-04    4.66e-03
   6  4.804625e-06    7.21e-05    6.11e-04   3.85e-02   9.37e-01  7.29e+06        1    5.15e-04    5.31e-03
   7  3.003028e-07    4.50e-06    7.64e-05   1.92e-02   9.37e-01  2.19e+07        1    4.49e-04    5.86e-03
   8  1.877006e-08    2.82e-07    9.54e-06   9.62e-03   9.37e-01  6.56e+07        1    7.00e-04    6.65e-03
   9  1.173223e-09    1.76e-08    1.19e-06   4.81e-03   9.37e-01  1.97e+08        1    4.69e-04    7.24e-03
  10  7.333425e-11    1.10e-09    1.49e-07   2.40e-03   9.37e-01  5.90e+08        1    4.77e-04    7.82e-03
  11  4.584044e-12    6.88e-11    1.86e-08   1.20e-03   9.37e-01  1.77e+09        1    6.49e-04    8.58e-03
  12  2.865573e-13    4.30e-12    2.33e-09   6.02e-04   9.37e-01  5.31e+09        1    6.43e-04    9.33e-03
  13  1.791438e-14    2.69e-13    2.91e-10   3.01e-04   9.37e-01  1.59e+10        1    6.26e-04    1.01e-02
  14  1.120029e-15    1.68e-14    3.64e-11   1.51e-04   9.37e-01  4.78e+10        1    7.81e-04    1.10e-02

Solver Summary (v 2.0.0-eigen-(3.3.8)-no_lapack-eigensparse-no_openmp)

                                     Original                  Reduced
Parameter blocks                            4                        4
Parameters                                  4                        4
Residual blocks                             1                        1
Residuals                                   4                        4

Minimizer                        TRUST_REGION

Dense linear algebra library            EIGEN
Trust region strategy     LEVENBERG_MARQUARDT

                                        Given                     Used
Linear solver                        DENSE_QR                 DENSE_QR
Threads                                     1                        1
Linear solver ordering              AUTOMATIC                        4

Cost:
Initial                          1.075000e+02
Final                            1.120029e-15
Change                           1.075000e+02

Minimizer iterations                       15
Successful steps                           15
Unsuccessful steps                          0

Time (in seconds):
Preprocessor                         0.000462

  Residual only evaluation           0.000325 (14)
  Jacobian & residual evaluation     0.001673 (15)
  Linear solver                      0.004690 (14)
Minimizer                            0.010687

Postprocessor                        0.000069
Total                                0.011219

Termination:                      CONVERGENCE (Gradient tolerance reached. Gradient max norm: 3.642190e-11 <= 1.000000e-10)

Final x1 = 0.000146222, x2 = -1.46222e-05, x3 = 2.40957e-05, x4 = 2.40957e-05

同样,也可以按照cost4_x1_auto, cost2_x2_auto等多种方式实现,读者可举一反三,自行实现验证,关键是一定要确保ceres::AutoDiffCostFunction<>中,输出残差维度和输入每个参数块维度,要和用户自定义残差计算模型(或代价函数)中,输出残差维度和输入每个参数块维度保持一致(通俗但不严谨的讲,可以理解为x[]和residual[]的长度),否则很有可能无法迭代出正确结果。

另外,按照cost1_x1指定输入和输出的维度,分别通过数值法和解析解法求解Powell方程的最小值。

3.4 cost1_x1_numeric
  1. 1个代价函数(CostFunction),输出残差维度为4;
  2. 1个参数块(ParametersBlock),输入参数维度为4;
  3. 导数计算采用数值微分法。

完整代码如下:

#include "ceres/ceres.h"
#include "gflags/gflags.h"
#include "glog/logging.h"

//	用户自定义残差计算模型
//	输入1个参数块,参数块维度为4
//	输出1个残差块,残差块维度为4
//	用户自定义残差计算模型
struct F1234
{
	bool operator()(const double* const x, double* residual) const
	{
		residual[0] = x[0] + 10.0 * x[1];
		residual[1] = sqrt(5.0) * (x[2] - x[3]);
		residual[2] = (x[1] - 2.0 * x[2]) * (x[1] - 2.0 * x[2]);
		residual[3] = sqrt(10.0) * (x[0] - x[3]) * (x[0] - x[3]);
		return true;
	}
};

int main(int argc, char** argv)
{
	GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, true);
	google::InitGoogleLogging(argv[0]);

	//	设置参数初始值,输入1个参数块,参数块的维度为4
	double x1234[4] = { 3, -1, 0, 1 };

	//	构建非线性最小二乘问题
	ceres::Problem problem;
	//	添加残差块,需要依次指定代价函数、损失函数、参数块
	//	本例中损失函数为单位函数,代价函数中需要输入1个参数块
	//	本例中数值计算导数方法为CENTRAL
	problem.AddResidualBlock(new ceres::NumericDiffCostFunction<F1234, ceres::CENTRAL, 4, 4>(new F1234), NULL, x1234);

	//	配置求解器参数
	ceres::Solver::Options options;
	//	设置最大迭代次数
	options.max_num_iterations = 100;
	//	指定线性求解器来求解问题
	options.linear_solver_type = ceres::DENSE_QR;
	//	输出每次迭代的信息
	options.minimizer_progress_to_stdout = true;

	//	输出日志内容
	ceres::Solver::Summary summary;

	std::cout << "Initial x1 = " << x1234[0]
		<< ", x2 = " << x1234[1]
		<< ", x3 = " << x1234[2]
		<< ", x4 = " << x1234[3]
		<< "\n";

	//	开始优化求解
	ceres::Solve(options, &problem, &summary);

	//	输出优化过程及结果
	std::cout << summary.FullReport() << "\n";
	std::cout << "Final x1 = " << x1234[0]
		<< ", x2 = " << x1234[1]
		<< ", x3 = " << x1234[2]
		<< ", x4 = " << x1234[3]
		<< "\n";

	std::system("pause");
	return 0;
}

结果如下:

Initial x1 = 3, x2 = -1, x3 = 0, x4 = 1
iter      cost      cost_change  |gradient|   |step|    tr_ratio  tr_radius  ls_iter  iter_time  total_time
   0  1.075000e+02    0.00e+00    1.55e+02   0.00e+00   0.00e+00  1.00e+04        0    5.58e-04    1.08e-03
   1  5.036190e+00    1.02e+02    2.00e+01   2.16e+00   9.53e-01  3.00e+04        1    1.09e-03    2.36e-03
   2  3.148168e-01    4.72e+00    2.50e+00   6.23e-01   9.37e-01  9.00e+04        1    4.22e-04    2.88e-03
   3  1.967760e-02    2.95e-01    3.13e-01   3.08e-01   9.37e-01  2.70e+05        1    4.18e-04    3.40e-03
   4  1.229900e-03    1.84e-02    3.91e-02   1.54e-01   9.37e-01  8.10e+05        1    4.14e-04    3.91e-03
   5  7.687123e-05    1.15e-03    4.89e-03   7.69e-02   9.37e-01  2.43e+06        1    4.15e-04    4.42e-03
   6  4.804625e-06    7.21e-05    6.11e-04   3.85e-02   9.37e-01  7.29e+06        1    6.04e-04    5.14e-03
   7  3.003028e-07    4.50e-06    7.64e-05   1.92e-02   9.37e-01  2.19e+07        1    4.81e-04    5.75e-03
   8  1.877006e-08    2.82e-07    9.54e-06   9.62e-03   9.37e-01  6.56e+07        1    5.56e-04    6.42e-03
   9  1.173223e-09    1.76e-08    1.19e-06   4.81e-03   9.37e-01  1.97e+08        1    4.80e-04    7.01e-03
  10  7.333425e-11    1.10e-09    1.49e-07   2.40e-03   9.37e-01  5.90e+08        1    6.15e-04    7.73e-03
  11  4.584044e-12    6.88e-11    1.86e-08   1.20e-03   9.37e-01  1.77e+09        1    6.46e-04    8.50e-03
  12  2.865573e-13    4.30e-12    2.33e-09   6.02e-04   9.37e-01  5.31e+09        1    6.46e-04    9.28e-03
  13  1.791438e-14    2.69e-13    2.91e-10   3.01e-04   9.37e-01  1.59e+10        1    6.57e-04    1.01e-02
  14  1.120029e-15    1.68e-14    3.64e-11   1.51e-04   9.37e-01  4.78e+10        1    6.38e-04    1.09e-02

Solver Summary (v 2.0.0-eigen-(3.3.8)-no_lapack-eigensparse-no_openmp)

                                     Original                  Reduced
Parameter blocks                            1                        1
Parameters                                  4                        4
Residual blocks                             1                        1
Residuals                                   4                        4

Minimizer                        TRUST_REGION

Dense linear algebra library            EIGEN
Trust region strategy     LEVENBERG_MARQUARDT

                                        Given                     Used
Linear solver                        DENSE_QR                 DENSE_QR
Threads                                     1                        1
Linear solver ordering              AUTOMATIC                        1

Cost:
Initial                          1.075000e+02
Final                            1.120029e-15
Change                           1.075000e+02

Minimizer iterations                       15
Successful steps                           15
Unsuccessful steps                          0

Time (in seconds):
Preprocessor                         0.000526

  Residual only evaluation           0.000313 (14)
  Jacobian & residual evaluation     0.001281 (15)
  Linear solver                      0.004963 (14)
Minimizer                            0.010534

Postprocessor                        0.000071
Total                                0.011131

Termination:                      CONVERGENCE (Gradient tolerance reached. Gradient max norm: 3.642190e-11 <= 1.000000e-10)

Final x1 = 0.000146222, x2 = -1.46222e-05, x3 = 2.40957e-05, x4 = 2.40957e-05
3.5 cost1_x1_analytic
  1. 1个代价函数(CostFunction),输出残差维度为4;
  2. 1个参数块(ParametersBlock),输入参数维度为4;
  3. 导数计算采用解析法。

Ceres中,每个雅可比分量表示如下:
j a c b i a n [ i ] [ r ∗ p a r a m e t e r s _ b l o c k _ s i z e _ [ i ] + c ] = ∂ r e s i d u a l [ r ] ∂ p a r a m e t e r s [ i ] [ c ] (4) jacbian[i][r * parameters\_block\_size\_[i] + c] = \frac{\partial residual[r]}{\partial parameters[i][c]} \tag{4} jacbian[i][rparameters_block_size_[i]+c]=parameters[i][c]residual[r](4)
式中, i i i 表示参数块索引, c c c 表示某个参数块中的参数索引, r r r 表示残差的索引。

由此可见,雅可比可以理解成是一个二维的数组,(不同于 C++ 中的二维数组,因为 C++ 中的二维数组列数都一样,类似于 std::vector<std::vector<double>>类型),是 double** 类型,表示一个数组,数组中的每个元素也是数组,有几个参数块就有几行,每行对应一个参数块,每行的列数为当前行参数块中参数的数量与残差维度的乘积。

对于本例,只有1个参数块,因此只有 jacobian[0] ,残差的维度为4,参数块中参数数量为4,因此 jacobian[0] 中共有4*4个分量。

完整代码如下:

#include "ceres/ceres.h"
#include "gflags/gflags.h"
#include "glog/logging.h"

class CostFunction1234
	: public ceres::SizedCostFunction<4, /*输出(residual)维度大小*/\
	4 /*输入参数块维度大小*/>
{
public:
	virtual ~CostFunction1234() {}

	//	用户自定义残差计算方法
	virtual bool Evaluate(double const* const* x, /*输入参数块*/\
		double* residual, /*输出残差*/\
		double** jacobians /*输出雅可比矩阵*/) const
	{
		//	本例中1个参数块中有4个输入参数
		double x1 = x[0][0];
		double x2 = x[0][1];
		double x3 = x[0][2];
		double x4 = x[0][3];

		//	本例中输出残差维度为4
		residual[0] = x1 + 10.0 * x2;
		residual[1] = sqrt(5.0) * (x3 - x4);
		residual[2] = (x2 - 2.0 * x3) * (x2 - 2.0 * x3);
		residual[3] = sqrt(10.0) * (x1 - x4) * (x1 - x4);

		if(jacobians != NULL && jacobians[0] != NULL)
		{
			// 第1个维度残差对参数块中的参数依次求偏导
			jacobians[0][0] = 1.0;
			jacobians[0][1] = 10.0;
			jacobians[0][2] = 0.0;
			jacobians[0][3] = 0.0;
			//	第2个维度残差对参数块中的参数依次求偏导
			jacobians[0][4] = 0.0;
			jacobians[0][5] = 0.0;
			jacobians[0][6] = sqrt(5.0);
			jacobians[0][7] = -sqrt(5.0);
			//	第3个维度残差对参数块中的参数依次求偏导
			jacobians[0][8] = 0.0;
			jacobians[0][9] = 2 * (x2 - 2 * x3);
			jacobians[0][10] = -4 * (x2 - 2 * x3);
			jacobians[0][11] = 0.0;
			//	第4个维度残差对参数块中的参数依次求偏导
			jacobians[0][12] = 2 * sqrt(10.0) * (x1 - x4);
			jacobians[0][13] = 0.0;
			jacobians[0][14] = 0.0;
			jacobians[0][15] = -2 * sqrt(10.0) * (x1 - x4);
		}
		return true;
	}
};

int main(int argc, char** argv)
{
	GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, true);
	google::InitGoogleLogging(argv[0]);

	//	设置参数初始值,输入1个参数块,参数块的维度为4
	double x1234[4] = { 3, -1, 0, 1 };

	//	构建非线性最小二乘问题
	ceres::Problem problem;
	//	添加残差块,需要依次指定代价函数、损失函数、参数块
	//	本例中损失函数为单位函数,代价函数需要输入1个参数块
	problem.AddResidualBlock(new CostFunction1234, NULL, x1234);

	//	配置求解器参数
	ceres::Solver::Options options;
	//	设置最大迭代次数
	options.max_num_iterations = 100;
	//	指定线性求解器来求解问题
	options.linear_solver_type = ceres::DENSE_QR;
	//	输出每次迭代的信息
	options.minimizer_progress_to_stdout = true;

	//	输出日志内容
	ceres::Solver::Summary summary;

	std::cout << "Initial x1 = " << x1234[0]
		<< ", x2 = " << x1234[1]
		<< ", x3 = " << x1234[2]
		<< ", x4 = " << x1234[3]
		<< "\n";

	//	开始优化求解
	ceres::Solve(options, &problem, &summary);

	//	输出优化过程及结果
	std::cout << summary.FullReport() << "\n";
	std::cout << "Final x1 = " << x1234[0]
		<< ", x2 = " << x1234[1]
		<< ", x3 = " << x1234[2]
		<< ", x4 = " << x1234[3]
		<< "\n";

	std::system("pause");
	return 0;
}

结果如下:

Initial x1 = 3, x2 = -1, x3 = 0, x4 = 1
iter      cost      cost_change  |gradient|   |step|    tr_ratio  tr_radius  ls_iter  iter_time  total_time
   0  1.075000e+02    0.00e+00    1.55e+02   0.00e+00   0.00e+00  1.00e+04        0    4.30e-04    8.54e-04
   1  5.036190e+00    1.02e+02    2.00e+01   2.16e+00   9.53e-01  3.00e+04        1    1.17e-03    2.19e-03
   2  3.148168e-01    4.72e+00    2.50e+00   6.23e-01   9.37e-01  9.00e+04        1    5.59e-04    2.88e-03
   3  1.967760e-02    2.95e-01    3.13e-01   3.08e-01   9.37e-01  2.70e+05        1    5.91e-04    3.59e-03
   4  1.229900e-03    1.84e-02    3.91e-02   1.54e-01   9.37e-01  8.10e+05        1    5.45e-04    4.25e-03
   5  7.687123e-05    1.15e-03    4.89e-03   7.69e-02   9.37e-01  2.43e+06        1    1.02e-03    5.47e-03
   6  4.804625e-06    7.21e-05    6.11e-04   3.85e-02   9.37e-01  7.29e+06        1    4.56e-04    6.06e-03
   7  3.003028e-07    4.50e-06    7.64e-05   1.92e-02   9.37e-01  2.19e+07        1    4.22e-04    6.59e-03
   8  1.877006e-08    2.82e-07    9.54e-06   9.62e-03   9.37e-01  6.56e+07        1    6.06e-04    7.30e-03
   9  1.173223e-09    1.76e-08    1.19e-06   4.81e-03   9.37e-01  1.97e+08        1    5.80e-04    8.00e-03
  10  7.333425e-11    1.10e-09    1.49e-07   2.40e-03   9.37e-01  5.90e+08        1    5.90e-04    8.71e-03
  11  4.584044e-12    6.88e-11    1.86e-08   1.20e-03   9.37e-01  1.77e+09        1    5.94e-04    9.42e-03
  12  2.865573e-13    4.30e-12    2.33e-09   6.02e-04   9.37e-01  5.31e+09        1    4.47e-04    9.97e-03
  13  1.791438e-14    2.69e-13    2.91e-10   3.01e-04   9.37e-01  1.59e+10        1    6.11e-04    1.07e-02
  14  1.120029e-15    1.68e-14    3.64e-11   1.51e-04   9.37e-01  4.78e+10        1    4.42e-04    1.13e-02

Solver Summary (v 2.0.0-eigen-(3.3.8)-no_lapack-eigensparse-no_openmp)

                                     Original                  Reduced
Parameter blocks                            1                        1
Parameters                                  4                        4
Residual blocks                             1                        1
Residuals                                   4                        4

Minimizer                        TRUST_REGION

Dense linear algebra library            EIGEN
Trust region strategy     LEVENBERG_MARQUARDT

                                        Given                     Used
Linear solver                        DENSE_QR                 DENSE_QR
Threads                                     1                        1
Linear solver ordering              AUTOMATIC                        1

Cost:
Initial                          1.075000e+02
Final                            1.120029e-15
Change                           1.075000e+02

Minimizer iterations                       15
Successful steps                           15
Unsuccessful steps                          0

Time (in seconds):
Preprocessor                         0.000423

  Residual only evaluation           0.000351 (14)
  Jacobian & residual evaluation     0.000778 (15)
  Linear solver                      0.005656 (14)
Minimizer                            0.011010

Postprocessor                        0.000057
Total                                0.011491

Termination:                      CONVERGENCE (Gradient tolerance reached. Gradient max norm: 3.642190e-11 <= 1.000000e-10)

Final x1 = 0.000146222, x2 = -1.46222e-05, x3 = 2.40957e-05, x4 = 2.40957e-05
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值