ceres使用总结

自动微分Jet原理

CostFunction

CostFunction是有用来计算残差e和雅阁比J的,给出状态值向量[x1,x2,...xn]后按道理就可以根据方程f(x1,x2,...xn)求解出方程的结果vector和雅阁比矩阵J

class CostFunction {
 public:
  virtual bool Evaluate(double const* const* parameters,
                        double* residuals,
                        double** jacobians) = 0;
  const vector<int32>& parameter_block_sizes();
  int num_residuals() const;

 protected:
  vector<int32>* mutable_parameter_block_sizes();
  void set_num_residuals(int num_residuals);
};
  1. CostFunction::parameter_block_sizes_
    每个状态值的维度,比如x1是pose的话维度就是7,x2是速度和bias的话维度就是9
  2. CostFunction::num_residuals_
    残差的维度,比如视觉就是2,相对pose就是6
  3. 继承函数Evaluate,必须继承用于计算出残差和雅各比的,在vins_mono的边缘化中就是使用的Evaluate函数来计算出原始的residuals和raw jacobians
//输入:parameters 输出:residuals jacobians
bool CostFunction::Evaluate(double const *const *parameters, double *residuals, double **jacobians)
  1. parameters:状态值向量,维度不一定为1
    x = [ x 1 x 2 ⋮ x n ] x=\left[ \begin{array}{l} x_1 \\ x_2 \\ \vdots \\x_n\end{array} \right] x= x1x2xn
  2. parameter_block_sizes_:每个状态值对应的参数维度
    s = [ s 1 s 2 ⋮ s n ] s=\left[ \begin{array}{l} s_1 \\ s_2\\ \vdots \\s_n\end{array} \right] s= s1s2sn
  3. residuals:残差,维度为m的残差对应的向量为
    e = [ e 1 e 2 ⋮ e m ] e=\left[ \begin{array}{l} e_1 \\ e_2 \\ \vdots \\e_m\end{array} \right] e= e1e2em
  4. jacobians:雅阁比矩阵的维度为n
    J = [ ∂ e 1 ∂ x 1 ( 1 × s 1 ) ∂ e 2 ∂ x 1 ( 1 × s 1 ) ⋯ ∂ e m ∂ x 1 ( 1 × s 1 ) ∂ e 1 ∂ x 2 ( 1 × s 2 ) ∂ e 2 ∂ x 2 ( 1 × s 2 ) ⋯ ∂ e m ∂ x 2 ( 1 × s 2 ) ⋮ ⋮ ⋮ ∂ e 1 ∂ x n ( 1 × s n ) ∂ e 2 ∂ x n ( 1 × s n ) ⋯ ∂ e m ∂ x n ( 1 × s n ) ] J=\left[ \begin{array}{l} \frac{\partial e_1}{\partial x_1}_{(1\times s_1)} & \frac{\partial e_2}{\partial x_1}_{(1\times s_1)} \cdots & \frac{\partial e_m}{\partial x_1}_{(1\times s_1)} \\ \frac{\partial e_1}{\partial x_2}_{(1\times s_2)} & \frac{\partial e_2}{\partial x_2}_{(1\times s_2)} \cdots & \frac{\partial e_m}{\partial x_2}_{(1\times s_2)} \\ \vdots &\vdots &\vdots \\ \frac{\partial e_1}{\partial x_n}_{(1\times s_n)} & \frac{\partial e_2}{\partial x_n}_{(1\times s_n)} \cdots & \frac{\partial e_m}{\partial x_n}_{(1\times s_n)} \\\end{array} \right] J= x1e1(1×s1)x2e1(1×s2)xne1(1×sn)x1e2(1×s1)x2e2(1×s2)xne2(1×sn)x1em(1×s1)x2em(1×s2)xnem(1×sn)
  5. ,第i行的维度为表示所有的残差对状态值xi的雅各比
    维度为:
    m × s i m\times s_i m×si
    雅各比为:
    J i = [ ∂ e 1 ∂ x i ( 1 × s i ) ∂ e 2 ∂ x i ( 1 × s i ) ⋯ ∂ e m ∂ x i ( 1 × s i ) ] J_i=\left[ \begin{array}{l} \frac{\partial e_1}{\partial x_i}_{(1\times s_i)} & \frac{\partial e_2}{\partial x_i}_{(1\times s_i)} \cdots & \frac{\partial e_m}{\partial x_i}_{(1\times s_i)} \end{array} \right] Ji=[xie1(1×si)xie2(1×si)xiem(1×si)]
    雅各比是按地址来表示array的,Ji就是表示的第i个雅各比的地址:
    J i = > j a c o b i a n s [ i ] J_i=>jacobians[i] Ji=>jacobians[i]

LocalParameterization

状态值本身维度和在流体表面维度不一致的情况下使用

class LocalParameterization {
 public:
  virtual ~LocalParameterization() = default;
  virtual bool Plus(const double* x,
                    const double* delta,
                    double* x_plus_delta) const = 0; //定义广义的加法
  virtual bool ComputeJacobian(const double* x, double* jacobian) const = 0;
  virtual bool MultiplyByJacobian(const double* x,
                                  const int num_rows,
                                  const double* global_matrix,
                                  double* local_matrix) const;
  virtual int GlobalSize() const = 0; //原始空间对应的状态值维度
  virtual int LocalSize() const = 0; //切平面上对应维度
};

