CogniPilot

MODELICA LIBRARY · ENGINEERING REFERENCE

Estimation/StrapdownINS/UKF/correctUnscented.mo

Download original .moGitHub source

Documentation: Estimation.StrapdownINS.UKF.correctUnscented

within Estimation.StrapdownINS.UKF;

function correctUnscented
  "Unscented measurement correction in local navigation coordinates"
  input State predicted;
  input Real sigmaMeasurement[:, SigmaCount]
    "Measurement function evaluated at sigma states from predicted covariance";
  input Real measurement[size(sigmaMeasurement, 1)];
  input Real measurementCovariance[size(sigmaMeasurement, 1),
    size(sigmaMeasurement, 1)];
  input Real innovationGate = 0.0;
  output State corrected;
  output Boolean accepted;
  output Integer rejectionReason;
  output Real normalizedInnovationSquared;
protected
  Integer measurementLength = size(sigmaMeasurement, 1);
  Real nominal[16];
  Real sigmaState[16, SigmaCount];
  Real correctedNominal[16];
  Real sigma[TangentLength, SigmaCount];
  Boolean sigmaFactorized;
  Boolean innovationFactorized;
  Boolean inputsUsable;
  Real measurementMean[measurementLength];
  Real measurementDeviation[measurementLength];
  Real stateDeviation[TangentLength];
  Real residual[measurementLength];
  Real innovationCovariance[measurementLength, measurementLength];
  Real crossCovariance[TangentLength, measurementLength];
  Real augmentedRhs[measurementLength, TangentLength + 1];
  Real augmentedSolution[measurementLength, TangentLength + 1];
  Real gain[TangentLength, measurementLength];
  Real correction[TangentLength];
  Real attitudeCorrection;
  Real trustScale;
  Real posteriorCovariance[TangentLength, TangentLength];
  Real correctedCovariance[TangentLength, TangentLength];
algorithm
  nominal := stateVector(predicted);
  (sigma, sigmaFactorized) := sigmaTangents(predicted.covariance);
  for sigmaIndex in 1:SigmaCount loop
    sigmaState[:, sigmaIndex] := injectVector(
      nominal, sigma[:, sigmaIndex]);
  end for;

  measurementMean := zeros(measurementLength);
  for sigmaIndex in 2:SigmaCount loop
    measurementMean := measurementMean
      + SigmaWeight * sigmaMeasurement[:, sigmaIndex];
  end for;
  residual := measurement - measurementMean;
  measurementDeviation := sigmaMeasurement[:, 1] - measurementMean;
  innovationCovariance := measurementCovariance
    + transpose({CentralCovarianceWeight * measurementDeviation})
      * {measurementDeviation};
  crossCovariance := zeros(TangentLength, measurementLength);
  for sigmaIndex in 2:SigmaCount loop
    stateDeviation := localErrorVector(nominal, sigmaState[:, sigmaIndex]);
    measurementDeviation := sigmaMeasurement[:, sigmaIndex] - measurementMean;
    crossCovariance := crossCovariance
      + transpose({SigmaWeight * stateDeviation}) * {measurementDeviation};
    innovationCovariance := innovationCovariance
      + transpose({SigmaWeight * measurementDeviation}) * {measurementDeviation};
  end for;
  innovationCovariance := LinearAlgebra.symmetrize(innovationCovariance);
  augmentedRhs := zeros(measurementLength, TangentLength + 1);
  augmentedRhs[:, 1:TangentLength] := transpose(crossCovariance);
  augmentedRhs[:, TangentLength + 1] := residual;
  (augmentedSolution, innovationFactorized) := LinearAlgebra.solveSPD(
    innovationCovariance, augmentedRhs);
  normalizedInnovationSquared := residual
    * augmentedSolution[:, TangentLength + 1];

  inputsUsable := true;
  for row in 1:measurementLength loop
    inputsUsable := inputsUsable
      and abs(residual[row]) < Estimation.StrapdownINS.ESKF.FiniteMagnitudeLimit
      and measurementCovariance[row, row] > 0.0;
    for column in 1:measurementLength loop
      inputsUsable := inputsUsable
        and abs(measurementCovariance[row, column])
          < Estimation.StrapdownINS.ESKF.FiniteMagnitudeLimit;
    end for;
  end for;
  accepted := sigmaFactorized and innovationFactorized and inputsUsable
    and normalizedInnovationSquared >= 0.0
    and normalizedInnovationSquared
      < Estimation.StrapdownINS.ESKF.FiniteMagnitudeLimit
    and (innovationGate <= 0.0 or normalizedInnovationSquared
      <= innovationGate * measurementLength);
  rejectionReason := if accepted then
      Estimation.StrapdownINS.CorrectionAccepted
    elseif not inputsUsable then
      Estimation.StrapdownINS.CorrectionRejectedCovarianceUnusable
    elseif not sigmaFactorized or not innovationFactorized then
      Estimation.StrapdownINS.CorrectionRejectedFactorization
    else Estimation.StrapdownINS.CorrectionRejectedGate;

  if accepted then
    gain := transpose(augmentedSolution[:, 1:TangentLength]);
    correction := gain * residual;
    attitudeCorrection := sqrt(correction[7:9] * correction[7:9]);
    trustScale := if attitudeCorrection
        > Estimation.StrapdownINS.ESKF.MaxAttitudeCorrection_rad then
      Estimation.StrapdownINS.ESKF.MaxAttitudeCorrection_rad
        / attitudeCorrection else 1.0;
    gain := cat(1, gain[1:6, :], trustScale * gain[7:9, :],
      gain[10:TangentLength, :]);
    correction := gain * residual;
    correctedNominal := injectVector(
      nominal, correction);
    // The attitude trust region row-scales the optimal unscented gain. Once
    // modified it is an arbitrary gain, so P-K*S*K' is no longer the valid
    // covariance update. Use the general form driven by the same gain that
    // generated the applied state correction.
    posteriorCovariance := LinearAlgebra.symmetrize(
      predicted.covariance
        - gain * transpose(crossCovariance)
        - crossCovariance * transpose(gain)
        + gain * innovationCovariance * transpose(gain));
    correctedCovariance := LinearAlgebra.symmetrize(
      Estimation.StrapdownINS.ESKF.conjugateReset(
        LieGroups.SE23.Quat.right_jacobian(correction[1:9]),
        posteriorCovariance));
  else
    correctedNominal := nominal;
    correctedCovariance := predicted.covariance;
  end if;
  corrected := State(
    positionWorldEnu_m=correctedNominal[1:3],
    velocityWorldEnu_m_s=correctedNominal[4:6],
    quaternionWorldBody=correctedNominal[7:10],
    gyroscopeBiasBodyFlu_rad_s=correctedNominal[11:13],
    accelerometerBiasBodyFlu_m_s2=correctedNominal[14:16],
    covariance=correctedCovariance);
end correctUnscented;