从0开始学习Ceres NO.2

上一篇文章从0开始学习Ceres NO.1中学了用ceres自动求解一个简单的最小化问题。对于同样的问题,这篇文章介绍两个事情:

如果不用模板呢?

在某些情况下我们可能无法定义残差的模板,那么我们不得不确定地给出残差的形式。(具体什么情况我现在也不知道,碰到了再回来补充吧)
那么我们定义残差的结构体就从模板变成了:

struct  NumericDiffCostFunctor{
    bool operator()(const double* const x, double* residual) const{
        residual[0] = 10.0 -x[0];
        return true;
    }
};

主函数与原来基本相同,只是CostFunction类的定义变成了,这里我们将求导的方式从自动求导转变成了数值微分求导:

ceres::CostFunction* cost_function =
        new ceres::NumericDiffCostFunction<NumericDiffCostFunctor, ceres::CENTRAL, 1, 1>(new NumericDiffCostFunctor);

ceres::NumericDiffCostFunction
用来表示接受非模板结构体使用数值微分求导的Cost函数类,跟AutoDiffCostFunction一样都是SizedCostFunction的子类。模板参数列表为

// 对应main函数中的<NumericDiffCostFunctor, ceres::CENTRAL, 1, 1>
template <typename CostFunctor,											// 自己定义的CostFunctor结构体
          NumericDiffMethodType method = CENTRAL,			// 用于计算的微分方法
          																										// CENTRAL表示中心有限差分f'(x) ~ (f(x+h) - f(x-h)) / 2h.
          																										// FORWARD表示前向有限差分 f'(x) ~ (f(x+h) - f(x)) / h.
          																										// RIDDERS表示自适应微分
          int kNumResiduals = 0,														// 输入参数块的维度
          int... Ns>																					// 参数块每个成员的维度

主函数为

int main(){
    double initial_x=5.0;
    double x = initial_x;
    
    ceres::Problem problem;
    ceres::CostFunction* cost_function =
        new ceres::NumericDiffCostFunction<NumericDiffCostFunctor, ceres::CENTRAL, 1, 1>(new 	NumericDiffCostFunctor);

    problem.AddResidualBlock(cost_function, nullptr, &x);

    ceres::Solver::Options options;
    options.minimizer_progress_to_stdout = true;
    ceres::Solver::Summary summary;
    ceres::Solve(options, &problem, &summary);

    cout << summary.BriefReport() << endl;
    cout << "x: " << initial_x << "-> " << x << endl;

    return 0;
}

如果自己定义求导呢?

在某些情况下使用自动求导是不现实的(具体什么情况下以后再补充),那么我们可以直接继承SizedCostFunction自己编写CostFunction类,如下

class QuadraticCostFunction: public ceres::SizedCostFunction<1, 1>{
public:
    virtual ~QuadraticCostFunction(){}
    virtual bool Evaluate(double const* const* parameters,
                            double* residuals,
                            double** jacobians) const{
        const double x = parameters[0][0];
        residuals[0] = 10-x;

        if(jacobians != nullptr && jacobians[0] != nullptr){
            jacobians[0][0] = -1;
        }
        return true;
    }
};

在这种简单问题下,我们对CostFunction的定义只有一个任务,即重写Evaluate函数。
Evaluate
CostFunction类中计算残差和雅克比矩阵的函数,输入为:parameters参与计算的参数、residuals返回的残差容器、jacobians返回的雅克比容器。当jacobians是nullptr时不用计算残差,否则需要给出雅克比计算的定义,jacobians每一块是residuals.size()*parameters[i].size()(即残差块对第i个参数块的求导),表示每个参数对残差的求导。当jacobians[i]是nullptr时不用计算该行雅克比,在第i个参数被固定不优化时会出现这种情况。
雅克比计算如下:
j a c o b i a n s [ i ] [ r ∗ p a r a m e t e r s [ i ] . s i z e ( ) + c ] = δ r e s i d u a l [ r ] δ p a r a m e t e r s [ i ] [ c ] jacobians[i][r*parameters[i].size()+c]=\frac{\delta residual[r]}{\delta parameters[i][c]} jacobians[i][rparameters[i].size()+c]=δparameters[i][c]δresidual[r]
主函数为

int main(){
    double initial_x=5.0;
    double x = initial_x;
    
    ceres::Problem problem;

    ceres::CostFunction* cost_function = new QuadraticCostFunction;

    problem.AddResidualBlock(cost_function, nullptr, &x);

    ceres::Solver::Options options;
    options.minimizer_progress_to_stdout = true;
    ceres::Solver::Summary summary;
    ceres::Solve(options, &problem, &summary);

    cout << summary.BriefReport() << endl;
    cout << "x: " << initial_x << "-> " << x << endl;

    return 0;
}

总结

本文介绍了如何不使用模板来构造微分求导以及如何自己定义求导构造CostFunction

ceres建议使用自动求导(automatic differentiation) 而不是 数值微分求导(numeric differentiation),因为使用模板类的自动求导效率更高,且数值微分求导容易出现数值误差,导致收敛速度慢
ceres还建议除非你必须使用自定义求导,否则还是使用上面两种方式吧

文章的源程序可以去我的github下,CeresLearning中tag名字为Ceres_Turotial_02的文件
请添加图片描述

文章列表

Ceres安装和卸载 ubuntu18.04
从0开始学习Ceres NO.1
从0开始学习Ceres NO.2
从0开始学习Ceres NO.3

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值