VINS-Mono笔记03_预积分

预积分理论

定义数学表示如下:

  • ( ⋅ ) w (\cdot)^w ()w表示世界坐标系(world frame)下的值, ( ⋅ ) b (\cdot)^b ()b​表示机器人坐标系(body frame)下的值, ( ⋅ ) c (\cdot)^c ()c表示相机坐标系下的值.
  • q b w \mathbf{q}^w_b qbw, p b w \mathbf{p}^w_b pbw表示从机器人坐标系到世界坐标系的的旋转.
  • b k b_k bk表示第 k k k帧图像对应的机器人坐标, c k c_k ck表示第 k k k帧图像对应的相机坐标.
  • ⊗ \otimes 表示四元数乘法.
  • g w = [ 0 , 0 , g ] T \mathbf{g}^w = [0,0,g]^T gw=[0,0,g]T表示世界坐标系下的重力加速度值.
  • ( ⋅ ) ^ \hat{(\cdot)} ()^表示物理量的测量值,即考虑噪声和零偏在内的值.

为什么需要预积分

IMU频率和图像频率不同,一般IMU频率高于图像频率,因此需要将两图像帧之间的所有IMU数据积分起来,得到两图像帧间的IMU测量值.

请添加图片描述

IMU可以输出3轴角速度和3轴加速度,其数学方程如下:
KaTeX parse error: No such environment: equation at position 8: \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲}̲ \begin{split} …

  • a t \mathbf{a}_t at ω t \boldsymbol{\omega}_t ωt表示真实值.
  • a t ^ \hat{\mathbf{a}_t} at^ ω ^ t \hat{\boldsymbol{\omega}}_t ω^t表示陀螺仪输出的测量值.
  • b a t \mathbf{b}_{a_t} bat b w t \mathbf{b}_{w_t} bwt表示零偏.
  • n a \mathbf{n}_a na n w \mathbf{n}_w nw表示噪声, n a ∼ N ( 0 , σ a 2 ) \mathbf{n}_a \sim \mathcal{N}(\mathbf{0},\boldsymbol{\sigma}_a^{2}) naN(0,σa2), n w ∼ N ( 0 , σ w 2 ) \mathbf{n}_w \sim \mathcal{N}(\mathbf{0},\boldsymbol{\sigma}_w^{2}) nwN(0,σw2).
  • IMU在静止不动时,输出垂直向下的重力加速度值 g w \mathbf{g}^w gw.

连续时间下对 b k b_k bk帧到 b k + 1 b_{k+1} bk+1帧间的IMU测量值做积分,得到的位置 p b k + 1 w \mathbf{p}^w_{b_{k+1}} pbk+1w​、速度 v b k + 1 w \mathbf{v}^w_{b_{k+1}} vbk+1w​和姿态 q b k + 1 w \mathbf{q }^{w}_{b_{k+1}} qbk+1w​​的表达式如下.

KaTeX parse error: No such environment: equation at position 8: \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲}̲ \begin{split} …

其中 Ω ( ω ) \boldsymbol{\Omega}(\boldsymbol{\omega}) Ω(ω) ⌊ ω ⌋ × \lfloor \boldsymbol{\omega} \rfloor_{\times} ω×的定义如下:
KaTeX parse error: No such environment: equation at position 8: \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲}̲ \begin{split} …
上式中位置 p b k + 1 w \mathbf{p}^w_{b_{k+1}} pbk+1w和速度 v b k + 1 w \mathbf{v}^w_{b_{k+1}} vbk+1w的积分容易理解:可以类比中学物理路程和速度针对加速度的积分;姿态 q b k + 1 w \mathbf{q }^{w}_{b_{k+1}} qbk+1w​​的积分表达式的来源如下:
d q d t = 1 2 Ω ( ω ) q \frac{d\mathbf{q}}{dt} = \frac{1}{2} \boldsymbol{\Omega}(\boldsymbol{\omega})\mathbf{q} dtdq=21Ω(ω)q
即被积表达式本身是位置四元数 q \mathbf{q} q​​​对时间 t t t​​​的微分.

连续时间下的预积分

