diff --git a/src/lib/matrix/matrix/Matrix.hpp b/src/lib/matrix/matrix/Matrix.hpp index 33d2e6cbf0bc..14400683f0ef 100644 --- a/src/lib/matrix/matrix/Matrix.hpp +++ b/src/lib/matrix/matrix/Matrix.hpp @@ -162,6 +162,24 @@ class Matrix return res; } + // Using this function reduces the number of temporary variables needed to compute A * B.T + template + Matrix multiplyByTranspose(const Matrix &other) const + { + Matrix res; + const Matrix &self = *this; + + for (size_t i = 0; i < M; i++) { + for (size_t k = 0; k < P; k++) { + for (size_t j = 0; j < N; j++) { + res(i, k) += self(i, j) * other(k, j); + } + } + } + + return res; + } + // Element-wise multiplication Matrix emult(const Matrix &other) const { diff --git a/src/modules/ekf2/EKF/ekf.h b/src/modules/ekf2/EKF/ekf.h index 11b98f4173e6..a017b7a1515e 100644 --- a/src/modules/ekf2/EKF/ekf.h +++ b/src/modules/ekf2/EKF/ekf.h @@ -472,6 +472,17 @@ class Ekf final : public EstimatorInterface { clearInhibitedStateKalmanGains(K); +#if false + // Matrix implementation of the Joseph stabilized covariance update + // This is extremely expensive to compute. Use for debugging purposes only. + auto A = matrix::eye(); + A -= K.multiplyByTranspose(H); + P_true = A * P_true; + P_true = P_true.multiplyByTranspose(A); + + const VectorState KR = K * R; + P_true += KR.multiplyByTranspose(K); +#else // Efficient implementation of the Joseph stabilized covariance update // Based on "G. J. Bierman. Factorization Methods for Discrete Sequential Estimation. Academic Press, Dover Publications, New York, 1977, 2006" @@ -479,9 +490,8 @@ class Ekf final : public EstimatorInterface VectorState PH = P * H; for (unsigned i = 0; i < State::size; i++) { - for (unsigned j = 0; j <= i; j++) { - P(i, j) = P(i, j) - K(i) * PH(j); - P(j, i) = P(i, j); + for (unsigned j = 0; j < State::size; j++) { + P(i, j) -= K(i) * PH(j); // P is now not symmetrical if K is not optimal (e.g.: some gains have been zeroed) } } @@ -490,11 +500,11 @@ class Ekf final : public EstimatorInterface for (unsigned i = 0; i < State::size; i++) { for (unsigned j = 0; j <= i; j++) { - float s = .5f * (P(i, j) - PH(i) * K(j) + P(i, j) - PH(j) * K(i)); - P(i, j) = s + K(i) * R * K(j); + P(i, j) = P(i, j) - PH(i) * K(j) + K(i) * R * K(j); P(j, i) = P(i, j); } } +#endif constrainStateVariances(); diff --git a/src/modules/ekf2/EKF/ekf_helper.cpp b/src/modules/ekf2/EKF/ekf_helper.cpp index 3a030cf63222..d4b455f85628 100644 --- a/src/modules/ekf2/EKF/ekf_helper.cpp +++ b/src/modules/ekf2/EKF/ekf_helper.cpp @@ -850,6 +850,17 @@ bool Ekf::fuseDirectStateMeasurement(const float innov, const float innov_var, c clearInhibitedStateKalmanGains(K); +#if false + // Matrix implementation of the Joseph stabilized covariance update + // This is extremely expensive to compute. Use for debugging purposes only. + auto A = matrix::eye(); + A -= K.multiplyByTranspose(H); + P_true = A * P_true; + P_true = P_true.multiplyByTranspose(A); + + const VectorState KR = K * R; + P_true += KR.multiplyByTranspose(K); +#else // Efficient implementation of the Joseph stabilized covariance update // Based on "G. J. Bierman. Factorization Methods for Discrete Sequential Estimation. Academic Press, Dover Publications, New York, 1977, 2006" @@ -857,9 +868,8 @@ bool Ekf::fuseDirectStateMeasurement(const float innov, const float innov_var, c VectorState PH = P.row(state_index); for (unsigned i = 0; i < State::size; i++) { - for (unsigned j = 0; j <= i; j++) { - P(i, j) = P(i, j) - K(i) * PH(j); - P(j, i) = P(i, j); + for (unsigned j = 0; j < State::size; j++) { + P(i, j) -= K(i) * PH(j); // P is now not symmetrical if K is not optimal (e.g.: some gains have been zeroed) } } @@ -868,11 +878,11 @@ bool Ekf::fuseDirectStateMeasurement(const float innov, const float innov_var, c for (unsigned i = 0; i < State::size; i++) { for (unsigned j = 0; j <= i; j++) { - float s = .5f * (P(i, j) - PH(i) * K(j) + P(i, j) - PH(j) * K(i)); - P(i, j) = s + K(i) * R * K(j); + P(i, j) = P(i, j) - PH(i) * K(j) + K(i) * R * K(j); P(j, i) = P(i, j); } } +#endif constrainStateVariances();