该优化库在slam中常用,记录一下,防止忘记
Ceres-solver 是解决具有边界约束的非线性最小二乘优化和一般无约束优化问题。=
首先就是一些概念的问题,它解决的是优化问题,也就是什么样的输入会使预计值接近观测值,所以有了残差的概念,残差就是观测值 - 预计值
里面一系列x被称为参数块(笔者理解就是优化对象),而fx函数被称为代价函数,是关于参数块的函数,代价函数用于衡量模型预测值与实际观测值之间的差异,优化过程中希望最小化这个函数。px被称为损失函数,损失函数是用来减少某些观测点对优化过程影响的一个工具,通常用于处理异常值或强调某些观测的影响。损失函数可以将代价函数中的每个残差转换为一个值,允许对某些残差赋予更大的权重或惩罚。
总体而言,代价函数用于计算整体误差,是优化的主要目标。损失函数则用于调整代价函数中每个误差的贡献,以便对异常值或特定观测给予不同的处理。这一些列整体被称为残差块(ResidualBlock
),残差块中包含了参数块、代价函数、损失函数。
Ceres 求解过程主要有两大步骤,构建问题和求解问题
先是自定义残差计算方式,也就是你要优化什么问题,举个例子,对于求解该函数的最小值问题,先构建残差计算方式:
struct MyCostFunctorAutoDiff
{
template<typename Type>
bool operator()(const Type* const x, Type* residual) const
{
residual[0] = 12.0 - x[0];
return true;
}
};
在ceres中,double
类型与 ceres::Jet
类型之间可以进行自动类型转换。所以要写12.0。
然后构建代价函数和问题:
在这里,先构建Ceres代价函数CostFuntion,用来计算残差,残差计算方法为自定义残差计算模型MyCostFunctorAutoDiff,由于只存在一个代价函数,使用自动微分方法AutoDiffCostFunction来计算导数,而AutoDiffCostFunction<MyCostFunctorAutoDiff, 1, 1>模板参数中,需要依次指定
用户自定义残差计算模型MyCostFunctorAutoDiff、输出(resudual)维度大小、输入(参数x)维度大小。这两个维度大小需要与残差计算模型中输入、输出参数的维度一致,对应residual[0]和x[0]。
在构建问题时添加残差块ResidualBlock
,需要依次指定代价函数CostFunction
,损失函数LossFunction
(这里为单位函数),参数块ParameterBlock
。
// 构建非线性最小二乘问题
ceres::Problem problem;
// 添加残差块,需要依次指定代价函数,损失函数,参数块
// 损失函数为单位函数
problem.AddResidualBlock(new ceres:: AutoDiffCostFunction<MyCostFunctorAutoDiff, 1, 1>(new MyCostFunctorAutoDiff());, nullptr, &x);
接着,配置求解器参数Options、输出日志内容Summary:
// 配置求解器参数
ceres::Solver::Options options;
//也可以指定最大迭代次数
//options.max_num_iterations = 25;
// 指定线性求解器来求解问题
options.linear_solver_type = ceres::DENSE_QR;
// 输出每次迭代的信息
options.minimizer_progress_to_stdout = true;
// 输出日志内容
ceres::Solver::Summary summary;
最终,优化求解:
// 开始优化求解
ceres::Solve(options, &problem, &summary);
这一系列就对x输出最终的最优解 ,经过这些,我们初步对ceres库有了一个了解