LCOV - code coverage report
Current view: top level - include - ekf.hpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 16 16 100.0 %
Date: 2024-11-26 20:23:51 Functions: 13 13 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (C) 2024 robinAZERTY [https://github.com/robinAZERTY]
       3             :  *
       4             :  * This file is part of ESP32AlgebraFilters library.
       5             :  *
       6             :  * ESP32AlgebraFilters library is free software: you can redistribute it and/or modify
       7             :  * it under the terms of the GNU General Public License as published by
       8             :  * the Free Software Foundation, either version 3 of the License, or
       9             :  * (at your option) any later version.
      10             :  *
      11             :  * ESP32AlgebraFilters library is distributed in the hope that it will be useful,
      12             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
      14             :  * GNU General Public License for more details.
      15             :  *
      16             :  * You should have received a copy of the GNU General Public License
      17             :  * along with ESP32AlgebraFilters library. If not, see <https://www.gnu.org/licenses/>.
      18             :  */
      19             : 
      20             : #ifndef EKF_HPP
      21             : #define EKF_HPP
      22             : 
      23             : #include "matrix.hpp"
      24             : #include "symMatrix.hpp"
      25             : using namespace operators;
      26             : 
      27             : /**
      28             :  * @class Ekf
      29             :  * @brief A generic implementation of an Extended Kalman Filter (EKF).
      30             :  *
      31             :  * This class provides the core EKF functionality, including state prediction and update steps for nonlinear systems.
      32             :  * It supports dynamic system models and allows for numerical differentiation of Jacobians for both state transition and measurement functions.
      33             :  *
      34             :  * You must provide several key functions and data to ensure the filter works properly:
      35             :  *
      36             :  * - **State Transition Function** (`f`): A function that models the evolution of the state vector over time, used for the prediction step.
      37             :  * - **Measurement Function** (`h`): A function that models the measurement process, mapping the state vector to the measurement space.
      38             :  * - **Initial State and Covariance** (`X` and `P`): The user must initialize the state vector `X` and the state covariance matrix `P` with reasonable values for the system.
      39             :  * - **Control Inputs** (`U`): If the system involves control inputs, the user must provide them during the prediction step.
      40             :  * - **Measurement Data** (`Z` and `R`): During the update step, the user must provide the measurement vector `Z` and the measurement noise covariance `R`.
      41             :  *
      42             :  * The EKF will then handle the state prediction, measurement update, and state covariance estimation.
      43             :  *
      44             :  * The filter supports multiple types of sensors, with each sensor having its own measurement function and Jacobian.
      45             :  * The number of sensor types (`z_num`) can be customized, allowing for flexibility in handling various measurements within the same filter.
      46             :  *
      47             :  * The class also supports numerical differentiation for computing Jacobians if analytical expressions are not available, making it versatile for different models.
      48             :  *
      49             :  * @tparam x_dim The dimension of the state vector.
      50             :  * @tparam u_dim The dimension of the control input vector.
      51             :  * @tparam c_dim The dimension of the system parameters (default is 1).
      52             :  * @tparam z_num The number of measurement types (default is 1).
      53             :  * @tparam T The data type used for calculations (default is float).
      54             :  */
      55             : template <size_t x_dim, size_t u_dim, size_t c_dim = 1, size_t z_num = 1, typename T = float>
      56             : class Ekf
      57             : {
      58             : private:
      59             : 
      60             :     /** State transition function */
      61             :     Vector_f3<T> f = nullptr;
      62             : 
      63             :     /** Measurement dimensions for each measurement type */
      64             :     size_t z_dim[z_num] = {0};
      65             : 
      66             :     /** Measurement functions for each measurement type */
      67             :     Vector_f2<T> h[z_num] = {nullptr};
      68             : 
      69             :     /** State transition Jacobians */
      70             :     Matrix_f3<T> Fx = nullptr;
      71             :     Matrix_f3<T> Fu = nullptr;
      72             :     Matrix_f3<T> Fc = nullptr;
      73             : 
      74             :     /** Measurement Jacobians for each measurement type */
      75             :     Matrix_f2<T> H[z_num] = {nullptr};
      76             : 
      77             :     /** Measurement residuals for each measurement type */
      78             :     Vector<T> y[z_num];
      79             : 
      80             :     /** Jacobian values for measurement functions */
      81             :     Matrix<T> H_val[z_num];
      82             : 
      83             :     /** partial computation of innovation covariance (without R added) */
      84             :     symMatrix<T> pre_S[z_num];
      85             : 
      86             :     /** innovation precision */
      87             :     ldl_matrix<T> S_inv[z_num];
      88             : 
      89             :     /** Predicted measurement for each measurement type */
      90             :     Vector<T> h_val[z_num];
      91             : 
      92             :     /** Previous state vector */
      93             :     Vector<T> prev_X = Vector<T>(x_dim);
      94             : 
      95             :     /** State transition matrices */
      96             :     Matrix<T> Fx_val_T = Matrix<T>(x_dim, x_dim);
      97             :     Matrix<T> Fu_val_T = Matrix<T>(u_dim, x_dim);
      98             :     Matrix<T> Fc_val_T = Matrix<T>(c_dim, x_dim);
      99             : 
     100             :     /** Kalman gain */
     101             :     rowMajorMatrix<T> K;
     102             : 
     103             :     /** Measurement Jacobian * State covariance for each measurement type */
     104             :     Matrix<T> H_P[z_num];
     105             : 
     106             :     inline void finite_diff_Fx(const size_t i);
     107             :     inline void finite_diff_Fu(const size_t i);
     108             : 
     109             :     /**
     110             :      * @brief Numerical differentiation for the state transition Jacobian (Fx) using finite differences.
     111             :      *
     112             :      * This function approximates the Jacobian of the state transition function with respect to the state vector.
     113             :      *
     114             :      * @param i The index of the state dimension to compute the Jacobian.
     115             :      */
     116        1499 :     void finite_diff_Fx()
     117             :     {
     118        5995 :         for (size_t i = 0; i < x_dim; i++)
     119        4496 :             finite_diff_Fx(i);
     120        1499 :     };
     121             : 
     122             :     /**
     123             :      * @brief Numerical differentiation for the control input transition Jacobian (Fu) using finite differences.
     124             :      *
     125             :      * This function approximates the Jacobian of the state transition function with respect to the control input vector.
     126             :      *
     127             :      * @param i The index of the control input dimension to compute the Jacobian.
     128             :      */
     129        1499 :     void finite_diff_Fu()
     130             :     {
     131        3997 :         for (size_t i = 0; i < u_dim; i++)
     132        2498 :             finite_diff_Fu(i);
     133        1499 :     };
     134             : 
     135             :     /**
     136             :      * @brief Numerical differentiation for the measurement Jacobian (H) using finite differences.
     137             :      *
     138             :      * This function approximates the Jacobian of the measurement function with respect to the state vector.
     139             :      *
     140             :      * @param z_idx The index of the measurement type.
     141             :      * @param i The index of the state dimension to compute the Jacobian.
     142             :      */
     143             :     void finite_diff_H(const size_t z_idx, const size_t i);
     144             : 
     145             :     /** Compute Jacobian for measurements using numerical differentiation */
     146         143 :     void finite_diff_H(const size_t z_idx)
     147             :     {
     148         565 :         for (size_t i = 0; i < x_dim; i++)
     149         422 :             finite_diff_H(z_idx, i);
     150         143 :     };
     151             : 
     152             :     /** Flag indicating whether the filter is initialized */
     153             :     bool initted = false;
     154             : 
     155             : public:
     156             :     /** Current state vector */
     157             :     Vector<T> X = Vector<T>(x_dim);
     158             : 
     159             :     /** Small state epsilon for numerical differentiation */
     160             :     Vector<T> dx = Vector<T>(x_dim);
     161             : 
     162             :     /** State covariance matrix */
     163             :     symMatrix<T> P = symMatrix<T>(x_dim, x_dim);
     164             : 
     165             :     /** Control input vector */
     166             :     Vector<T> U = Vector<T>(u_dim);
     167             : 
     168             :     /** Small control input epsilon for numerical differentiation */
     169             :     Vector<T> du = Vector<T>(u_dim);
     170             : 
     171             :     /** Process noise covariance matrix */
     172             :     symMatrix<T> Cov_U = symMatrix<T>(u_dim);
     173             : 
     174             :     /** System parameters vector */
     175             :     Vector<T> C = Vector<T>(c_dim);
     176             : 
     177             :     /** Small system parameters epsilon for numerical differentiation */
     178             :     Vector<T> dc = Vector<T>(c_dim);
     179             : 
     180             :     /**
     181             :      * @brief Default constructor for the EKF filter.
     182             :      *
     183             :      * Initializes the state vector `X`, the state covariance `P`, and other necessary vectors to default values.
     184             :      * Sets small values for numerical differentiation.
     185             :      */
     186             :     Ekf();
     187             : 
     188             :     /** Constructor with a state transition function */
     189           2 :     Ekf(Vector_f3<T> f) : Ekf() { setPredictionFunction(f); }
     190             : 
     191             :     /** Destructor */
     192          20 :     ~Ekf() {};
     193             : 
     194             :     /** Set the state prediction function */
     195           2 :     void setPredictionFunction(Vector_f3<T> f) { this->f = f; }
     196             : 
     197             :     /** Set the Jacobian for state transition */
     198             :     void setJacobianFunction_Fx(Matrix_f3<T> Fx) { this->Fx = Fx; }
     199             : 
     200             :     /** Set the Jacobian for control input transition */
     201             :     void setJacobianFunction_Fu(Matrix_f3<T> Fu) { this->Fu = Fu; }
     202             : 
     203             :     /**
     204             :      * @brief Sets the measurement function for a specific sensor type.
     205             :      *
     206             :      * This function defines the measurement function and measurement dimension for a particular sensor type.
     207             :      * It also initializes the corresponding Jacobians and matrices.
     208             :      *
     209             :      * @param h The measurement function for the sensor type.
     210             :      * @param z_dim The dimension of the measurement vector for the sensor.
     211             :      * @param z_idx The index of the sensor type (default is 0).
     212             :      */
     213             :     void setMeasurementFunction(Vector_f2<T> h, size_t z_dim, size_t z_idx = 0);
     214             : 
     215             :     /** Set the Jacobian for the measurement function */
     216             :     void setJacobianFunction_H(Matrix_f2<T> H, size_t z_idx = 0) { this->H[z_idx] = H; }
     217             : 
     218             :     /**
     219             :      * @brief Predicts the state using the state transition function.
     220             :      *
     221             :      * This function computes the predicted state `X` based on the previous state and the state transition function.
     222             :      * It also updates the state covariance matrix `P` based on the state transition Jacobian.
     223             :      *
     224             :      * Throws an exception if the state transition function is not set.
     225             :      */
     226             :     inline void predict();
     227             : 
     228             :     /**
     229             :      * @brief First part of the update function, computes the measurement prediction and the measurement Jacobian.
     230             :      *
     231             :      * Additionally, it computes the common expression `H*P` which is used to reduce the number of matrix multiplications during the update step.
     232             :      *
     233             :      * @param z_idx The index of the measurement type.
     234             :      */
     235             :     inline void compute_H_P(const size_t z_idx = 0);
     236             : 
     237             :     /**
     238             :      * @brief Second part of the update function, computes the residual and the residual precision.
     239             :      *
     240             :      * This function is a part of the update step.
     241             :      *
     242             :      * @param Z The new measurement vector.
     243             :      * @param R The measurement noise covariance matrix.
     244             :      * @param z_idx The index of the measurement type.
     245             :      */
     246             :     inline void computeResidualPrecision(const symMatrix<T> &R, const size_t z_idx = 0);
     247             : 
     248             :     /**
     249             :      * @brief Third part of the update function, computes the residual and updates the state and covariance.
     250             :      *
     251             :      *  This function compute the difference between the measurement and the predicted measurement
     252             :      *
     253             :      * @param Z The new measurement vector.
     254             :      * @param z_idx The index of the measurement type.
     255             :      */
     256             :     inline void computeResidual(const Vector<T> &Z, const size_t z_idx = 0);
     257             : 
     258             :     /**
     259             :      * @brief Final part of the update function, computes the state and covariance.
     260             :      *
     261             :      * This function compute the Kalman gain and updates the state and covariance based on the measurement residual and the residual precision.
     262             :      *
     263             :      * @param z_idx The index of the measurement type.
     264             :      * @param updateMahalanobis Flag indicating whether to update the Mahalanobis distance.
     265             :      */
     266             :     inline void updateStateCovariance(const size_t z_idx = 0);
     267             : 
     268             :     /**
     269             :      * @brief Compute the Mahalanobis distance for a specific measurement type relative to last update of residual and residual precision.
     270             :      *
     271             :      * Should be called after the "computeResidualPrecision" function
     272             :      * Can be used to monitor the likelihood of the given measurement.
     273             :      * It is useful for detecting outliers or resolving identification issues (when you receive a measurement and you don't know witch measurement type it is).
     274             :      * If everything is perfectly modelled, the average Mahalanobis distance should be 1 (after averaging over a long period).
     275             :      *
     276             :      * @param z_idx The index of the measurement type.
     277             :      * @return The Mahalanobis distance.
     278             :      */
     279          93 :     inline T mahalanobisDistance(const size_t z_idx = 0) { return y[z_idx].dot(*(S_inv[z_idx] * y[z_idx]).release()); }
     280             : 
     281             :     /**
     282             :      * @brief Updates the state and covariance matrix using a new measurement and measurement noise covariance.
     283             :      *
     284             :      * This function updates the state vector `X` and the state covariance matrix `P` based on the new measurement `Z`
     285             :      * and the measurement noise covariance matrix `R`. The Kalman gain is also computed and used to adjust the state.
     286             :      * Throws an exception if the measurement function is not set or if there is a dimension mismatch.
     287             :      * This function can be split into multiple parts for better readability and modularity as follows:
     288             :      * @code
     289             :      * compute_H_P(z_idx);
     290             :      * computeResidualPrecision(R, z_idx);
     291             :      * computeResidual(Z, z_idx);
     292             :      * updateStateCovariance(z_idx);
     293             :      * @endcode
     294             :      * Before calling @code updateStateCovariance @endcode, you can check the Mahalanobis distance using @ref mahalanobisDistance.
     295             :      *
     296             :      * @param Z The new measurement vector.
     297             :      * @param R The measurement noise covariance matrix.
     298             :      * @param z_idx The index of the measurement type (default is 0).
     299             :      */
     300             :     inline void update(const Vector<T> &Z, const symMatrix<T> &R, const size_t z_idx = 0);
     301             : };
     302             : 
     303             : #ifndef EKF_CPP
     304             : #include "ekf.cpp"
     305             : #endif
     306             : 
     307             : #endif // EKF_HPP

Generated by: LCOV version 1.14