Hello World
我们尝试找到如下函数的最小值
貌似这个问题太简单了哈,最小值点在 处取得,但是计算机不是人,我们只能通过程序的方式来让他求解,这就是 Ceres 要做的事。接下来我们就来通过 Ceres 来求解该问题。
第一步是编写一个 functor 来评估函数
struct CostFunctor {
template <typename T>
bool operator()(const T* const x, T* residual) const {
residual[0] = 10.0 - x[0];
return true;
}
};
上述最重要的部分是 operator()
这个重载函数,它是一个模板函数,它所有的输入和输出类型都是某种类型 T
。在这里使用模板则允许 Ceres 调用 CostFunctor::operator<T>()
,当只需要残差值时使用 T=double
,当同时需要雅可比时使用特殊类型 T=Jet
(这个类型是 Ceres 为什么能够进行自动求导的关键)。在 Derivatives 中,也就是后文的导数章节,我们将更详细地讨论向 Ceres 提供导数的各种方法。
一旦我们有了计算残差的方法,就可以利用它去构建一个非线性最小二乘法问题,并使用 Ceres 解决它。
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>();
problem.AddResidualBlock(cost_function, nullptr, &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;
}
AutoDiffCostFunction
将一个CostFunctor
作为输入,自动对其进行微分,并给出一个 CostFunction
接口。
编译并运行 examples/helloworld.cc 将会得到如下结果
iter cost cost_change |gradient| |step| tr_ratio tr_radius ls_iter iter_time total_time
0 4.512500e+01 0.00e+00 9.50e+00 0.00e+00 0.00e+00 1.00e+04 0 5.33e-04 3.46e-03
1 4.511598e-07 4.51e+01 9.50e-04 9.50e+00 1.00e+00 3.00e+04 1 5.00e-04 4.05e-03
2 5.012552e-16 4.51e-07 3.17e-08 9.50e-04 1.00e+00 9.00e+04 1 1.60e-05 4.09e-03
Ceres Solver Report: Iterations: 2, Initial cost: 4.512500e+01, Final cost: 5.012552e-16, Termination: CONVERGENCE
x : 0.5 -> 10
的初始值设置为 (Ceres 的英文文档这里写的 ,貌似是个笔误),经过两轮迭代后为 ,细心的读者会发现,这是一个线性问题,一次线性求解就足以得到最优值,然而 Ceres 求解器的默认配置是针对非线性问题的,为了简单起见,我们在本例中没有更改,不过,使用 Ceres 只进行一次迭代也确实可以得到这个问题的解。我们将在讨论 Ceres 的收敛性和参数设置时更详细地讨论这些问题。
Footnotes
实际上,求解器运行了三次迭代,通过观察第三次迭代中线性求解器返回的值,它发现参数块的更新太小,于是宣布收敛。Ceres 只在迭代结束时打印显示,一旦检测到收敛就会终止,这就是为什么你在这里只看到两次迭代而不是三次。
Last updated