File size: 2,890 Bytes
25ade36 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | /**
* Custom EKF methods
*
* Copyright (C) 2024 Simon D. Levy
*
* MIT License
*/
/// @private
static void outer(
const _float_t x[EKF_N],
const _float_t y[EKF_N],
_float_t a[EKF_N*EKF_N])
{
for (int i=0; i<EKF_N; i++) {
for (int j=0; j<EKF_N; j++) {
a[i*EKF_N+j] = x[i] * y[j];
}
}
}
/// @private
static _float_t dot(const _float_t x[EKF_N], const _float_t y[EKF_N])
{
_float_t d = 0;
for (int k=0; k<EKF_N; k++) {
d += x[k] * y[k];
}
return d;
}
/**
* Runs a custom update on the covariance matrix
* @param ekf pointer to an ekf_t structure
* @param A from the update P <- A P A^T
*/
static void ekf_custom_multiply_covariance(
ekf_t * ekf, const _float_t A[EKF_N*EKF_N])
{
_float_t AP[EKF_N*EKF_N] = {};
_mulmat(A, ekf->P, AP, EKF_N, EKF_N, EKF_N);
_float_t At[EKF_N*EKF_N] = {};
_transpose(A, At, EKF_N, EKF_N);
_mulmat(AP, At, ekf->P, EKF_N, EKF_N, EKF_N);
}
/**
* Enforces symmetry of the covariance matrix and ensures that the its values stay bounded
* @param ekf pointer to an ekf_t structure
* @param minval minimum covariance bound
* @param maxval maximum covariance bound
*
*/
static void ekf_custom_cleanup_covariance(
ekf_t * ekf, const float minval, const float maxval)
{
for (int i=0; i<EKF_N; i++) {
for (int j=i; j<EKF_N; j++) {
const _float_t pval = (ekf->P[i*EKF_N+j] + ekf->P[EKF_N*j+i]) / 2;
ekf->P[i*EKF_N+j] = ekf->P[j*EKF_N+i] =
pval > maxval ? maxval :
(i==j && pval < minval) ? minval :
pval;
}
}
}
/**
* Updates the EKF with a single scalar observation
* @param ekf pointer to an ekf_t structure
* @param z the observation
* @param hx the predicted value
* @param h one column of the sensor-function Jacobian matrix H
* @param r one entry in the measurement-noise matrix R
*
*/
static void ekf_custom_scalar_update(
ekf_t * ekf,
const _float_t z,
const _float_t hx,
const _float_t h[EKF_N],
const _float_t r)
{
(void)ekf_update;
// G_k = P_k H^T_k (H_k P_k H^T_k + R)^{-1}
_float_t ph[EKF_N] = {};
_mulvec(ekf->P, h, ph, EKF_N, EKF_N);
const _float_t hphtr_inv = 1 / (r + dot(h, ph));
_float_t g[EKF_N] = {};
for (int i=0; i<EKF_N; ++i) {
g[i] = ph[i] * hphtr_inv;
}
// \hat{x}_k = \hat{x_k} + G_k(z_k - h(\hat{x}_k))
for (int i=0; i<EKF_N; ++i) {
ekf->x[i] += g[i] * (z - hx);
}
// P_k = (I - G_k H_k) P_k$
_float_t GH[EKF_N*EKF_N];
outer(g, h, GH);
ekf_update_step3(ekf, GH);
// Does this belong here, or in caller?
for (int i=0; i<EKF_N; i++) {
for (int j=i; j<EKF_N; j++) {
ekf->P[i*EKF_N+j] += r * g[i] * g[j];
}
}
}
|