| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // This file is part of INSTINCT, the INS Toolkit for Integrated | ||
| 2 | // Navigation Concepts and Training by the Institute of Navigation of | ||
| 3 | // the University of Stuttgart, Germany. | ||
| 4 | // | ||
| 5 | // This Source Code Form is subject to the terms of the Mozilla Public | ||
| 6 | // License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| 7 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
| 8 | |||
| 9 | /// @file KalmanFilter.hpp | ||
| 10 | /// @brief Generalized Kalman Filter class | ||
| 11 | /// @author T. Topp (topp@ins.uni-stuttgart.de) | ||
| 12 | /// @date 2020-11-25 | ||
| 13 | |||
| 14 | #pragma once | ||
| 15 | |||
| 16 | #include <Eigen/Core> | ||
| 17 | #include <Eigen/Dense> | ||
| 18 | |||
| 19 | #include "Navigation/Math/Math.hpp" | ||
| 20 | |||
| 21 | namespace NAV | ||
| 22 | { | ||
| 23 | /// @brief Generalized Kalman Filter class | ||
| 24 | class KalmanFilter | ||
| 25 | { | ||
| 26 | public: | ||
| 27 | /// @brief Constructor | ||
| 28 | /// @param[in] n Number of States | ||
| 29 | /// @param[in] m Number of Measurements | ||
| 30 | 236 | KalmanFilter(int n, int m) | |
| 31 | 236 | { | |
| 32 | // xฬ State vector | ||
| 33 |
2/4✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 236 times.
✗ Branch 5 not taken.
|
236 | x = Eigen::MatrixXd::Zero(n, 1); |
| 34 | |||
| 35 | // ๐ Error covariance matrix | ||
| 36 |
2/4✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 236 times.
✗ Branch 5 not taken.
|
236 | P = Eigen::MatrixXd::Zero(n, n); |
| 37 | |||
| 38 | // ๐ฝ State transition matrix | ||
| 39 |
2/4✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 236 times.
✗ Branch 5 not taken.
|
236 | Phi = Eigen::MatrixXd::Zero(n, n); |
| 40 | |||
| 41 | // ๐ System/Process noise covariance matrix | ||
| 42 |
2/4✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 236 times.
✗ Branch 5 not taken.
|
236 | Q = Eigen::MatrixXd::Zero(n, n); |
| 43 | |||
| 44 | // ๐ณ Measurement vector | ||
| 45 |
2/4✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 236 times.
✗ Branch 5 not taken.
|
236 | z = Eigen::MatrixXd::Zero(m, 1); |
| 46 | |||
| 47 | // ๐ Measurement sensitivity Matrix | ||
| 48 |
2/4✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 236 times.
✗ Branch 5 not taken.
|
236 | H = Eigen::MatrixXd::Zero(m, n); |
| 49 | |||
| 50 | // ๐ = ๐ธ{๐ฐโ๐ฐโแต} Measurement noise covariance matrix | ||
| 51 |
2/4✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 236 times.
✗ Branch 5 not taken.
|
236 | R = Eigen::MatrixXd::Zero(m, m); |
| 52 | |||
| 53 | // ๐ฆ Measurement prediction covariance matrix | ||
| 54 |
2/4✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 236 times.
✗ Branch 5 not taken.
|
236 | S = Eigen::MatrixXd::Zero(m, m); |
| 55 | |||
| 56 | // ๐ Kalman gain matrix | ||
| 57 |
2/4✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 236 times.
✗ Branch 5 not taken.
|
236 | K = Eigen::MatrixXd::Zero(n, m); |
| 58 | |||
| 59 | // ๐ฐ Identity Matrix | ||
| 60 |
2/4✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 236 times.
✗ Branch 5 not taken.
|
236 | I = Eigen::MatrixXd::Identity(n, n); |
| 61 | 236 | } | |
| 62 | |||
| 63 | /// @brief Default constructor | ||
| 64 | KalmanFilter() = delete; | ||
| 65 | |||
| 66 | /// @brief Sets all Vectors and matrices to 0 | ||
| 67 | 6 | void setZero() | |
| 68 | { | ||
| 69 | // xฬ State vector | ||
| 70 | 6 | x.setZero(); | |
| 71 | |||
| 72 | // ๐ Error covariance matrix | ||
| 73 | 6 | P.setZero(); | |
| 74 | |||
| 75 | // ๐ฝ State transition matrix | ||
| 76 | 6 | Phi.setZero(); | |
| 77 | |||
| 78 | // ๐ System/Process noise covariance matrix | ||
| 79 | 6 | Q.setZero(); | |
| 80 | |||
| 81 | // ๐ณ Measurement vector | ||
| 82 | 6 | z.setZero(); | |
| 83 | |||
| 84 | // ๐ Measurement sensitivity Matrix | ||
| 85 | 6 | H.setZero(); | |
| 86 | |||
| 87 | // ๐ = ๐ธ{๐ฐโ๐ฐโแต} Measurement noise covariance matrix | ||
| 88 | 6 | R.setZero(); | |
| 89 | |||
| 90 | // ๐ฆ Measurement prediction covariance matrix | ||
| 91 | 6 | S.setZero(); | |
| 92 | |||
| 93 | // ๐ Kalman gain matrix | ||
| 94 | 6 | K.setZero(); | |
| 95 | 6 | } | |
| 96 | |||
| 97 | /// @brief Do a Time Update | ||
| 98 | /// @attention Update the State transition matrix (๐ฝ) and the Process noise covariance matrix (๐) before calling this | ||
| 99 | /// @note See P. Groves (2013) - Principles of GNSS, Inertial, and Multisensor Integrated Navigation Systems (ch. 3.2.2) | ||
| 100 | 17386 | void predict() | |
| 101 | { | ||
| 102 | // Math: \mathbf{\hat{x}}_k^- = \mathbf{\Phi}_{k-1}\mathbf{\hat{x}}_{k-1}^+ \qquad \text{P. Groves}\,(3.14) | ||
| 103 |
2/4✓ Branch 1 taken 17386 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 17386 times.
✗ Branch 5 not taken.
|
17386 | x = Phi * x; |
| 104 | |||
| 105 | // Math: \mathbf{P}_k^- = \mathbf{\Phi}_{k-1} P_{k-1}^+ \mathbf{\Phi}_{k-1}^T + \mathbf{Q}_{k-1} \qquad \text{P. Groves}\,(3.15) | ||
| 106 |
5/10✓ Branch 1 taken 17386 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 17386 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 17386 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 17386 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 17386 times.
✗ Branch 14 not taken.
|
17386 | P = Phi * P * Phi.transpose() + Q; |
| 107 | 17386 | } | |
| 108 | |||
| 109 | /// @brief Do a Measurement Update with a Measurement ๐ณ | ||
| 110 | /// @attention Update the Measurement sensitivity Matrix (๐), the Measurement noise covariance matrix (๐) | ||
| 111 | /// and the Measurement vector (๐ณ) before calling this | ||
| 112 | /// @note See P. Groves (2013) - Principles of GNSS, Inertial, and Multisensor Integrated Navigation Systems (ch. 3.2.2) | ||
| 113 | 17386 | void correct() | |
| 114 | { | ||
| 115 |
5/10✓ Branch 1 taken 17386 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 17386 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 17386 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 17386 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 17386 times.
✗ Branch 14 not taken.
|
17386 | S = H * P * H.transpose() + R; |
| 116 | |||
| 117 | // Math: \mathbf{K}_k = \mathbf{P}_k^- \mathbf{H}_k^T (\mathbf{H}_k \mathbf{P}_k^- \mathbf{H}_k^T + R_k)^{-1} \qquad \text{P. Groves}\,(3.21) | ||
| 118 |
5/10✓ Branch 1 taken 17386 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 17386 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 17386 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 17386 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 17386 times.
✗ Branch 14 not taken.
|
17386 | K = P * H.transpose() * S.inverse(); |
| 119 | |||
| 120 | // Math: \begin{align*} \mathbf{\hat{x}}_k^+ &= \mathbf{\hat{x}}_k^- + \mathbf{K}_k (\mathbf{z}_k - \mathbf{H}_k \mathbf{\hat{x}}_k^-) \\ &= \mathbf{\hat{x}}_k^- + \mathbf{K}_k \mathbf{\delta z}_k^{-} \end{align*} \qquad \text{P. Groves}\,(3.24) | ||
| 121 |
5/10✓ Branch 1 taken 17386 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 17386 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 17386 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 17386 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 17386 times.
✗ Branch 14 not taken.
|
17386 | x = x + K * (z - H * x); |
| 122 | |||
| 123 | // Math: \mathbf{P}_k^+ = (\mathbf{I} - \mathbf{K}_k \mathbf{H}_k) \mathbf{P}_k^- \qquad \text{P. Groves}\,(3.25) | ||
| 124 |
4/8✓ Branch 1 taken 17386 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 17386 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 17386 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 17386 times.
✗ Branch 11 not taken.
|
17386 | P = (I - K * H) * P; |
| 125 | 17386 | } | |
| 126 | |||
| 127 | /// @brief Do a Measurement Update with a Measurement Innovation ๐น๐ณ | ||
| 128 | /// @attention Update the Measurement sensitivity Matrix (๐), the Measurement noise covariance matrix (๐) | ||
| 129 | /// and the Measurement vector (๐ณ) before calling this | ||
| 130 | /// @note See P. Groves (2013) - Principles of GNSS, Inertial, and Multisensor Integrated Navigation Systems (ch. 3.2.2) | ||
| 131 | /// @note See Brown & Hwang (2012) - Introduction to Random Signals and Applied Kalman Filtering (ch. 5.5 - figure 5.5) | ||
| 132 | ✗ | void correctWithMeasurementInnovation() | |
| 133 | { | ||
| 134 | // Math: \mathbf{K}_k = \mathbf{P}_k^- \mathbf{H}_k^T (\mathbf{H}_k \mathbf{P}_k^- \mathbf{H}_k^T + R_k)^{-1} \qquad \text{P. Groves}\,(3.21) | ||
| 135 | ✗ | K = P * H.transpose() * (H * P * H.transpose() + R).inverse(); | |
| 136 | |||
| 137 | // Math: \begin{align*} \mathbf{\hat{x}}_k^+ &= \mathbf{\hat{x}}_k^- + \mathbf{K}_k (\mathbf{z}_k - \mathbf{H}_k \mathbf{\hat{x}}_k^-) \\ &= \mathbf{\hat{x}}_k^- + \mathbf{K}_k \mathbf{\delta z}_k^{-} \end{align*} \qquad \text{P. Groves}\,(3.24) | ||
| 138 | ✗ | x = x + K * z; | |
| 139 | |||
| 140 | // Math: \mathbf{P}_k^+ = (\mathbf{I} - \mathbf{K}_k \mathbf{H}_k) \mathbf{P}_k^- \qquad \text{P. Groves}\,(3.25) | ||
| 141 | // P = (I - K * H) * P; | ||
| 142 | |||
| 143 | // Math: \mathbf{P}_k^+ = (\mathbf{I} - \mathbf{K}_k \mathbf{H}_k) \mathbf{P}_k^- (\mathbf{I} - \mathbf{K}_k \mathbf{H}_k)^T + \mathbf{K}_k \mathbf{R}_k \mathbf{K}_k^T \qquad \text{Brown & Hwang}\,(p. 145, eq. 4.2.11) | ||
| 144 | ✗ | P = (I - K * H) * P * (I - K * H).transpose() + K * R * K.transpose(); | |
| 145 | ✗ | } | |
| 146 | |||
| 147 | /// @brief Updates the state transition matrix ๐ฝ limited to first order in ๐ ๐โ | ||
| 148 | /// @param[in] F System Matrix | ||
| 149 | /// @param[in] tau_s time interval in [s] | ||
| 150 | /// @note See Groves (2013) chapter 14.2.4, equation (14.72) | ||
| 151 | static Eigen::MatrixXd transitionMatrix(const Eigen::MatrixXd& F, double tau_s) | ||
| 152 | { | ||
| 153 | // Transition matrix ๐ฝ | ||
| 154 | return Eigen::MatrixXd::Identity(F.rows(), F.cols()) + F * tau_s; | ||
| 155 | } | ||
| 156 | |||
| 157 | /// xฬ State vector | ||
| 158 | Eigen::MatrixXd x; | ||
| 159 | |||
| 160 | /// ๐ Error covariance matrix | ||
| 161 | Eigen::MatrixXd P; | ||
| 162 | |||
| 163 | /// ๐ฝ State transition matrix | ||
| 164 | Eigen::MatrixXd Phi; | ||
| 165 | |||
| 166 | /// ๐ System/Process noise covariance matrix | ||
| 167 | Eigen::MatrixXd Q; | ||
| 168 | |||
| 169 | /// ๐ณ Measurement vector | ||
| 170 | Eigen::MatrixXd z; | ||
| 171 | |||
| 172 | /// ๐ Measurement sensitivity Matrix | ||
| 173 | Eigen::MatrixXd H; | ||
| 174 | |||
| 175 | /// ๐ = ๐ธ{๐ฐโ๐ฐโแต} Measurement noise covariance matrix | ||
| 176 | Eigen::MatrixXd R; | ||
| 177 | |||
| 178 | /// ๐ฆ Measurement prediction covariance matrix | ||
| 179 | Eigen::MatrixXd S; | ||
| 180 | |||
| 181 | /// ๐ Kalman gain matrix | ||
| 182 | Eigen::MatrixXd K; | ||
| 183 | |||
| 184 | private: | ||
| 185 | /// ๐ฐ Identity Matrix (n x n) | ||
| 186 | Eigen::MatrixXd I; | ||
| 187 | }; | ||
| 188 | |||
| 189 | /// @brief Calculates the state transition matrix ๐ฝ limited to specified order in ๐ ๐โ | ||
| 190 | /// @param[in] F System Matrix | ||
| 191 | /// @param[in] tau_s time interval in [s] | ||
| 192 | /// @param[in] order The order of the Taylor polynom to calculate | ||
| 193 | /// @note See \cite Groves2013 Groves, ch. 3.2.3, eq. 3.34, p. 98 | ||
| 194 | template<typename Derived> | ||
| 195 | ✗ | typename Derived::PlainObject transitionMatrix_Phi_Taylor(const Eigen::MatrixBase<Derived>& F, double tau_s, size_t order) | |
| 196 | { | ||
| 197 | // Transition matrix ๐ฝ | ||
| 198 | ✗ | typename Derived::PlainObject Phi; | |
| 199 | |||
| 200 | if constexpr (Derived::RowsAtCompileTime == Eigen::Dynamic) | ||
| 201 | { | ||
| 202 | ✗ | Phi = Eigen::MatrixBase<Derived>::Identity(F.rows(), F.cols()); | |
| 203 | } | ||
| 204 | else | ||
| 205 | { | ||
| 206 | ✗ | Phi = Eigen::MatrixBase<Derived>::Identity(); | |
| 207 | } | ||
| 208 | // std::cout << "Phi = I"; | ||
| 209 | |||
| 210 | ✗ | for (size_t i = 1; i <= order; i++) | |
| 211 | { | ||
| 212 | ✗ | typename Derived::PlainObject Fpower = F; | |
| 213 | // std::cout << " + (F"; | ||
| 214 | |||
| 215 | ✗ | for (size_t j = 1; j < i; j++) // F^j | |
| 216 | { | ||
| 217 | // std::cout << "*F"; | ||
| 218 | ✗ | Fpower *= F; | |
| 219 | } | ||
| 220 | // std::cout << "*tau_s^" << i << ")"; | ||
| 221 | // std::cout << "/" << math::factorial(i); | ||
| 222 | ✗ | Phi += (Fpower * std::pow(tau_s, i)) / math::factorial(i); | |
| 223 | } | ||
| 224 | // std::cout << "\n"; | ||
| 225 | |||
| 226 | ✗ | return Phi; | |
| 227 | ✗ | } | |
| 228 | |||
| 229 | /// @brief Calculates the state transition matrix ๐ฝ using the exponential matrix | ||
| 230 | /// @param[in] F System Matrix | ||
| 231 | /// @param[in] tau_s time interval in [s] | ||
| 232 | /// @note See \cite Groves2013 Groves, ch. 3.2.3, eq. 3.33, p. 97 | ||
| 233 | /// @attention The cost of the computation is approximately 20n^3 for matrices of size n. The number 20 depends weakly on the norm of the matrix. | ||
| 234 | template<typename Derived> | ||
| 235 | typename Derived::PlainObject transitionMatrix_Phi_exp(const Eigen::MatrixBase<Derived>& F, typename Derived::Scalar tau_s) | ||
| 236 | { | ||
| 237 | // Transition matrix ๐ฝ | ||
| 238 | return (F * tau_s).exp(); | ||
| 239 | } | ||
| 240 | |||
| 241 | } // namespace NAV | ||
| 242 |