00001 #ifndef INDII_ML_FILTER_UNSCENTEDTRANSFORMATION 00002 #define INDII_ML_FILTER_UNSCENTEDTRANSFORMATION 00003 00004 #include "../aux/vector.hpp" 00005 #include "../aux/GaussianPdf.hpp" 00006 00007 #include "UnscentedTransformationModel.hpp" 00008 #include "../ode/ParameterCollection.hpp" 00009 00010 namespace indii { 00011 namespace ml { 00012 namespace filter { 00013 00014 /** 00015 * Unscented transformation. 00016 * 00017 * @author Lawrence Murray <lawrence@indii.org> 00018 * @version $Rev: 436 $ 00019 * @date $Date: 2008-04-28 00:23:37 +0100 (Mon, 28 Apr 2008) $ 00020 * 00021 * The unscented transformation propagates a Gaussian distributed random 00022 * variable through a nonlinear function, estimating its new mean and 00023 * covariance after application of the function. This is achieved by 00024 * deterministic sampling of a set of <i>sigma points</i> from the 00025 * distribution, propagation of these points through the function and 00026 * recalculation of the mean and covariance. 00027 * 00028 * @section UnscentedTransformation_details Details 00029 * 00030 * As described in @ref Julier1997 "Julier & Uhlmann (1997)" and @ref Wan2000 00031 * "Wan & van der Merwe (2000)", let \f$L\f$ be the number of dimensions of 00032 * the input random variable \f$\mathbf{x}\f$, with mean 00033 * \f$\mathbf{\bar{x}}\f$ and covariance matrix \f$P_x\f$. Let: 00034 * 00035 * \f[\lambda = \alpha^2 (L + \kappa) - L\f] 00036 * 00037 * \f$2L + 1\f$ sigma points \f$\mathcal{X}_i\f$ are defined as: 00038 * 00039 * \f[ 00040 * \begin{array}{lcll} 00041 * \mathcal{X}_0 & = & \mathbf{\bar{x}} \\ 00042 * \mathcal{X}_i & = & \mathbf{\bar{x}} + \left(\sqrt{(L+\lambda)P_x} 00043 * \right)_i & i = 1,\ldots,L \\ 00044 * \mathcal{X}_i & = & \mathbf{\bar{x}} - \left(\sqrt{(L+\lambda)P_x} 00045 * \right)_{i-L} & i = L+1,\ldots,2L \\ 00046 * \end{array} 00047 * \f] 00048 * 00049 * where \f$\left(\sqrt{(L+\lambda)P_x}\right)_i\f$ is the \f$i\f$th column of 00050 * the matrix square root (Cholesky decomposition). 00051 * 00052 * Mean weights \f$W^{(m)}_i\f$ and covariance weights \f$W^{(c)}_i\f$ are 00053 * defined as: 00054 * 00055 * \f[ 00056 * \begin{array}{lclcll} 00057 * W^{(m)}_0 & = & & & \lambda/(L+\lambda) \\ 00058 * W^{(c)}_0 & = & & & \lambda/(L+\lambda)+(1-\alpha^2+\beta) \\ 00059 * W^{(m)}_i & = & W^{(c)}_i & = & 1/\left(2\left(L+\lambda\right)\right) & 00060 * i = 1,\ldots,2L \\ 00061 * \end{array} 00062 * \f] 00063 * 00064 * Each sigma point is then propagated through the given function \f$f\f$: 00065 * 00066 * \f[ 00067 * \begin{array}{lcll} 00068 * \mathcal{Y}_i & = & f(\mathcal{X}_i) & i = 0,\ldots,2L \\ 00069 * \end{array} 00070 * \f] 00071 * 00072 * Finally, the mean and covariance of \f$f(\mathbf{x})\f$ are estimated as: 00073 * 00074 * \f{eqnarray*} 00075 * \mathbf{\bar{y}} & \approx & \sum_{i=0}^{2L}W^{(m)}_i\mathcal{Y}_i \\ 00076 * P_y & \approx & \sum_{i=0}^{2L}W^{(c)}_i 00077 * (\mathcal{Y}_i-\mathbf{\bar{y}})(\mathcal{Y}_i-\mathbf{\bar{y}})^T \\ 00078 * \f} 00079 * 00080 * and the cross-correlation matrix \f$P_{xy}\f$ as: 00081 * 00082 * \f[P_{xy} \approx \sum_{i=0}^{2L}W^{(c)}_i 00083 * (\mathcal{X}_i-\mathbf{\bar{x}})(\mathcal{Y}_i-\mathbf{\bar{y}})^T\f] 00084 * 00085 * @section implementation Implementation 00086 * 00087 * This particular implementation uses a modified form of the above 00088 * formulas so that it need not hold the complete set of sigma points 00089 * in memory, as required in the calculation of \f$P_y\f$ above. 00090 * 00091 * First, we rename \f$\mathbf{\bar{y}}\f$ to \f$\mathbf{\bar{y}}_m\f$; 00092 * the mean calculated using mean weights 00093 * \f$W^{(m)}_0,\ldots,W^{(m)}_{2L}\f$. We then define a second mean 00094 * \f$\mathbf{\bar{y}}_c\f$ calculated using covariance weights 00095 * \f$W^{(c)}_0,\ldots,W^{(c)}_{2L}\f$: 00096 * 00097 * \f{eqnarray*} 00098 * \mathbf{\bar{y}}_c & \approx & \sum_{i=0}^{2L}W^{(c)}_i\mathcal{Y}_i 00099 * \f} 00100 * 00101 * Noting that \f$W^{(c)}_i = W^{(m)}_i\f$ for \f$i = 1,\ldots,2L\f$, the two 00102 * means need not be calculated independently. 00103 * 00104 * The calculation of the covariance \f$P_y\f$ can then be modified to: 00105 * 00106 * \f{eqnarray*} 00107 * P_y & \approx & \sum_{i=0}^{2L}W^{(c)}_i 00108 * (\mathcal{Y}_i-\mathbf{\bar{y}}_m) 00109 * (\mathcal{Y}_i-\mathbf{\bar{y}}_m)^T \\ 00110 * & = & \sum_{i=0}^{2L}W^{(c)}_i 00111 * (\mathcal{Y}_i\mathcal{Y}_i^T - \mathcal{Y}_i\mathbf{\bar{y}}_m^T - 00112 * \mathbf{\bar{y}}_m\mathcal{Y}_i^T + 00113 * \mathbf{\bar{y}}_m\mathbf{\bar{y}}_m^T) \\ 00114 * & = & \sum_{i=0}^{2L}W^{(c)}_i\mathcal{Y}_i\mathcal{Y}_i^T - 00115 * \mathbf{\bar{y}}_c\mathbf{\bar{y}}_m^T - 00116 * \mathbf{\bar{y}}_m\mathbf{\bar{y}}_c^T + 00117 * \sum_{i=0}^{2L}W^{(c)}_i\mathbf{\bar{y}}_m\mathbf{\bar{y}}_m^T 00118 * \f} 00119 * 00120 * @section UnscentedTransformation_references References 00121 * 00122 * @anchor Julier1997 00123 * Julier, S.J. & Uhlmann, J.K. A New Extension of the Kalman %Filter 00124 * to nonlinear Systems <i>The Proceedings of AeroSense: The 11th 00125 * International Symposium on Aerospace/Defense Sensing, Simulation 00126 * and Controls, Multi Sensor Fusion, Tracking and Resource 00127 * Management</i>, <b>1997</b>. 00128 * 00129 * @anchor Wan2000 00130 * Wan, E.A. & van der Merwe, R. The Unscented Kalman %Filter for 00131 * Nonlinear Estimation. <i>Proceedings of IEEE Symposium on Adaptive 00132 * Systems for Signal Processing Communications and Control 00133 * (AS-SPCC)</i>, <b>2000</b>. 00134 */ 00135 template <class T = unsigned int> 00136 class UnscentedTransformation : public indii::ml::ode::ParameterCollection { 00137 public: 00138 /** 00139 * Parameter indices. 00140 */ 00141 enum Parameter { 00142 /** 00143 * \f$\alpha\f$; spread of the sigma points about 00144 * \f$\mathbf{\bar{x}}\f$. Usually set to a small positive value. 00145 */ 00146 ALPHA, 00147 00148 /** 00149 * \f$\beta\f$; incorporates prior knowledge of the distribution of 00150 * \f$\mathbf{x}\f$. Value of 2 is optimal for Gaussian distributions. 00151 */ 00152 BETA, 00153 00154 /** 00155 * \f$\kappa\f$; secondary scaling parameter. Usually set to zero. 00156 */ 00157 KAPPA 00158 }; 00159 00160 /** 00161 * Create new transformation with default parameter values. Default values 00162 * are obtained from UnscentedTransformationDefaults. 00163 * 00164 * @param model Model representing the function \f$f\f$. Callee claims 00165 * ownership. 00166 */ 00167 UnscentedTransformation(UnscentedTransformationModel<T>& model); 00168 00169 /** 00170 * Destructor. 00171 */ 00172 ~UnscentedTransformation(); 00173 00174 /** 00175 * Apply the unscented transformation. 00176 * 00177 * @param x \f$P(\mathbf{x})\f$; distribution over the random variable to 00178 * propagate through the function. 00179 * @param delta \f$\Delta t\f$; length of time through which to propagate 00180 * the distribution, if relevant. 00181 * @param P If specified, the cross correlation matrix between the input and 00182 * output of the function is estimated using sigma points and written into 00183 * this matrix. The matrix should be of size \f$n \times m\f$, where \f$n\f$ 00184 * is the dimensionality of the input space and \f$m\f$ the dimensionality 00185 * of the output space. 00186 * 00187 * @return \f$P(f(\mathbf{x}))\f$; distribution over the function output. 00188 */ 00189 indii::ml::aux::GaussianPdf transform(const indii::ml::aux::GaussianPdf& x, 00190 T delta = 0, indii::ml::aux::matrix* P = NULL); 00191 00192 private: 00193 /** 00194 * Number of parameters 00195 */ 00196 static const unsigned int NUM_PARAMETERS = 3; 00197 00198 /** 00199 * Model defining function through which to propagate points. 00200 */ 00201 UnscentedTransformationModel<T>& model; 00202 00203 }; 00204 00205 } 00206 } 00207 } 00208 00209 #include "UnscentedTransformationDefaults.hpp" 00210 00211 #include <math.h> 00212 00213 #include "boost/numeric/bindings/traits/ublas_vector.hpp" 00214 #include "boost/numeric/bindings/traits/ublas_matrix.hpp" 00215 #include "boost/numeric/bindings/traits/ublas_symmetric.hpp" 00216 #include "boost/numeric/bindings/lapack/lapack.hpp" 00217 00218 using namespace indii::ml::filter; 00219 00220 namespace aux = indii::ml::aux; 00221 namespace ublas = boost::numeric::ublas; 00222 namespace lapack = boost::numeric::bindings::lapack; 00223 00224 template <class T> 00225 UnscentedTransformation<T>::UnscentedTransformation( 00226 UnscentedTransformationModel<T>& model) : 00227 ParameterCollection(NUM_PARAMETERS), model(model) { 00228 setParameter(ALPHA, UnscentedTransformationDefaults::ALPHA); 00229 setParameter(BETA, UnscentedTransformationDefaults::BETA); 00230 setParameter(KAPPA, UnscentedTransformationDefaults::KAPPA); 00231 } 00232 00233 template <class T> 00234 UnscentedTransformation<T>::~UnscentedTransformation() { 00235 // 00236 } 00237 00238 template <class T> 00239 aux::GaussianPdf UnscentedTransformation<T>::transform( 00240 const aux::GaussianPdf& x, T delta, aux::matrix* P) { 00241 /* pre-condition */ 00242 assert (x.getDimensions() > 0); 00243 00244 const unsigned int L = x.getDimensions(); 00245 const double LAMBDA = pow(getParameter(ALPHA), 2.0) * 00246 (L + getParameter(KAPPA)) - L; 00247 00248 /* calculate weights */ 00249 const double W_m_0 = LAMBDA / ((double)L + LAMBDA); 00250 const double W_c_0 = W_m_0 + (1.0 - pow(getParameter(ALPHA), 2.0) + 00251 getParameter(BETA)); 00252 const double W_m_i = 1.0 / (2.0 * ((double)L + LAMBDA)); 00253 const double W_c_i = W_m_i; 00254 //const double W_m_t = 2.0 * (double)L * W_m_i + W_m_0; // total mean weight 00255 const double W_c_t = 2.0 * (double)L * W_c_i + W_c_0; // total cov weight 00256 00257 /* calculate Cholesky decomposition of covariance matrix */ 00258 int err; 00259 aux::matrix tmp(x.getCovariance()); 00260 err = lapack::potrf('L', tmp); 00261 assert(err == 0); 00262 ublas::triangular_adaptor<aux::matrix, ublas::lower> cholesky(tmp); 00263 00264 /* calculate sigma points 1,...,2L, propagate and sum as we go */ 00265 unsigned int i, j; 00266 const aux::vector &x_mu = x.getExpectation(); 00267 aux::matrix A(sqrt((double)L + LAMBDA) * cholesky); 00268 00269 /* dimensionality of output unknown, so initialise vectors using sigma point 00270 * 1 */ 00271 aux::vector X(x_mu + ublas::column(A,0)); 00272 aux::vector X_mu_m(X); 00273 aux::vector Y(model.propagate(X,delta)); 00274 aux::vector Y_mu_m(Y); 00275 aux::symmetric_matrix Y_sigma(outer_prod(Y,Y)); 00276 aux::matrix* XY_sigma = P; 00277 if (XY_sigma != NULL) { 00278 *XY_sigma = outer_prod(X,Y); 00279 } 00280 00281 /* include sigma point L+1 */ 00282 noalias(X) = x_mu - ublas::column(A,0); 00283 noalias(X_mu_m) += X; 00284 noalias(Y) = model.propagate(X,delta); 00285 noalias(Y_mu_m) += Y; 00286 noalias(Y_sigma) += outer_prod(Y,Y); 00287 if (XY_sigma != NULL) { 00288 noalias(*XY_sigma) += outer_prod(X,Y); 00289 } 00290 00291 /* include sigma points 2,...,L,L+2,...,2L */ 00292 for (i = 2; i <= L; i++) { 00293 /* sigma point i */ 00294 noalias(X) = x_mu + ublas::column(A,i-1); 00295 noalias(X_mu_m) += X; 00296 noalias(Y) = model.propagate(X,delta); 00297 noalias(Y_mu_m) += Y; 00298 noalias(Y_sigma) += outer_prod(Y,Y); 00299 if (XY_sigma != NULL) { 00300 noalias(*XY_sigma) += outer_prod(X,Y); 00301 } 00302 00303 /* sigma point L+i */ 00304 noalias(X) = x_mu - ublas::column(A,i-1); 00305 noalias(X_mu_m) += X; 00306 noalias(Y) = model.propagate(X,delta); 00307 noalias(Y_mu_m) += Y; 00308 noalias(Y_sigma) += outer_prod(Y,Y); 00309 if (XY_sigma != NULL) { 00310 noalias(*XY_sigma) += outer_prod(X,Y); 00311 } 00312 } 00313 00314 aux::vector X_mu_c(X_mu_m*W_c_i); 00315 aux::vector Y_mu_c(Y_mu_m*W_c_i); 00316 X_mu_m *= W_m_i; 00317 Y_mu_m *= W_m_i; 00318 00319 /* include sigma point 0 */ 00320 noalias(X) = x_mu; 00321 noalias(X_mu_m) += W_m_0*X; 00322 noalias(X_mu_c) += W_c_0*X; 00323 noalias(Y) = model.propagate(X,delta); 00324 noalias(Y_mu_m) += W_m_0*Y; 00325 noalias(Y_mu_c) += W_c_0*Y; 00326 00327 /* finalise covariance */ 00328 Y_sigma *= W_c_i; 00329 noalias(Y_sigma) += W_c_0*outer_prod(Y,Y); 00330 noalias(Y_sigma) += W_c_t*outer_prod(Y_mu_m,Y_mu_m); 00331 00332 //noalias(Y_sigma) -= outer_prod(Y_mu_m,Y_mu_c) + outer_prod(Y_mu_c,Y_mu_m); 00333 aux::matrix tmp1(outer_prod(Y_mu_m,Y_mu_c)); 00334 noalias(Y_sigma) -= tmp1 + trans(tmp1); 00335 00336 /* finalise cross-correlation */ 00337 if (XY_sigma != NULL) { 00338 *XY_sigma *= W_c_i; 00339 noalias(*XY_sigma) += W_c_0*outer_prod(X,Y); 00340 noalias(*XY_sigma) += W_c_t*outer_prod(X_mu_m,Y_mu_m); 00341 noalias(*XY_sigma) -= outer_prod(X_mu_m,Y_mu_c)+outer_prod(X_mu_c,Y_mu_m); 00342 } 00343 00344 /* create distribution */ 00345 return aux::GaussianPdf(Y_mu_m, Y_sigma); 00346 } 00347 00348 #endif
1.5.3