CogniPilot

MODELICA LIBRARY · ENGINEERING REFERENCE

Tests/EstimationTests.mo

Download original .moGitHub source

Documentation: Tests.EstimationTests · Tests.EstimationTests.run

within Tests;
model EstimationTests "Initialization, prediction, correction, and failure invariants"
  function run "Execute the static estimator assertions"
    output Boolean passed;
  protected
    constant Real tolerance = 1.0e-9;
    Estimation.MocapExternalOdometryErrorState.InitialVariances initialVariances;
    Estimation.MocapExternalOdometryErrorState.InitialVariances zeroVariances;
    Estimation.MocapExternalOdometryErrorState.ProcessNoise noProcessNoise;
    Estimation.MocapExternalOdometryErrorState.ProcessNoise processNoise;
    Estimation.MocapExternalOdometryErrorState.PoseMeasurement exactMeasurement;
    Estimation.MocapExternalOdometryErrorState.PoseMeasurement negativeQuaternionMeasurement;
    Estimation.MocapExternalOdometryErrorState.PoseMeasurementNoise measurementNoise;
    Estimation.MocapExternalOdometryErrorState.PoseMeasurementNoise zeroMeasurementNoise;
    Estimation.MocapExternalOdometryErrorState.State initialized;
    Estimation.MocapExternalOdometryErrorState.State moving;
    Estimation.MocapExternalOdometryErrorState.State zeroDtPrediction;
    Estimation.MocapExternalOdometryErrorState.State predicted;
    Estimation.MocapExternalOdometryErrorState.State corrected;
    Estimation.MocapExternalOdometryErrorState.State signCorrected;
    Estimation.MocapExternalOdometryErrorState.State zeroState;
    Estimation.MocapExternalOdometryErrorState.State rejectedState;
    Estimation.MocapExternalOdometryErrorState.State skippedState;
    Estimation.MocapExternalOdometryErrorState.NominalState injected;
    Estimation.MocapExternalOdometryErrorState.Covariance transition;
    Estimation.MocapExternalOdometryErrorState.Covariance discreteNoise;
    Estimation.MocapExternalOdometryErrorState.MeasurementMatrix measurementMatrix;
    Real expectedAttitudeTransition[3, 3];
    Boolean accepted;
    Boolean signAccepted;
    Boolean rejected;
    Boolean skippedAccepted;
  algorithm
  initialVariances := Estimation.MocapExternalOdometryErrorState.InitialVariances(
    attitude=0.04,
    velocity=0.09,
    position=0.16,
    angularVelocity=0.25);
  zeroVariances := Estimation.MocapExternalOdometryErrorState.InitialVariances(
    attitude=0.0,
    velocity=0.0,
    position=0.0,
    angularVelocity=0.0);
  noProcessNoise := Estimation.MocapExternalOdometryErrorState.ProcessNoise(
    attitude=0.0,
    velocity=0.0,
    position=0.0,
    angularVelocity=0.0);
  processNoise := Estimation.MocapExternalOdometryErrorState.ProcessNoise(
    attitude=1.0e-5,
    velocity=2.0e-4,
    position=3.0e-6,
    angularVelocity=4.0e-5);
  measurementNoise := Estimation.MocapExternalOdometryErrorState.PoseMeasurementNoise(
    attitude=1.0e-4,
    position=4.0e-4);
  zeroMeasurementNoise := Estimation.MocapExternalOdometryErrorState.PoseMeasurementNoise(
    attitude=0.0,
    position=0.0);

  initialized := Estimation.MocapExternalOdometryErrorState.initialize(
    {1.0, -2.0, 3.0},
    {2.0, 0.0, 0.0, 0.0},
    initialVariances);
  moving.attitude := initialized.attitude;
  moving.velocity := {2.0, -1.0, 0.5};
  moving.position := initialized.position;
  moving.angularVelocity := {0.1, -0.2, 0.3};
  moving.covariance := initialized.covariance;
  zeroDtPrediction := Estimation.MocapExternalOdometryErrorState.predict(
    moving,
    0.0,
    processNoise);
  predicted := Estimation.MocapExternalOdometryErrorState.predict(
    moving,
    0.1,
    processNoise);
  transition := Estimation.MocapExternalOdometryErrorState.tangentTransition(
    0.1, moving.angularVelocity);
  expectedAttitudeTransition := transpose(LieGroups.SO3.Quat.to_DCM(
    LieGroups.SO3.Quat.exp_map(0.1 * moving.angularVelocity)));
  discreteNoise := Estimation.MocapExternalOdometryErrorState.discreteProcessCovariance(
    0.1, processNoise);
  measurementMatrix := Estimation.MocapExternalOdometryErrorState.poseMeasurementMatrix();

  exactMeasurement := Estimation.MocapExternalOdometryErrorState.PoseMeasurement(
    position=predicted.position,
    attitude=predicted.attitude);
  negativeQuaternionMeasurement := Estimation.MocapExternalOdometryErrorState.PoseMeasurement(
    position=predicted.position,
    attitude=-predicted.attitude);
  (corrected, accepted) := Estimation.MocapExternalOdometryErrorState.correct(
    predicted,
    exactMeasurement,
    measurementNoise);
  (signCorrected, signAccepted) := Estimation.MocapExternalOdometryErrorState.correct(
    predicted,
    negativeQuaternionMeasurement,
    measurementNoise);

  zeroState := Estimation.MocapExternalOdometryErrorState.initialize(
    zeros(3),
    {1.0, 0.0, 0.0, 0.0},
    zeroVariances);
  (rejectedState, rejected) := Estimation.MocapExternalOdometryErrorState.correct(
    zeroState,
    Estimation.MocapExternalOdometryErrorState.PoseMeasurement(
      position=zeros(3),
      attitude={1.0, 0.0, 0.0, 0.0}),
    zeroMeasurementNoise);
  (skippedState, skippedAccepted) := Estimation.MocapExternalOdometryErrorState.step(
    moving,
    0.1,
    false,
    exactMeasurement,
    processNoise,
    measurementNoise);
  injected := Estimation.MocapExternalOdometryErrorState.inject(
    Estimation.MocapExternalOdometryErrorState.NominalState(
      attitude=moving.attitude,
      velocity=moving.velocity,
      position=moving.position,
      angularVelocity=moving.angularVelocity),
    {0.0, 0.0, 0.1, 1.0, 2.0, 3.0, -1.0, -2.0, -3.0, 0.4, 0.5, 0.6});

  assert(Tests.Assertions.maxAbsVector(initialized.attitude - {1, 0, 0, 0}) < tolerance,
    "geometric error-state EKF initialization did not normalize attitude");
  assert(Tests.Assertions.maxAbsVector(initialized.velocity) < tolerance,
    "geometric error-state EKF initialization velocity was not zero");
  assert(Tests.Assertions.maxAbsVector(initialized.position - {1, -2, 3}) < tolerance,
    "geometric error-state EKF initialization position was incorrect");
  assert(abs(initialized.covariance[1, 1] - 0.04) < tolerance and
         abs(initialized.covariance[4, 4] - 0.09) < tolerance and
         abs(initialized.covariance[7, 7] - 0.16) < tolerance and
         abs(initialized.covariance[10, 10] - 0.25) < tolerance,
    "geometric error-state EKF initialization covariance ordering was incorrect");
  assert(Tests.Assertions.maxAbsVector(zeroDtPrediction.position - moving.position) < tolerance and
         Tests.Assertions.maxAbsMatrix(zeroDtPrediction.covariance - moving.covariance) < tolerance,
    "geometric error-state EKF zero-duration prediction was not an identity");
  assert(Tests.Assertions.maxAbsVector(
      predicted.position - (moving.position + 0.1 * moving.velocity)) < tolerance,
    "geometric error-state EKF constant-velocity position prediction failed");
  assert(Tests.Assertions.maxAbsMatrix(
           transition[1:3, 1:3] - expectedAttitudeTransition) < tolerance and
         abs(transition[7, 4] - 0.1) < tolerance and
         abs(discreteNoise[1, 1] - 0.1 * processNoise.attitude) < tolerance and
         abs(measurementMatrix[1, 1] - 1.0) < tolerance and
         abs(measurementMatrix[4, 7] - 1.0) < tolerance,
    "Separated estimator transition/noise/measurement maps were inconsistent");
  assert(abs(predicted.attitude * predicted.attitude - 1.0) < tolerance,
    "geometric error-state EKF prediction did not preserve quaternion norm");
  assert(Tests.Assertions.maxAbsMatrix(
      predicted.covariance - transpose(predicted.covariance)) < tolerance,
    "geometric error-state EKF prediction covariance was not symmetric");
  assert(accepted and signAccepted,
    "geometric error-state EKF rejected a positive-definite innovation covariance");
  assert(Tests.Assertions.maxAbsVector(corrected.position - predicted.position) < tolerance and
         Tests.Assertions.maxAbsVector(signCorrected.position - predicted.position) < tolerance,
    "A zero-residual geometric error-state EKF correction changed the nominal position");
  assert(abs(abs(corrected.attitude * predicted.attitude) - 1.0) < tolerance and
         abs(abs(signCorrected.attitude * predicted.attitude) - 1.0) < tolerance,
    "A zero-residual or sign-equivalent quaternion correction changed attitude");
  assert(corrected.covariance[1, 1] < predicted.covariance[1, 1] and
         corrected.covariance[7, 7] < predicted.covariance[7, 7],
    "geometric error-state EKF correction did not reduce observed covariance");
  assert(Tests.Assertions.maxAbsMatrix(
      corrected.covariance - transpose(corrected.covariance)) < tolerance,
    "geometric error-state EKF corrected covariance was not symmetric");
  assert(not rejected and Tests.Assertions.maxAbsMatrix(
         rejectedState.covariance - zeroState.covariance) < tolerance,
    "geometric error-state EKF singular innovation handling failed");
  assert(not skippedAccepted and Tests.Assertions.maxAbsMatrix(
         skippedState.covariance - predicted.covariance) < tolerance,
    "geometric error-state EKF skipped correction did not return prediction");
  assert(Tests.Assertions.maxAbsVector(injected.velocity - (moving.velocity + {1, 2, 3})) < tolerance and
         Tests.Assertions.maxAbsVector(injected.position - (moving.position - {1, 2, 3})) < tolerance and
         Tests.Assertions.maxAbsVector(injected.angularVelocity - (moving.angularVelocity + {0.4, 0.5, 0.6})) < tolerance,
    "geometric error-state EKF tangent injection ordering failed");
  for i in 1:12 loop
    assert(corrected.covariance[i, i] >= -tolerance,
      "geometric error-state EKF corrected covariance had a negative diagonal");
  end for;
  passed := true;
  end run;

  parameter Boolean passed = run();
equation
  assert(passed, "Estimator assertion function did not complete");
end EstimationTests;