Main MRPT website > C++ reference
MRPT logo
CKalmanFilterCapable.h
Go to the documentation of this file.
00001 /* +---------------------------------------------------------------------------+
00002    |          The Mobile Robot Programming Toolkit (MRPT) C++ library          |
00003    |                                                                           |
00004    |                       http://www.mrpt.org/                                |
00005    |                                                                           |
00006    |   Copyright (C) 2005-2011  University of Malaga                           |
00007    |                                                                           |
00008    |    This software was written by the Machine Perception and Intelligent    |
00009    |      Robotics Lab, University of Malaga (Spain).                          |
00010    |    Contact: Jose-Luis Blanco  <jlblanco@ctima.uma.es>                     |
00011    |                                                                           |
00012    |  This file is part of the MRPT project.                                   |
00013    |                                                                           |
00014    |     MRPT is free software: you can redistribute it and/or modify          |
00015    |     it under the terms of the GNU General Public License as published by  |
00016    |     the Free Software Foundation, either version 3 of the License, or     |
00017    |     (at your option) any later version.                                   |
00018    |                                                                           |
00019    |   MRPT is distributed in the hope that it will be useful,                 |
00020    |     but WITHOUT ANY WARRANTY; without even the implied warranty of        |
00021    |     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         |
00022    |     GNU General Public License for more details.                          |
00023    |                                                                           |
00024    |     You should have received a copy of the GNU General Public License     |
00025    |     along with MRPT.  If not, see <http://www.gnu.org/licenses/>.         |
00026    |                                                                           |
00027    +---------------------------------------------------------------------------+ */
00028 #ifndef CKalmanFilterCapable_H
00029 #define CKalmanFilterCapable_H
00030 
00031 #include <mrpt/math/CMatrixFixedNumeric.h>
00032 #include <mrpt/math/CMatrixTemplateNumeric.h>
00033 #include <mrpt/math/CArray.h>
00034 #include <mrpt/math/utils.h>
00035 
00036 #include <mrpt/utils/CTimeLogger.h>
00037 #include <mrpt/utils/CLoadableOptions.h>
00038 #include <mrpt/utils/CDebugOutputCapable.h>
00039 #include <mrpt/utils/stl_extensions.h>
00040 #include <mrpt/system/os.h>
00041 #include <mrpt/utils/CTicTac.h>
00042 #include <mrpt/utils/CFileOutputStream.h>
00043 #include <mrpt/utils/TEnumType.h>
00044 
00045 #include <mrpt/bayes/link_pragmas.h>
00046 
00047 
00048 namespace mrpt
00049 {
00050         namespace bayes
00051         {
00052                 using namespace mrpt::utils;
00053                 using namespace mrpt::math;
00054                 using namespace mrpt;
00055                 using namespace std;
00056 
00057                 /** The Kalman Filter algorithm to employ in bayes::CKalmanFilterCapable
00058                   *  For further details on each algorithm see the tutorial: http://www.mrpt.org/Kalman_Filters
00059                   * \sa bayes::CKalmanFilterCapable::KF_options
00060                   * \ingroup mrpt_bayes_grp
00061                   */
00062                 enum TKFMethod {
00063                         kfEKFNaive = 0,
00064                         kfEKFAlaDavison,
00065                         kfIKFFull,
00066                         kfIKF
00067                 };
00068 
00069                 // Forward declaration:
00070                 template <size_t VEH_SIZE, size_t OBS_SIZE, size_t FEAT_SIZE, size_t ACT_SIZE, typename KFTYPE> class CKalmanFilterCapable;
00071 
00072                 namespace detail {
00073                 struct CRunOneKalmanIteration_addNewLandmarks;
00074                 }
00075 
00076                 /** Generic options for the Kalman Filter algorithm in itself.
00077                   * \ingroup mrpt_bayes_grp
00078                   */
00079                 struct BAYES_IMPEXP TKF_options : public utils::CLoadableOptions
00080                 {
00081                         TKF_options();
00082 
00083                         void  loadFromConfigFile(
00084                                 const mrpt::utils::CConfigFileBase      &source,
00085                                 const std::string               &section);
00086 
00087                         /** This method must display clearly all the contents of the structure in textual form, sending it to a CStream. */
00088                         void  dumpToTextStream(CStream  &out) const;
00089 
00090                         TKFMethod       method;                         //!< The method to employ (default: kfEKFNaive)
00091                         bool            verbose;                //!< If set to true timing and other information will be dumped during the execution (default=false)
00092                         int             IKF_iterations; //!< Number of refinement iterations, only for the IKF method.
00093                         bool            enable_profiler;//!< If enabled (default=false), detailed timing information will be dumped to the console thru a CTimerLog at the end of the execution.
00094                         bool            use_analytic_transition_jacobian;       //!< (default=true) If true, OnTransitionJacobian will be called; otherwise, the Jacobian will be estimated from a numeric approximation by calling several times to OnTransitionModel.
00095                         bool            use_analytic_observation_jacobian;      //!< (default=true) If true, OnObservationJacobians will be called; otherwise, the Jacobian will be estimated from a numeric approximation by calling several times to OnObservationModel.
00096                         bool            debug_verify_analytic_jacobians; //!< (default=false) If true, will compute all the Jacobians numerically and compare them to the analytical ones, throwing an exception on mismatch.
00097                         double          debug_verify_analytic_jacobians_threshold; //!< (default-1e-2) Sets the threshold for the difference between the analytic and the numerical jacobians
00098                 };
00099 
00100                 /** Auxiliary functions, for internal usage of MRPT classes */
00101                 namespace detail
00102                 {
00103                         // Auxiliary functions.
00104                         template <size_t VEH_SIZE, size_t OBS_SIZE, size_t FEAT_SIZE, size_t ACT_SIZE, typename KFTYPE>
00105                         inline size_t getNumberOfLandmarksInMap(const CKalmanFilterCapable<VEH_SIZE,OBS_SIZE,FEAT_SIZE,ACT_SIZE,KFTYPE> &obj);
00106                         // Specialization:
00107                         template <size_t VEH_SIZE, size_t OBS_SIZE, size_t ACT_SIZE, typename KFTYPE>
00108                         inline size_t getNumberOfLandmarksInMap(const CKalmanFilterCapable<VEH_SIZE,OBS_SIZE,0 /*FEAT_SIZE*/,ACT_SIZE,KFTYPE> &obj);
00109 
00110                         template <size_t VEH_SIZE, size_t OBS_SIZE, size_t FEAT_SIZE, size_t ACT_SIZE, typename KFTYPE>
00111                         inline bool isMapEmpty(const CKalmanFilterCapable<VEH_SIZE,OBS_SIZE,FEAT_SIZE,ACT_SIZE,KFTYPE> &obj);
00112                         // Specialization:
00113                         template <size_t VEH_SIZE, size_t OBS_SIZE, size_t ACT_SIZE, typename KFTYPE>
00114                         inline bool isMapEmpty(const CKalmanFilterCapable<VEH_SIZE,OBS_SIZE,0 /*FEAT_SIZE*/,ACT_SIZE,KFTYPE> &obj);
00115                 }
00116 
00117 
00118                 /** Virtual base for Kalman Filter (EKF,IEKF,UKF) implementations.
00119                  *   This base class stores the state vector and covariance matrix of the system. It has virtual methods that must be completed
00120                  *    by derived classes to address a given filtering problem. The main entry point of the algorithm is CKalmanFilterCapable::runOneKalmanIteration, which
00121                  *    should be called AFTER setting the desired filter options in KF_options, as well as any options in the derived class.
00122                  *   Note that the main entry point is protected, so derived classes must offer another method more specific to a given problem which, internally, calls runOneKalmanIteration.
00123                  *
00124                  *  For further details and examples, check out the tutorial: http://www.mrpt.org/Kalman_Filters
00125                  *
00126                  *  The Kalman filter algorithms are generic, but this implementation is biased to ease the implementation
00127                  *  of SLAM-like problems. However, it can be also applied to many generic problems not related to robotics or SLAM.
00128                  *
00129                  *  The meaning of the template parameters is:
00130                  *      - VEH_SIZE: The dimension of the "vehicle state": either the full state vector or the "vehicle" part if applicable.
00131                  *      - OBS_SIZE: The dimension of each observation (eg, 2 for pixel coordinates, 3 for 3D coordinates,etc).
00132                  *      - FEAT_SIZE: The dimension of the features in the system state (the "map"), or 0 if not applicable (the default if not implemented).
00133                  *      - ACT_SIZE: The dimension of each "action" u_k (or 0 if not applicable).
00134                  *      - KFTYPE: The numeric type of the matrices (default: double)
00135                  *
00136                  * Revisions:
00137                  *      - 2007: Antonio J. Ortiz de Galisteo (AJOGD)
00138                  *      - 2008/FEB: All KF classes corrected, reorganized, and rewritten (JLBC).
00139                  *      - 2008/MAR: Implemented IKF (JLBC).
00140                  *      - 2009/DEC: Totally rewritten as a generic template using fixed-size matrices where possible (JLBC).
00141                  *
00142                  *  \sa mrpt::slam::CRangeBearingKFSLAM, mrpt::slam::CRangeBearingKFSLAM2D
00143                  * \ingroup mrpt_bayes_grp
00144                  */
00145                 template <size_t VEH_SIZE, size_t OBS_SIZE, size_t FEAT_SIZE, size_t ACT_SIZE, typename KFTYPE = double>
00146                 class CKalmanFilterCapable : public mrpt::utils::CDebugOutputCapable
00147                 {
00148                 public:
00149                         static inline size_t get_vehicle_size() { return VEH_SIZE; }
00150                         static inline size_t get_observation_size() { return OBS_SIZE; }
00151                         static inline size_t get_feature_size() { return FEAT_SIZE; }
00152                         static inline size_t get_action_size() { return ACT_SIZE; }
00153                         inline size_t getNumberOfLandmarksInTheMap() const { return detail::getNumberOfLandmarksInMap(*this); }
00154                         inline bool   isMapEmpty() const { return detail::isMapEmpty(*this); }
00155 
00156 
00157                         typedef KFTYPE kftype; //!< The numeric type used in the Kalman Filter (default=double)
00158                         typedef CKalmanFilterCapable<VEH_SIZE,OBS_SIZE,FEAT_SIZE,ACT_SIZE,KFTYPE>  KFCLASS;  //!< My class, in a shorter name!
00159 
00160                         // ---------- Many useful typedefs to short the notation a bit... --------
00161                         typedef mrpt::dynamicsize_vector<KFTYPE> KFVector;
00162                         typedef CMatrixTemplateNumeric<KFTYPE>   KFMatrix;
00163 
00164                         typedef CMatrixFixedNumeric<KFTYPE,VEH_SIZE,VEH_SIZE>   KFMatrix_VxV;
00165                         typedef CMatrixFixedNumeric<KFTYPE,OBS_SIZE,OBS_SIZE>   KFMatrix_OxO;
00166                         typedef CMatrixFixedNumeric<KFTYPE,FEAT_SIZE,FEAT_SIZE> KFMatrix_FxF;
00167                         typedef CMatrixFixedNumeric<KFTYPE,ACT_SIZE,ACT_SIZE>   KFMatrix_AxA;
00168 
00169                         typedef CMatrixFixedNumeric<KFTYPE,VEH_SIZE,OBS_SIZE>   KFMatrix_VxO;
00170                         typedef CMatrixFixedNumeric<KFTYPE,VEH_SIZE,FEAT_SIZE>  KFMatrix_VxF;
00171 
00172                         typedef CMatrixFixedNumeric<KFTYPE,FEAT_SIZE,VEH_SIZE>  KFMatrix_FxV;
00173                         typedef CMatrixFixedNumeric<KFTYPE,FEAT_SIZE,OBS_SIZE>  KFMatrix_FxO;
00174 
00175                         typedef CMatrixFixedNumeric<KFTYPE,OBS_SIZE,FEAT_SIZE>  KFMatrix_OxF;
00176                         typedef CMatrixFixedNumeric<KFTYPE,OBS_SIZE,VEH_SIZE>   KFMatrix_OxV;
00177 
00178                         typedef CArrayNumeric<KFTYPE,VEH_SIZE>  KFArray_VEH;
00179                         typedef CArrayNumeric<KFTYPE,ACT_SIZE>  KFArray_ACT;
00180                         typedef CArrayNumeric<KFTYPE,OBS_SIZE>  KFArray_OBS;
00181                         typedef typename mrpt::aligned_containers<KFArray_OBS>::vector_t  vector_KFArray_OBS;
00182                         typedef CArrayNumeric<KFTYPE,FEAT_SIZE> KFArray_FEAT;
00183 
00184                         inline size_t getStateVectorLength() const { return m_xkk.size(); }
00185 
00186                         /** Returns the mean of the estimated value of the idx'th landmark (not applicable to non-SLAM problems).
00187                           * \exception std::exception On idx>= getNumberOfLandmarksInTheMap()
00188                           */
00189                         inline void getLandmarkMean(size_t idx, KFArray_FEAT &feat ) const {
00190                                 ASSERT_(idx<getNumberOfLandmarksInTheMap())
00191                                 ::memcpy(&feat[0], &m_xkk[VEH_SIZE+idx*FEAT_SIZE], FEAT_SIZE*sizeof(m_xkk[0]));
00192                         }
00193                         /** Returns the covariance of the idx'th landmark (not applicable to non-SLAM problems).
00194                           * \exception std::exception On idx>= getNumberOfLandmarksInTheMap()
00195                           */
00196                         inline void getLandmarkCov(size_t idx, KFMatrix_FxF &feat_cov ) const {
00197                                 m_pkk.extractMatrix(VEH_SIZE+idx*FEAT_SIZE,VEH_SIZE+idx*FEAT_SIZE,feat_cov);
00198                         }
00199 
00200                 protected:
00201                         /** @name Kalman filter state
00202                                 @{ */
00203 
00204                         KFVector  m_xkk;  //!< The system state vector.
00205                         KFMatrix  m_pkk;  //!< The system full covariance matrix.
00206 
00207                         /** @} */
00208 
00209                         mrpt::utils::CTimeLogger  m_timLogger;
00210 
00211                         /** @name Virtual methods for Kalman Filter implementation
00212                                 @{
00213                          */
00214 
00215                         /** Must return the action vector u.
00216                           * \param out_u The action vector which will be passed to OnTransitionModel
00217                           */
00218                         virtual void OnGetAction( KFArray_ACT &out_u ) const = 0;
00219 
00220                         /** Implements the transition model \f$ \hat{x}_{k|k-1} = f( \hat{x}_{k-1|k-1}, u_k ) \f$
00221                           * \param in_u The vector returned by OnGetAction.
00222                           * \param inout_x At input has \f[ \hat{x}_{k-1|k-1} \f] , at output must have \f$ \hat{x}_{k|k-1} \f$ .
00223                           * \param out_skip Set this to true if for some reason you want to skip the prediction step (to do not modify either the vector or the covariance). Default:false
00224                           * \note Even if you return "out_skip=true", the value of "inout_x" MUST be updated as usual (this is to allow numeric approximation of Jacobians).
00225                           */
00226                         virtual void OnTransitionModel(
00227                                 const KFArray_ACT &in_u,
00228                                 KFArray_VEH       &inout_x,
00229                                 bool &out_skipPrediction
00230                                 )  const = 0;
00231 
00232                         /** Implements the transition Jacobian \f$ \frac{\partial f}{\partial x} \f$
00233                           * \param out_F Must return the Jacobian.
00234                           *  The returned matrix must be \f$V \times V\f$ with V being either the size of the whole state vector (for non-SLAM problems) or VEH_SIZE (for SLAM problems).
00235                           */
00236                         virtual void OnTransitionJacobian( KFMatrix_VxV  &out_F ) const
00237                         {
00238                                 m_user_didnt_implement_jacobian=true;
00239                         }
00240 
00241                         /** Only called if using a numeric approximation of the transition Jacobian, this method must return the increments in each dimension of the vehicle state vector while estimating the Jacobian.
00242                           */
00243                         virtual void OnTransitionJacobianNumericGetIncrements(KFArray_VEH &out_increments) const
00244                         {
00245                                 for (size_t i=0;i<VEH_SIZE;i++) out_increments[i] = 1e-6;
00246                         }
00247 
00248                         /** Implements the transition noise covariance \f$ Q_k \f$
00249                           * \param out_Q Must return the covariance matrix.
00250                           *  The returned matrix must be of the same size than the jacobian from OnTransitionJacobian
00251                           */
00252                         virtual void OnTransitionNoise( KFMatrix_VxV &out_Q )  const = 0;
00253 
00254                         /** This will be called before OnGetObservationsAndDataAssociation to allow the application to reduce the number of covariance landmark predictions to be made.
00255                           *  For example, features which are known to be "out of sight" shouldn't be added to the output list to speed up the calculations.
00256                           * \param in_all_prediction_means The mean of each landmark predictions; the computation or not of the corresponding covariances is what we're trying to determined with this method.
00257                           * \param out_LM_indices_to_predict The list of landmark indices in the map [0,getNumberOfLandmarksInTheMap()-1] that should be predicted.
00258                           * \note This is not a pure virtual method, so it should be implemented only if desired. The default implementation returns a vector with all the landmarks in the map.
00259                           * \sa OnGetObservations, OnDataAssociation
00260                           */
00261                         virtual void OnPreComputingPredictions(
00262                                 const vector_KFArray_OBS &in_all_prediction_means,
00263                                 mrpt::vector_size_t                             &out_LM_indices_to_predict ) const
00264                         {
00265                                 // Default: all of them:
00266                                 const size_t N = this->getNumberOfLandmarksInTheMap();
00267                                 out_LM_indices_to_predict.resize(N);
00268                                 for (size_t i=0;i<N;i++) out_LM_indices_to_predict[i]=i;
00269                         }
00270 
00271                         /** Return the observation NOISE covariance matrix, that is, the model of the Gaussian additive noise of the sensor.
00272                           * \param out_R The noise covariance matrix. It might be non diagonal, but it'll usually be.
00273                           * \note Upon call, it can be assumed that the previous contents of out_R are all zeros.
00274                           */
00275                         virtual void OnGetObservationNoise(KFMatrix_OxO &out_R)  const = 0;
00276 
00277                         /** This is called between the KF prediction step and the update step, and the application must return the observations and, when applicable, the data association between these observations and the current map.
00278                           *
00279                           * \param out_z N vectors, each for one "observation" of length OBS_SIZE, N being the number of "observations": how many observed landmarks for a map, or just one if not applicable.
00280                           * \param out_data_association An empty vector or, where applicable, a vector where the i'th element corresponds to the position of the observation in the i'th row of out_z within the system state vector (in the range [0,getNumberOfLandmarksInTheMap()-1]), or -1 if it is a new map element and we want to insert it at the end of this KF iteration.
00281                           * \param in_all_predictions A vector with the prediction of ALL the landmarks in the map. Note that, in contrast, in_S only comprises a subset of all the landmarks.
00282                           * \param in_S The full covariance matrix of the observation predictions (i.e. the "innovation covariance matrix"). This is a M·O x M·O matrix with M=length of "in_lm_indices_in_S".
00283                           * \param in_lm_indices_in_S The indices of the map landmarks (range [0,getNumberOfLandmarksInTheMap()-1]) that can be found in the matrix in_S.
00284                           *
00285                           *  This method will be called just once for each complete KF iteration.
00286                           * \note It is assumed that the observations are independent, i.e. there are NO cross-covariances between them.
00287                           */
00288                         virtual void OnGetObservationsAndDataAssociation(
00289                                 vector_KFArray_OBS                      &out_z,
00290                                 mrpt::vector_int            &out_data_association,
00291                                 const vector_KFArray_OBS        &in_all_predictions,
00292                                 const KFMatrix              &in_S,
00293                                 const vector_size_t         &in_lm_indices_in_S,
00294                                 const KFMatrix_OxO          &in_R
00295                                 ) = 0;
00296 
00297                         /** Implements the observation prediction \f$ h_i(x) \f$.
00298                           * \param idx_landmark_to_predict The indices of the landmarks in the map whose predictions are expected as output. For non SLAM-like problems, this input value is undefined and the application should just generate one observation for the given problem.
00299                           * \param out_predictions The predicted observations.
00300                           */
00301                         virtual void OnObservationModel(
00302                                 const mrpt::vector_size_t       &idx_landmarks_to_predict,
00303                                 vector_KFArray_OBS  &out_predictions
00304                                 ) const = 0;
00305 
00306                         /** Implements the observation Jacobians \f$ \frac{\partial h_i}{\partial x} \f$ and (when applicable) \f$ \frac{\partial h_i}{\partial y_i} \f$.
00307                           * \param idx_landmark_to_predict The index of the landmark in the map whose prediction is expected as output. For non SLAM-like problems, this will be zero and the expected output is for the whole state vector.
00308                           * \param Hx  The output Jacobian \f$ \frac{\partial h_i}{\partial x} \f$.
00309                           * \param Hy  The output Jacobian \f$ \frac{\partial h_i}{\partial y_i} \f$.
00310                           */
00311                         virtual void OnObservationJacobians(
00312                                 const size_t &idx_landmark_to_predict,
00313                                 KFMatrix_OxV &Hx,
00314                                 KFMatrix_OxF &Hy
00315                                 ) const
00316                         {
00317                                 m_user_didnt_implement_jacobian=true;
00318                         }
00319 
00320                         /** Only called if using a numeric approximation of the observation Jacobians, this method must return the increments in each dimension of the vehicle state vector while estimating the Jacobian.
00321                           */
00322                         virtual void OnObservationJacobiansNumericGetIncrements(
00323                                         KFArray_VEH  &out_veh_increments,
00324                                         KFArray_FEAT &out_feat_increments ) const
00325                         {
00326                                 for (size_t i=0;i<VEH_SIZE;i++) out_veh_increments[i] = 1e-6;
00327                                 for (size_t i=0;i<FEAT_SIZE;i++) out_feat_increments[i] = 1e-6;
00328                         }
00329 
00330                         /** Computes A=A-B, which may need to be re-implemented depending on the topology of the individual scalar components (eg, angles).
00331                           */
00332                         virtual void OnSubstractObservationVectors(KFArray_OBS &A, const KFArray_OBS &B) const
00333                         {
00334                                 A -= B;
00335                         }
00336 
00337                         /** If applicable to the given problem, this method implements the inverse observation model needed to extend the "map" with a new "element".
00338                           * \param in_z The observation vector whose inverse sensor model is to be computed. This is actually one of the vector<> returned by OnGetObservationsAndDataAssociation().
00339                           * \param out_yn The F-length vector with the inverse observation model \f$ y_n=y(x,z_n) \f$.
00340                           * \param out_dyn_dxv The \f$F \times V\f$ Jacobian of the inv. sensor model wrt the robot pose \f$ \frac{\partial y_n}{\partial x_v} \f$.
00341                           * \param out_dyn_dhn The \f$F \times O\f$ Jacobian of the inv. sensor model wrt the observation vector \f$ \frac{\partial y_n}{\partial h_n} \f$.
00342                           *
00343                           *  - O: OBS_SIZE
00344                           *  - V: VEH_SIZE
00345                           *  - F: FEAT_SIZE
00346                           *
00347                           * \note OnNewLandmarkAddedToMap will be also called after calling this method if a landmark is actually being added to the map.
00348                           * \deprecated This version of the method is deprecated. The alternative method is preferred to allow a greater flexibility.
00349                           */
00350                         virtual void  OnInverseObservationModel(
00351                                 const KFArray_OBS & in_z,
00352                                 KFArray_FEAT  & out_yn,
00353                                 KFMatrix_FxV  & out_dyn_dxv,
00354                                 KFMatrix_FxO  & out_dyn_dhn ) const
00355                         {
00356                                 MRPT_START
00357                                 THROW_EXCEPTION("Inverse sensor model required but not implemented in derived class.")
00358                                 MRPT_END
00359                         }
00360 
00361                         /** If applicable to the given problem, this method implements the inverse observation model needed to extend the "map" with a new "element".
00362                           *  The uncertainty in the new map feature comes from two parts: one from the vehicle uncertainty (through the out_dyn_dxv Jacobian),
00363                           *   and another from the uncertainty in the observation itself. By default, out_use_dyn_dhn_jacobian=true on call, and if it's left at "true",
00364                           *   the base KalmanFilter class will compute the uncertainty of the landmark relative position from out_dyn_dhn.
00365                           *  Only in some problems (e.g. MonoSLAM), it'll be needed for the application to directly return the covariance matrix \a out_dyn_dhn_R_dyn_dhnT, which is the equivalent to:
00366                           *
00367                           *         \f$ \frac{\partial y_n}{\partial h_n} R \frac{\partial y_n}{\partial h_n}^\top \f$.
00368                           *
00369                           *  but may be computed from additional terms, or whatever needed by the user.
00370                           *
00371                           * \param in_z The observation vector whose inverse sensor model is to be computed. This is actually one of the vector<> returned by OnGetObservationsAndDataAssociation().
00372                           * \param out_yn The F-length vector with the inverse observation model \f$ y_n=y(x,z_n) \f$.
00373                           * \param out_dyn_dxv The \f$F \times V\f$ Jacobian of the inv. sensor model wrt the robot pose \f$ \frac{\partial y_n}{\partial x_v} \f$.
00374                           * \param out_dyn_dhn The \f$F \times O\f$ Jacobian of the inv. sensor model wrt the observation vector \f$ \frac{\partial y_n}{\partial h_n} \f$.
00375                           * \param out_dyn_dhn_R_dyn_dhnT See the discussion above.
00376                           *
00377                           *  - O: OBS_SIZE
00378                           *  - V: VEH_SIZE
00379                           *  - F: FEAT_SIZE
00380                           *
00381                           * \note OnNewLandmarkAddedToMap will be also called after calling this method if a landmark is actually being added to the map.
00382                           */
00383                         virtual void  OnInverseObservationModel(
00384                                 const KFArray_OBS & in_z,
00385                                 KFArray_FEAT  & out_yn,
00386                                 KFMatrix_FxV  & out_dyn_dxv,
00387                                 KFMatrix_FxO  & out_dyn_dhn,
00388                                 KFMatrix_FxF  & out_dyn_dhn_R_dyn_dhnT,
00389                                 bool          & out_use_dyn_dhn_jacobian
00390                                  ) const
00391                         {
00392                                 MRPT_START
00393                                 OnInverseObservationModel(in_z,out_yn,out_dyn_dxv,out_dyn_dhn);
00394                                 out_use_dyn_dhn_jacobian=true;
00395                                 MRPT_END
00396                         }
00397 
00398                         /** If applicable to the given problem, do here any special handling of adding a new landmark to the map.
00399                           * \param in_obsIndex The index of the observation whose inverse sensor is to be computed. It corresponds to the row in in_z where the observation can be found.
00400                           * \param in_idxNewFeat The index that this new feature will have in the state vector (0:just after the vehicle state, 1: after that,...). Save this number so data association can be done according to these indices.
00401                           * \sa OnInverseObservationModel
00402                           */
00403                         virtual void OnNewLandmarkAddedToMap(
00404                                 const size_t    in_obsIdx,
00405                                 const size_t    in_idxNewFeat )
00406                         {
00407                                 // Do nothing in this base class.
00408                         }
00409 
00410                         /** This method is called after the prediction and after the update, to give the user an opportunity to normalize the state vector (eg, keep angles within -pi,pi range) if the application requires it.
00411                           */
00412                         virtual void OnNormalizeStateVector()
00413                         {
00414                                 // Do nothing in this base class.
00415                         }
00416 
00417                         /** This method is called after finishing one KF iteration and before returning from runOneKalmanIteration().
00418                           */
00419                         virtual void OnPostIteration()
00420                         {
00421                                 // Do nothing in this base class.
00422                         }
00423 
00424                         /** @}
00425                          */
00426 
00427                 public:
00428                         CKalmanFilterCapable() {} //!< Default constructor
00429                         virtual ~CKalmanFilterCapable() {}  //!< Destructor
00430 
00431                         mrpt::utils::CTimeLogger &getProfiler() { return m_timLogger; }
00432 
00433                         TKF_options KF_options; //!< Generic options for the Kalman Filter algorithm itself.
00434 
00435 
00436                 private:
00437                         //  "Local" variables to runOneKalmanIteration, declared here to avoid
00438                         //   allocating them over and over again with each call.
00439                         //  (The variables that go into the stack remains in the function body)
00440                         vector_KFArray_OBS              all_predictions;
00441                         vector_size_t                   predictLMidxs;
00442                         KFMatrix                                dh_dx;
00443                         KFMatrix                                dh_dx_full;
00444                         vector_size_t                   idxs;
00445                         KFMatrix                                S;
00446                         KFMatrix                                Pkk_subset;
00447                         vector_KFArray_OBS              Z;              // Each entry is one observation:
00448                         KFMatrix                                K;              // Kalman gain
00449                         KFMatrix                                S_1;    // Inverse of S
00450                         KFMatrix                                dh_dx_full_obs;
00451                         KFMatrix                                aux_K_dh_dx;
00452 
00453                 protected:
00454 
00455                         /** The main entry point, executes one complete step: prediction + update.
00456                           *  It is protected since derived classes must provide a problem-specific entry point for users.
00457                           *  The exact order in which this method calls the virtual method is explained in http://www.mrpt.org/Kalman_Filters
00458                           */
00459                         void runOneKalmanIteration()
00460                         {
00461                                 MRPT_START
00462 
00463                                 m_timLogger.enable(KF_options.enable_profiler || KF_options.verbose);
00464                                 m_timLogger.enter("KF:complete_step");
00465 
00466                                 ASSERT_(size_t(m_xkk.size())==m_pkk.getColCount())
00467                                 ASSERT_(size_t(m_xkk.size())>=VEH_SIZE)
00468 
00469                                 // =============================================================
00470                                 //  1. CREATE ACTION MATRIX u FROM ODOMETRY
00471                                 // =============================================================
00472                                 KFArray_ACT  u;
00473 
00474                                 m_timLogger.enter("KF:1.OnGetAction");
00475                                 OnGetAction(u);
00476                                 m_timLogger.leave("KF:1.OnGetAction");
00477 
00478                                 // Sanity check:
00479                                 if (FEAT_SIZE) { ASSERTDEB_( (((m_xkk.size()-VEH_SIZE)/FEAT_SIZE)*FEAT_SIZE)== (m_xkk.size()-VEH_SIZE) ) }
00480 
00481                                 // =============================================================
00482                                 //  2. PREDICTION OF NEW POSE xv_{k+1|k}
00483                                 // =============================================================
00484                                 m_timLogger.enter("KF:2.prediction stage");
00485 
00486                                 const size_t N_map = getNumberOfLandmarksInTheMap();
00487 
00488                                 KFArray_VEH xv( &m_xkk[0] );  // Vehicle pose
00489 
00490                                 bool skipPrediction=false; // Wether to skip the prediction step (in SLAM this is desired for the first iteration...)
00491 
00492                                 // Update mean: xv will have the updated pose until we update it in the filterState later.
00493                                 //  This is to maintain a copy of the last robot pose in the state vector, required for the Jacobian computation.
00494                                 OnTransitionModel(u, xv, skipPrediction);
00495 
00496                                 if ( !skipPrediction )
00497                                 {
00498                                         // =============================================================
00499                                         //  3. PREDICTION OF COVARIANCE P_{k+1|k}
00500                                         // =============================================================
00501                                         // First, we compute de Jacobian fv_by_xv  (derivative of f_vehicle wrt x_vehicle):
00502                                         KFMatrix_VxV  dfv_dxv;
00503 
00504                                         // Try closed-form Jacobian first:
00505                                         m_user_didnt_implement_jacobian=false; // Set to true by the default method if not reimplemented in base class.
00506                                         if (KF_options.use_analytic_transition_jacobian)
00507                                                 OnTransitionJacobian(dfv_dxv);
00508 
00509                                         if (m_user_didnt_implement_jacobian || !KF_options.use_analytic_transition_jacobian || KF_options.debug_verify_analytic_jacobians)
00510                                         {       // Numeric approximation:
00511                                                 KFArray_VEH xkk_vehicle( &m_xkk[0] );  // A copy of the vehicle part of the state vector.
00512                                                 KFArray_VEH xkk_veh_increments;
00513                                                 OnTransitionJacobianNumericGetIncrements(xkk_veh_increments);
00514 
00515                                                 mrpt::math::estimateJacobian(
00516                                                         xkk_vehicle,
00517                                                         &KF_aux_estimate_trans_jacobian, //(const VECTORLIKE &x,const USERPARAM &y, VECTORLIKE3  &out),
00518                                                         xkk_veh_increments,
00519                                                         std::make_pair<KFCLASS*,KFArray_ACT>(this,u),
00520                                                         dfv_dxv);
00521 
00522                                                 if (KF_options.debug_verify_analytic_jacobians)
00523                                                 {
00524                                                         KFMatrix_VxV dfv_dxv_gt(UNINITIALIZED_MATRIX);
00525                                                         OnTransitionJacobian(dfv_dxv_gt);
00526                                                         if ((dfv_dxv-dfv_dxv_gt).Abs().sumAll()>KF_options.debug_verify_analytic_jacobians_threshold)
00527                                                         {
00528                                                                 std::cerr << "[KalmanFilter] ERROR: User analytical transition Jacobians are wrong: \n"
00529                                                                         << " Real dfv_dxv: \n" << dfv_dxv << "\n Analytical dfv_dxv:\n" << dfv_dxv_gt << "Diff:\n" << (dfv_dxv-dfv_dxv_gt) << "\n";
00530                                                                 THROW_EXCEPTION("ERROR: User analytical transition Jacobians are wrong (More details dumped to cerr)")
00531                                                         }
00532                                                 }
00533 
00534                                         }
00535 
00536                                         // Q is the process noise covariance matrix, is associated to the robot movement and is necesary to calculate the prediction P(k+1|k)
00537                                         KFMatrix_VxV  Q;
00538                                         OnTransitionNoise(Q);
00539 
00540                                         // ====================================
00541                                         //  3.1:  Pxx submatrix
00542                                         // ====================================
00543                                         // Replace old covariance:
00544                                         m_pkk.block(0,0,VEH_SIZE,VEH_SIZE) =
00545                                                 Q +
00546                                                 dfv_dxv * m_pkk.block(0,0,VEH_SIZE,VEH_SIZE) * dfv_dxv.transpose();
00547 
00548                                         // ====================================
00549                                         //  3.2:  All Pxy_i
00550                                         // ====================================
00551                                         // Now, update the cov. of landmarks, if any:
00552                                         KFMatrix_VxF aux;
00553                                         for (size_t i=0 ; i<N_map ; i++)
00554                                         {
00555                                                 // aux = dfv_dxv(...) * m_pkk(...)
00556                                                 dfv_dxv.multiply_subMatrix(
00557                                                         m_pkk,
00558                                                         aux,  // Output
00559                                                         VEH_SIZE+i*FEAT_SIZE,   // Offset col
00560                                                         0,                                               // Offset row
00561                                                         FEAT_SIZE                        // Number of columns desired in output
00562                                                 );
00563 
00564                                                 m_pkk.insertMatrix         (0,                VEH_SIZE+i*FEAT_SIZE,  aux );
00565                                                 m_pkk.insertMatrixTranspose(VEH_SIZE+i*FEAT_SIZE, 0               ,  aux );
00566                                         }
00567 
00568                                         // =============================================================
00569                                         //  4. NOW WE CAN OVERWRITE THE NEW STATE VECTOR
00570                                         // =============================================================
00571                                         for (size_t i=0;i<VEH_SIZE;i++)
00572                                                 m_xkk[i]=xv[i];
00573 
00574                                         // Normalize, if neccesary.
00575                                         OnNormalizeStateVector();
00576 
00577                                 } // end if (!skipPrediction)
00578 
00579 
00580                                 const double tim_pred = m_timLogger.leave("KF:2.prediction stage");
00581 
00582 
00583                                 // =============================================================
00584                                 //  5. PREDICTION OF OBSERVATIONS AND COMPUTE JACOBIANS
00585                                 // =============================================================
00586                                 m_timLogger.enter("KF:3.predict all obs");
00587 
00588                                 KFMatrix_OxO  R;        // Sensor uncertainty (covariance matrix): R
00589                                 OnGetObservationNoise(R);
00590 
00591                                 // Predict the observations for all the map LMs, so the user
00592                                 //  can decide if their covariances (more costly) must be computed as well:
00593                                 all_predictions.resize(N_map);
00594                                 OnObservationModel(
00595                                         mrpt::math::sequenceStdVec<size_t,1>(0,N_map),
00596                                         all_predictions);
00597 
00598                                 const double tim_pred_obs = m_timLogger.leave("KF:3.predict all obs");
00599 
00600                                 m_timLogger.enter("KF:4.decide pred obs");
00601 
00602                                 // Decide if some of the covariances shouldn't be predicted.
00603                                 predictLMidxs.clear();
00604                                 if (FEAT_SIZE==0)
00605                                         // In non-SLAM problems, just do one prediction, for the entire system state:
00606                                         predictLMidxs.assign(1,0);
00607                                 else
00608                                         // On normal SLAM problems:
00609                                         OnPreComputingPredictions(all_predictions, predictLMidxs);
00610 
00611 
00612                                 m_timLogger.leave("KF:4.decide pred obs");
00613 
00614                                 // =============================================================
00615                                 //  6. COMPUTE INNOVATION MATRIX "S"
00616                                 // =============================================================
00617                                 // Do the prediction of the observation covariances:
00618                                 // Compute S:  S = H P ~H + R
00619                                 //
00620                                 // Build a big dh_dx Jacobian composed of the small block Jacobians.
00621                                 // , but: it's actually a subset of the full Jacobian, since the non-predicted
00622                                 //  features do not appear.
00623                                 //
00624                                 //  dh_dx: O·M x V+M·F
00625                                 //      S: O·M x O·M
00626                                 //  M = |predictLMidxs|
00627                                 //  O=size of each observation.
00628                                 //  F=size of features in the map
00629                                 //
00630 
00631                                 // This is the beginning of a loop if we are doing sequential
00632                                 //  data association, where after each incorporation of an observation
00633                                 //  into the filter we recompute the vehicle state, then reconsider the
00634                                 //  data associations:
00635                                 // TKFFusionMethod fusion_strategy
00636 
00637 
00638                                 m_timLogger.enter("KF:5.build Jacobians");
00639 
00640                                 size_t N_pred = FEAT_SIZE==0 ?
00641                                         1 /* In non-SLAM problems, there'll be only 1 fixed observation */ :
00642                                         predictLMidxs.size();
00643 
00644                                 vector_int  data_association;  // -1: New map feature.>=0: Indexes in the state vector
00645 
00646                                 // The next loop will only do more than one iteration if the heuristic in OnPreComputingPredictions() fails, 
00647                                 //  which will be detected by the addition of extra landmarks to predict into "missing_predictions_to_add"
00648                                 std::vector<size_t> missing_predictions_to_add;
00649 
00650                                 // Indices in xkk (& Pkk) that are involved in this observation (used below).
00651                                 idxs.clear();
00652                                 idxs.reserve(VEH_SIZE+N_pred*FEAT_SIZE);
00653                                 for (size_t i=0;i<VEH_SIZE;i++) idxs.push_back(i);
00654 
00655                                 dh_dx.zeros(N_pred*OBS_SIZE, VEH_SIZE + FEAT_SIZE * N_pred ); // Init to zeros.
00656                                 dh_dx_full.zeros(N_pred*OBS_SIZE, VEH_SIZE + FEAT_SIZE * N_map ); // Init to zeros.
00657 
00658                                 size_t first_new_pred = 0; // This will be >0 only if we perform multiple loops due to failures in the prediction heuristic.
00659 
00660                                 do  
00661                                 {
00662                                         if (!missing_predictions_to_add.empty())
00663                                         {
00664                                                 const size_t nNew = missing_predictions_to_add.size();
00665                                                 printf_debug("[KF] *Performance Warning*: %u LMs were not correctly predicted by OnPreComputingPredictions()\n",static_cast<unsigned int>(nNew));
00666 
00667                                                 ASSERTDEB_(FEAT_SIZE!=0)
00668                                                 for (size_t j=0;j<nNew;j++)
00669                                                         predictLMidxs.push_back( missing_predictions_to_add[j] );
00670 
00671                                                 N_pred = predictLMidxs.size();
00672                                                 missing_predictions_to_add.clear();
00673                                         }
00674 
00675                                         dh_dx.setSize(N_pred*OBS_SIZE, VEH_SIZE + FEAT_SIZE * N_pred ); // Pad with zeros.
00676                                         dh_dx_full.setSize(N_pred*OBS_SIZE, VEH_SIZE + FEAT_SIZE * N_map ); // Pad with zeros.
00677 
00678                                         for (size_t i=first_new_pred;i<N_pred;++i)
00679                                         {
00680                                                 const size_t lm_idx = FEAT_SIZE==0 ? 0 : predictLMidxs[i];
00681                                                 KFMatrix_OxV Hx(UNINITIALIZED_MATRIX);
00682                                                 KFMatrix_OxF Hy(UNINITIALIZED_MATRIX);
00683 
00684                                                 // Try the analitic Jacobian first:
00685                                                 m_user_didnt_implement_jacobian=false; // Set to true by the default method if not reimplemented in base class.
00686                                                 if (KF_options.use_analytic_observation_jacobian)
00687                                                         OnObservationJacobians(lm_idx,Hx,Hy);
00688 
00689                                                 if (m_user_didnt_implement_jacobian || !KF_options.use_analytic_observation_jacobian || KF_options.debug_verify_analytic_jacobians)
00690                                                 {       // Numeric approximation:
00691                                                         const size_t lm_idx_in_statevector = VEH_SIZE+lm_idx*FEAT_SIZE;
00692 
00693                                                         const KFArray_VEH  x_vehicle( &m_xkk[0] );
00694                                                         const KFArray_FEAT x_feat( &m_xkk[lm_idx_in_statevector] );
00695 
00696                                                         KFArray_VEH  xkk_veh_increments;
00697                                                         KFArray_FEAT feat_increments;
00698                                                         OnObservationJacobiansNumericGetIncrements(xkk_veh_increments, feat_increments);
00699 
00700                                                         mrpt::math::estimateJacobian(
00701                                                                 x_vehicle,
00702                                                                 &KF_aux_estimate_obs_Hx_jacobian,
00703                                                                 xkk_veh_increments,
00704                                                                 std::make_pair<KFCLASS*,size_t>(this,lm_idx),
00705                                                                 Hx);
00706                                                         // The state vector was temporarily modified by KF_aux_estimate_*, restore it:
00707                                                         ::memcpy(&m_xkk[0],&x_vehicle[0],sizeof(m_xkk[0])*VEH_SIZE);
00708 
00709                                                         mrpt::math::estimateJacobian(
00710                                                                 x_feat,
00711                                                                 &KF_aux_estimate_obs_Hy_jacobian,
00712                                                                 feat_increments,
00713                                                                 std::make_pair<KFCLASS*,size_t>(this,lm_idx),
00714                                                                 Hy);
00715                                                         // The state vector was temporarily modified by KF_aux_estimate_*, restore it:
00716                                                         ::memcpy(&m_xkk[lm_idx_in_statevector],&x_feat[0],sizeof(m_xkk[0])*FEAT_SIZE);
00717 
00718                                                         if (KF_options.debug_verify_analytic_jacobians)
00719                                                         {
00720                                                                 KFMatrix_OxV Hx_gt(UNINITIALIZED_MATRIX);
00721                                                                 KFMatrix_OxF Hy_gt(UNINITIALIZED_MATRIX);
00722                                                                 OnObservationJacobians(lm_idx,Hx_gt,Hy_gt);
00723                                                                 if ((Hx-Hx_gt).Abs().sumAll()>KF_options.debug_verify_analytic_jacobians_threshold) {
00724                                                                         std::cerr << "[KalmanFilter] ERROR: User analytical observation Hx Jacobians are wrong: \n"
00725                                                                                 << " Real Hx: \n" << Hx << "\n Analytical Hx:\n" << Hx_gt << "Diff:\n" << Hx-Hx_gt << "\n";
00726                                                                         THROW_EXCEPTION("ERROR: User analytical observation Hx Jacobians are wrong (More details dumped to cerr)")
00727                                                                 }
00728                                                                 if ((Hy-Hy_gt).Abs().sumAll()>KF_options.debug_verify_analytic_jacobians_threshold) {
00729                                                                         std::cerr << "[KalmanFilter] ERROR: User analytical observation Hy Jacobians are wrong: \n"
00730                                                                                 << " Real Hy: \n" << Hy << "\n Analytical Hx:\n" << Hy_gt << "Diff:\n" << Hy-Hy_gt << "\n";
00731                                                                         THROW_EXCEPTION("ERROR: User analytical observation Hy Jacobians are wrong (More details dumped to cerr)")
00732                                                                 }
00733                                                         }
00734                                                 }
00735 
00736                                                 dh_dx.insertMatrix(i*OBS_SIZE,0, Hx);
00737                                                 if (FEAT_SIZE!=0)
00738                                                         dh_dx.insertMatrix(i*OBS_SIZE,VEH_SIZE+i*FEAT_SIZE, Hy);
00739 
00740                                                 dh_dx_full.insertMatrix(i*OBS_SIZE,0, Hx);
00741                                                 if (FEAT_SIZE!=0)
00742                                                 {
00743                                                         dh_dx_full.insertMatrix(i*OBS_SIZE,VEH_SIZE+lm_idx*FEAT_SIZE, Hy);
00744 
00745                                                         for (size_t k=0;k<FEAT_SIZE;k++)
00746                                                                 idxs.push_back(k+VEH_SIZE+FEAT_SIZE*lm_idx);
00747                                                 }
00748                                         }
00749                                         m_timLogger.leave("KF:5.build Jacobians");
00750 
00751                                         // Compute S:  S = H P ~H + R
00752                                         // *TODO*: This can be accelerated by exploiting the sparsity of dh_dx!!!
00753                                         // ------------------------------------
00754                                         m_timLogger.enter("KF:6.build S");
00755 
00756                                         S.setSize(N_pred*OBS_SIZE,N_pred*OBS_SIZE);
00757 
00758                                         // (TODO: Implement multiply_HCHt for a subset of COV directly.)
00759                                         // Extract the subset of m_Pkk that is involved in this observation:
00760                                         m_pkk.extractSubmatrixSymmetrical(idxs,Pkk_subset);
00761 
00762                                         // S = dh_dx * m_pkk(subset) * (~dh_dx);
00763                                         dh_dx.multiply_HCHt(Pkk_subset,S);
00764 
00765                                         // Sum the "R" term:
00766                                         if ( FEAT_SIZE>0 )
00767                                         {
00768                                                 for (size_t i=0;i<N_pred;++i)
00769                                                 {
00770                                                         const size_t obs_idx_off = i*OBS_SIZE;
00771                                                         for (size_t j=0;j<OBS_SIZE;j++)
00772                                                                 for (size_t k=0;k<OBS_SIZE;k++)
00773                                                                         S.get_unsafe(obs_idx_off+j,obs_idx_off+k) += R.get_unsafe(j,k);
00774                                                 }
00775                                         }
00776                                         else
00777                                         {       // Not a SLAM-like EKF problem:
00778                                                 ASSERTDEB_(S.getColCount() == OBS_SIZE );
00779                                                 S+=R;
00780                                         }
00781 
00782                                         m_timLogger.leave("KF:6.build S");
00783 
00784 
00785                                         Z.clear();      // Each entry is one observation:
00786 
00787                                         m_timLogger.enter("KF:7.get obs & DA");
00788 
00789                                         // Get observations and do data-association:
00790                                         OnGetObservationsAndDataAssociation(
00791                                                 Z, data_association, // Out
00792                                                 all_predictions, S, predictLMidxs, R  // In
00793                                                 );
00794                                         ASSERTDEB_(data_association.size()==Z.size() || (data_association.empty() && FEAT_SIZE==0));
00795 
00796                                         // Check if an observation hasn't been predicted in OnPreComputingPredictions() but has been actually 
00797                                         //  observed. This may imply an error in the heuristic of OnPreComputingPredictions(), and forces us
00798                                         //  to rebuild the matrices 
00799                                         missing_predictions_to_add.clear();
00800                                         if (FEAT_SIZE!=0)
00801                                         {
00802                                                 for (size_t i=0;i<data_association.size();++i)
00803                                                 {
00804                                                         if (data_association[i]<0) continue;
00805                                                         const size_t assoc_idx_in_map = static_cast<size_t>(data_association[i]);
00806                                                         const size_t assoc_idx_in_pred = mrpt::utils::find_in_vector(assoc_idx_in_map, predictLMidxs);
00807                                                         if (assoc_idx_in_pred==std::string::npos)
00808                                                                 missing_predictions_to_add.push_back(assoc_idx_in_map);
00809                                                 }
00810                                         }
00811 
00812                                         first_new_pred = N_pred;  // If we do another loop, start at the begin of new predictions
00813 
00814                                 } while (!missing_predictions_to_add.empty());
00815 
00816 
00817                                 const double tim_obs_DA = m_timLogger.leave("KF:7.get obs & DA");
00818 
00819                                 // =============================================================
00820                                 //  7. UPDATE USING THE KALMAN GAIN
00821                                 // =============================================================
00822                                 // Update, only if there are observations!
00823                                 if ( !Z.empty() )
00824                                 {
00825                                         m_timLogger.enter("KF:8.update stage");
00826 
00827                                         switch (KF_options.method)
00828                                         {
00829                                                 // -----------------------
00830                                                 //  FULL KF- METHOD
00831                                                 // -----------------------
00832                                         case kfEKFNaive:
00833                                         case kfIKFFull:
00834                                         {
00835                                                 // Build the whole Jacobian dh_dx matrix
00836                                                 // ---------------------------------------------
00837                                                 // Keep only those whose DA is not -1
00838                                                 vector_int  mapIndicesForKFUpdate(data_association.size());
00839                                                 mapIndicesForKFUpdate.resize( std::distance(mapIndicesForKFUpdate.begin(),
00840                                                         std::remove_copy_if(
00841                                                                 data_association.begin(),
00842                                                                 data_association.end(),
00843                                                                 mapIndicesForKFUpdate.begin(),
00844                                                                 binder1st<equal_to<int> >(equal_to<int>(),-1) ) ) );
00845 
00846                                                 const size_t N_upd = (FEAT_SIZE==0) ?
00847                                                         1 :     // Non-SLAM problems: Just one observation for the entire system.
00848                                                         mapIndicesForKFUpdate.size(); // SLAM: # of observed known landmarks
00849 
00850                                                 // Just one, or several update iterations??
00851                                                 const size_t nKF_iterations = (KF_options.method==kfEKFNaive) ?  1 : KF_options.IKF_iterations;
00852 
00853                                                 const KFVector xkk_0 = m_xkk;
00854 
00855                                                 // For each IKF iteration (or 1 for EKF)
00856                                                 if (N_upd>0) // Do not update if we have no observations!
00857                                                 {
00858                                                         for (size_t IKF_iteration=0;IKF_iteration<nKF_iterations;IKF_iteration++)
00859                                                         {
00860                                                                 // Compute ytilde = OBS - PREDICTION
00861                                                                 KFVector  ytilde(OBS_SIZE*N_upd);
00862                                                                 size_t    ytilde_idx = 0;
00863 
00864                                                                 // TODO: Use a Matrix view of "dh_dx_full" instead of creating a copy into "dh_dx_full_obs"
00865                                                                 dh_dx_full_obs.zeros(N_upd*OBS_SIZE, VEH_SIZE + FEAT_SIZE * N_map ); // Init to zeros.
00866                                                                 KFMatrix S_observed;    // The KF "S" matrix: A re-ordered, subset, version of the prediction S:
00867 
00868                                                                 if (FEAT_SIZE!=0)
00869                                                                 {       // SLAM problems:
00870                                                                         vector_size_t S_idxs;
00871                                                                         S_idxs.reserve(OBS_SIZE*N_upd);
00872 
00873                                                                         const size_t row_len = VEH_SIZE + FEAT_SIZE * N_map;
00874 
00875                                                                         for (size_t i=0;i<data_association.size();++i)
00876                                                                         {
00877                                                                                 if (data_association[i]<0) continue;
00878 
00879                                                                                 const size_t assoc_idx_in_map = static_cast<size_t>(data_association[i]);
00880                                                                                 const size_t assoc_idx_in_pred = mrpt::utils::find_in_vector(assoc_idx_in_map, predictLMidxs);
00881                                                                                 ASSERTMSG_(assoc_idx_in_pred!=string::npos, "OnPreComputingPredictions() didn't recommend the prediction of a landmark which has been actually observed!")
00882                                                                                 // TODO: In these cases, extend the prediction right now instead of launching an exception... or is this a bad idea??
00883 
00884                                                                                 // Build the subset dh_dx_full_obs:
00885                                                                                 dh_dx_full_obs.block(S_idxs.size()             ,0, OBS_SIZE, row_len)
00886                                                                                 =
00887                                                                                 dh_dx_full.block    (assoc_idx_in_pred*OBS_SIZE,0, OBS_SIZE, row_len);
00888                                                                                 // S_idxs.size() is used as counter for "dh_dx_full_obs".
00889                                                                                 for (size_t k=0;k<OBS_SIZE;k++)
00890                                                                                         S_idxs.push_back(assoc_idx_in_pred*OBS_SIZE+k);
00891 
00892                                                                                 // ytilde_i = Z[i] - all_predictions[i]
00893                                                                                 KFArray_OBS ytilde_i = Z[i];
00894                                                                                 OnSubstractObservationVectors(ytilde_i,all_predictions[predictLMidxs[assoc_idx_in_pred]]);
00895                                                                                 for (size_t k=0;k<OBS_SIZE;k++)
00896                                                                                         ytilde[ytilde_idx++] = ytilde_i[k];
00897                                                                         }
00898                                                                         // Extract the subset that is involved in this observation:
00899                                                                         S.extractSubmatrixSymmetrical(S_idxs,S_observed);
00900                                                                 }
00901                                                                 else
00902                                                                 {       // Non-SLAM problems:
00903                                                                         ASSERT_(Z.size()==1 && all_predictions.size()==1)
00904 
00905                                                                         dh_dx_full_obs = dh_dx_full;
00906                                                                         KFArray_OBS ytilde_i = Z[0];
00907                                                                         OnSubstractObservationVectors(ytilde_i,all_predictions[0]);
00908                                                                         for (size_t k=0;k<OBS_SIZE;k++)
00909                                                                                 ytilde[ytilde_idx++] = ytilde_i[k];
00910                                                                         // Extract the subset that is involved in this observation:
00911                                                                         S_observed = S;
00912                                                                 }
00913 
00914                                                                 // Compute the full K matrix:
00915                                                                 // ------------------------------
00916                                                                 m_timLogger.enter("KF:8.update stage:1.FULLKF:build K");
00917 
00918                                                                 K.setSize(m_pkk.getRowCount(), S_observed.getColCount() );
00919 
00920                                                                 // K = m_pkk * (~dh_dx) * S.inv() );
00921                                                                 K.multiply_ABt(m_pkk, dh_dx_full_obs);
00922 
00923                                                                 //KFMatrix S_1( S_observed.getRowCount(), S_observed.getColCount() );
00924                                                                 S_observed.inv(S_1); // Do NOT call inv_fast since it destroys S
00925                                                                 K*=S_1;
00926 
00927                                                                 m_timLogger.leave("KF:8.update stage:1.FULLKF:build K");
00928 
00929                                                                 // Use the full K matrix to update the mean:
00930                                                                 if (nKF_iterations==1)
00931                                                                 {
00932                                                                         m_timLogger.enter("KF:8.update stage:2.FULLKF:update xkk");
00933                                                                         m_xkk += K * ytilde;
00934                                                                         m_timLogger.leave("KF:8.update stage:2.FULLKF:update xkk");
00935                                                                 }
00936                                                                 else
00937                                                                 {
00938                                                                         m_timLogger.enter("KF:8.update stage:2.FULLKF:iter.update xkk");
00939 
00940                                                                         KFVector  HAx_column;
00941                                                                         dh_dx_full_obs.multiply_Ab( m_xkk - xkk_0, HAx_column);
00942 
00943                                                                         m_xkk = xkk_0;
00944                                                                         K.multiply_Ab(
00945                                                                                 (ytilde-HAx_column),
00946                                                                                 m_xkk,
00947                                                                                 true /* Accumulate in output */
00948                                                                                 );
00949 
00950                                                                         m_timLogger.leave("KF:8.update stage:2.FULLKF:iter.update xkk");
00951                                                                 }
00952 
00953                                                                 // Update the covariance just at the end
00954                                                                 //  of iterations if we are in IKF, always in normal EKF.
00955                                                                 if (IKF_iteration == (nKF_iterations-1) )
00956                                                                 {
00957                                                                         m_timLogger.enter("KF:8.update stage:3.FULLKF:update Pkk");
00958 
00959                                                                         // Use the full K matrix to update the covariance:
00960                                                                         //m_pkk = (I - K*dh_dx ) * m_pkk;
00961                                                                         // TODO: "Optimize this: sparsity!"
00962 
00963                                                                         // K * dh_dx_full
00964                                                                         aux_K_dh_dx.multiply(K,dh_dx_full_obs);
00965 
00966                                                                         // aux_K_dh_dx  <-- I-aux_K_dh_dx
00967                                                                         const size_t stat_len = aux_K_dh_dx.getColCount();
00968                                                                         for (size_t r=0;r<stat_len;r++)
00969                                                                                 for (size_t c=0;c<stat_len;c++)
00970                                                                                         if (r==c)
00971                                                                                              aux_K_dh_dx.get_unsafe(r,c)=-aux_K_dh_dx.get_unsafe(r,c) + kftype(1);
00972                                                                                         else aux_K_dh_dx.get_unsafe(r,c)=-aux_K_dh_dx.get_unsafe(r,c);
00973 
00974                                                                         m_pkk.multiply_result_is_symmetric(aux_K_dh_dx, m_pkk );
00975 
00976                                                                         m_timLogger.leave("KF:8.update stage:3.FULLKF:update Pkk");
00977                                                                 }
00978                                                         } // end for each IKF iteration
00979                                                 }
00980                                         }
00981                                         break;
00982 
00983                                         // --------------------------------------------------------------------
00984                                         // - EKF 'a la' Davison: One observation element at once
00985                                         // --------------------------------------------------------------------
00986                                         case kfEKFAlaDavison:
00987                                         {
00988                                                 // For each observed landmark/whole system state:
00989                                                 for (size_t obsIdx=0;obsIdx<Z.size();obsIdx++)
00990                                                 {
00991                                                         // Known & mapped landmark?
00992                                                         bool   doit;
00993                                                         size_t idxInTheFilter=0;
00994 
00995                                                         if (data_association.empty())
00996                                                         {
00997                                                                 doit = true;
00998                                                         }
00999                                                         else
01000                                                         {
01001                                                                 doit = data_association[obsIdx] >= 0;
01002                                                                 if (doit)
01003                                                                         idxInTheFilter = data_association[obsIdx];
01004                                                         }
01005 
01006                                                         if ( doit )
01007                                                         {
01008                                                                 m_timLogger.enter("KF:8.update stage:1.ScalarAtOnce.prepare");
01009 
01010                                                                 // Already mapped: OK
01011                                                                 const size_t idx_off = VEH_SIZE + idxInTheFilter*FEAT_SIZE; // The offset in m_xkk & Pkk.
01012 
01013                                                                 // Compute just the part of the Jacobian that we need using the current updated m_xkk:
01014                                                                 vector_KFArray_OBS  pred_obs;
01015                                                                 OnObservationModel( vector_size_t(1,idxInTheFilter),pred_obs);
01016                                                                 ASSERTDEB_(pred_obs.size()==1);
01017 
01018                                                                 // ytilde = observation - prediction
01019                                                                 KFArray_OBS ytilde = Z[obsIdx];
01020                                                                 OnSubstractObservationVectors(ytilde, pred_obs[0]);
01021 
01022                                                                 // Jacobians:
01023                                                                 // dh_dx: already is (N_pred*OBS_SIZE) x (VEH_SIZE + FEAT_SIZE * N_pred )
01024                                                                 //         with N_pred = |predictLMidxs|
01025 
01026                                                                 KFMatrix_OxV Hx(UNINITIALIZED_MATRIX);
01027                                                                 KFMatrix_OxF Hy(UNINITIALIZED_MATRIX);
01028                                                                 const size_t i_idx_in_preds = mrpt::utils::find_in_vector(idxInTheFilter,predictLMidxs);
01029                                                                 ASSERTMSG_(i_idx_in_preds!=string::npos, "OnPreComputingPredictions() didn't recommend the prediction of a landmark which has been actually observed!")
01030                                                                 dh_dx.extractMatrix(i_idx_in_preds*OBS_SIZE,0, Hx);
01031                                                                 dh_dx.extractMatrix(i_idx_in_preds*OBS_SIZE,VEH_SIZE+i_idx_in_preds*OBS_SIZE, Hy);
01032 
01033 
01034                                                                 m_timLogger.leave("KF:8.update stage:1.ScalarAtOnce.prepare");
01035 
01036                                                                 // For each component of the observation:
01037                                                                 for (size_t j=0;j<OBS_SIZE;j++)
01038                                                                 {
01039                                                                         m_timLogger.enter("KF:8.update stage:2.ScalarAtOnce.update");
01040 
01041                                                                         // Compute the scalar S_i for each component j of the observation:
01042                                                                         // Sij = dhij_dxv Pxx dhij_dxv^t + 2 * dhij_dyi Pyix dhij_dxv + dhij_dyi Pyiyi dhij_dyi^t + R
01043                                                                         //          ^^
01044                                                                         //         Hx:(O*LxSv)
01045                                                                         //       \----------------------/ \--------------------------/  \------------------------/ \-/
01046                                                                         //               TERM 1                   TERM 2                        TERM 3              R
01047                                                                         //
01048                                                                         // O: Observation size (3)
01049                                                                         // L: # landmarks
01050                                                                         // Sv: Vehicle state size (6)
01051                                                                         //
01052 
01053                 #if defined(_DEBUG)
01054                                                                         {
01055                                                                                 // This algorithm is applicable only if the scalar component of the sensor noise are INDEPENDENT:
01056                                                                                 for (size_t a=0;a<OBS_SIZE;a++)
01057                                                                                         for (size_t b=0;b<OBS_SIZE;b++)
01058                                                                                                 if ( a!=b )
01059                                                                                                         if (R(a,b)!=0)
01060                                                                                                                 THROW_EXCEPTION("This KF algorithm assumes independent noise components in the observation (matrix R). Select another KF algorithm.")
01061                                                                         }
01062                 #endif
01063                                                                         // R:
01064                                                                         KFTYPE Sij = R.get_unsafe(j,j);
01065 
01066                                                                         // TERM 1:
01067                                                                         for (size_t k=0;k<VEH_SIZE;k++)
01068                                                                         {
01069                                                                                 KFTYPE accum = 0;
01070                                                                                 for (size_t q=0;q<VEH_SIZE;q++)
01071                                                                                         accum += Hx.get_unsafe(j,q) * m_pkk.get_unsafe(q,k);
01072                                                                                 Sij+= Hx.get_unsafe(j,k) * accum;
01073                                                                         }
01074 
01075                                                                         // TERM 2:
01076                                                                         KFTYPE term2=0;
01077                                                                         for (size_t k=0;k<VEH_SIZE;k++)
01078                                                                         {
01079                                                                                 KFTYPE accum = 0;
01080                                                                                 for (size_t q=0;q<FEAT_SIZE;q++)
01081                                                                                         accum += Hy.get_unsafe(j,q) * m_pkk.get_unsafe(idx_off+q,k);
01082                                                                                 term2+= Hx.get_unsafe(j,k) * accum;
01083                                                                         }
01084                                                                         Sij += 2 * term2;
01085 
01086                                                                         // TERM 3:
01087                                                                         for (size_t k=0;k<FEAT_SIZE;k++)
01088                                                                         {
01089                                                                                 KFTYPE accum = 0;
01090                                                                                 for (size_t q=0;q<FEAT_SIZE;q++)
01091                                                                                         accum += Hy.get_unsafe(j,q) * m_pkk.get_unsafe(idx_off+q,idx_off+k);
01092                                                                                 Sij+= Hy.get_unsafe(j,k) * accum;
01093                                                                         }
01094 
01095                                                                         // Compute the Kalman gain "Kij" for this observation element:
01096                                                                         // -->  K = m_pkk * (~dh_dx) * S.inv() );
01097                                                                         size_t N = m_pkk.getColCount();
01098                                                                         vector<KFTYPE> Kij( N );
01099 
01100                                                                         for (size_t k=0;k<N;k++)
01101                                                                         {
01102                                                                                 KFTYPE K_tmp = 0;
01103 
01104                                                                                 // dhi_dxv term
01105                                                                                 size_t q;
01106                                                                                 for (q=0;q<VEH_SIZE;q++)
01107                                                                                         K_tmp+= m_pkk.get_unsafe(k,q) * Hx.get_unsafe(j,q);
01108 
01109                                                                                 // dhi_dyi term
01110                                                                                 for (q=0;q<FEAT_SIZE;q++)
01111                                                                                         K_tmp+= m_pkk.get_unsafe(k,idx_off+q) * Hy.get_unsafe(j,q);
01112 
01113                                                                                 Kij[k] = K_tmp / Sij;
01114                                                                         } // end for k
01115 
01116 
01117                                                                         // Update the state vector m_xkk:
01118                                                                         //  x' = x + Kij * ytilde(ij)
01119                                                                         for (size_t k=0;k<N;k++)
01120                                                                                 m_xkk[k] += Kij[k] * ytilde[j];
01121 
01122                                                                         // Update the covariance Pkk:
01123                                                                         // P' =  P - Kij * Sij * Kij^t
01124                                                                         {
01125                                                                                 for (size_t k=0;k<N;k++)
01126                                                                                 {
01127                                                                                         for (size_t q=k;q<N;q++)  // Half matrix
01128                                                                                         {
01129                                                                                                 m_pkk(k,q) -= Sij * Kij[k] * Kij[q];
01130                                                                                                 // It is symmetric
01131                                                                                                 m_pkk(q,k) = m_pkk(k,q);
01132                                                                                         }
01133 
01134                 #if defined(_DEBUG) || (MRPT_ALWAYS_CHECKS_DEBUG)
01135                                                                                         if (m_pkk(k,k)<0)
01136                                                                                         {
01137                                                                                                 m_pkk.saveToTextFile("Pkk_err.txt");
01138                                                                                                 mrpt::system::vectorToTextFile(Kij,"Kij.txt");
01139                                                                                                 ASSERT_(m_pkk(k,k)>0)
01140                                                                                         }
01141                 #endif
01142                                                                                 }
01143                                                                         }
01144 
01145 
01146                                                                         m_timLogger.leave("KF:8.update stage:2.ScalarAtOnce.update");
01147                                                                 } // end for j
01148                                                         } // end if mapped
01149                                                 } // end for each observed LM.
01150                                         }
01151                                         break;
01152 
01153                                         // --------------------------------------------------------------------
01154                                         // - IKF method, processing each observation scalar secuentially:
01155                                         // --------------------------------------------------------------------
01156                                         case kfIKF:  // TODO !!
01157                                         {
01158                                                 THROW_EXCEPTION("IKF scalar by scalar not implemented yet.");
01159 #if 0
01160                                                 KFMatrix h,Hx,Hy;
01161 
01162                                                 // Just one, or several update iterations??
01163                                                 size_t nKF_iterations = KF_options.IKF_iterations;
01164 
01165                                                 // To avoid wasting time, if we are doing 1 iteration only, do not reserve memory for this matrix:
01166                                                 KFMatrix                *saved_Pkk=NULL;
01167                                                 if (nKF_iterations>1)
01168                                                 {
01169                                                         // Create a copy of Pkk for later restoring it at the beginning of each iteration:
01170                                                         saved_Pkk = new KFMatrix( m_pkk );
01171                                                 }
01172 
01173                                                 KFVector        xkk_0 = m_xkk; // First state vector at the beginning of IKF
01174                                                 KFVector        xkk_next_iter = m_xkk;  // for IKF only
01175 
01176                                                 // For each IKF iteration (or 1 for EKF)
01177                                                 for (size_t IKF_iteration=0;IKF_iteration<nKF_iterations;IKF_iteration++)
01178                                                 {
01179                                                         // Restore initial covariance matrix.
01180                                                         // The updated mean is stored in m_xkk and is improved with each estimation,
01181                                                         //  and so do the Jacobians which use that improving mean.
01182                                                         if (IKF_iteration>0)
01183                                                         {
01184                                                                 m_pkk = *saved_Pkk;
01185                                                                 xkk_next_iter = xkk_0;
01186                                                         }
01187 
01188                                                         // For each observed landmark/whole system state:
01189                                                         for (size_t obsIdx=0;obsIdx<Z.getRowCount();obsIdx++)
01190                                                         {
01191                                                                 // Known & mapped landmark?
01192                                                                 bool   doit;
01193                                                                 size_t idxInTheFilter=0;
01194 
01195                                                                 if (data_association.empty())
01196                                                                 {
01197                                                                         doit = true;
01198                                                                 }
01199                                                                 else
01200                                                                 {
01201                                                                         doit = data_association[obsIdx] >= 0;
01202                                                                         if (doit)
01203                                                                                 idxInTheFilter = data_association[obsIdx];
01204                                                                 }
01205 
01206                                                                 if ( doit )
01207                                                                 {
01208                                                                         // Already mapped: OK
01209                                                                         const size_t idx_off      = VEH_SIZE + idxInTheFilter *FEAT_SIZE; // The offset in m_xkk & Pkk.
01210                                                                         const size_t R_row_offset = obsIdx*OBS_SIZE;  // Offset row in R
01211 
01212                                                                         // Compute just the part of the Jacobian that we need using the current updated m_xkk:
01213                                                                         KFVector   ytilde; // Diff. observation - prediction
01214                                                                         OnObservationModelAndJacobians(
01215                                                                                 Z,
01216                                                                                 data_association,
01217                                                                                 false,                  // Only a partial Jacobian
01218                                                                                 (int)obsIdx,    // Which row from Z
01219                                                                                 ytilde,
01220                                                                                 Hx,
01221                                                                                 Hy );
01222 
01223                                                                         ASSERTDEB_(ytilde.size() == OBS_SIZE )
01224                                                                         ASSERTDEB_(Hx.getRowCount() == OBS_SIZE )
01225                                                                         ASSERTDEB_(Hx.getColCount() == VEH_SIZE )
01226 
01227                                                                         if (FEAT_SIZE>0)
01228                                                                         {
01229                                                                                 ASSERTDEB_(Hy.getRowCount() == OBS_SIZE )
01230                                                                                 ASSERTDEB_(Hy.getColCount() == FEAT_SIZE )
01231                                                                         }
01232 
01233                                                                         // Compute the OxO matrix S_i for each observation:
01234                                                                         // Si  = TERM1 + TERM2 + TERM2^t + TERM3 + R
01235                                                                         //
01236                                                                         // TERM1: dhi_dxv Pxx dhi_dxv^t
01237                                                                         //          ^^
01238                                                                         //         Hx:(OxV)
01239                                                                         //
01240                                                                         // TERM2: dhi_dyi Pyix dhi_dxv
01241                                                                         //          ^^
01242                                                                         //         Hy:(OxF)
01243                                                                         //
01244                                                                         // TERM3: dhi_dyi Pyiyi dhi_dyi^t
01245                                                                         //
01246                                                                         // i: obsIdx
01247                                                                         // O: Observation size
01248                                                                         // F: Feature size
01249                                                                         // V: Vehicle state size
01250                                                                         //
01251 
01252                                                                         // R:
01253                                                                         KFMatrix Si(OBS_SIZE,OBS_SIZE);
01254                                                                         R.extractMatrix(R_row_offset,0, Si);
01255 
01256                                                                         size_t k;
01257                                                                         KFMatrix        term(OBS_SIZE,OBS_SIZE);
01258 
01259                                                                         // TERM1: dhi_dxv Pxx dhi_dxv^t
01260                                                                         Hx.multiply_HCHt(
01261                                                                                 m_pkk,          // The cov. matrix
01262                                                                                 Si,                     // The output
01263                                                                                 true,           // Yes, submatrix of m_pkk only
01264                                                                                 0,                      // Offset in m_pkk
01265                                                                                 true            // Yes, accum results in output.
01266                                                                         );
01267 
01268                                                                         // TERM2: dhi_dyi Pyix dhi_dxv
01269                                                                         //  + its transpose:
01270                                                                         KFMatrix        Pyix( FEAT_SIZE, VEH_SIZE );
01271                                                                         m_pkk.extractMatrix(idx_off,0, Pyix);  // Extract cross cov. to Pyix
01272 
01273                                                                         term.multiply_ABCt( Hy, Pyix, Hx ); //term = Hy * Pyix * ~Hx;
01274                                                                         Si.add_AAt( term );  // Si += A + ~A
01275 
01276                                                                         // TERM3: dhi_dyi Pyiyi dhi_dyi^t
01277                                                                         Hy.multiply_HCHt(
01278                                                                                 m_pkk,          // The cov. matrix
01279                                                                                 Si,                     // The output
01280                                                                                 true,           // Yes, submatrix of m_pkk only
01281                                                                                 idx_off,    // Offset in m_pkk
01282                                                                                 true            // Yes, accum results in output.
01283                                                                         );
01284 
01285                                                                         // Compute the inverse of Si:
01286                                                                         KFMatrix Si_1(OBS_SIZE,OBS_SIZE);
01287 
01288                                                                         // Compute the Kalman gain "Ki" for this i'th observation:
01289                                                                         // -->  Ki = m_pkk * (~dh_dx) * S.inv();
01290                                                                         size_t N = m_pkk.getColCount();
01291 
01292                                                                         KFMatrix Ki( N, OBS_SIZE );
01293 
01294                                                                         for (k=0;k<N;k++)  // for each row of K
01295                                                                         {
01296                                                                                 size_t q;
01297 
01298                                                                                 for (size_t c=0;c<OBS_SIZE;c++)  // for each column of K
01299                                                                                 {
01300                                                                                         KFTYPE  K_tmp = 0;
01301 
01302                                                                                         // dhi_dxv term
01303                                                                                         for (q=0;q<VEH_SIZE;q++)
01304                                                                                                 K_tmp+= m_pkk.get_unsafe(k,q) * Hx.get_unsafe(c,q);
01305 
01306                                                                                         // dhi_dyi term
01307                                                                                         for (q=0;q<FEAT_SIZE;q++)
01308                                                                                                 K_tmp+= m_pkk.get_unsafe(k,idx_off+q) * Hy.get_unsafe(c,q);
01309 
01310                                                                                         Ki.set_unsafe(k,c, K_tmp);
01311                                                                                 } // end for c
01312                                                                         } // end for k
01313 
01314                                                                         Ki.multiply(Ki, Si.inv() );  // K = (...) * S^-1
01315 
01316 
01317                                                                         // Update the state vector m_xkk:
01318                                                                         if (nKF_iterations==1)
01319                                                                         {
01320                                                                                 // EKF:
01321                                                                                 //  x' = x + Kij * ytilde(ij)
01322                                                                                 for (k=0;k<N;k++)
01323                                                                                         for (size_t q=0;q<OBS_SIZE;q++)
01324                                                                                                 m_xkk[k] += Ki.get_unsafe(k,q) * ytilde[q];
01325                                                                         }
01326                                                                         else
01327                                                                         {
01328                                                                                 // IKF:
01329                                                                                 mrpt::dynamicsize_vector<KFTYPE> HAx(OBS_SIZE);
01330                                                                                 size_t o,q;
01331                                                                                 // HAx = H*(x0-xi)
01332                                                                                 for (o=0;o<OBS_SIZE;o++)
01333                                                                                 {
01334                                                                                         KFTYPE  tmp = 0;
01335                                                                                         for (q=0;q<VEH_SIZE;q++)
01336                                                                                                 tmp += Hx.get_unsafe(o,q) * (xkk_0[q] - m_xkk[q]);
01337 
01338                                                                                         for (q=0;q<FEAT_SIZE;q++)
01339                                                                                                 tmp += Hy.get_unsafe(o,q) * (xkk_0[idx_off+q] - m_xkk[idx_off+q]);
01340 
01341                                                                                         HAx[o] = tmp;
01342                                                                                 }
01343 
01344                                                                                 // Compute only once (ytilde[j] - HAx)
01345                                                                                 for (o=0;o<OBS_SIZE;o++)
01346                                                                                         HAx[o] = ytilde[o] - HAx[o];
01347 
01348                                                                                 //  x' = x_0 + Kij * ( ytilde(ij) - H*(x0-xi))   -> xi: i=this iteration
01349                                                                                 //  xkk_next_iter is initialized to xkk_0 at each IKF iteration.
01350                                                                                 for (k=0;k<N;k++)
01351                                                                                 {
01352                                                                                         KFTYPE   tmp = xkk_next_iter[k];
01353                                                                                         //m_xkk[k] = xkk_0[k] + Kij[k] * (ytilde[j] - HAx );
01354                                                                                         for (o=0;o<OBS_SIZE;o++)
01355                                                                                                 tmp += Ki.get_unsafe(k,o) * HAx[o];
01356 
01357                                                                                         xkk_next_iter[k] = tmp;
01358                                                                                 }
01359                                                                         }
01360 
01361                                                                         // Update the covariance Pkk:
01362                                                                         // P' =  P - Kij * Sij * Kij^t
01363                                                                         //if (IKF_iteration==(nKF_iterations-1))   // Just for the last IKF iteration
01364                                                                         {
01365                                                                                 // m_pkk -= Ki * Si * ~Ki;
01366                                                                                 Ki.multiplyByMatrixAndByTransposeNonSymmetric(
01367                                                                                         Si,
01368                                                                                         m_pkk,   // Output
01369                                                                                         true,    // Accumulate
01370                                                                                         true);   // Substract instead of sum.
01371 
01372                                                                                 m_pkk.force_symmetry();
01373 
01374                         /*                                                      for (k=0;k<N;k++)
01375                                                                                 {
01376                                                                                         for (size_t q=k;q<N;q++)  // Half matrix
01377                                                                                         {
01378                                                                                                 m_pkk(k,q) -= Sij * Kij[k] * Kij[q];
01379                                                                                                 // It is symmetric
01380                                                                                                 m_pkk(q,k) = m_pkk(k,q);
01381                                                                                         }
01382 
01383                         #if defined(_DEBUG) || (MRPT_ALWAYS_CHECKS_DEBUG)
01384                                                                                         if (m_pkk(k,k)<0)
01385                                                                                         {
01386                                                                                                 m_pkk.saveToTextFile("Pkk_err.txt");
01387                                                                                                 mrpt::system::vectorToTextFile(Kij,"Kij.txt");
01388                                                                                                 ASSERT_(m_pkk(k,k)>0)
01389                                                                                         }
01390                         #endif
01391                                                                                 }
01392                                                                                 */
01393                                                                         }
01394 
01395                                                                 } // end if doit
01396 
01397                                                         } // end for each observed LM.
01398 
01399                                                         // Now, save the new iterated mean:
01400                                                         if (nKF_iterations>1)
01401                                                         {
01402                         #if 0
01403                                                                 cout << "IKF iter: " << IKF_iteration << " -> " << xkk_next_iter << endl;
01404                         #endif
01405                                                                 m_xkk = xkk_next_iter;
01406                                                         }
01407 
01408                                                 } // end for each IKF_iteration
01409 
01410                                                 // Copy of pkk not required anymore:
01411                                                 if (saved_Pkk) delete saved_Pkk;
01412 
01413 #endif
01414                                                 }
01415                                                 break;
01416 
01417                                                 default:
01418                                                         THROW_EXCEPTION("Invalid value of options.KF_method");
01419                                                 } // end switch method
01420 
01421                                         }
01422 
01423                                         const double tim_update = m_timLogger.leave("KF:8.update stage");
01424 
01425                                         OnNormalizeStateVector();
01426 
01427                                         // =============================================================
01428                                         //  8. INTRODUCE NEW LANDMARKS
01429                                         // =============================================================
01430                                         if (!data_association.empty())
01431                                         {
01432                                                 detail::CRunOneKalmanIteration_addNewLandmarks()(*this, Z, data_association,R);
01433                                         } // end if data_association!=empty
01434 
01435                                         if (KF_options.verbose)
01436                                         {
01437                                                 printf_debug("[KF] %u LMs | Pr: %.2fms | Pr.Obs: %.2fms | Obs.DA: %.2fms | Upd: %.2fms\n",
01438                                                         static_cast<unsigned int>(getNumberOfLandmarksInTheMap()),
01439                                                         1e3*tim_pred,
01440                                                         1e3*tim_pred_obs,
01441                                                         1e3*tim_obs_DA,
01442                                                         1e3*tim_update
01443                                                         );
01444                                         }
01445 
01446                                         // Post iteration user code:
01447                                         OnPostIteration();
01448 
01449                                         m_timLogger.leave("KF:complete_step");
01450 
01451                                         MRPT_END
01452 
01453                                 } // End of "runOneKalmanIteration"
01454 
01455 
01456 
01457                 private:
01458                         mutable bool m_user_didnt_implement_jacobian;
01459 
01460                         /** Auxiliary functions for Jacobian numeric estimation */
01461                         static void KF_aux_estimate_trans_jacobian(
01462                                 const KFArray_VEH &x,
01463                                 const std::pair<KFCLASS*,KFArray_ACT> &dat,
01464                                 KFArray_VEH &out_x)
01465                         {
01466                                 bool dumm;
01467                                 out_x=x;
01468                                 dat.first->OnTransitionModel(dat.second,out_x, dumm);
01469                         }
01470                         static void KF_aux_estimate_obs_Hx_jacobian(
01471                                 const KFArray_VEH &x,
01472                                 const std::pair<KFCLASS*,size_t> &dat,
01473                                 KFArray_OBS &out_x)
01474                         {
01475                                 vector_size_t  idxs_to_predict(1,dat.second);
01476                                 vector_KFArray_OBS prediction;
01477                                 // Overwrite (temporarily!) the affected part of the state vector:
01478                                 ::memcpy(&dat.first->m_xkk[0],&x[0],sizeof(x[0])*VEH_SIZE);
01479                                 dat.first->OnObservationModel(idxs_to_predict,prediction);
01480                                 ASSERTDEB_(prediction.size()==1);
01481                                 out_x=prediction[0];
01482                         }
01483                         static void KF_aux_estimate_obs_Hy_jacobian(
01484                                 const KFArray_FEAT &x,
01485                                 const std::pair<KFCLASS*,size_t> &dat,
01486                                 KFArray_OBS &out_x)
01487                         {
01488                                 vector_size_t  idxs_to_predict(1,dat.second);
01489                                 vector_KFArray_OBS  prediction;
01490                                 const size_t lm_idx_in_statevector = VEH_SIZE+FEAT_SIZE*dat.second;
01491                                 // Overwrite (temporarily!) the affected part of the state vector:
01492                                 ::memcpy(&dat.first->m_xkk[lm_idx_in_statevector],&x[0],sizeof(x[0])*FEAT_SIZE);
01493                                 dat.first->OnObservationModel(idxs_to_predict,prediction);
01494                                 ASSERTDEB_(prediction.size()==1);
01495                                 out_x=prediction[0];
01496                         }
01497 
01498 
01499                         friend struct detail::CRunOneKalmanIteration_addNewLandmarks;
01500 
01501                 }; // end class
01502 
01503                 namespace detail
01504                 {
01505                         // generic version for SLAM. There is a speciation below for NON-SLAM problems.
01506                         struct CRunOneKalmanIteration_addNewLandmarks {
01507                                 template <size_t VEH_SIZE, size_t OBS_SIZE, size_t FEAT_SIZE, size_t ACT_SIZE, typename KFTYPE>
01508                                 void operator()(
01509                                         CKalmanFilterCapable<VEH_SIZE,OBS_SIZE,FEAT_SIZE,ACT_SIZE,KFTYPE> &obj,
01510                                         const typename CKalmanFilterCapable<VEH_SIZE,OBS_SIZE,FEAT_SIZE,ACT_SIZE,KFTYPE>::vector_KFArray_OBS & Z,
01511                                         const vector_int       &data_association,
01512                                         const typename CKalmanFilterCapable<VEH_SIZE,OBS_SIZE,FEAT_SIZE,ACT_SIZE,KFTYPE>::KFMatrix_OxO          &R
01513                                         )
01514                                 {
01515                                         typedef CKalmanFilterCapable<VEH_SIZE,OBS_SIZE,FEAT_SIZE,ACT_SIZE,KFTYPE> KF;
01516 
01517                                         for (size_t idxObs=0;idxObs<Z.size();idxObs++)
01518                                         {
01519                                                 // Is already in the map?
01520                                                 if ( data_association[idxObs] < 0 )  // Not in the map yet!
01521                                                 {
01522                                                         obj.m_timLogger.enter("KF:9.create new LMs");
01523                                                         // Add it:
01524 
01525                                                         // Append to map of IDs <-> position in the state vector:
01526                                                         ASSERTDEB_(FEAT_SIZE>0)
01527                                                         ASSERTDEB_( 0 == ((obj.m_xkk.size() - VEH_SIZE) % FEAT_SIZE) ) // Sanity test
01528 
01529                                                         const size_t  newIndexInMap = (obj.m_xkk.size() - VEH_SIZE) / FEAT_SIZE;
01530 
01531                                                         // Inverse sensor model:
01532                                                         typename KF::KFArray_FEAT yn;
01533                                                         typename KF::KFMatrix_FxV dyn_dxv;
01534                                                         typename KF::KFMatrix_FxO dyn_dhn;
01535                                                         typename KF::KFMatrix_FxF dyn_dhn_R_dyn_dhnT;
01536                                                         bool use_dyn_dhn_jacobian=true;
01537 
01538                                                         // Compute the inv. sensor model and its Jacobians:
01539                                                         obj.OnInverseObservationModel(
01540                                                                 Z[idxObs],
01541                                                                 yn,
01542                                                                 dyn_dxv,
01543                                                                 dyn_dhn,
01544                                                                 dyn_dhn_R_dyn_dhnT,
01545                                                                 use_dyn_dhn_jacobian );
01546 
01547                                                         // And let the application do any special handling of adding a new feature to the map:
01548                                                         obj.OnNewLandmarkAddedToMap(
01549                                                                 idxObs,
01550                                                                 newIndexInMap
01551                                                                 );
01552 
01553                                                         ASSERTDEB_( yn.size() == FEAT_SIZE )
01554 
01555                                                         // Append to m_xkk:
01556                                                         size_t q;
01557                                                         size_t idx = obj.m_xkk.size();
01558                                                         obj.m_xkk.resize( obj.m_xkk.size() + FEAT_SIZE );
01559 
01560                                                         for (q=0;q<FEAT_SIZE;q++)
01561                                                                 obj.m_xkk[idx+q] = yn[q];
01562 
01563                                                         // --------------------
01564                                                         // Append to Pkk:
01565                                                         // --------------------
01566                                                         ASSERTDEB_( obj.m_pkk.getColCount()==idx && obj.m_pkk.getRowCount()==idx );
01567 
01568                                                         obj.m_pkk.setSize( idx+FEAT_SIZE,idx+FEAT_SIZE );
01569 
01570                                                         // Fill the Pxyn term:
01571                                                         // --------------------
01572                                                         typename KF::KFMatrix_VxV Pxx;
01573                                                         obj.m_pkk.extractMatrix(0,0,Pxx);
01574                                                         typename KF::KFMatrix_FxV Pxyn; // Pxyn = dyn_dxv * Pxx
01575                                                         Pxyn.multiply( dyn_dxv, Pxx );
01576 
01577                                                         obj.m_pkk.insertMatrix( idx,0, Pxyn );
01578                                                         obj.m_pkk.insertMatrixTranspose( 0,idx, Pxyn );
01579 
01580                                                         // Fill the Pyiyn terms:
01581                                                         // --------------------
01582                                                         const size_t nLMs = (idx-VEH_SIZE)/FEAT_SIZE; // Number of previous landmarks:
01583                                                         for (q=0;q<nLMs;q++)
01584                                                         {
01585                                                                 typename KF::KFMatrix_VxF  P_x_yq(UNINITIALIZED_MATRIX);
01586                                                                 obj.m_pkk.extractMatrix(0,VEH_SIZE+q*FEAT_SIZE,P_x_yq) ;
01587 
01588                                                                 typename KF::KFMatrix_FxF P_cross(UNINITIALIZED_MATRIX);
01589                                                                 P_cross.multiply(dyn_dxv, P_x_yq );
01590 
01591                                                                 obj.m_pkk.insertMatrix(idx,VEH_SIZE+q*FEAT_SIZE, P_cross );
01592                                                                 obj.m_pkk.insertMatrixTranspose(VEH_SIZE+q*FEAT_SIZE,idx, P_cross );
01593                                                         } // end each previous LM(q)
01594 
01595                                                         // Fill the Pynyn term:
01596                                                         //  P_yn_yn =  (dyn_dxv * Pxx * ~dyn_dxv) + (dyn_dhn * R * ~dyn_dhn);
01597                                                         // --------------------
01598                                                         typename KF::KFMatrix_FxF P_yn_yn(UNINITIALIZED_MATRIX);
01599                                                         dyn_dxv.multiply_HCHt(Pxx,  P_yn_yn);
01600                                                         if (use_dyn_dhn_jacobian)
01601                                                                         dyn_dhn.multiply_HCHt(R, P_yn_yn, true); // Accumulate in P_yn_yn
01602                                                         else    P_yn_yn+=dyn_dhn_R_dyn_dhnT;
01603 
01604                                                         obj.m_pkk.insertMatrix(idx,idx, P_yn_yn );
01605 
01606                                                         obj.m_timLogger.leave("KF:9.create new LMs");
01607                                                 }
01608                                         }
01609                                 }
01610 
01611                                 template <size_t VEH_SIZE, size_t OBS_SIZE, size_t ACT_SIZE, typename KFTYPE>
01612                                 void operator()(
01613                                         CKalmanFilterCapable<VEH_SIZE,OBS_SIZE,0 /* FEAT_SIZE=0 */,ACT_SIZE,KFTYPE> &obj,
01614                                         const typename CKalmanFilterCapable<VEH_SIZE,OBS_SIZE,0 /* FEAT_SIZE=0 */,ACT_SIZE,KFTYPE>::vector_KFArray_OBS & Z,
01615                                         const vector_int       &data_association,
01616                                         const typename CKalmanFilterCapable<VEH_SIZE,OBS_SIZE,0 /* FEAT_SIZE=0 */,ACT_SIZE,KFTYPE>::KFMatrix_OxO                &R
01617                                 )
01618                                 {
01619                                         // Do nothing: this is NOT a SLAM problem.
01620                                 }
01621 
01622                         }; // end runOneKalmanIteration_addNewLandmarks
01623 
01624                         template <size_t VEH_SIZE, size_t OBS_SIZE, size_t FEAT_SIZE, size_t ACT_SIZE, typename KFTYPE>
01625                         inline size_t getNumberOfLandmarksInMap(const CKalmanFilterCapable<VEH_SIZE,OBS_SIZE,FEAT_SIZE,ACT_SIZE,KFTYPE> &obj)
01626                         {
01627                                 return (obj.getStateVectorLength()-VEH_SIZE)/FEAT_SIZE;
01628                         }
01629                         // Specialization for FEAT_SIZE=0
01630                         template <size_t VEH_SIZE, size_t OBS_SIZE, size_t ACT_SIZE, typename KFTYPE>
01631                         inline size_t getNumberOfLandmarksInMap(const CKalmanFilterCapable<VEH_SIZE,OBS_SIZE,0 /*FEAT_SIZE*/,ACT_SIZE,KFTYPE> &obj)
01632                         {
01633                                 return 0;
01634                         }
01635 
01636                         template <size_t VEH_SIZE, size_t OBS_SIZE, size_t FEAT_SIZE, size_t ACT_SIZE, typename KFTYPE>
01637                         inline bool isMapEmpty(const CKalmanFilterCapable<VEH_SIZE,OBS_SIZE,FEAT_SIZE,ACT_SIZE,KFTYPE> &obj)
01638                         {
01639                                 return (obj.getStateVectorLength()==VEH_SIZE);
01640                         }
01641                         // Specialization for FEAT_SIZE=0
01642                         template <size_t VEH_SIZE, size_t OBS_SIZE, size_t ACT_SIZE, typename KFTYPE>
01643                         inline bool isMapEmpty (const CKalmanFilterCapable<VEH_SIZE,OBS_SIZE,0 /*FEAT_SIZE*/,ACT_SIZE,KFTYPE> &obj)
01644                         {
01645                                 return true;
01646                         }
01647 
01648                 } // end namespace "detail"
01649 
01650         } // end namespace
01651 
01652         // Specializations MUST occur at the same namespace:
01653         namespace utils
01654         {
01655                 template <>
01656                 struct TEnumTypeFiller<bayes::TKFMethod>
01657                 {
01658                         typedef bayes::TKFMethod enum_t;
01659                         static void fill(bimap<enum_t,std::string>  &m_map)
01660                         {
01661                                 m_map.insert(bayes::kfEKFNaive,          "kfEKFNaive");
01662                                 m_map.insert(bayes::kfEKFAlaDavison,     "kfEKFAlaDavison");
01663                                 m_map.insert(bayes::kfIKFFull,           "kfIKFFull");
01664                                 m_map.insert(bayes::kfIKF,               "kfIKF");
01665                         }
01666                 };
01667         } // End of namespace
01668 
01669 } // end namespace
01670 
01671 #endif



Page generated by Doxygen 1.7.5 for MRPT 0.9.5 SVN: at Thu Oct 13 21:25:36 UTC 2011