| 1 | /*! |
|---|
| 2 | \file |
|---|
| 3 | \brief Bayesian Filtering for linear Gaussian models (Kalman Filter) and extensions |
|---|
| 4 | \author Vaclav Smidl. |
|---|
| 5 | |
|---|
| 6 | ----------------------------------- |
|---|
| 7 | BDM++ - C++ library for Bayesian Decision Making under Uncertainty |
|---|
| 8 | |
|---|
| 9 | Using IT++ for numerical operations |
|---|
| 10 | ----------------------------------- |
|---|
| 11 | */ |
|---|
| 12 | |
|---|
| 13 | #ifndef KF_H |
|---|
| 14 | #define KF_H |
|---|
| 15 | |
|---|
| 16 | #include <itpp/itbase.h> |
|---|
| 17 | #include "../stat/libFN.h" |
|---|
| 18 | #include "../stat/libEF.h" |
|---|
| 19 | #include "../math/chmat.h" |
|---|
| 20 | |
|---|
| 21 | using namespace itpp; |
|---|
| 22 | |
|---|
| 23 | /*! |
|---|
| 24 | * \brief Basic Kalman filter with full matrices (education purpose only)! Will be deleted soon! |
|---|
| 25 | */ |
|---|
| 26 | |
|---|
| 27 | class KalmanFull { |
|---|
| 28 | protected: |
|---|
| 29 | int dimx, dimy, dimu; |
|---|
| 30 | mat A, B, C, D, R, Q; |
|---|
| 31 | |
|---|
| 32 | //cache |
|---|
| 33 | mat _Pp, _Ry, _iRy, _K; |
|---|
| 34 | public: |
|---|
| 35 | //posterior |
|---|
| 36 | //! Mean value of the posterior density |
|---|
| 37 | vec mu; |
|---|
| 38 | //! Variance of the posterior density |
|---|
| 39 | mat P; |
|---|
| 40 | |
|---|
| 41 | bool evalll; |
|---|
| 42 | double ll; |
|---|
| 43 | public: |
|---|
| 44 | //! Full constructor |
|---|
| 45 | KalmanFull ( mat A, mat B, mat C, mat D, mat R, mat Q, mat P0, vec mu0 ); |
|---|
| 46 | //! Here dt = [yt;ut] of appropriate dimensions |
|---|
| 47 | void bayes ( const vec &dt ); |
|---|
| 48 | //! print elements of KF |
|---|
| 49 | friend std::ostream &operator<< ( std::ostream &os, const KalmanFull &kf ); |
|---|
| 50 | //! For EKFfull; |
|---|
| 51 | KalmanFull(){}; |
|---|
| 52 | }; |
|---|
| 53 | |
|---|
| 54 | |
|---|
| 55 | /*! |
|---|
| 56 | * \brief Kalman filter with covariance matrices in square root form. |
|---|
| 57 | |
|---|
| 58 | Parameter evolution model:\f[ x_t = A x_{t-1} + B u_t + Q^{1/2} e_t \f] |
|---|
| 59 | Observation model: \f[ y_t = C x_{t-1} + C u_t + Q^{1/2} w_t. \f] |
|---|
| 60 | Where $e_t$ and $w_t$ are independent vectors Normal(0,1)-distributed disturbances. |
|---|
| 61 | */ |
|---|
| 62 | template<class sq_T> |
|---|
| 63 | |
|---|
| 64 | class Kalman : public BM { |
|---|
| 65 | protected: |
|---|
| 66 | //! Indetifier of output rv |
|---|
| 67 | RV rvy; |
|---|
| 68 | //! Indetifier of exogeneous rv |
|---|
| 69 | RV rvu; |
|---|
| 70 | //! cache of rv.count() |
|---|
| 71 | int dimx; |
|---|
| 72 | //! cache of rvy.count() |
|---|
| 73 | int dimy; |
|---|
| 74 | //! cache of rvu.count() |
|---|
| 75 | int dimu; |
|---|
| 76 | //! Matrix A |
|---|
| 77 | mat A; |
|---|
| 78 | //! Matrix B |
|---|
| 79 | mat B; |
|---|
| 80 | //! Matrix C |
|---|
| 81 | mat C; |
|---|
| 82 | //! Matrix D |
|---|
| 83 | mat D; |
|---|
| 84 | //! Matrix Q in square-root form |
|---|
| 85 | sq_T Q; |
|---|
| 86 | //! Matrix R in square-root form |
|---|
| 87 | sq_T R; |
|---|
| 88 | |
|---|
| 89 | //!posterior density on $x_t$ |
|---|
| 90 | enorm<sq_T> est; |
|---|
| 91 | //!preditive density on $y_t$ |
|---|
| 92 | enorm<sq_T> fy; |
|---|
| 93 | |
|---|
| 94 | //! placeholder for Kalman gain |
|---|
| 95 | mat _K; |
|---|
| 96 | //! cache of fy.mu |
|---|
| 97 | vec* _yp; |
|---|
| 98 | //! cache of fy.R |
|---|
| 99 | sq_T* _Ry; |
|---|
| 100 | //! cache of fy.iR |
|---|
| 101 | sq_T* _iRy; |
|---|
| 102 | //!cache of est.mu |
|---|
| 103 | vec* _mu; |
|---|
| 104 | //!cache of est.R |
|---|
| 105 | sq_T* _P; |
|---|
| 106 | //!cache of est.iR |
|---|
| 107 | sq_T* _iP; |
|---|
| 108 | |
|---|
| 109 | public: |
|---|
| 110 | //! Default constructor |
|---|
| 111 | Kalman ( RV rvx0, RV rvy0, RV rvu0 ); |
|---|
| 112 | //! Copy constructor |
|---|
| 113 | Kalman ( const Kalman<sq_T> &K0 ); |
|---|
| 114 | //! Set parameters with check of relevance |
|---|
| 115 | void set_parameters ( const mat &A0,const mat &B0,const mat &C0,const mat &D0,const sq_T &R0,const sq_T &Q0 ); |
|---|
| 116 | //! Set estimate values, used e.g. in initialization. |
|---|
| 117 | void set_est ( const vec &mu0, const sq_T &P0 ) { |
|---|
| 118 | sq_T pom(dimy); |
|---|
| 119 | est.set_parameters ( mu0,P0 ); |
|---|
| 120 | P0.mult_sym(C,pom); |
|---|
| 121 | fy.set_parameters ( C*mu0, pom ); |
|---|
| 122 | }; |
|---|
| 123 | |
|---|
| 124 | //! Here dt = [yt;ut] of appropriate dimensions |
|---|
| 125 | void bayes ( const vec &dt ); |
|---|
| 126 | //!access function |
|---|
| 127 | epdf& _epdf() {return est;} |
|---|
| 128 | //!access function |
|---|
| 129 | mat& __K() {return _K;} |
|---|
| 130 | //!access function |
|---|
| 131 | vec _dP() {return _P->getD();} |
|---|
| 132 | }; |
|---|
| 133 | |
|---|
| 134 | /*! \brief Kalman filter in square root form |
|---|
| 135 | */ |
|---|
| 136 | class KalmanCh : public Kalman<chmat>{ |
|---|
| 137 | protected: |
|---|
| 138 | //! pre array (triangular matrix) |
|---|
| 139 | mat preA; |
|---|
| 140 | //! post array (triangular matrix) |
|---|
| 141 | mat postA; |
|---|
| 142 | |
|---|
| 143 | public: |
|---|
| 144 | //! Default constructor |
|---|
| 145 | KalmanCh ( RV rvx0, RV rvy0, RV rvu0 ):Kalman<chmat>(rvx0,rvy0,rvu0),preA(dimy+dimx+dimx,dimy+dimx),postA(dimy+dimx,dimy+dimx){}; |
|---|
| 146 | //! Set parameters with check of relevance |
|---|
| 147 | void set_parameters ( const mat &A0,const mat &B0,const mat &C0,const mat &D0,const chmat &R0,const chmat &Q0 ); |
|---|
| 148 | void set_est ( const vec &mu0, const chmat &P0 ) { |
|---|
| 149 | est.set_parameters ( mu0,P0 ); |
|---|
| 150 | }; |
|---|
| 151 | |
|---|
| 152 | |
|---|
| 153 | /*!\brief Here dt = [yt;ut] of appropriate dimensions |
|---|
| 154 | |
|---|
| 155 | The following equality hold::\f[ |
|---|
| 156 | \left[\begin{array}{cc} |
|---|
| 157 | R^{0.5}\\ |
|---|
| 158 | P_{t|t-1}^{0.5}C' & P_{t|t-1}^{0.5}CA'\\ |
|---|
| 159 | & Q^{0.5}\end{array}\right]<\mathrm{orth.oper.}>=\left[\begin{array}{cc} |
|---|
| 160 | R_{y}^{0.5} & KA'\\ |
|---|
| 161 | & P_{t+1|t}^{0.5}\\ |
|---|
| 162 | \\\end{array}\right]\f] |
|---|
| 163 | |
|---|
| 164 | Thus this objevt evaluates only predictors! Not filtering densities. |
|---|
| 165 | */ |
|---|
| 166 | void bayes ( const vec &dt ); |
|---|
| 167 | }; |
|---|
| 168 | |
|---|
| 169 | /*! |
|---|
| 170 | \brief Extended Kalman Filter in full matrices |
|---|
| 171 | |
|---|
| 172 | An approximation of the exact Bayesian filter with Gaussian noices and non-linear evolutions of their mean. |
|---|
| 173 | */ |
|---|
| 174 | class EKFfull : public KalmanFull, public BM { |
|---|
| 175 | //! Internal Model f(x,u) |
|---|
| 176 | diffbifn* pfxu; |
|---|
| 177 | //! Observation Model h(x,u) |
|---|
| 178 | diffbifn* phxu; |
|---|
| 179 | public: |
|---|
| 180 | //! Default constructor |
|---|
| 181 | EKFfull ( RV rvx, RV rvy, RV rvu ); |
|---|
| 182 | //! Set nonlinear functions for mean values and covariance matrices. |
|---|
| 183 | void set_parameters ( diffbifn* pfxu, diffbifn* phxu, const mat Q0, const mat R0 ); |
|---|
| 184 | //! Here dt = [yt;ut] of appropriate dimensions |
|---|
| 185 | void bayes ( const vec &dt ); |
|---|
| 186 | //! set estimates |
|---|
| 187 | void set_est (vec mu0, mat P0){mu=mu0;P=P0;}; |
|---|
| 188 | //!dummy! |
|---|
| 189 | epdf& _epdf(){enorm<fsqmat> E(rv); return E;}; |
|---|
| 190 | }; |
|---|
| 191 | |
|---|
| 192 | /*! |
|---|
| 193 | \brief Extended Kalman Filter |
|---|
| 194 | |
|---|
| 195 | An approximation of the exact Bayesian filter with Gaussian noices and non-linear evolutions of their mean. |
|---|
| 196 | */ |
|---|
| 197 | template<class sq_T> |
|---|
| 198 | class EKF : public Kalman<fsqmat> { |
|---|
| 199 | //! Internal Model f(x,u) |
|---|
| 200 | diffbifn* pfxu; |
|---|
| 201 | //! Observation Model h(x,u) |
|---|
| 202 | diffbifn* phxu; |
|---|
| 203 | public: |
|---|
| 204 | //! Default constructor |
|---|
| 205 | EKF ( RV rvx, RV rvy, RV rvu ); |
|---|
| 206 | //! Set nonlinear functions for mean values and covariance matrices. |
|---|
| 207 | void set_parameters ( diffbifn* pfxu, diffbifn* phxu, const sq_T Q0, const sq_T R0 ); |
|---|
| 208 | //! Here dt = [yt;ut] of appropriate dimensions |
|---|
| 209 | void bayes ( const vec &dt ); |
|---|
| 210 | }; |
|---|
| 211 | |
|---|
| 212 | /*! |
|---|
| 213 | \brief Extended Kalman Filter in Square root |
|---|
| 214 | |
|---|
| 215 | An approximation of the exact Bayesian filter with Gaussian noices and non-linear evolutions of their mean. |
|---|
| 216 | */ |
|---|
| 217 | |
|---|
| 218 | class EKFCh : public KalmanCh { |
|---|
| 219 | //! Internal Model f(x,u) |
|---|
| 220 | diffbifn* pfxu; |
|---|
| 221 | //! Observation Model h(x,u) |
|---|
| 222 | diffbifn* phxu; |
|---|
| 223 | public: |
|---|
| 224 | //! Default constructor |
|---|
| 225 | EKFCh ( RV rvx, RV rvy, RV rvu ); |
|---|
| 226 | //! Set nonlinear functions for mean values and covariance matrices. |
|---|
| 227 | void set_parameters ( diffbifn* pfxu, diffbifn* phxu, const chmat Q0, const chmat R0 ); |
|---|
| 228 | //! Here dt = [yt;ut] of appropriate dimensions |
|---|
| 229 | void bayes ( const vec &dt ); |
|---|
| 230 | }; |
|---|
| 231 | |
|---|
| 232 | /*! |
|---|
| 233 | \brief Kalman Filter with conditional diagonal matrices R and Q. |
|---|
| 234 | */ |
|---|
| 235 | |
|---|
| 236 | class KFcondQR : public Kalman<ldmat>, public BMcond { |
|---|
| 237 | //protected: |
|---|
| 238 | public: |
|---|
| 239 | //!Default constructor |
|---|
| 240 | KFcondQR ( RV rvx, RV rvy, RV rvu, RV rvRQ ) : Kalman<ldmat> ( rvx, rvy,rvu ),BMcond ( rvRQ ) {}; |
|---|
| 241 | |
|---|
| 242 | void condition ( const vec &RQ ); |
|---|
| 243 | }; |
|---|
| 244 | |
|---|
| 245 | /*! |
|---|
| 246 | \brief Kalman Filter with conditional diagonal matrices R and Q. |
|---|
| 247 | */ |
|---|
| 248 | |
|---|
| 249 | class KFcondR : public Kalman<ldmat>, public BMcond { |
|---|
| 250 | //protected: |
|---|
| 251 | public: |
|---|
| 252 | //!Default constructor |
|---|
| 253 | KFcondR ( RV rvx, RV rvy, RV rvu, RV rvR ) : Kalman<ldmat> ( rvx, rvy,rvu ),BMcond ( rvR ) {}; |
|---|
| 254 | |
|---|
| 255 | void condition ( const vec &R ); |
|---|
| 256 | }; |
|---|
| 257 | |
|---|
| 258 | //////// INstance |
|---|
| 259 | |
|---|
| 260 | template<class sq_T> |
|---|
| 261 | Kalman<sq_T>::Kalman ( const Kalman<sq_T> &K0 ) : BM ( K0.rv ),rvy ( K0.rvy ),rvu ( K0.rvu ), |
|---|
| 262 | dimx ( rv.count() ), dimy ( rvy.count() ),dimu ( rvu.count() ), |
|---|
| 263 | A ( dimx,dimx ), B ( dimx,dimu ), C ( dimy,dimx ), D ( dimy,dimu ), |
|---|
| 264 | Q(dimx), R(dimy), |
|---|
| 265 | est ( rv ), fy ( rvy ) { |
|---|
| 266 | |
|---|
| 267 | this->set_parameters ( K0.A, K0.B, K0.C, K0.D, K0.R, K0.Q ); |
|---|
| 268 | |
|---|
| 269 | //establish pointers |
|---|
| 270 | _mu = est._mu(); |
|---|
| 271 | est._R ( _P,_iP ); |
|---|
| 272 | |
|---|
| 273 | //fy |
|---|
| 274 | _yp = fy._mu(); |
|---|
| 275 | fy._R ( _Ry,_iRy ); |
|---|
| 276 | |
|---|
| 277 | // copy values in pointers |
|---|
| 278 | *_mu = *K0._mu; |
|---|
| 279 | *_P = *K0._P; |
|---|
| 280 | *_iP = *K0._iP; |
|---|
| 281 | *_yp = *K0._yp; |
|---|
| 282 | *_iRy = *K0._iRy; |
|---|
| 283 | *_Ry = *K0._Ry; |
|---|
| 284 | |
|---|
| 285 | } |
|---|
| 286 | |
|---|
| 287 | template<class sq_T> |
|---|
| 288 | Kalman<sq_T>::Kalman ( RV rvx, RV rvy0, RV rvu0 ) : BM ( rvx ),rvy ( rvy0 ),rvu ( rvu0 ), |
|---|
| 289 | dimx ( rvx.count() ), dimy ( rvy.count() ),dimu ( rvu.count() ), |
|---|
| 290 | A ( dimx,dimx ), B ( dimx,dimu ), C ( dimy,dimx ), D ( dimy,dimu ), |
|---|
| 291 | Q(dimx), R (dimy), |
|---|
| 292 | est ( rvx ), fy ( rvy ) { |
|---|
| 293 | //assign cache |
|---|
| 294 | //est |
|---|
| 295 | _mu = est._mu(); |
|---|
| 296 | est._R ( _P,_iP ); |
|---|
| 297 | |
|---|
| 298 | //fy |
|---|
| 299 | _yp = fy._mu(); |
|---|
| 300 | fy._R ( _Ry,_iRy ); |
|---|
| 301 | }; |
|---|
| 302 | |
|---|
| 303 | template<class sq_T> |
|---|
| 304 | void Kalman<sq_T>::set_parameters ( const mat &A0,const mat &B0, const mat &C0, const mat &D0, const sq_T &R0, const sq_T &Q0 ) { |
|---|
| 305 | it_assert_debug ( A0.cols() ==dimx, "Kalman: A is not square" ); |
|---|
| 306 | it_assert_debug ( B0.rows() ==dimx, "Kalman: B is not compatible" ); |
|---|
| 307 | it_assert_debug ( C0.cols() ==dimx, "Kalman: C is not square" ); |
|---|
| 308 | it_assert_debug ( ( D0.rows() ==dimy ) || ( D0.cols() ==dimu ), "Kalman: D is not compatible" ); |
|---|
| 309 | it_assert_debug ( ( R0.cols() ==dimy ) || ( R0.rows() ==dimy ), "Kalman: R is not compatible" ); |
|---|
| 310 | it_assert_debug ( ( Q0.cols() ==dimx ) || ( Q0.rows() ==dimx ), "Kalman: Q is not compatible" ); |
|---|
| 311 | |
|---|
| 312 | A = A0; |
|---|
| 313 | B = B0; |
|---|
| 314 | C = C0; |
|---|
| 315 | D = D0; |
|---|
| 316 | R = R0; |
|---|
| 317 | Q = Q0; |
|---|
| 318 | } |
|---|
| 319 | |
|---|
| 320 | template<class sq_T> |
|---|
| 321 | void Kalman<sq_T>::bayes ( const vec &dt ) { |
|---|
| 322 | it_assert_debug ( dt.length() == ( dimy+dimu ),"KalmanFull::bayes wrong size of dt" ); |
|---|
| 323 | |
|---|
| 324 | vec u = dt.get ( dimy,dimy+dimu-1 ); |
|---|
| 325 | vec y = dt.get ( 0,dimy-1 ); |
|---|
| 326 | //Time update |
|---|
| 327 | *_mu = A* ( *_mu ) + B*u; |
|---|
| 328 | //P = A*P*A.transpose() + Q; in sq_T |
|---|
| 329 | _P->mult_sym ( A ); |
|---|
| 330 | ( *_P ) +=Q; |
|---|
| 331 | |
|---|
| 332 | //Data update |
|---|
| 333 | //_Ry = C*P*C.transpose() + R; in sq_T |
|---|
| 334 | _P->mult_sym ( C, *_Ry ); |
|---|
| 335 | ( *_Ry ) +=R; |
|---|
| 336 | |
|---|
| 337 | mat Pfull = _P->to_mat(); |
|---|
| 338 | |
|---|
| 339 | _Ry->inv ( *_iRy ); // result is in _iRy; |
|---|
| 340 | fy._cached ( true ); |
|---|
| 341 | _K = Pfull*C.transpose() * ( _iRy->to_mat() ); |
|---|
| 342 | |
|---|
| 343 | sq_T pom ( ( int ) Pfull.rows() ); |
|---|
| 344 | _iRy->mult_sym_t ( C*Pfull,pom ); |
|---|
| 345 | ( *_P ) -= pom; // P = P -PC'iRy*CP; |
|---|
| 346 | ( *_yp ) = C* ( *_mu ) +D*u; //y prediction |
|---|
| 347 | ( *_mu ) += _K* ( y- ( *_yp ) ); |
|---|
| 348 | |
|---|
| 349 | |
|---|
| 350 | if ( evalll==true ) { //likelihood of observation y |
|---|
| 351 | ll=fy.evalpdflog ( y ); |
|---|
| 352 | } |
|---|
| 353 | |
|---|
| 354 | //cout << "y: " << y-(*_yp) <<" R: " << _Ry->to_mat() << " iR: " << _iRy->to_mat() << " ll: " << ll <<endl; |
|---|
| 355 | |
|---|
| 356 | }; |
|---|
| 357 | |
|---|
| 358 | |
|---|
| 359 | |
|---|
| 360 | //TODO why not const pointer?? |
|---|
| 361 | |
|---|
| 362 | template<class sq_T> |
|---|
| 363 | EKF<sq_T>::EKF ( RV rvx0, RV rvy0, RV rvu0 ) : Kalman<sq_T> ( rvx0,rvy0,rvu0 ) {} |
|---|
| 364 | |
|---|
| 365 | template<class sq_T> |
|---|
| 366 | void EKF<sq_T>::set_parameters ( diffbifn* pfxu0, diffbifn* phxu0,const sq_T Q0,const sq_T R0 ) { |
|---|
| 367 | pfxu = pfxu0; |
|---|
| 368 | phxu = phxu0; |
|---|
| 369 | |
|---|
| 370 | //initialize matrices A C, later, these will be only updated! |
|---|
| 371 | pfxu->dfdx_cond ( *_mu,zeros ( dimu ),A,true ); |
|---|
| 372 | // pfxu->dfdu_cond ( *_mu,zeros ( dimu ),B,true ); |
|---|
| 373 | B.clear(); |
|---|
| 374 | phxu->dfdx_cond ( *_mu,zeros ( dimu ),C,true ); |
|---|
| 375 | // phxu->dfdu_cond ( *_mu,zeros ( dimu ),D,true ); |
|---|
| 376 | D.clear(); |
|---|
| 377 | |
|---|
| 378 | R = R0; |
|---|
| 379 | Q = Q0; |
|---|
| 380 | } |
|---|
| 381 | |
|---|
| 382 | template<class sq_T> |
|---|
| 383 | void EKF<sq_T>::bayes ( const vec &dt ) { |
|---|
| 384 | it_assert_debug ( dt.length() == ( dimy+dimu ),"KalmanFull::bayes wrong size of dt" ); |
|---|
| 385 | |
|---|
| 386 | vec u = dt.get ( dimy,dimy+dimu-1 ); |
|---|
| 387 | vec y = dt.get ( 0,dimy-1 ); |
|---|
| 388 | //Time update |
|---|
| 389 | *_mu = pfxu->eval ( *_mu, u ); |
|---|
| 390 | pfxu->dfdx_cond ( *_mu,u,A,false ); //update A by a derivative of fx |
|---|
| 391 | |
|---|
| 392 | //P = A*P*A.transpose() + Q; in sq_T |
|---|
| 393 | _P->mult_sym ( A ); |
|---|
| 394 | ( *_P ) +=Q; |
|---|
| 395 | |
|---|
| 396 | //Data update |
|---|
| 397 | phxu->dfdx_cond ( *_mu,u,C,false ); //update C by a derivative hx |
|---|
| 398 | //_Ry = C*P*C.transpose() + R; in sq_T |
|---|
| 399 | _P->mult_sym ( C, *_Ry ); |
|---|
| 400 | ( *_Ry ) +=R; |
|---|
| 401 | |
|---|
| 402 | mat Pfull = _P->to_mat(); |
|---|
| 403 | |
|---|
| 404 | _Ry->inv ( *_iRy ); // result is in _iRy; |
|---|
| 405 | fy._cached ( true ); |
|---|
| 406 | _K = Pfull*C.transpose() * ( _iRy->to_mat() ); |
|---|
| 407 | |
|---|
| 408 | sq_T pom ( ( int ) Pfull.rows() ); |
|---|
| 409 | _iRy->mult_sym_t ( C*Pfull,pom ); |
|---|
| 410 | ( *_P ) -= pom; // P = P -PC'iRy*CP; |
|---|
| 411 | *_yp = phxu->eval ( *_mu,u ); //y prediction |
|---|
| 412 | ( *_mu ) += _K* ( y-*_yp ); |
|---|
| 413 | |
|---|
| 414 | if ( evalll==true ) {ll+=fy.evalpdflog ( y );} |
|---|
| 415 | }; |
|---|
| 416 | |
|---|
| 417 | |
|---|
| 418 | #endif // KF_H |
|---|
| 419 | |
|---|