indii/ml/filter/UnscentedKalmanFilter.hpp

00001 #ifndef INDII_ML_ESTIMATOR_UNSCENTEDKALMANFILTER_HPP
00002 #define INDII_ML_ESTIMATOR_UNSCENTEDKALMANFILTER_HPP
00003 
00004 #include "../aux/vector.hpp"
00005 #include "../aux/matrix.hpp"
00006 #include "../aux/GaussianPdf.hpp"
00007 
00008 #include "Filter.hpp"
00009 #include "UnscentedTransformation.hpp"
00010 
00011 namespace indii {
00012   namespace ml {
00013     namespace filter {
00014 
00015       template <class T> class UnscentedKalmanFilterModel;
00016       template <class T> class UnscentedKalmanFilterTransitionAdaptor;
00017       template <class T> class UnscentedKalmanFilterMeasurementAdaptor;
00018 
00019 /**
00020  * Unscented Kalman %filter.
00021  * 
00022  * @author Lawrence Murray <lawrence@indii.org>
00023  * @version $Rev: 544 $
00024  * @date $Date: 2008-09-01 15:04:39 +0100 (Mon, 01 Sep 2008) $
00025  *
00026  * @param T The type of time.
00027  *
00028  * UnscentedKalmanFilter is suitable for systems with nonlinear
00029  * transition and measurement functions, approximating state and noise
00030  * with indii::ml::aux::GaussianPdf distributions.
00031  *
00032  * @see UnscentedTransformation
00033  * @see indii::ml::filter for general usage guidelines.
00034  */
00035 template<class T = unsigned int>
00036 class UnscentedKalmanFilter : public Filter<T> {
00037 
00038   friend class UnscentedKalmanFilterTransitionAdaptor<T>;
00039   friend class UnscentedKalmanFilterMeasurementAdaptor<T>;
00040 
00041 public:
00042   /**
00043    * Create new unscented Kalman %filter.
00044    *
00045    * @param model Model to estimate.
00046    * @param p_x0 \f$p(\mathbf{x}_0)\f$; prior over the
00047    * initial state \f$\mathbf{x}(0)\f$.
00048    */
00049   UnscentedKalmanFilter(UnscentedKalmanFilterModel<T>* model,
00050       const indii::ml::aux::GaussianPdf& p_x0);
00051 
00052   /**
00053    * Destructor.
00054    */
00055   virtual ~UnscentedKalmanFilter();
00056 
00057   /**
00058    * Get the unscented transformation used for the transition
00059    * function. This allows its parameters to be adjusted.
00060    */
00061   UnscentedTransformation<T>* getTransitionTransformation();
00062 
00063   /**
00064    * Get the unscented transformation used for the measurement
00065    * function. This allows its parameters to be adjusted.
00066    */
00067   UnscentedTransformation<T>* getMeasurementTransformation();
00068 
00069   virtual void filter(const T tnp1, const indii::ml::aux::vector& ytnp1);
00070 
00071   virtual indii::ml::aux::GaussianPdf measure();
00072 
00073 protected:
00074   /**
00075    * Size of the state space.
00076    */
00077   const unsigned int N;
00078 
00079   /**
00080    * Size of the system noise space.
00081    */
00082   const unsigned int W;
00083 
00084   /**
00085    * Size of the measurement noise space.
00086    */
00087   const unsigned int V;
00088 
00089   /**
00090    * \f$p(\mathcal{X}_n\,|\,\mathbf{y}_{1:n})\f$; filter density over
00091    * augmented state.
00092    */
00093   indii::ml::aux::GaussianPdf p_Xtn_ytn;
00094 
00095   /**
00096    * Model.
00097    */
00098   UnscentedKalmanFilterModel<T>* model;
00099 
00100   /**
00101    * Unscented transformation for transition function.
00102    */
00103   UnscentedTransformationModel<T>* transitionModel;
00104 
00105   /**
00106    * Unscented transformation for measurement function.
00107    */
00108   UnscentedTransformationModel<T>* measurementModel;
00109 
00110   /**
00111    * Unscented transformation for transition function.
00112    */
00113   UnscentedTransformation<T>* transitionTransform;
00114 
00115   /**
00116    * Unscented transformation for measurement function.
00117    */
00118   UnscentedTransformation<T>* measurementTransform;
00119 
00120 };
00121 
00122     }
00123   }
00124 }
00125 
00126 #include "UnscentedKalmanFilterTransitionAdaptor.hpp"
00127 #include "UnscentedKalmanFilterMeasurementAdaptor.hpp"
00128 #include "UnscentedKalmanFilterModel.hpp"
00129 
00130 #include "boost/numeric/bindings/traits/ublas_matrix.hpp"
00131 #include "boost/numeric/bindings/traits/ublas_vector.hpp"
00132 #include "boost/numeric/bindings/traits/ublas_symmetric.hpp"
00133 #include "boost/numeric/bindings/lapack/lapack.hpp"
00134 
00135 template <class T>
00136 indii::ml::filter::UnscentedKalmanFilter<T>::UnscentedKalmanFilter(
00137     indii::ml::filter::UnscentedKalmanFilterModel<T>* model,
00138     const indii::ml::aux::GaussianPdf& p_x0) :
00139     Filter<T>(p_x0),
00140     N(model->getStateSize()),
00141     W(model->getSystemNoise().getDimensions()),
00142     V(model->getMeasurementNoise().getDimensions()),
00143     p_Xtn_ytn(N+W+V),
00144     model(model) {
00145   namespace aux = indii::ml::aux;
00146   namespace ublas = boost::numeric::ublas;
00147 
00148   /* pre-condition */
00149   assert (p_x0.getDimensions() == model->getStateSize());
00150     
00151   /* unscented transformations */
00152   transitionModel = new UnscentedKalmanFilterTransitionAdaptor<T>(model);
00153   transitionTransform = new UnscentedTransformation<T>(*transitionModel);
00154   measurementModel = new UnscentedKalmanFilterMeasurementAdaptor<T>(model);
00155   measurementTransform = new UnscentedTransformation<T>(*measurementModel);
00156 
00157   /* ranges for subvectors and submatrices */
00158   ublas::range xRange(0,N);
00159   ublas::range wRange(N,N+W);
00160   ublas::range vRange(N+W,N+W+V);
00161 
00162   /* initialise augmented state */
00163   aux::vector mu(N+W+V);
00164   aux::symmetric_matrix sigma(N+W+V);
00165   aux::GaussianPdf& w = model->getSystemNoise();
00166   aux::GaussianPdf& v = model->getMeasurementNoise();
00167 
00168   noalias(project(mu, xRange)) = p_x0.getExpectation();
00169   noalias(project(mu, wRange)) = w.getExpectation();
00170   noalias(project(mu, vRange)) = v.getExpectation();
00171 
00172   noalias(project(sigma, xRange, xRange)) = p_x0.getCovariance();
00173   noalias(project(sigma, wRange, xRange)) = aux::zero_matrix(W, N);
00174   noalias(project(sigma, wRange, wRange)) = w.getCovariance();
00175   noalias(project(sigma, vRange, xRange)) = aux::zero_matrix(V, N);
00176   noalias(project(sigma, vRange, wRange)) = aux::zero_matrix(V, W);
00177   noalias(project(sigma, vRange, vRange)) = v.getCovariance();
00178 
00179   p_Xtn_ytn.setExpectation(mu);
00180   p_Xtn_ytn.setCovariance(sigma);
00181 }
00182 
00183 template <class T>
00184 indii::ml::filter::UnscentedKalmanFilter<T>::~UnscentedKalmanFilter() {
00185   delete transitionTransform;
00186   delete measurementTransform;
00187   delete transitionModel;
00188   delete measurementModel;
00189 }
00190 
00191 template <class T>
00192 void indii::ml::filter::UnscentedKalmanFilter<T>::filter(const T tnp1,
00193     const indii::ml::aux::vector& ytnp1) {
00194   namespace aux = indii::ml::aux;
00195   namespace ublas = boost::numeric::ublas;
00196   namespace lapack = boost::numeric::bindings::lapack;
00197 
00198   /* pre-condition */
00199   assert (tnp1 > this->tn);
00200   assert (ytnp1.size() % V == 0);
00201 
00202   const T delta = tnp1 - this->tn;
00203   this->tn = tnp1;
00204 
00205   /* ranges for subvectors and submatrices */
00206   ublas::range xRange(0,N);
00207   ublas::range wRange(N,N+W);
00208   ublas::range vRange(N+W,N+W+V);
00209 
00210   /* unscented transformation of augmented state through transition
00211      function */
00212   aux::GaussianPdf p_xtnp1_ytn(transitionTransform->transform(p_Xtn_ytn,
00213       delta));
00214 
00215   /* prepare augmented state for measurement function */
00216   aux::vector mu(p_Xtn_ytn.getExpectation());
00217   aux::symmetric_matrix sigma(p_Xtn_ytn.getCovariance());
00218 
00219   noalias(project(mu, xRange)) = p_xtnp1_ytn.getExpectation();
00220   noalias(project(sigma, xRange, xRange)) = p_xtnp1_ytn.getCovariance();
00221 
00222   aux::GaussianPdf p_Xtnp1_ytn(mu, sigma);
00223 
00224   /* unscented transformation of augmented state through measurement
00225      function */
00226   aux::matrix P_xy(N+W+V,V); // cross-correlation matrix
00227   aux::GaussianPdf p_ytnp1_Xtnp1(measurementTransform->transform(p_Xtnp1_ytn,
00228       delta, &P_xy));
00229 
00230   /* update p_xtn_ytn based on measurement */
00231   const aux::vector &y_ytnp1_Xtnp1 = p_ytnp1_Xtnp1.getExpectation();
00232   const aux::symmetric_matrix &P_ytnp1_Xtnp1 = p_ytnp1_Xtnp1.getCovariance();
00233   const aux::vector &x_Xtnp1_ytn = p_Xtnp1_ytn.getExpectation();
00234   const aux::symmetric_matrix &P_Xtnp1_ytn = p_Xtnp1_ytn.getCovariance();
00235 
00236   /* 
00237    * Calculate Kalman gain directly without intermediate calculation
00238    * of inverse, improves performance marginally.
00239    *
00240    * Calculate Kalman gain. <tt>K = P_xy *
00241    * inv(P_ytnp1_Xtnp1)</tt>. Calculate as <tt>trans(K) =
00242    * inv(P_ytnp1_Xtnp1) * trans(P_xy), so that LAPACK gesv function
00243    * can be used, noting that <tt>trans(P_ytnp1_Xtnp1) ==
00244    * P_ytnp1_Xtnp1</tt> as <tt>P_ytnp1_Xtnp1</tt> is symmetric.
00245    *
00246    * Note that Kalman gain applies only to state variables, not noise
00247    * variables, so means and covariances can be projected down to only
00248    * state variables for the update.
00249    */
00250   aux::matrix X(P_ytnp1_Xtnp1);
00251   aux::matrix KT(trans(project(P_xy,xRange,ublas::range(0,V))));
00252   int err;
00253   err = lapack::gesv(X,KT);
00254   assert (err == 0);
00255   aux::matrix K(trans(KT));
00256 
00257   noalias(project(mu,xRange)) = project(x_Xtnp1_ytn,xRange) +
00258       prod(K, ytnp1 - y_ytnp1_Xtnp1);
00259 
00260   X.resize(P_ytnp1_Xtnp1.size1(), KT.size2(), false);
00261   noalias(X) = prod(P_ytnp1_Xtnp1, KT);
00262   noalias(project(sigma,xRange,xRange)) = project(P_Xtnp1_ytn,xRange,xRange)
00263       - prod(K, X);
00264 
00265   p_Xtn_ytn.setExpectation(mu);
00266   p_Xtn_ytn.setCovariance(sigma);
00267   this->p_xtn_ytn.setExpectation(project(mu, xRange));
00268   this->p_xtn_ytn.setCovariance(project(sigma, xRange, xRange));
00269 
00270   /* post-condition */
00271   assert (this->tn == tnp1);
00272 }
00273 
00274 template <class T>
00275 indii::ml::aux::GaussianPdf
00276     indii::ml::filter::UnscentedKalmanFilter<T>::measure() {
00277   return measurementTransform->transform(p_Xtn_ytn);
00278 }
00279 
00280 template <class T>
00281 indii::ml::filter::UnscentedTransformation<T>*
00282     indii::ml::filter::UnscentedKalmanFilter<T>::getTransitionTransformation() {
00283   return transitionTransform;
00284 }
00285 
00286 template <class T>
00287 indii::ml::filter::UnscentedTransformation<T>*
00288     indii::ml::filter::UnscentedKalmanFilter<T>::getMeasurementTransformation() {
00289   return measurementTransform;
00290 }
00291 
00292 #endif

Generated on Wed Dec 17 15:11:57 2008 for dysii Dynamical Systems Library by  doxygen 1.5.3