只要是非线性的参数块都是定义的LocalParameterization,比如vins mono中的航向角是控制在[-180,180]之间就是一个非线性的
1.航向角相关的local parameter

  • 定义非线性函数
template <typename T>
T NormalizeAngle(const T& angle_degrees) {
  if (angle_degrees > T(180.0))
  	return angle_degrees - T(360.0);
  else if (angle_degrees < T(-180.0))
  	return angle_degrees + T(360.0);
  else
  	return angle_degrees;
};
  • 定义LocalParameterization的方法
class AngleLocalParameterization {
 public:

  template <typename T>
  bool operator()(const T* theta_radians, const T* delta_theta_radians,
                  T* theta_radians_plus_delta) const {
    *theta_radians_plus_delta =
        NormalizeAngle(*theta_radians + *delta_theta_radians);

    return true;
  }

  static ceres::LocalParameterization* Create() {
    return (new ceres::AutoDiffLocalParameterization<AngleLocalParameterization,
                                                     1, 1>);
  }
};

2.姿态相关的local parameter

1.声明local parameter的类型
ceres::LocalParameterization *local_parameter = new ceres::EigenQuaternionParameterization
2.添加local parameter数据
problem.AddParameterBlock(frame.q.coeffs().data(),4,local_parameter);
3.如果要参数为const的话
problem.SetParameterBlockConstant(frame.q.coeffs().data());

q.coeffs() //数据存储方式为: [x,y,z,w]

3.pose定义的local parameter

  • local parameter类型定义
#include <eigen3/Eigen/Dense>
#include <ceres/ceres.h>
class PoseLocalParameterization : public ceres::LocalParameterization
{
    virtual bool Plus(const double *x, const double *delta, double *x_plus_delta) const;
    virtual bool ComputeJacobian(const double *x, double *jacobian) const;
    virtual int GlobalSize() const { return 7; };
    virtual int LocalSize() const { return 6; };
};
  • 重定义广义加法
#include "pose_local_parameterization.h"
bool PoseLocalParameterization::Plus(const double *x, const double *delta, double *x_plus_delta) const
{
    Eigen::Map<const Eigen::Vector3d> _p(x);
    Eigen::Map<const Eigen::Quaterniond> _q(x + 3);

    Eigen::Map<const Eigen::Vector3d> dp(delta);

    Eigen::Quaterniond dq = Utility::deltaQ(Eigen::Map<const Eigen::Vector3d>(delta + 3));

    Eigen::Map<Eigen::Vector3d> p(x_plus_delta);
    Eigen::Map<Eigen::Quaterniond> q(x_plus_delta + 3);

    p = _p + dp;
    q = (_q * dq).normalized();

    return true;
}
  • 重定义雅各比矩阵
bool PoseLocalParameterization::ComputeJacobian(const double *x, double *jacobian) const
{
    Eigen::Map<Eigen::Matrix<double, 7, 6, Eigen::RowMajor>> j(jacobian);
    j.topRows<6>().setIdentity();
    j.bottomRows<1>().setZero();

    return true;
}

数据内部存储顺序

  1. ceres::QuaternionParameterization:内部存储顺序为(w,x,y,z)
  2. ceres::EigenQuaternionParameterization:内部存储顺序为(x,y,z,w)
  3. Eigen::Quaternion(w,x,y,z):内部存储顺序为(x,y,z,w)(与构造函数没有保持一致)

SizedCostFunction

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

class QuadraticCostFunction : public ceres::SizedCostFunction<1 /* number of residuals */, 3 /* size of parameter block */> {
public:
    QuadraticCostFunction(double x, double y) : x_(x), y_(y) {}

    virtual bool Evaluate(double const* const* parameters, double* residuals, double** jacobians) const override {
        const double a = parameters[0][0]; // Parameter a
        const double b = parameters[0][1]; // Parameter b
        const double c = parameters[0][2]; // Parameter c

        residuals[0] = y_ - (a * x_ * x_ + b * x_ + c);

        if (jacobians != nullptr && jacobians[0] != nullptr) {
            // Compute Jacobian if requested
            double* jacobian = jacobians[0];
            jacobian[0] = -x_ * x_; // Derivative of residual w.r.t parameter a
            jacobian[1] = -x_;      // Derivative of residual w.r.t parameter b
            jacobian[2] = -1.0;     // Derivative of residual w.r.t parameter c
        }

        return true;
    }

private:
    const double x_;
    const double y_;
};

int main() {
    double a = 1.0;
    double b = 2.0;
    double c = 3.0;

    double x_data[] = {1.0, 2.0, 3.0, 4.0, 5.0};
    double y_data[] = {5.1, 12.4, 26.1, 44.2, 69.5};

    ceres::Problem problem;

    for (int i = 0; i < 5; ++i) {
        ceres::CostFunction* cost_function =
            new QuadraticCostFunction(x_data[i], y_data[i]);

        problem.AddResidualBlock(cost_function, nullptr, &a, &b, &c);
    }

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

    ceres::Solver::Summary summary;
    ceres::Solve(options, &problem, &summary);

    std::cout << summary.FullReport() << "\n";
    std::cout << "Final solution: a = " << a << ", b = " << b << ", c = " << c << "\n";

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值