在上述连续时间位置 p b k + 1 w \mathbf{p}^w_{b_{k+1}} pbk+1w和速度 v b k + 1 w \mathbf{v}^w_{b_{k+1}} vbk+1w积分表达式中都用到了世界坐标系下的姿态 R t w \mathbf{R}^w_t Rtw(亦即 q t w \mathbf{q }^{w}_t qtw),这依赖于 b k b_k bk帧的姿态 R b k w \mathbf{R}^w_{b_k} Rbkw,一旦 b k b_k bk帧的姿态 R b k w \mathbf{R}^w_{b_k} Rbkw改变,就要重新积分,增大运算量.

请添加图片描述

请添加图片描述

为防止重复积分的问题,将参考系转换至 b k b_k bk​​帧,即在上式左右两端同时乘以 R w b k \mathbf{R}^{b_k}_w Rwbk​​(亦即 q w b k \mathbf{q}^{b_k}_w qwbk​​),得到 b k + 1 b_{k+1} bk+1​​帧相对于 b k b_k bk​​帧的位置 p b k + 1 b k \mathbf{p}^{b_k}_{b_{k+1}} pbk+1bk​​、速度 v b k + 1 w \mathbf{v}^w_{b_{k+1}} vbk+1w​​和姿态 q b k + 1 w \mathbf{q }^{w}_{b_{k+1}} qbk+1w​​的表达式如下:
KaTeX parse error: No such environment: equation at position 8: \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲}̲ \begin{split}\…
其中积分式 α b k + 1 b k \boldsymbol{\alpha}^{b_k}_{b_{k+1}} αbk+1bk​​、 β b k + 1 b k \boldsymbol{\beta}^{b_k}_{b_{k+1}} βbk+1bk​​和 γ b k + 1 b k \boldsymbol{\gamma}^{b_k}_{b_{k+1}} γbk+1bk​​被称为预积分量:
KaTeX parse error: No such environment: equation at position 8: \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲}̲ \label{eq:inte…
这样积分部分就只与IMU输出以及当前时刻相对于 b k b_k bk​​​帧的姿态 R t b k \mathbf{R}^{b_k}_{t} Rtbk​​有关.

离散时间下的预积分

离散时间下,预积分量的估计值表达式如下:
KaTeX parse error: No such environment: equation at position 8: \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲}̲ \label{eq:prop…
实际编程中使用中值积分方式,也就是将上式中IMU测量值 a ^ i \hat{\mathbf{a}}_i a^i ω ^ i \hat{\boldsymbol{\omega}}_i ω^i用连续两帧的均值 a ^ i + a ^ i + 1 2 \frac{\hat{\mathbf{a}}_i+\hat{\mathbf{a}}_{i+1}}{2} 2a^i+a^i+1 ω ^ i + ω ^ i + 1 2 \frac{\hat{\mathbf{\omega}}_i+\hat{\mathbf{\omega}}_{i+1}}{2} 2ω^i+ω^i+1代替.​

在实际编程中 α ^ i b k \hat{\boldsymbol{\alpha}}^{b_k}_{i} α^ibk β ^ i b k \hat{\boldsymbol{\beta}}^{b_k}_{i} β^ibk被初始化为零矩阵, γ ^ i b k \hat{\boldsymbol{\gamma}}^{b_k}_{i} γ^ibk被初始化为单位矩阵.

预积分量的协方差

使用误差卡尔曼滤波,可以得到连续时间下预积分量标准差的导数表达式如下:
KaTeX parse error: No such environment: equation at position 8: \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲}̲ \begin{aligned…
将其离散化,再加上亿点点数学推导,可以得到:

请添加图片描述

数学不好太要命了,像我这样的人可能一辈子都理解不了这个推导过程了.有人专门写了一篇论文来推导VINS-Mono论文里的数学表达式,见Formula Derivation and Analysis of the VINS-Mono.

在一些情况下,陀螺仪零偏 b a t \mathbf{b}_{a_t} bat b w t \mathbf{b}_{w_t} bwt也是未知的,在这种情况下,VINS-Mono将陀螺仪零偏也作为待优化变量处理,这样就又带来了重复积分的问题:理论上每经过一次优化,零偏 b a t \mathbf{b}_{a_t} bat b w t \mathbf{b}_{w_t} bwt值发生改变后,就要重新积分计算预积分量 α b k + 1 b k \boldsymbol{\alpha}^{b_k}_{b_{k+1}} αbk+1bk β b k + 1 b k \boldsymbol{\beta}^{b_k}_{b_{k+1}} βbk+1bk γ b k + 1 b k \boldsymbol{\gamma}^{b_k}_{b_{k+1}} γbk+1bk:

请添加图片描述

为解决这一问题,对预积分量 α b k + 1 b k \boldsymbol{\alpha}^{b_k}_{b_{k+1}} αbk+1bk β b k + 1 b k \boldsymbol{\beta}^{b_k}_{b_{k+1}} βbk+1bk γ b k + 1 b k \boldsymbol{\gamma}^{b_k}_{b_{k+1}} γbk+1bk对于零偏 b a t \mathbf{b}_{a_t} bat b w t \mathbf{b}_{w_t} bwt做一阶泰勒展开,在零偏值变化不大时使用一阶微分估计变化量,避免重新做积分运算.同样地,因为我不会数学,其数学推导略.
KaTeX parse error: No such environment: equation at position 8: \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲}̲ \label{eq:corr…

预积分代码实现

预积分类IntegrationBase

预积分类实现在vins_estimator包下src/factor/integration_base.h文件内的IntegrationBase类中.主要成员变量如下:

请添加图片描述

成员变量数据类型意义
linearized_accconst Eigen::Vector3d b 0 b_0 b0时刻(参与预积分的首个IMU帧)加速度测量值 a ^ 0 \hat{\mathbf{a}}_0 a^0
linearized_gyrconst Eigen::Vector3d b 0 b_0 b0时刻(参与预积分的首个IMU帧)角速度测量值 ω ^ 0 \hat{\boldsymbol{\omega}}_0 ω^0
acc_0Eigen::Vector3d b k b_k bk时刻加速度测量值 a ^ k \hat{\mathbf{a}}_k a^k
acc_1Eigen::Vector3d b k + 1 b_{k+1} bk+1时刻加速度测量值 a ^ k + 1 \hat{\mathbf{a}}_{k+1} a^k+1
gyr_0Eigen::Vector3d b k b_k bk时刻角速度测量值 ω ^ k \hat{\boldsymbol{\omega}}_k ω^k
gyr_1Eigen::Vector3d b k + 1 b_{k+1} bk+1时刻角速度测量值 ω ^ k + 1 \hat{\boldsymbol{\omega}}_{k+1} ω^k+1
linearized_baEigen::Vector3d加速度零偏 b a i \mathbf{b}_{a_i} bai
linearized_bgEigen::Vector3d陀螺角速度零偏 b a i \mathbf{b}_{a_i} bai
delta_pEigen::Vector3d预积分量 α i + 1 b k \boldsymbol{\alpha}^{b_k}_{i+1} αi+1bk
delta_vEigen::Vector3d预积分量 β i + 1 b k \boldsymbol{\beta}^{b_k}_{i+1} βi+1bk
delta_qEigen::Quaterniond预积分量 γ i + 1 b k \boldsymbol{\gamma}^{b_k}_{i+1} γi+1bk
dt_bufstd::vector<double>存储所有帧间的 d t d_t dt
acc_bufstd::vector<Eigen::Vector3d>存储所有帧的加速度测量值 a ^ i \hat{\mathbf{a}}_i a^i
gyr_bufstd::vector<Eigen::Vector3d>存储所有帧的角速度测量值 ω ^ i \hat{\boldsymbol{\omega}}_i ω^i
covarianceEigen::Matrix<double, 15, 15>见下图
jacobianEigen::Matrix<double, 15, 15>见下图
noiseEigen::Matrix<double, 18, 18>见下图

请添加图片描述

预积分类的成员函数

构造函数IntegrationBase::IntegrationBase()

α ^ i b k \hat{\boldsymbol{\alpha}}^{b_k}_{i} α^ibk β ^ i b k \hat{\boldsymbol{\beta}}^{b_k}_{i} β^ibk被初始化为零矩阵, γ ^ i b k \hat{\boldsymbol{\gamma}}^{b_k}_{i} γ^ibk被初始化为单位矩阵;jacobian被初始化为单位矩阵,covariance被初始化为零矩阵,noise被初始化为单位矩阵乘以对应传感器的噪声方差(不确定度).

IntegrationBase(const Eigen::Vector3d &_acc_0,
                const Eigen::Vector3d &_gyr_0,
                const Eigen::Vector3d &_linearized_ba,
                const Eigen::Vector3d &_linearized_bg) : 
acc_0{_acc_0},
gyr_0{_gyr_0},
linearized_acc{_acc_0},
linearized_gyr{_gyr_0},
linearized_ba{_linearized_ba},
linearized_bg{_linearized_bg},
jacobian{Eigen::Matrix<double, 15, 15>::Identity()},
covariance{Eigen::Matrix<double, 15, 15>::Zero()},
sum_dt{0.0},
delta_p{Eigen::Vector3d::Zero()},
delta_q{Eigen::Quaterniond::Identity()},
delta_v{Eigen::Vector3d::Zero()} {
    noise = Eigen::Matrix<double, 18, 18>::Zero();
    noise.block<3, 3>(0, 0) = (ACC_N * ACC_N) * Eigen::Matrix3d::Identity();
    noise.block<3, 3>(3, 3) = (GYR_N * GYR_N) * Eigen::Matrix3d::Identity();
    noise.block<3, 3>(6, 6) = (ACC_N * ACC_N) * Eigen::Matrix3d::Identity();
    noise.block<3, 3>(9, 9) = (GYR_N * GYR_N) * Eigen::Matrix3d::Identity();
    noise.block<3, 3>(12, 12) = (ACC_W * ACC_W) * Eigen::Matrix3d::Identity();
    noise.block<3, 3>(15, 15) = (GYR_W * GYR_W) * Eigen::Matrix3d::Identity();
}

传入IMU数据帧时调用IntegrationBase::push_back()进行预积分

每传入一IMU帧,调用一次push_back()函数,其内部调用midPointIntegration()函数进行中值预积分:

void push_back(double dt, const Eigen::Vector3d &acc, const Eigen::Vector3d &gyr) {
  // 1.记录对应IMU帧测量值
  dt_buf.push_back(dt);
  acc_buf.push_back(acc);
  gyr_buf.push_back(gyr);
  // 2. IMU传播
  propagate(dt, acc, gyr);
}
void propagate(double _dt, const Eigen::Vector3d &_acc_1, const Eigen::Vector3d &_gyr_1) {
  // 1.记录本帧IMU帧测量值
  dt = _dt;
  acc_1 = _acc_1;
  gyr_1 = _gyr_1;
  Vector3d result_delta_p;
  Quaterniond result_delta_q;
  Vector3d result_delta_v;
  Vector3d result_linearized_ba;
  Vector3d result_linearized_bg;
  // 2.中值预积分
  midPointIntegration(_dt,
                      acc_0,
                      gyr_0,
                      _acc_1,
                      _gyr_1,
                      delta_p,
                      delta_q,
                      delta_v,
                      linearized_ba,
                      linearized_bg,
                      result_delta_p,
                      result_delta_q,
                      result_delta_v,
                      result_linearized_ba,
                      result_linearized_bg,
                      1);
  // 3. 更新成员变量
  delta_p = result_delta_p;
  delta_q = result_delta_q;
  delta_v = result_delta_v;
  linearized_ba = result_linearized_ba;
  linearized_bg = result_linearized_bg;
  delta_q.normalize();
  sum_dt += dt;
  acc_0 = acc_1;
  gyr_0 = gyr_1;
}
void midPointIntegration(double _dt,
                         const Eigen::Vector3d &_acc_0,
                         const Eigen::Vector3d &_gyr_0,
                         const Eigen::Vector3d &_acc_1,
                         const Eigen::Vector3d &_gyr_1,
                         const Eigen::Vector3d &delta_p,
                         const Eigen::Quaterniond &delta_q,
                         const Eigen::Vector3d &delta_v,
                         const Eigen::Vector3d &linearized_ba,
                         const Eigen::Vector3d &linearized_bg,
                         Eigen::Vector3d &result_delta_p,
                         Eigen::Quaterniond &result_delta_q,
                         Eigen::Vector3d &result_delta_v,
                         Eigen::Vector3d &result_linearized_ba,
                         Eigen::Vector3d &result_linearized_bg,
                         bool update_jacobian) {
  // 1.计算预积分量
  Vector3d un_acc_0 = delta_q * (_acc_0 - linearized_ba);
  Vector3d un_gyr = 0.5 * (_gyr_0 + _gyr_1) - linearized_bg;
  result_delta_q = delta_q * Quaterniond(1, un_gyr(0) * _dt / 2, un_gyr(1) * _dt / 2, un_gyr(2) * _dt / 2);
  Vector3d un_acc_1 = result_delta_q * (_acc_1 - linearized_ba);
  Vector3d un_acc = 0.5 * (un_acc_0 + un_acc_1);
  result_delta_p = delta_p + delta_v * _dt + 0.5 * un_acc * _dt * _dt;
  result_delta_v = delta_v + un_acc * _dt;
  result_linearized_ba = linearized_ba;
  result_linearized_bg = linearized_bg;

  // 2.计算协方差
  if (update_jacobian) {
    Vector3d w_x = 0.5 * (_gyr_0 + _gyr_1) - linearized_bg;
    Vector3d a_0_x = _acc_0 - linearized_ba;
    Vector3d a_1_x = _acc_1 - linearized_ba;
    Matrix3d R_w_x, R_a_0_x, R_a_1_x;
    R_w_x << 0, -w_x(2), w_x(1), w_x(2), 0, -w_x(0), -w_x(1), w_x(0), 0;
    R_a_0_x << 0, -a_0_x(2), a_0_x(1), a_0_x(2), 0, -a_0_x(0), -a_0_x(1), a_0_x(0), 0;
    R_a_1_x << 0, -a_1_x(2), a_1_x(1), a_1_x(2), 0, -a_1_x(0), -a_1_x(1), a_1_x(0), 0;
    // 2.1. 计算矩阵F 
    MatrixXd F = MatrixXd::Zero(15, 15);
    F.block<3, 3>(0, 0) = Matrix3d::Identity();
    F.block<3, 3>(0, 3) = -0.25 * delta_q.toRotationMatrix() * R_a_0_x * _dt * _dt
        + -0.25 * result_delta_q.toRotationMatrix() * R_a_1_x * (Matrix3d::Identity() - R_w_x * _dt) * _dt * _dt;
    F.block<3, 3>(0, 6) = MatrixXd::Identity(3, 3) * _dt;
    F.block<3, 3>(0, 9) = -0.25 * (delta_q.toRotationMatrix() + result_delta_q.toRotationMatrix()) * _dt * _dt;
    F.block<3, 3>(0, 12) = -0.25 * result_delta_q.toRotationMatrix() * R_a_1_x * _dt * _dt * -_dt;
    F.block<3, 3>(3, 3) = Matrix3d::Identity() - R_w_x * _dt;
    F.block<3, 3>(3, 12) = -1.0 * MatrixXd::Identity(3, 3) * _dt;
    F.block<3, 3>(6, 3) = -0.5 * delta_q.toRotationMatrix() * R_a_0_x * _dt
        + -0.5 * result_delta_q.toRotationMatrix() * R_a_1_x * (Matrix3d::Identity() - R_w_x * _dt) * _dt;
    F.block<3, 3>(6, 6) = Matrix3d::Identity();
    F.block<3, 3>(6, 9) = -0.5 * (delta_q.toRotationMatrix() + result_delta_q.toRotationMatrix()) * _dt;
    F.block<3, 3>(6, 12) = -0.5 * result_delta_q.toRotationMatrix() * R_a_1_x * _dt * -_dt;
    F.block<3, 3>(9, 9) = Matrix3d::Identity();
    F.block<3, 3>(12, 12) = Matrix3d::Identity();
    // 2.2. 计算矩阵V
    MatrixXd V = MatrixXd::Zero(15, 18);
    V.block<3, 3>(0, 0) = 0.25 * delta_q.toRotationMatrix() * _dt * _dt;
    V.block<3, 3>(0, 3) = 0.25 * -result_delta_q.toRotationMatrix() * R_a_1_x * _dt * _dt * 0.5 * _dt;
    V.block<3, 3>(0, 6) = 0.25 * result_delta_q.toRotationMatrix() * _dt * _dt;
    V.block<3, 3>(0, 9) = V.block<3, 3>(0, 3);
    V.block<3, 3>(3, 3) = 0.5 * MatrixXd::Identity(3, 3) * _dt;
    V.block<3, 3>(3, 9) = 0.5 * MatrixXd::Identity(3, 3) * _dt;
    V.block<3, 3>(6, 0) = 0.5 * delta_q.toRotationMatrix() * _dt;
    V.block<3, 3>(6, 3) = 0.5 * -result_delta_q.toRotationMatrix() * R_a_1_x * _dt * 0.5 * _dt;
    V.block<3, 3>(6, 6) = 0.5 * result_delta_q.toRotationMatrix() * _dt;
    V.block<3, 3>(6, 9) = V.block<3, 3>(6, 3);
    V.block<3, 3>(9, 12) = MatrixXd::Identity(3, 3) * _dt;
    V.block<3, 3>(12, 15) = MatrixXd::Identity(3, 3) * _dt;
	// 2.3. 更新jacobian和covariance
    jacobian = F * jacobian;
    covariance = F * covariance * F.transpose() + V * noise * V.transpose();
  }
}

请添加图片描述

零偏改变时调用IntegrationBase::repropagate()重新积分

零偏改变较大时调用repropagate()进行重新积分,其内部对xxx_buf中的每一帧数据调用propagate().

void repropagate(const Eigen::Vector3d &_linearized_ba, const Eigen::Vector3d &_linearized_bg) {
  // 1. 重新初始化成员变量
  sum_dt = 0.0;
  acc_0 = linearized_acc;
  gyr_0 = linearized_gyr;
  delta_p.setZero();
  delta_q.setIdentity();
  delta_v.setZero();
  linearized_ba = _linearized_ba;
  linearized_bg = _linearized_bg;
  jacobian.setIdentity();
  covariance.setZero();
  // 2. 逐帧积分
  for (int i = 0; i < static_cast<int>(dt_buf.size()); i++) {
    propagate(dt_buf[i], acc_buf[i], gyr_buf[i]);
  }
}

优化过程中调用IntegrationBase::evaluate()计算IMU残差

Eigen::Matrix<double, 15, 1> evaluate(const Eigen::Vector3d &Pi,
                                      const Eigen::Quaterniond &Qi,
                                      const Eigen::Vector3d &Vi,
                                      const Eigen::Vector3d &Bai,
                                      const Eigen::Vector3d &Bgi,
                                      const Eigen::Vector3d &Pj,
                                      const Eigen::Quaterniond &Qj,
                                      const Eigen::Vector3d &Vj,
                                      const Eigen::Vector3d &Baj,
                                      const Eigen::Vector3d &Bgj) {
  Eigen::Matrix<double, 15, 1> residuals;

  //其中各增量关于 bias 的 Jacobian 可从公式(16)的大 Jacobian 中的相应位置获得。
  Eigen::Matrix3d dp_dba = jacobian.block<3, 3>(O_P, O_BA);
  Eigen::Matrix3d dp_dbg = jacobian.block<3, 3>(O_P, O_BG);

  Eigen::Matrix3d dq_dbg = jacobian.block<3, 3>(O_R, O_BG);

  Eigen::Matrix3d dv_dba = jacobian.block<3, 3>(O_V, O_BA);
  Eigen::Matrix3d dv_dbg = jacobian.block<3, 3>(O_V, O_BG);

  //当每次迭代时,我们得到一个新的 bias,又得根据公式(5)重新对第 k 帧和第 k+1 帧之间的 IMU 预积分,非常耗时。这里假设预积分的变化量与 bias 是线性关系,可以写成:
  Eigen::Vector3d dba = Bai - linearized_ba;
  Eigen::Vector3d dbg = Bgi - linearized_bg;

  Eigen::Quaterniond corrected_delta_q = delta_q * Utility::deltaQ(dq_dbg * dbg);
  Eigen::Vector3d corrected_delta_v = delta_v + dv_dba * dba + dv_dbg * dbg;
  Eigen::Vector3d corrected_delta_p = delta_p + dp_dba * dba + dp_dbg * dbg;

  residuals.block<3, 1>(O_P, 0) =
      Qi.inverse() * (0.5 * G * sum_dt * sum_dt + Pj - Pi - Vi * sum_dt) - corrected_delta_p;
  residuals.block<3, 1>(O_R, 0) = 2 * (corrected_delta_q.inverse() * (Qi.inverse() * Qj)).vec();
  residuals.block<3, 1>(O_V, 0) = Qi.inverse() * (G * sum_dt + Vj - Vi) - corrected_delta_v;
  residuals.block<3, 1>(O_BA, 0) = Baj - Bai;
  residuals.block<3, 1>(O_BG, 0) = Bgj - Bgi;
  return residuals;
}
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值