Main MRPT website > C++ reference
MRPT logo
CRangeBearingKFSLAM.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 CRangeBearingKFSLAM_H
00029 #define CRangeBearingKFSLAM_H
00030 
00031 #include <mrpt/utils/CDebugOutputCapable.h>
00032 #include <mrpt/math/CMatrixTemplateNumeric.h>
00033 #include <mrpt/math/CVectorTemplate.h>
00034 #include <mrpt/utils/CConfigFileBase.h>
00035 #include <mrpt/utils/CLoadableOptions.h>
00036 #include <mrpt/opengl.h>
00037 #include <mrpt/bayes/CKalmanFilterCapable.h>
00038 
00039 #include <mrpt/utils/safe_pointers.h>
00040 #include <mrpt/utils/bimap.h>
00041 
00042 #include <mrpt/slam/CSensoryFrame.h>
00043 #include <mrpt/slam/CActionCollection.h>
00044 #include <mrpt/slam/CObservationBearingRange.h>
00045 #include <mrpt/poses/CPoint3D.h>
00046 #include <mrpt/poses/CPose3DPDFGaussian.h>
00047 #include <mrpt/poses/CPose3DQuatPDFGaussian.h>
00048 #include <mrpt/slam/CLandmark.h>
00049 #include <mrpt/slam/CSimpleMap.h>
00050 #include <mrpt/slam/CIncrementalMapPartitioner.h>
00051 #include <mrpt/slam/data_association.h>
00052 
00053 #include <mrpt/slam/link_pragmas.h>
00054 
00055 namespace mrpt
00056 {
00057         namespace slam
00058         {
00059                 using namespace mrpt::bayes;
00060 
00061                 /** An implementation of EKF-based SLAM with range-bearing sensors, odometry, a full 6D robot pose, and 3D landmarks.
00062                   *  The main method is "processActionObservation" which processes pairs of action/observation.
00063                   *  The state vector comprises: 3D robot position, a quaternion for its attitude, and the 3D landmarks in the map.
00064                   *
00065                   *   The following Wiki page describes an front-end application based on this class:
00066                   *     http://www.mrpt.org/Application:kf-slam
00067                   *
00068                   *  For the theory behind this implementation, see the technical report in:
00069                   *     http://www.mrpt.org/6D-SLAM
00070                   *
00071                   * \sa An implementation for 2D only: CRangeBearingKFSLAM2D
00072                   * \ingroup metric_slam_grp
00073                   */
00074                 class SLAM_IMPEXP  CRangeBearingKFSLAM :
00075                         public bayes::CKalmanFilterCapable<7 /* x y z  qr qx qy qz*/,3 /* range yaw pitch */, 3 /* x y z */,   7  /* Ax Ay Az Aqr Aqx Aqy Aqz */      >
00076                                                        // <size_t VEH_SIZE,            size_t OBS_SIZE,        size_t FEAT_SIZE, size_t ACT_SIZE, size typename kftype = double>
00077                 {
00078                  public:
00079                          /** Constructor.
00080                            */
00081                          CRangeBearingKFSLAM( );
00082 
00083                          /** Destructor:
00084                            */
00085                          virtual ~CRangeBearingKFSLAM();
00086 
00087                          void reset(); //!< Reset the state of the SLAM filter: The map is emptied and the robot put back to (0,0,0).
00088 
00089                         /** Process one new action and observations to update the map and robot pose estimate. See the description of the class at the top of this page.
00090                          *  \param action May contain odometry
00091                          *      \param SF The set of observations, must contain at least one CObservationBearingRange
00092                          */
00093                         void  processActionObservation(
00094                                 CActionCollectionPtr &action,
00095                                 CSensoryFramePtr     &SF );
00096 
00097                         /** Returns the complete mean and cov.
00098                           *  \param out_robotPose The mean and the 7x7 covariance matrix of the robot 6D pose
00099                           *  \param out_landmarksPositions One entry for each of the M landmark positions (3D).
00100                           *  \param out_landmarkIDs Each element[index] (for indices of out_landmarksPositions) gives the corresponding landmark ID.
00101                           *  \param out_fullState The complete state vector (7+3M).
00102                           *  \param out_fullCovariance The full (7+3M)x(7+3M) covariance matrix of the filter.
00103                           * \sa getCurrentRobotPose
00104                           */
00105                         void  getCurrentState(
00106                                 CPose3DQuatPDFGaussian &out_robotPose,
00107                                 std::vector<CPoint3D>  &out_landmarksPositions,
00108                                 std::map<unsigned int,CLandmark::TLandmarkID> &out_landmarkIDs,
00109                                 CVectorDouble      &out_fullState,
00110                                 CMatrixDouble      &out_fullCovariance
00111                                 ) const;
00112 
00113                         /** Returns the complete mean and cov.
00114                           *  \param out_robotPose The mean and the 7x7 covariance matrix of the robot 6D pose
00115                           *  \param out_landmarksPositions One entry for each of the M landmark positions (3D).
00116                           *  \param out_landmarkIDs Each element[index] (for indices of out_landmarksPositions) gives the corresponding landmark ID.
00117                           *  \param out_fullState The complete state vector (7+3M).
00118                           *  \param out_fullCovariance The full (7+3M)x(7+3M) covariance matrix of the filter.
00119                           * \sa getCurrentRobotPose
00120                           */
00121                         inline void  getCurrentState(
00122                                 CPose3DPDFGaussian &out_robotPose,
00123                                 std::vector<CPoint3D>  &out_landmarksPositions,
00124                                 std::map<unsigned int,CLandmark::TLandmarkID> &out_landmarkIDs,
00125                                 CVectorDouble      &out_fullState,
00126                                 CMatrixDouble      &out_fullCovariance
00127                                 ) const
00128                         {
00129                                 CPose3DQuatPDFGaussian q(UNINITIALIZED_QUATERNION);
00130                                 this->getCurrentState(q,out_landmarksPositions,out_landmarkIDs,out_fullState,out_fullCovariance);
00131                                 out_robotPose = CPose3DPDFGaussian(q);
00132                         }
00133 
00134                         /** Returns the mean & the 7x7 covariance matrix of the robot 6D pose (with rotation as a quaternion).
00135                           * \sa getCurrentState, getCurrentRobotPoseMean
00136                           */
00137                         void  getCurrentRobotPose( CPose3DQuatPDFGaussian &out_robotPose ) const;
00138 
00139                         /** Get the current robot pose mean, as a 3D+quaternion pose.
00140                           * \sa getCurrentRobotPose
00141                           */
00142                         mrpt::poses::CPose3DQuat getCurrentRobotPoseMean() const;
00143 
00144                         /** Returns the mean & the 6x6 covariance matrix of the robot 6D pose (with rotation as 3 angles).
00145                           * \sa getCurrentState
00146                           */
00147                         inline void  getCurrentRobotPose( CPose3DPDFGaussian &out_robotPose ) const
00148                         {
00149                                 CPose3DQuatPDFGaussian q(UNINITIALIZED_QUATERNION);
00150                                 this->getCurrentRobotPose(q);
00151                                 out_robotPose = CPose3DPDFGaussian(q);
00152                         }
00153 
00154                         /** Returns a 3D representation of the landmarks in the map and the robot 3D position according to the current filter state.
00155                           *  \param out_objects
00156                           */
00157                         void  getAs3DObject( mrpt::opengl::CSetOfObjectsPtr     &outObj ) const;
00158 
00159                         /** Load options from a ini-like file/text
00160                           */
00161                         void loadOptions( const mrpt::utils::CConfigFileBase &ini );
00162 
00163                         /** The options for the algorithm
00164                           */
00165                         struct SLAM_IMPEXP TOptions : utils::CLoadableOptions
00166                         {
00167                                 /** Default values
00168                                   */
00169                                 TOptions();
00170 
00171                                 /** Load from a config file/text
00172                                   */
00173                                 void loadFromConfigFile(
00174                                         const mrpt::utils::CConfigFileBase      &source,
00175                                         const std::string               &section);
00176 
00177                                 /** This method must display clearly all the contents of the structure in textual form, sending it to a CStream.
00178                                 */
00179                                 void  dumpToTextStream(CStream  &out) const;
00180 
00181                                 /** A 7-length vector with the std. deviation of the transition model in (x,y,z, qr,qx,qy,qz) used only when there is no odometry (if there is odo, its uncertainty values will be used instead); x y z: In meters.
00182                                   */
00183                                 vector_float stds_Q_no_odo;
00184 
00185                                 /** The std. deviation of the sensor (for the matrix R in the kalman filters), in meters and radians.
00186                                   */
00187                                 float std_sensor_range, std_sensor_yaw, std_sensor_pitch;
00188 
00189                                 /** Additional std. dev. to sum to the motion model in the z axis (useful when there is only 2D odometry and we want to put things hard to the algorithm) (default=0)
00190                                   */
00191                                 float std_odo_z_additional;
00192 
00193                                 /** If set to true (default=false), map will be partitioned using the method stated by partitioningMethod
00194                                   */
00195                                 bool doPartitioningExperiment;
00196 
00197                                 /** Default = 3
00198                                   */
00199                                 float quantiles_3D_representation;
00200 
00201                                 /** Applicable only if "doPartitioningExperiment=true".
00202                                   *   0: Automatically detect partition through graph-cut.
00203                                   *   N>=1: Cut every "N" observations.
00204                                   */
00205                                 int  partitioningMethod;
00206 
00207                                 // Data association:
00208                                 TDataAssociationMethod  data_assoc_method;
00209                                 TDataAssociationMetric  data_assoc_metric;
00210                                 double                                  data_assoc_IC_chi2_thres;  //!< Threshold in [0,1] for the chi2square test for individual compatibility between predictions and observations (default: 0.99)
00211                                 TDataAssociationMetric  data_assoc_IC_metric;      //!< Whether to use mahalanobis (->chi2 criterion) vs. Matching likelihood.
00212                                 double                                  data_assoc_IC_ml_threshold;//!< Only if data_assoc_IC_metric==ML, the log-ML threshold (Default=0.0)
00213 
00214                                 bool                    create_simplemap; //!< Whether to fill m_SFs (default=false)
00215 
00216                                 bool            force_ignore_odometry; //!< Whether to ignore the input odometry and behave as if there was no odometry at all (default: false)
00217                         } options;
00218 
00219                         /** Information for data-association:
00220                           * \sa getLastDataAssociation
00221                           */
00222                         struct SLAM_IMPEXP TDataAssocInfo
00223                         {
00224                                 TDataAssocInfo() :
00225                                         Y_pred_means(0,0),
00226                                         Y_pred_covs(0,0)
00227                                 {
00228                                 }
00229 
00230                                 void clear() {
00231                                         results.clear();
00232                                         predictions_IDs.clear();
00233                                         newly_inserted_landmarks.clear();
00234                                 }
00235 
00236                                 // Predictions from the map:
00237                                 CMatrixTemplateNumeric<kftype>  Y_pred_means,Y_pred_covs;
00238                                 mrpt::vector_size_t                             predictions_IDs;
00239 
00240                                 /** Map from the 0-based index within the last observation and the landmark 0-based index in the map (the robot-map state vector)
00241                                     Only used for stats and so. */
00242                                 std::map<size_t,size_t>  newly_inserted_landmarks;
00243 
00244                                 // DA results:
00245                                 TDataAssociationResults                 results;
00246                         };
00247 
00248                         /** Returns a read-only reference to the information on the last data-association */
00249                         const TDataAssocInfo & getLastDataAssociation() const {
00250                                 return m_last_data_association;
00251                         }
00252 
00253 
00254                         /** Return the last partition of the sequence of sensoryframes (it is NOT a partition of the map!!)
00255                           *  Only if options.doPartitioningExperiment = true
00256                           * \sa getLastPartitionLandmarks
00257                           */
00258                         void getLastPartition( std::vector<vector_uint> &parts )
00259                         {
00260                                 parts = m_lastPartitionSet;
00261                         }
00262 
00263                         /** Return the partitioning of the landmarks in clusters accoring to the last partition.
00264                           *  Note that the same landmark may appear in different clusters (the partition is not in the space of landmarks)
00265                           *  Only if options.doPartitioningExperiment = true
00266                           *  \param landmarksMembership The i'th element of this vector is the set of clusters to which the i'th landmark in the map belongs to (landmark index != landmark ID !!).
00267                           * \sa getLastPartition
00268                           */
00269                         void getLastPartitionLandmarks( std::vector<vector_uint>        &landmarksMembership ) const;
00270 
00271                         /** For testing only: returns the partitioning as "getLastPartitionLandmarks" but as if a fixed-size submaps (size K) were have been used.
00272                           */
00273                         void getLastPartitionLandmarksAsIfFixedSubmaps( size_t K, std::vector<vector_uint>      &landmarksMembership );
00274 
00275 
00276                         /** Computes the ratio of the missing information matrix elements which are ignored under a certain partitioning of the landmarks.
00277                           * \sa getLastPartitionLandmarks, getLastPartitionLandmarksAsIfFixedSubmaps
00278                           */
00279                         double computeOffDiagonalBlocksApproximationError( const std::vector<vector_uint>       &landmarksMembership ) const;
00280 
00281 
00282                         /** The partitioning of the entire map is recomputed again.
00283                           *  Only when options.doPartitioningExperiment = true.
00284                           *  This can be used after changing the parameters of the partitioning method.
00285                           *  After this method, you can call getLastPartitionLandmarks.
00286                           * \sa getLastPartitionLandmarks
00287                           */
00288                         void reconsiderPartitionsNow();
00289 
00290 
00291                         /** Provides access to the parameters of the map partitioning algorithm.
00292                           */
00293                         CIncrementalMapPartitioner::TOptions * mapPartitionOptions()
00294                         {
00295                                 return &mapPartitioner.options;
00296                         }
00297 
00298                         /** Save the current state of the filter (robot pose & map) to a MATLAB script which displays all the elements in 2D
00299                           */
00300                         void saveMapAndPath2DRepresentationAsMATLABFile(
00301                                 const std::string &fil,
00302                                 float              stdCount=3.0f,
00303                                 const std::string &styleLandmarks = std::string("b"),
00304                                 const std::string &stylePath = std::string("r"),
00305                                 const std::string &styleRobot = std::string("r") ) const;
00306 
00307 
00308 
00309                  protected:
00310 
00311                         /** @name Virtual methods for Kalman Filter implementation
00312                                 @{
00313                          */
00314 
00315                         /** Must return the action vector u.
00316                           * \param out_u The action vector which will be passed to OnTransitionModel
00317                           */
00318                         void OnGetAction( KFArray_ACT &out_u ) const;
00319 
00320                         /** Implements the transition model \f$ \hat{x}_{k|k-1} = f( \hat{x}_{k-1|k-1}, u_k ) \f$
00321                           * \param in_u The vector returned by OnGetAction.
00322                           * \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$ .
00323                           * \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
00324                           */
00325                         void OnTransitionModel(
00326                                 const KFArray_ACT &in_u,
00327                                 KFArray_VEH       &inout_x,
00328                                 bool &out_skipPrediction
00329                                 ) const;
00330 
00331                         /** Implements the transition Jacobian \f$ \frac{\partial f}{\partial x} \f$
00332                           * \param out_F Must return the Jacobian.
00333                           *  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).
00334                           */
00335                         void OnTransitionJacobian( KFMatrix_VxV  &out_F ) const;
00336 
00337                         /** Implements the transition noise covariance \f$ Q_k \f$
00338                           * \param out_Q Must return the covariance matrix.
00339                           *  The returned matrix must be of the same size than the jacobian from OnTransitionJacobian
00340                           */
00341                         void OnTransitionNoise( KFMatrix_VxV &out_Q ) const;
00342 
00343                         /** 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.
00344                           *
00345                           * \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.
00346                           * \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.
00347                           * \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".
00348                           * \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.
00349                           *
00350                           *  This method will be called just once for each complete KF iteration.
00351                           * \note It is assumed that the observations are independent, i.e. there are NO cross-covariances between them.
00352                           */
00353                         void OnGetObservationsAndDataAssociation(
00354                                 vector_KFArray_OBS    &out_z,
00355                                 vector_int                  &out_data_association,
00356                                 const vector_KFArray_OBS   &in_all_predictions,
00357                                 const KFMatrix              &in_S,
00358                                 const vector_size_t         &in_lm_indices_in_S,
00359                                 const KFMatrix_OxO          &in_R
00360                                 );
00361 
00362                         void OnObservationModel(
00363                                 const vector_size_t       &idx_landmarks_to_predict,
00364                                 vector_KFArray_OBS  &out_predictions
00365                                 ) const;
00366 
00367                         /** Implements the observation Jacobians \f$ \frac{\partial h_i}{\partial x} \f$ and (when applicable) \f$ \frac{\partial h_i}{\partial y_i} \f$.
00368                           * \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.
00369                           * \param Hx  The output Jacobian \f$ \frac{\partial h_i}{\partial x} \f$.
00370                           * \param Hy  The output Jacobian \f$ \frac{\partial h_i}{\partial y_i} \f$.
00371                           */
00372                         void OnObservationJacobians(
00373                                 const size_t &idx_landmark_to_predict,
00374                                 KFMatrix_OxV &Hx,
00375                                 KFMatrix_OxF &Hy
00376                                 ) const;
00377 
00378                         /** Computes A=A-B, which may need to be re-implemented depending on the topology of the individual scalar components (eg, angles).
00379                           */
00380                         void OnSubstractObservationVectors(KFArray_OBS &A, const KFArray_OBS &B) const;
00381 
00382                         /** Return the observation NOISE covariance matrix, that is, the model of the Gaussian additive noise of the sensor.
00383                           * \param out_R The noise covariance matrix. It might be non diagonal, but it'll usually be.
00384                           */
00385                         void OnGetObservationNoise(KFMatrix_OxO &out_R) const;
00386 
00387                         /** This will be called before OnGetObservationsAndDataAssociation to allow the application to reduce the number of covariance landmark predictions to be made.
00388                           *  For example, features which are known to be "out of sight" shouldn't be added to the output list to speed up the calculations.
00389                           * \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.
00390                           * \param out_LM_indices_to_predict The list of landmark indices in the map [0,getNumberOfLandmarksInTheMap()-1] that should be predicted.
00391                           * \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.
00392                           * \sa OnGetObservations, OnDataAssociation
00393                           */
00394                         void OnPreComputingPredictions(
00395                                 const vector_KFArray_OBS        &in_all_prediction_means,
00396                                 vector_size_t                           &out_LM_indices_to_predict ) const;
00397 
00398                         /** If applicable to the given problem, this method implements the inverse observation model needed to extend the "map" with a new "element".
00399                           * \param in_z The observation vector whose inverse sensor model is to be computed. This is actually one of the vector<> returned by OnGetObservations().
00400                           * \param out_yn The F-length vector with the inverse observation model \f$ y_n=y(x,z_n) \f$.
00401                           * \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$.
00402                           * \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$.
00403                           *
00404                           *  - O: OBS_SIZE
00405                           *  - V: VEH_SIZE
00406                           *  - F: FEAT_SIZE
00407                           *
00408                           * \note OnNewLandmarkAddedToMap will be also called after calling this method if a landmark is actually being added to the map.
00409                           */
00410                         void  OnInverseObservationModel(
00411                                 const KFArray_OBS & in_z,
00412                                 KFArray_FEAT  & out_yn,
00413                                 KFMatrix_FxV  & out_dyn_dxv,
00414                                 KFMatrix_FxO  & out_dyn_dhn ) const;
00415 
00416                         /** If applicable to the given problem, do here any special handling of adding a new landmark to the map.
00417                           * \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.
00418                           * \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.
00419                           * \sa OnInverseObservationModel
00420                           */
00421                         void OnNewLandmarkAddedToMap(
00422                                 const size_t    in_obsIdx,
00423                                 const size_t    in_idxNewFeat );
00424 
00425 
00426                         /** 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.
00427                           */
00428                         void OnNormalizeStateVector();
00429 
00430                         /** @}
00431                          */
00432 
00433                         /** Set up by processActionObservation
00434                           */
00435                         CActionCollectionPtr    m_action;
00436 
00437                         /** Set up by processActionObservation
00438                           */
00439                         CSensoryFramePtr                m_SF;
00440 
00441                         /** The mapping between landmark IDs and indexes in the Pkk cov. matrix:
00442                           */
00443                         mrpt::utils::bimap<CLandmark::TLandmarkID,unsigned int> m_IDs;
00444 
00445 
00446                         /** Used for map partitioning experiments
00447                           */
00448                         CIncrementalMapPartitioner  mapPartitioner;
00449 
00450                         /** The sequence of all the observations and the robot path (kept for debugging, statistics,etc)
00451                           */
00452                         CSimpleMap      m_SFs;
00453 
00454                         std::vector<vector_uint>        m_lastPartitionSet;
00455 
00456                         TDataAssocInfo m_last_data_association; //!< Last data association
00457 
00458                         /** Return the last odometry, as a pose increment. */
00459                         mrpt::poses::CPose3DQuat getIncrementFromOdometry() const;
00460 
00461 
00462                 }; // end class
00463         } // End of namespace
00464 } // End of namespace
00465 
00466 
00467 
00468 
00469 #endif



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