CogniPilot

MODELICA LIBRARY · ENGINEERING REFERENCE

Estimation/FusionHorizon/composeDelta.mo

Download original .moGitHub source

Documentation: Estimation.FusionHorizon.composeDelta

within Estimation.FusionHorizon;

function composeDelta
  "Compose two adjacent SE_2(3) preintegral right factors and their Jacobians"
  input Estimation.FusionHorizon.Delta first "Right factor over the earlier span";
  input Estimation.FusionHorizon.Delta second "Right factor over the later span";
  output Estimation.FusionHorizon.Delta composed;
protected
  Real firstRotation[3, 3] "R1, the rotation of the earlier factor";
  Real secondRotation[3, 3] "R2, the rotation of the later factor";
  Real secondSpan_s;
  Real rotatedPosition_m[3];
  Real rotatedVelocity_m_s[3];
  Real velocityCoupling[3, 3] "-R1 * wedge(dv2), the attitude-to-velocity term";
  Real positionCoupling[3, 3] "-R1 * wedge(dp2), the attitude-to-position term";
  Real composedPosition_m[3];
  Real composedVelocity_m_s[3];
  Real composedQuaternion[4];
algorithm
  // Adjacent right factors multiply, exactly: with X(T) = L X(0) R and L, R
  // independent of X(0) (FOH paper Lemma 3), two spans give
  // X = L(T1+T2) X(0) (R1 R2). Composing the buffer therefore reproduces a
  // single integration pass over the same samples, which is the composition
  // property of Lemma 5 (Sec. IV-C) and the reason a FIFO of deltas can stand
  // in for the raw sample history.
  //
  // The velocity-to-position coupling dv1 * dt2 is the nilpotent B block of the
  // extended algebra. It is what distinguishes this from the plain SE_2(3)
  // product in LieGroups.SE23.Quat.product, and dropping it is the classical
  // way to get an integrator that is right in attitude and wrong in position.
  firstRotation := LieGroups.SO3.Quat.to_DCM(first.deltaQuaternionBodyFlu);
  secondRotation := LieGroups.SO3.Quat.to_DCM(second.deltaQuaternionBodyFlu);
  secondSpan_s := second.integrationTime_s;
  rotatedPosition_m := firstRotation * second.deltaPositionBodyFlu_m;
  rotatedVelocity_m_s := firstRotation * second.deltaVelocityBodyFlu_m_s;

  composedPosition_m := first.deltaPositionBodyFlu_m
    + first.deltaVelocityBodyFlu_m_s * secondSpan_s
    + rotatedPosition_m;
  composedVelocity_m_s := first.deltaVelocityBodyFlu_m_s + rotatedVelocity_m_s;
  composedQuaternion := LieGroups.SO3.Quat.normalize(
    LieGroups.SO3.Quat.product(
      first.deltaQuaternionBodyFlu, second.deltaQuaternionBodyFlu));

  // Bias sensitivities are the first variation of the same composition, in the
  // right-perturbation convention R(b + db) = R(b) * Exp(J * db) that
  // preintegrateImuStep already produces. Conjugating the earlier rotation
  // Jacobian by R2 is what makes the chain associative:
  //   R1 Exp(J1 db) R2 Exp(J2 db) = (R1 R2) Exp((R2' J1 + J2) db).
  // The two coupling terms come from wedge(x) y = -wedge(y) x applied to the
  // rotated increments, and are formed once and reused so the composed
  // position and velocity Jacobians share them.
  velocityCoupling := -firstRotation
    * LieGroups.SO3.Quat.wedge(second.deltaVelocityBodyFlu_m_s);
  positionCoupling := -firstRotation
    * LieGroups.SO3.Quat.wedge(second.deltaPositionBodyFlu_m);

  composed := Estimation.FusionHorizon.Delta(
    deltaPositionBodyFlu_m=composedPosition_m,
    deltaVelocityBodyFlu_m_s=composedVelocity_m_s,
    deltaQuaternionBodyFlu=composedQuaternion,
    integrationTime_s=first.integrationTime_s + secondSpan_s,
    deltaRotationGyroscopeBiasJacobian_s=
      transpose(secondRotation) * first.deltaRotationGyroscopeBiasJacobian_s
        + second.deltaRotationGyroscopeBiasJacobian_s,
    deltaVelocityGyroscopeBiasJacobian_m=
      first.deltaVelocityGyroscopeBiasJacobian_m
        + velocityCoupling * first.deltaRotationGyroscopeBiasJacobian_s
        + firstRotation * second.deltaVelocityGyroscopeBiasJacobian_m,
    deltaVelocityAccelerometerBiasJacobian_s=
      first.deltaVelocityAccelerometerBiasJacobian_s
        + firstRotation * second.deltaVelocityAccelerometerBiasJacobian_s,
    deltaPositionGyroscopeBiasJacobian_m_s=
      first.deltaPositionGyroscopeBiasJacobian_m_s
        + first.deltaVelocityGyroscopeBiasJacobian_m * secondSpan_s
        + positionCoupling * first.deltaRotationGyroscopeBiasJacobian_s
        + firstRotation * second.deltaPositionGyroscopeBiasJacobian_m_s,
    deltaPositionAccelerometerBiasJacobian_s2=
      first.deltaPositionAccelerometerBiasJacobian_s2
        + first.deltaVelocityAccelerometerBiasJacobian_s * secondSpan_s
        + firstRotation * second.deltaPositionAccelerometerBiasJacobian_s2);
end composeDelta;