ceres Analytic Derivatives

ceres Analytic Derivatives

ceres是一个非线性最小二乘的库
其中主要求解方式有Analytic Derivatives,AutoDerivatives & Numeric Derivatives这三种。AutoDerivatives最常用也最方便使用,Analytic Derivatives在雅各比矩阵计算上可能会快于AutoDerivatives,所以在某些地方可以使用Analytic Derivatives来加速优化过程。

Analytic Derivatives需要完成ceres::SizedCostFunction中Evaluate的定义
其中,SizedCostFunction声明为
在这里插入图片描述
这里<>中的第一个参数为残差的数量(残差可以是一个向量),后面的int表示参数块的大小,可以输入多个参数块(比如多个数组或者vector等),然后在AddResidualBlock()时,输入的参数的大小要和<>中的一致

Evaluate的函数声明为:
在这里插入图片描述
这里parameters为参数块,由于参数块可以输入多个,所以他是一个指向double指针的指针,residulas为残差,最多是一位向量,所以是一个指向double的指针。雅各比矩阵显然是一个二维的(如果有m维残差向量,n维参数向量,那么雅各比是一个mxn的矩阵),这里重点是雅各比是如何存储的

比如现在有两个参数块,分别是vector p1, vector p2,大小分别为3,2
p1[0]在parameters[0][0]中,p1[1]在parameters[0][1],p1[2]在parameters[0][2]中
p2[0]在parameters[1][0]中,p2[1]在parameters[1][1]中

然后残差是二维的r1 在residuals[0]中,r2在residuals[1]中

重点为雅各比矩阵存储,第i个参数块都放在parameters[i]指向的double数组中,所有残差依次对第i个参数块中所有参数求导的结果都依次放在jacobians[i]中
在这里插入图片描述
这里,c为parameters[I]中的第c个参数
延用上面的例子,
jacobians[0][0]存放r1对p1[0]求导
jacobians[0][1]存放r1对p1[1]求导
jacobians[0][2]存放r1对p1[2]求导
jacobians[0][3]存放r2对p1[0]求导
jacobians[0][4]存放r2对p1[1]求导
jacobians[0][5]存放r2对p1[2]求导

这里给出两个SizedCostFunction为多维的情况的代码,一个是slambook2中的位姿估计的改写,一个是曲线拟合

下面是slam十四讲中ch7,用ceres Analytic Derivatives对pose_estimation_3d2d的修改,放了SizedCostFunction

class pnp_optimization : public ceres::SizedCostFunction<2, 6>{
public:
    pnp_optimization(const Eigen::Vector3d &point3d, const Eigen::Vector2d &point2d, const Eigen::Matrix3d &K) :
            _point3d(point3d), _point2d(point2d), Kk(K) {}
            
    virtual bool Evaluate(double const* const* parameters,
                          double* residuals,
                          double** jacobians) const {
        typedef Eigen::Matrix<double, 6, 1> vector6d;
        vector6d se3;
        se3 << parameters[0][0], parameters[0][1], parameters[0][2], parameters[0][3],parameters[0][4], parameters[0][5];
        Sophus::SE3d T = Sophus::SE3d::exp(se3);
        double fx = Kk(0, 0);
        double fy = Kk(1, 1);
        double cx = Kk(0, 2);
        double cy = Kk(1, 2);
        Eigen::Vector3d pc = T * _point3d;
        Eigen::Vector2d proj(fx * pc[0] / pc[2] + cx, fy * pc[1] / pc[2] + cy);
        Eigen::Vector2d e = _point2d - proj;

        residuals[0] = e[0];
        residuals[1] = e[1];

        //cout << residuals[0] << " " << residuals[1] << endl;

        if (jacobians != nullptr && jacobians[0] != nullptr){
            double inv_z = 1.0 / pc[2];
            double inv_z2 = inv_z * inv_z;

            jacobians[0][0] = -fx * inv_z;
            jacobians[0][1] = 0;
            jacobians[0][2] = fx * pc[0] * inv_z2;
            jacobians[0][3] = fx * pc[0] * pc[1] * inv_z2;
            jacobians[0][4] = -fx + fx * pc[0] * pc[0] * inv_z2;
            jacobians[0][5] = fx * pc[1] * inv_z;
            jacobians[0][6] = 0;
            jacobians[0][7] = -fy * inv_z;
            jacobians[0][8] = fy * pc[1] * inv_z2;
            jacobians[0][9] = fy + fy * pc[1] * pc[1] * inv_z2;
            jacobians[0][10] = -fy * pc[0] * pc[1] * inv_z2;
            jacobians[0][11] = -fy * pc[0] * inv_z;
        }
        return true;
    }
private:
    const Eigen::Vector3d _point3d;
    const Eigen::Vector2d _point2d;
    const Eigen::Matrix3d Kk;
};

下面是自己写的一个曲线拟合的例子,sizedcostfuction为2x2

#include <iostream>
#include <ceres/ceres.h>

#define DATA_LEN 100

using namespace std;
float getRand();

class test : public ceres::SizedCostFunction<2,2> {
public:
    test (const double x, const vector<double> y) : x_(x), y_(y) {}
    virtual bool Evaluate (double const* const* parameters,
                           double* residuals,
                           double** jacobians) const {
        double a, b;
        a = parameters[0][0];
        b = parameters[0][1];

        residuals[0] = y_[0] - a*x_*x_ - b*x_;
        residuals[1] = y_[1] - a*x_ - b;

        if (jacobians != nullptr) {
            if (jacobians[0] != nullptr) {
                jacobians[0][0] = -x_*x_;
                jacobians[0][1] = -x_;
                jacobians[0][2] = -x_;
                jacobians[0][3] = 0;
                //printf("%lf, %lf, %lf, %lf\n", jacobians[0][0], jacobians[0][1], jacobians[0][2], jacobians[0][3]);
            }
        }
    }

private:
    const double x_;
    const vector<double> y_;
};

int main() {
    double para[2];
    vector<vector<double>> y_true(DATA_LEN, vector<double>(2, 0));
    vector<vector<double>> y_in(DATA_LEN, vector<double>(2, 0));

    para[0] = 2;
    para[1] = 0.5;
    for (int i = 0; i < DATA_LEN; i++) {
        //x = 0 : 1 : DATA_LEN
        //y1 = ax^2+bx;
        //y2 = ax + b;
        double x = i;
        y_true[i][0] = para[0] * x * x + para[1] * x;
        y_true[i][1] = para[0] * x + para[1];

        y_in[i][0] = y_true[i][0] + getRand();
        y_in[i][1] = y_true[i][1] + getRand();
    }
    double mypara[2] = {1, 2};
    ceres::Problem problem;
    for (int i = 0; i < DATA_LEN; i++) {
        ceres::CostFunction *pCostFunction = new test(i, y_in[i]);
        problem.AddResidualBlock(pCostFunction, NULL, mypara);
    }

    ceres::Solver::Options options;
    options.linear_solver_type = ceres::DENSE_NORMAL_CHOLESKY;
    options.minimizer_progress_to_stdout = true;

    ceres::Solver::Summary summary;
    ceres::Solve(options, &problem, &summary);
    cout << mypara[0] << endl << mypara[1] << endl;

    return 0;
}

float getRand()
{
    return 2.0 * rand() / RAND_MAX - 1.0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值