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 00029 #ifndef COccupancyGridMap2D_H 00030 #define COccupancyGridMap2D_H 00031 00032 #include <mrpt/utils/CSerializable.h> 00033 #include <mrpt/utils/CLoadableOptions.h> 00034 #include <mrpt/utils/CImage.h> 00035 #include <mrpt/utils/CDynamicGrid.h> 00036 #include <mrpt/slam/CMetricMap.h> 00037 #include <mrpt/utils/TMatchingPair.h> 00038 #include <mrpt/slam/CLogOddsGridMap2D.h> 00039 00040 #include <mrpt/maps/link_pragmas.h> 00041 00042 #include <mrpt/utils/safe_pointers.h> 00043 00044 #include <mrpt/config.h> 00045 00046 #if !defined(OCCUPANCY_GRIDMAP_CELL_SIZE_8BITS) && !defined(OCCUPANCY_GRIDMAP_CELL_SIZE_16BITS) 00047 #error One of OCCUPANCY_GRIDMAP_CELL_SIZE_16BITS or OCCUPANCY_GRIDMAP_CELL_SIZE_8BITS must be defined. 00048 #endif 00049 00050 #if defined(OCCUPANCY_GRIDMAP_CELL_SIZE_8BITS) && defined(OCCUPANCY_GRIDMAP_CELL_SIZE_16BITS) 00051 #error Only one of OCCUPANCY_GRIDMAP_CELL_SIZE_16BITS or OCCUPANCY_GRIDMAP_CELL_SIZE_8BITS must be defined at a time. 00052 #endif 00053 00054 00055 namespace mrpt 00056 { 00057 namespace poses { class CPose2D; } 00058 namespace slam 00059 { 00060 using namespace mrpt::poses; 00061 using namespace mrpt::utils; 00062 00063 class CObservation2DRangeScan; 00064 class CObservationRange; 00065 class CObservation; 00066 class CPointsMap; 00067 00068 DEFINE_SERIALIZABLE_PRE_CUSTOM_BASE_LINKAGE( COccupancyGridMap2D, CMetricMap, MAPS_IMPEXP ) 00069 00070 /** A class for storing an occupancy grid map. 00071 * COccupancyGridMap2D is a class for storing a metric map 00072 * representation in the form of a probabilistic occupancy 00073 * grid map: value of 0 means certainly occupied, 1 means 00074 * a certainly empty cell. Initially 0.5 means uncertainty. 00075 * 00076 * The cells keep the log-odd representation of probabilities instead of the probabilities themselves. 00077 * More details can be found at http://www.mrpt.org/Occupancy_Grids 00078 * 00079 * The algorithm for updating the grid from a laser scanner can optionally take into account the progressive widening of the beams, as 00080 * described in the <a href="http://www.mrpt.org/Occupancy_Grids" > wiki </a> (this feature was introduced in MRPT 0.6.4). 00081 * 00082 * Some implemented methods are: 00083 * - Update of individual cells 00084 * - Insertion of observations 00085 * - Voronoi diagram and critical points (\a buildVoronoiDiagram) 00086 * - Saving and loading from/to a bitmap 00087 * - Laser scans simulation for the map contents 00088 * - Entropy and information methods (See computeEntropy) 00089 * 00090 * \ingroup mrpt_maps_grp 00091 **/ 00092 class MAPS_IMPEXP COccupancyGridMap2D : 00093 public CMetricMap, 00094 // Inherit from the corresponding specialization of CLogOddsGridMap2D<>: 00095 #ifdef OCCUPANCY_GRIDMAP_CELL_SIZE_8BITS 00096 public CLogOddsGridMap2D<int8_t> 00097 #else 00098 public CLogOddsGridMap2D<int16_t> 00099 #endif 00100 { 00101 // This must be added to any CSerializable derived class: 00102 DEFINE_SERIALIZABLE( COccupancyGridMap2D ) 00103 00104 public: 00105 /** The type of the map cells: */ 00106 #ifdef OCCUPANCY_GRIDMAP_CELL_SIZE_8BITS 00107 typedef int8_t cellType; 00108 typedef uint8_t cellTypeUnsigned; 00109 #else 00110 typedef int16_t cellType; 00111 typedef uint16_t cellTypeUnsigned; 00112 #endif 00113 00114 /** Discrete to float conversion factors: The min/max values of the integer cell type, eg.[0,255] or [0,65535] */ 00115 static const cellType OCCGRID_CELLTYPE_MIN = CLogOddsGridMap2D<cellType>::CELLTYPE_MIN; 00116 static const cellType OCCGRID_CELLTYPE_MAX = CLogOddsGridMap2D<cellType>::CELLTYPE_MAX; 00117 static const cellType OCCGRID_P2LTABLE_SIZE = CLogOddsGridMap2D<cellType>::P2LTABLE_SIZE; 00118 00119 protected: 00120 00121 friend class CMultiMetricMap; 00122 friend class CMultiMetricMapPDF; 00123 00124 static CLogOddsGridMapLUT<cellType> m_logodd_lut; //!< Lookup tables for log-odds 00125 00126 /** This is the buffer for storing the cells.In this dynamic 00127 * size buffer are stored the cell values as 00128 * "bytes", stored row by row, from left to right cells. 00129 */ 00130 std::vector<cellType> map; 00131 00132 /** The size of the grid in cells. 00133 */ 00134 uint32_t size_x,size_y; 00135 00136 /** The limits of the grid in "units" (meters). 00137 */ 00138 float x_min,x_max,y_min,y_max; 00139 00140 /** Cell size, i.e. resolution of the grid map. 00141 */ 00142 float resolution; 00143 00144 /** These are auxiliary variables to speed up the computation of observation likelihood values for LF method among others, at a high cost in memory (see TLikelihoodOptions::enableLikelihoodCache). 00145 */ 00146 std::vector<double> precomputedLikelihood; 00147 bool precomputedLikelihoodToBeRecomputed; 00148 00149 /** Used for Voronoi calculation.Same struct as "map", but contains a "0" if not a basis point. */ 00150 CDynamicGrid<uint8_t> m_basis_map; 00151 00152 /** Used to store the Voronoi diagram. 00153 * Contains the distance of each cell to its closer obstacles 00154 * in 1/100th distance units (i.e. in centimeters), or 0 if not into the Voronoi diagram. 00155 */ 00156 CDynamicGrid<uint16_t> m_voronoi_diagram; 00157 00158 bool m_is_empty; //!< True upon construction; used by isEmpty() 00159 00160 virtual void OnPostSuccesfulInsertObs(const CObservation *); //!< See base class 00161 00162 /** The free-cells threshold used to compute the Voronoi diagram. 00163 */ 00164 float voroni_free_threshold; 00165 00166 /** Frees the dynamic memory buffers of map. 00167 */ 00168 void freeMap(); 00169 00170 /** Entropy computation internal function: 00171 */ 00172 static double H(double p); 00173 00174 /** Change the contents [0,1] of a cell, given its index. 00175 */ 00176 inline void setCell_nocheck(int x,int y,float value) 00177 { 00178 map[x+y*size_x]=p2l(value); 00179 } 00180 00181 /** Read the real valued [0,1] contents of a cell, given its index. 00182 */ 00183 inline float getCell_nocheck(int x,int y) const 00184 { 00185 return l2p(map[x+y*size_x]); 00186 } 00187 00188 /** Changes a cell by its absolute index (Do not use it normally) 00189 */ 00190 inline void setRawCell(unsigned int cellIndex, cellType b) 00191 { 00192 if (cellIndex<size_x*size_y) 00193 { 00194 map[cellIndex] = b; 00195 } 00196 } 00197 00198 /** Internally used to speed-up entropy calculation 00199 */ 00200 static std::vector<float> entropyTable; 00201 00202 00203 /** One of the methods that can be selected for implementing "computeObservationLikelihood" (This method is the Range-Scan Likelihood Consensus for gridmaps, see the ICRA2007 paper by Blanco et al.) 00204 */ 00205 double computeObservationLikelihood_Consensus( 00206 const CObservation *obs, 00207 const CPose2D &takenFrom ); 00208 00209 /** One of the methods that can be selected for implementing "computeObservationLikelihood" 00210 * TODO: This method is described in.... 00211 */ 00212 double computeObservationLikelihood_ConsensusOWA( 00213 const CObservation *obs, 00214 const CPose2D &takenFrom ); 00215 00216 /** One of the methods that can be selected for implementing "computeObservationLikelihood". 00217 */ 00218 double computeObservationLikelihood_CellsDifference( 00219 const CObservation *obs, 00220 const CPose2D &takenFrom ); 00221 00222 /** One of the methods that can be selected for implementing "computeObservationLikelihood". 00223 */ 00224 double computeObservationLikelihood_MI( 00225 const CObservation *obs, 00226 const CPose2D &takenFrom ); 00227 00228 /** One of the methods that can be selected for implementing "computeObservationLikelihood". 00229 */ 00230 double computeObservationLikelihood_rayTracing( 00231 const CObservation *obs, 00232 const CPose2D &takenFrom ); 00233 00234 /** One of the methods that can be selected for implementing "computeObservationLikelihood". 00235 */ 00236 double computeObservationLikelihood_likelihoodField_Thrun( 00237 const CObservation *obs, 00238 const CPose2D &takenFrom ); 00239 00240 /** One of the methods that can be selected for implementing "computeObservationLikelihood". 00241 */ 00242 double computeObservationLikelihood_likelihoodField_II( 00243 const CObservation *obs, 00244 const CPose2D &takenFrom ); 00245 00246 /** Clear the map: It set all cells to their default occupancy value (0.5), without changing the resolution (the grid extension is reset to the default values). 00247 */ 00248 virtual void internal_clear( ); 00249 00250 /** Insert the observation information into this map. 00251 * 00252 * \param obs The observation 00253 * \param robotPose The 3D pose of the robot mobile base in the map reference system, or NULL (default) if you want to use CPose2D(0,0,deg) 00254 * 00255 * After successfull execution, "lastObservationInsertionInfo" is updated. 00256 * 00257 * \sa insertionOptions, CObservation::insertObservationInto 00258 */ 00259 virtual bool internal_insertObservation( const CObservation *obs, const CPose3D *robotPose = NULL ); 00260 00261 public: 00262 00263 /** Performs the Bayesian fusion of a new observation of a cell. 00264 * \sa updateInfoChangeOnly, updateCell_fast_occupied, updateCell_fast_free 00265 */ 00266 void updateCell(int x,int y, float v); 00267 00268 /** An internal structure for storing data related to counting the new information apported by some observation. 00269 */ 00270 struct MAPS_IMPEXP TUpdateCellsInfoChangeOnly 00271 { 00272 TUpdateCellsInfoChangeOnly( bool enabled = false, 00273 double I_change = 0, 00274 int cellsUpdated=0) : enabled(enabled), 00275 I_change(I_change), 00276 cellsUpdated(cellsUpdated), 00277 laserRaysSkip(1) 00278 { 00279 } 00280 00281 /** If set to false (default), this struct is not used. Set to true only when measuring the info of an observation. 00282 */ 00283 bool enabled; 00284 00285 /** The cummulative change in Information: This is updated only from the "updateCell" method. 00286 */ 00287 double I_change; 00288 00289 /** The cummulative updated cells count: This is updated only from the "updateCell" method. 00290 */ 00291 int cellsUpdated; 00292 00293 /** In this mode, some laser rays can be skips to speep-up 00294 */ 00295 int laserRaysSkip; 00296 } updateInfoChangeOnly; 00297 00298 /** Constructor. 00299 */ 00300 COccupancyGridMap2D( float min_x = -20.0f, 00301 float max_x = 20.0f, 00302 float min_y = -20.0f, 00303 float max_y = 20.0f, 00304 float resolution = 0.05f 00305 ); 00306 00307 /** Fills all the cells with a default value. 00308 */ 00309 void fill(float default_value = 0.5f ); 00310 00311 /** Destructor. 00312 */ 00313 virtual ~COccupancyGridMap2D(); 00314 00315 /** Change the size of gridmap, erasing all its previous contents. 00316 * \param x_min The "x" coordinates of left most side of grid. 00317 * \param x_max The "x" coordinates of right most side of grid. 00318 * \param y_min The "y" coordinates of top most side of grid. 00319 * \param y_max The "y" coordinates of bottom most side of grid. 00320 * \param resolution The new size of cells. 00321 * \param default_value The value of cells, tipically 0.5. 00322 * \sa ResizeGrid 00323 */ 00324 void setSize(float x_min,float x_max,float y_min,float y_max,float resolution,float default_value = 0.5f); 00325 00326 /** Change the size of gridmap, maintaining previous contents. 00327 * \param new_x_min The "x" coordinates of new left most side of grid. 00328 * \param new_x_max The "x" coordinates of new right most side of grid. 00329 * \param new_y_min The "y" coordinates of new top most side of grid. 00330 * \param new_y_max The "y" coordinates of new bottom most side of grid. 00331 * \param new_cells_default_value The value of the new cells, tipically 0.5. 00332 * \param additionalMargin If set to true (default), an additional margin of a few meters will be added to the grid, ONLY if the new coordinates are larger than current ones. 00333 * \sa setSize 00334 */ 00335 void resizeGrid(float new_x_min,float new_x_max,float new_y_min,float new_y_max,float new_cells_default_value = 0.5f, bool additionalMargin = true) MRPT_NO_THROWS; 00336 00337 /** Returns the area of the gridmap, in square meters */ 00338 inline double getArea() const { return size_x*size_y*square(resolution); } 00339 00340 /** Returns the horizontal size of grid map in cells count. 00341 */ 00342 inline unsigned int getSizeX() const { return size_x; } 00343 00344 /** Returns the vertical size of grid map in cells count. 00345 */ 00346 inline unsigned int getSizeY() const { return size_y; } 00347 00348 /** Returns the "x" coordinate of left side of grid map. 00349 */ 00350 inline float getXMin() const { return x_min; } 00351 00352 /** Returns the "x" coordinate of right side of grid map. 00353 */ 00354 inline float getXMax() const { return x_max; } 00355 00356 /** Returns the "y" coordinate of top side of grid map. 00357 */ 00358 inline float getYMin() const { return y_min; } 00359 00360 /** Returns the "y" coordinate of bottom side of grid map. 00361 */ 00362 inline float getYMax() const { return y_max; } 00363 00364 /** Returns the resolution of the grid map. 00365 */ 00366 inline float getResolution() const { return resolution; } 00367 00368 /** Transform a coordinate value into a cell index. 00369 */ 00370 inline int x2idx(float x) const { return static_cast<int>((x-x_min)/resolution ); } 00371 inline int y2idx(float y) const { return static_cast<int>((y-y_min)/resolution ); } 00372 00373 inline int x2idx(double x) const { return static_cast<int>((x-x_min)/resolution ); } 00374 inline int y2idx(double y) const { return static_cast<int>((y-y_min)/resolution ); } 00375 00376 /** Transform a cell index into a coordinate value. 00377 */ 00378 inline float idx2x(const size_t cx) const { return x_min+(cx+0.5f)*resolution; } 00379 inline float idx2y(const size_t cy) const { return y_min+(cy+0.5f)*resolution; } 00380 00381 /** Transform a coordinate value into a cell index, using a diferent "x_min" value 00382 */ 00383 inline int x2idx(float x,float x_min) const { return static_cast<int>((x-x_min)/resolution ); } 00384 inline int y2idx(float y, float y_min) const { return static_cast<int>((y-y_min)/resolution ); } 00385 00386 /** Scales an integer representation of the log-odd into a real valued probability in [0,1], using p=exp(l)/(1+exp(l)) 00387 */ 00388 static inline float l2p(const cellType l) 00389 { 00390 return m_logodd_lut.l2p(l); 00391 } 00392 00393 /** Scales an integer representation of the log-odd into a linear scale [0,255], using p=exp(l)/(1+exp(l)) 00394 */ 00395 static inline uint8_t l2p_255(const cellType l) 00396 { 00397 return m_logodd_lut.l2p_255(l); 00398 } 00399 00400 /** Scales a real valued probability in [0,1] to an integer representation of: log(p)-log(1-p) in the valid range of cellType. 00401 */ 00402 static inline cellType p2l(const float p) 00403 { 00404 return m_logodd_lut.p2l(p); 00405 } 00406 00407 /** Change the contents [0,1] of a cell, given its index. 00408 */ 00409 inline void setCell(int x,int y,float value) 00410 { 00411 // The x> comparison implicitly holds if x<0 00412 if (static_cast<unsigned int>(x)>=size_x || static_cast<unsigned int>(y)>=size_y) 00413 return; 00414 else map[x+y*size_x]=p2l(value); 00415 } 00416 00417 /** Read the real valued [0,1] contents of a cell, given its index. 00418 */ 00419 inline float getCell(int x,int y) const 00420 { 00421 // The x> comparison implicitly holds if x<0 00422 if (static_cast<unsigned int>(x)>=size_x || static_cast<unsigned int>(y)>=size_y) 00423 return 0.5f; 00424 else return l2p(map[x+y*size_x]); 00425 } 00426 00427 /** Access to a "row": mainly used for drawing grid as a bitmap efficiently, do not use it normally. 00428 */ 00429 inline cellType *getRow( int cy ) { if (cy<0 || static_cast<unsigned int>(cy)>=size_y) return NULL; else return &map[0+cy*size_x]; } 00430 00431 /** Access to a "row": mainly used for drawing grid as a bitmap efficiently, do not use it normally. 00432 */ 00433 inline const cellType *getRow( int cy ) const { if (cy<0 || static_cast<unsigned int>(cy)>=size_y) return NULL; else return &map[0+cy*size_x]; } 00434 00435 /** Change the contents [0,1] of a cell, given its coordinates. 00436 */ 00437 inline void setPos(float x,float y,float value) { setCell(x2idx(x),y2idx(y),value); } 00438 00439 /** Read the real valued [0,1] contents of a cell, given its coordinates. 00440 */ 00441 inline float getPos(float x,float y) const { return getCell(x2idx(x),y2idx(y)); } 00442 00443 /** Returns "true" if cell is "static", i.e.if its occupancy is below a given threshold. 00444 */ 00445 inline bool isStaticPos(float x,float y,float threshold = 0.7f) const { return isStaticCell(x2idx(x),y2idx(y),threshold); } 00446 inline bool isStaticCell(int cx,int cy,float threshold = 0.7f) const { return (getCell(cx,cy)<=threshold); } 00447 00448 /** Change a cell in the "basis" maps.Used for Voronoi calculation. 00449 */ 00450 inline void setBasisCell(int x,int y,uint8_t value) 00451 { 00452 uint8_t *cell=m_basis_map.cellByIndex(x,y); 00453 #ifdef _DEBUG 00454 ASSERT_ABOVEEQ_(x,0) 00455 ASSERT_ABOVEEQ_(y,0) 00456 ASSERT_BELOWEQ_(x,int(m_basis_map.getSizeX())) 00457 ASSERT_BELOWEQ_(y,int(m_basis_map.getSizeY())) 00458 #endif 00459 *cell = value; 00460 } 00461 00462 /** Reads a cell in the "basis" maps.Used for Voronoi calculation. 00463 */ 00464 inline unsigned char getBasisCell(int x,int y) const 00465 { 00466 const uint8_t *cell=m_basis_map.cellByIndex(x,y); 00467 #ifdef _DEBUG 00468 ASSERT_ABOVEEQ_(x,0) 00469 ASSERT_ABOVEEQ_(y,0) 00470 ASSERT_BELOWEQ_(x,int(m_basis_map.getSizeX())) 00471 ASSERT_BELOWEQ_(y,int(m_basis_map.getSizeY())) 00472 #endif 00473 return *cell; 00474 } 00475 00476 /** Used for returning entropy related information 00477 * \sa computeEntropy 00478 */ 00479 struct MAPS_IMPEXP TEntropyInfo 00480 { 00481 TEntropyInfo() : H(0),I(0),mean_H(0),mean_I(0),effectiveMappedArea(0),effectiveMappedCells(0) 00482 { 00483 } 00484 00485 /** The target variable for absolute entropy, computed as:<br><center>H(map)=Sum<sub>x,y</sub>{ -p(x,y)·ln(p(x,y)) -(1-p(x,y))·ln(1-p(x,y)) }</center><br><br> 00486 */ 00487 double H; 00488 00489 /** The target variable for absolute "information", defining I(x) = 1 - H(x) 00490 */ 00491 double I; 00492 00493 /** The target variable for mean entropy, defined as entropy per cell: mean_H(map) = H(map) / (cells) 00494 */ 00495 double mean_H; 00496 00497 /** The target variable for mean information, defined as information per cell: mean_I(map) = I(map) / (cells) 00498 */ 00499 double mean_I; 00500 00501 /** The target variable for the area of cells with information, i.e. p(x)!=0.5 00502 */ 00503 double effectiveMappedArea; 00504 00505 /** The mapped area in cells. 00506 */ 00507 unsigned long effectiveMappedCells; 00508 }; 00509 00510 /** With this struct options are provided to the observation insertion process. 00511 * \sa CObservation::insertIntoGridMap 00512 */ 00513 class MAPS_IMPEXP TInsertionOptions : public mrpt::utils::CLoadableOptions 00514 { 00515 public: 00516 /** Initilization of default parameters 00517 */ 00518 TInsertionOptions( ); 00519 00520 /** This method load the options from a ".ini" file. 00521 * Only those parameters found in the given "section" and having 00522 * the same name that the variable are loaded. Those not found in 00523 * the file will stay with their previous values (usually the default 00524 * values loaded at initialization). An example of an ".ini" file: 00525 * \code 00526 * [section] 00527 * resolution=0.10 ; blah blah... 00528 * modeSelection=1 ; 0=blah, 1=blah,... 00529 * \endcode 00530 */ 00531 void loadFromConfigFile( 00532 const mrpt::utils::CConfigFileBase &source, 00533 const std::string §ion); 00534 00535 /** This method must display clearly all the contents of the structure in textual form, sending it to a CStream. 00536 */ 00537 void dumpToTextStream(CStream &out) const; 00538 00539 00540 /** The altitude (z-axis) of 2D scans (within a 0.01m tolerance) for they to be inserted in this map! 00541 */ 00542 float mapAltitude; 00543 00544 /** The parameter "mapAltitude" has effect while inserting observations in the grid only if this is true. 00545 */ 00546 bool useMapAltitude; 00547 00548 /** The largest distance at which cells will be updated (Default 15 meters) 00549 */ 00550 float maxDistanceInsertion; 00551 00552 /** A value in the range [0.5,1] used for updating cell with a bayesian approach (default 0.8) 00553 */ 00554 float maxOccupancyUpdateCertainty; 00555 00556 /** If set to true (default), invalid range values (no echo rays) as consider as free space until "maxOccupancyUpdateCertainty", but ONLY when the previous and next rays are also an invalid ray. 00557 */ 00558 bool considerInvalidRangesAsFreeSpace; 00559 00560 /** Specify the decimation of the range scan (default=1 : take all the range values!) 00561 */ 00562 uint16_t decimation; 00563 00564 /** The tolerance in rads in pitch & roll for a laser scan to be considered horizontal, then processed by calls to this class (default=0). */ 00565 float horizontalTolerance; 00566 00567 /** Gaussian sigma of the filter used in getAsImageFiltered (for features detection) (Default=1) (0:Disabled) */ 00568 float CFD_features_gaussian_size; 00569 00570 /** Size of the Median filter used in getAsImageFiltered (for features detection) (Default=3) (0:Disabled) */ 00571 float CFD_features_median_size; 00572 00573 bool wideningBeamsWithDistance; //!< Enabled: Rays widen with distance to approximate the real behavior of lasers, disabled: insert rays as simple lines (Default=true) 00574 00575 }; 00576 00577 /** With this struct options are provided to the observation insertion process. 00578 * \sa CObservation::insertIntoGridMap 00579 */ 00580 TInsertionOptions insertionOptions; 00581 00582 /** The type for selecting a likelihood computation method 00583 */ 00584 enum TLikelihoodMethod 00585 { 00586 lmMeanInformation = 0, 00587 lmRayTracing, 00588 lmConsensus, 00589 lmCellsDifference, 00590 lmLikelihoodField_Thrun, 00591 lmLikelihoodField_II, 00592 lmConsensusOWA 00593 }; 00594 00595 /** With this struct options are provided to the observation likelihood computation process. 00596 */ 00597 class MAPS_IMPEXP TLikelihoodOptions : public mrpt::utils::CLoadableOptions 00598 { 00599 public: 00600 /** Initilization of default parameters 00601 */ 00602 TLikelihoodOptions(); 00603 00604 /** This method load the options from a ".ini" file. 00605 * Only those parameters found in the given "section" and having 00606 * the same name that the variable are loaded. Those not found in 00607 * the file will stay with their previous values (usually the default 00608 * values loaded at initialization). An example of an ".ini" file: 00609 * \code 00610 * [section] 00611 * resolution=0.10 ; blah blah... 00612 * modeSelection=1 ; 0=blah, 1=blah,... 00613 * \endcode 00614 */ 00615 void loadFromConfigFile( 00616 const mrpt::utils::CConfigFileBase &source, 00617 const std::string §ion); 00618 00619 /** This method must display clearly all the contents of the structure in textual form, sending it to a CStream. 00620 */ 00621 void dumpToTextStream(CStream &out) const; 00622 00623 /** The selected method to compute an observation likelihood. 00624 */ 00625 TLikelihoodMethod likelihoodMethod; 00626 00627 /** [LikelihoodField] The laser range "sigma" used in computations; Default value = 0.35 00628 */ 00629 float LF_stdHit; 00630 00631 /** [LikelihoodField] Ratios of the hit/random components of the likelihood; Default values=0.95/0.5 00632 */ 00633 float LF_zHit, LF_zRandom; 00634 00635 /** [LikelihoodField] The max. range of the sensor (def=81meters) 00636 */ 00637 float LF_maxRange; 00638 00639 /** [LikelihoodField] The decimation of the points in a scan, default=1 == no decimation. 00640 */ 00641 uint32_t LF_decimation; 00642 00643 /** [LikelihoodField] The max. distance for searching correspondences around each sensed point 00644 */ 00645 float LF_maxCorrsDistance; 00646 00647 /** [LikelihoodField] Set this to "true" ot use an alternative method, where the likelihood of the whole range scan is computed by "averaging" of individual ranges, instead of by the "product". 00648 */ 00649 bool LF_alternateAverageMethod; 00650 00651 /** [MI] The exponent in the MI likelihood computation. Default value = 5 00652 */ 00653 float MI_exponent; 00654 00655 /** [MI] The scan rays decimation: at every N rays, one will be used to compute the MI: 00656 */ 00657 uint32_t MI_skip_rays; 00658 00659 /** [MI] The ratio for the max. distance used in the MI computation and in the insertion of scans, e.g. if set to 2.0 the MI will use twice the distance that the update distance. 00660 */ 00661 float MI_ratio_max_distance; 00662 00663 /** [rayTracing] If true (default), the rayTracing method will ignore measured ranges shorter than the simulated ones. 00664 */ 00665 bool rayTracing_useDistanceFilter; 00666 00667 /** [rayTracing] One out of "rayTracing_decimation" rays will be simulated and compared only: set to 1 to use all the sensed ranges. 00668 */ 00669 int32_t rayTracing_decimation; 00670 00671 /** [rayTracing] The laser range sigma. 00672 */ 00673 float rayTracing_stdHit; 00674 00675 /** [Consensus] The down-sample ratio of ranges (default=1, consider all the ranges) 00676 */ 00677 int32_t consensus_takeEachRange; 00678 00679 /** [Consensus] The power factor for the likelihood (default=5) 00680 */ 00681 float consensus_pow; 00682 00683 /** [OWA] The sequence of weights to be multiplied to of the ordered list of likelihood values (first one is the largest); the size of this vector determines the number of highest likelihood values to fuse. 00684 */ 00685 std::vector<float> OWA_weights; 00686 00687 /** Enables the usage of a cache of likelihood values (for LF methods), if set to true (default=false). 00688 */ 00689 bool enableLikelihoodCache; 00690 00691 } likelihoodOptions; 00692 00693 /** Auxiliary private class. 00694 */ 00695 typedef std::pair<double,CPoint2D> TPairLikelihoodIndex; 00696 00697 /** Some members of this struct will contain intermediate or output data after calling "computeObservationLikelihood" for some likelihood functions. 00698 */ 00699 class TLikelihoodOutput 00700 { 00701 public: 00702 TLikelihoodOutput() : OWA_pairList(), OWA_individualLikValues() 00703 {} 00704 00705 /** [OWA method] This will contain the ascending-ordered list of pairs:(likelihood values, 2D point in map coordinates). 00706 */ 00707 std::vector<TPairLikelihoodIndex> OWA_pairList; 00708 00709 /** [OWA method] This will contain the ascending-ordered list of likelihood values for individual range measurements in the scan. 00710 */ 00711 std::vector<double> OWA_individualLikValues; 00712 00713 } likelihoodOutputs; 00714 00715 /** Performs a downsampling of the gridmap, by a given factor: resolution/=ratio 00716 */ 00717 void subSample( int downRatio ); 00718 00719 /** Computes the entropy and related values of this grid map. 00720 * The entropy is computed as the summed entropy of each cell, taking them as discrete random variables following a Bernoulli distribution: 00721 * \param info The output information is returned here. 00722 */ 00723 void computeEntropy( TEntropyInfo &info ) const; 00724 00725 /** @name Voronoi methods 00726 @{ */ 00727 00728 /** Build the Voronoi diagram of the grid map. 00729 * \param threshold The threshold for binarizing the map. 00730 * \param robot_size Size in "units" (meters) of robot, approx. 00731 * \param x1 Left coordinate of area to be computed. Default, entire map. 00732 * \param x2 Right coordinate of area to be computed. Default, entire map. 00733 * \param y1 Top coordinate of area to be computed. Default, entire map. 00734 * \param y2 Bottom coordinate of area to be computed. Default, entire map. 00735 * \sa findCriticalPoints 00736 */ 00737 void buildVoronoiDiagram(float threshold, float robot_size,int x1=0,int x2=0, int y1=0,int y2=0); 00738 00739 /** Reads a the clearance of a cell (in centimeters), after building the Voronoi diagram with \a buildVoronoiDiagram */ 00740 inline uint16_t getVoroniClearance(int cx,int cy) const 00741 { 00742 #ifdef _DEBUG 00743 ASSERT_ABOVEEQ_(cx,0) 00744 ASSERT_ABOVEEQ_(cy,0) 00745 ASSERT_BELOWEQ_(cx,int(m_voronoi_diagram.getSizeX())) 00746 ASSERT_BELOWEQ_(cy,int(m_voronoi_diagram.getSizeY())) 00747 #endif 00748 const uint16_t *cell=m_voronoi_diagram.cellByIndex(cx,cy); 00749 return *cell; 00750 } 00751 00752 protected: 00753 /** Used to set the clearance of a cell, while building the Voronoi diagram. */ 00754 inline void setVoroniClearance(int cx,int cy,uint16_t dist) 00755 { 00756 uint16_t *cell=m_voronoi_diagram.cellByIndex(cx,cy); 00757 #ifdef _DEBUG 00758 ASSERT_ABOVEEQ_(cx,0) 00759 ASSERT_ABOVEEQ_(cy,0) 00760 ASSERT_BELOWEQ_(cx,int(m_voronoi_diagram.getSizeX())) 00761 ASSERT_BELOWEQ_(cy,int(m_voronoi_diagram.getSizeY())) 00762 #endif 00763 *cell = dist; 00764 } 00765 00766 public: 00767 00768 /** Return the auxiliary "basis" map built while building the Voronoi diagram \sa buildVoronoiDiagram */ 00769 inline const CDynamicGrid<uint8_t> & getBasisMap() const { return m_basis_map; } 00770 00771 /** Return the Voronoi diagram; each cell contains the distance to its closer obstacle, or 0 if not part of the Voronoi diagram \sa buildVoronoiDiagram */ 00772 inline const CDynamicGrid<uint16_t> & getVoronoiDiagram() const { return m_voronoi_diagram; } 00773 00774 /** Builds a list with the critical points from Voronoi diagram, which must 00775 * must be built before calling this method. 00776 * \param filter_distance The minimum distance between two critical points. 00777 * \sa buildVoronoiDiagram 00778 */ 00779 void findCriticalPoints( float filter_distance ); 00780 00781 /** @} */ // End of Voronoi methods 00782 00783 00784 /** Compute the clearance of a given cell, and returns its two first 00785 * basis (closest obstacle) points.Used to build Voronoi and critical points. 00786 * \return The clearance of the cell, in 1/100 of "cell". 00787 * \param cx The cell index 00788 * \param cy The cell index 00789 * \param basis_x Target buffer for coordinates of basis, having a size of two "ints". 00790 * \param basis_y Target buffer for coordinates of basis, having a size of two "ints". 00791 * \param nBasis The number of found basis: Can be 0,1 or 2. 00792 * \param GetContourPoint If "true" the basis are not returned, but the closest free cells.Default at false. 00793 * \sa Build_VoronoiDiagram 00794 */ 00795 int computeClearance( int cx, int cy, int *basis_x, int *basis_y, int *nBasis, bool GetContourPoint = false ) const; 00796 00797 /** An alternative method for computing the clearance of a given location (in meters). 00798 * \return The clearance (distance to closest OCCUPIED cell), in meters. 00799 */ 00800 float computeClearance( float x, float y, float maxSearchDistance ) const; 00801 00802 /** Compute the 'cost' of traversing a segment of the map according to the occupancy of traversed cells. 00803 * \return This returns '1-mean(traversed cells occupancy)', i.e. 0.5 for unknown cells, 1 for a free path. 00804 */ 00805 float computePathCost( float x1, float y1, float x2, float y2 ) const; 00806 00807 00808 /** \name Sensor simulators 00809 @{ 00810 */ 00811 00812 /** Simulates a laser range scan into the current grid map. 00813 * The simulated scan is stored in a CObservation2DRangeScan object, which is also used 00814 * to pass some parameters: all previously stored characteristics (as aperture,...) are 00815 * taken into account for simulation. Only a few more parameters are needed. Additive gaussian noise can be optionally added to the simulated scan. 00816 * \param inout_Scan [IN/OUT] This must be filled with desired parameters before calling, and will contain the scan samples on return. 00817 * \param robotPose [IN] The robot pose in this map coordinates. Recall that sensor pose relative to this robot pose must be specified in the observation object. 00818 * \param threshold [IN] The minimum occupancy threshold to consider a cell to be occupied, for example 0.5. 00819 * \param N [IN] The count of range scan "rays", by default to 361. 00820 * \param noiseStd [IN] The standard deviation of measurement noise. If not desired, set to 0. 00821 * \param decimation [IN] The rays that will be simulated are at indexes: 0, D, 2D, 3D, ... Default is D=1 00822 * \param angleNoiseStd [IN] The sigma of an optional Gaussian noise added to the angles at which ranges are measured (in radians). 00823 * 00824 * \sa sonarSimulator 00825 */ 00826 void laserScanSimulator( 00827 CObservation2DRangeScan &inout_Scan, 00828 const CPose2D &robotPose, 00829 float threshold = 0.5f, 00830 size_t N = 361, 00831 float noiseStd = 0, 00832 unsigned int decimation = 1, 00833 float angleNoiseStd = DEG2RAD(0) ) const; 00834 00835 /** Simulates the observations of a sonar rig into the current grid map. 00836 * The simulated ranges are stored in a CObservationRange object, which is also used 00837 * to pass in some needed parameters, as the poses of the sonar sensors onto the mobile robot. 00838 * \param inout_observation [IN/OUT] This must be filled with desired parameters before calling, and will contain the simulated ranges on return. 00839 * \param robotPose [IN] The robot pose in this map coordinates. Recall that sensor pose relative to this robot pose must be specified in the observation object. 00840 * \param threshold [IN] The minimum occupancy threshold to consider a cell to be occupied, for example 0.5. 00841 * \param rangeNoiseStd [IN] The standard deviation of measurement noise. If not desired, set to 0. 00842 * \param angleNoiseStd [IN] The sigma of an optional Gaussian noise added to the angles at which ranges are measured (in radians). 00843 * 00844 * \sa laserScanSimulator 00845 */ 00846 void sonarSimulator( 00847 CObservationRange &inout_observation, 00848 const CPose2D &robotPose, 00849 float threshold = 0.5f, 00850 float rangeNoiseStd = 0, 00851 float angleNoiseStd = DEG2RAD(0) ) const; 00852 00853 /** Simulate just one "ray" in the grid map. This method is used internally to sonarSimulator and laserScanSimulator. 00854 */ 00855 inline void simulateScanRay( 00856 const double x,const double y,const double angle_direction, 00857 float &out_range,bool &out_valid, 00858 const unsigned int max_ray_len, 00859 const float threshold_free=0.5f, 00860 const double noiseStd=0, const double angleNoiseStd=0 ) const; 00861 00862 00863 /** @} */ 00864 00865 /** Computes the likelihood that a given observation was taken from a given pose in the world being modeled with this map. 00866 * See "likelihoodOptions" for configuration parameters. 00867 * 00868 * \param takenFrom The robot's pose the observation is supposed to be taken from. 00869 * \param obs The observation. 00870 * \return This method returns a likelihood in the range [0,1]. 00871 * 00872 * Used in particle filter algorithms, see: CMultiMetricMapPDF::prediction_and_update 00873 * 00874 * \sa likelihoodOptions, likelihoodOutputs 00875 */ 00876 double computeObservationLikelihood( const CObservation *obs, const CPose3D &takenFrom ); 00877 00878 /** Returns true if this map is able to compute a sensible likelihood function for this observation (i.e. an occupancy grid map cannot with an image). 00879 * \param obs The observation. 00880 * \sa computeObservationLikelihood 00881 */ 00882 bool canComputeObservationLikelihood( const CObservation *obs ); 00883 00884 /** Computes the likelihood [0,1] of a set of points, given the current grid map as reference. 00885 * \param pm The points map 00886 * \param relativePose The relative pose of the points map in this map's coordinates, or NULL for (0,0,0). 00887 * See "likelihoodOptions" for configuration parameters. 00888 */ 00889 double computeLikelihoodField_Thrun( const CPointsMap *pm, const CPose2D *relativePose = NULL); 00890 00891 /** Computes the likelihood [0,1] of a set of points, given the current grid map as reference. 00892 * \param pm The points map 00893 * \param relativePose The relative pose of the points map in this map's coordinates, or NULL for (0,0,0). 00894 * See "likelihoodOptions" for configuration parameters. 00895 */ 00896 double computeLikelihoodField_II( const CPointsMap *pm, const CPose2D *relativePose = NULL); 00897 00898 /** Saves the gridmap as a graphical file (BMP,PNG,...). 00899 * The format will be derived from the file extension (see CImage::saveToFile ) 00900 * \return False on any error. 00901 */ 00902 bool saveAsBitmapFile(const std::string &file) const; 00903 00904 /** Saves a composite image with two gridmaps and lines representing a set of correspondences between them. 00905 * \sa saveAsEMFTwoMapsWithCorrespondences 00906 * \return False on any error. 00907 */ 00908 static bool saveAsBitmapTwoMapsWithCorrespondences( 00909 const std::string &fileName, 00910 const COccupancyGridMap2D *m1, 00911 const COccupancyGridMap2D *m2, 00912 const TMatchingPairList &corrs); 00913 00914 /** Saves a composite image with two gridmaps and numbers for the correspondences between them. 00915 * \sa saveAsBitmapTwoMapsWithCorrespondences 00916 * \return False on any error. 00917 */ 00918 static bool saveAsEMFTwoMapsWithCorrespondences( 00919 const std::string &fileName, 00920 const COccupancyGridMap2D *m1, 00921 const COccupancyGridMap2D *m2, 00922 const TMatchingPairList &corrs); 00923 00924 /** Saves the gridmap as a graphical bitmap file, 8 bit gray scale, 1 pixel is 1 cell, and with an overlay of landmarks. 00925 * \note The template parameter CLANDMARKSMAP is assumed to be mrpt::slam::CLandmarksMap normally. 00926 * \return False on any error. 00927 */ 00928 template <class CLANDMARKSMAP> 00929 bool saveAsBitmapFileWithLandmarks( 00930 const std::string &file, 00931 const CLANDMARKSMAP *landmarks, 00932 bool addTextLabels = false, 00933 const mrpt::utils::TColor &marks_color = mrpt::utils::TColor(0,0,255) ) const 00934 { 00935 MRPT_START 00936 CImage img(1,1,3); 00937 getAsImageFiltered( img, false, true ); // in RGB 00938 const bool topleft = img.isOriginTopLeft(); 00939 for (unsigned int i=0;i<landmarks->landmarks.size();i++) 00940 { 00941 const typename CLANDMARKSMAP::landmark_type *lm = landmarks->landmarks.get( i ); 00942 int px = x2idx( lm->pose_mean.x ); 00943 int py = topleft ? size_y-1- y2idx( lm->pose_mean.y ) : y2idx( lm->pose_mean.y ); 00944 img.rectangle( px - 7, (py + 7), px +7, (py -7), marks_color ); 00945 img.rectangle( px - 6, (py + 6), px +6, (py -6), marks_color ); 00946 if (addTextLabels) 00947 img.textOut(px,py-8,format("%u",i), TColor::black); 00948 } 00949 return img.saveToFile(file.c_str() ); 00950 MRPT_END 00951 } 00952 00953 00954 /** Returns the grid as a 8-bit graylevel image, where each pixel is a cell (output image is RGB only if forceRGB is true) 00955 * If "tricolor" is true, only three gray levels will appear in the image: gray for unobserved cells, and black/white for occupied/empty cells respectively. 00956 * \sa getAsImageFiltered 00957 */ 00958 void getAsImage( utils::CImage &img, bool verticalFlip = false, bool forceRGB=false, bool tricolor = false) const; 00959 00960 /** Returns the grid as a 8-bit graylevel image, where each pixel is a cell (output image is RGB only if forceRGB is true) - This method filters the image for easy feature detection 00961 * If "tricolor" is true, only three gray levels will appear in the image: gray for unobserved cells, and black/white for occupied/empty cells respectively. 00962 * \sa getAsImage 00963 */ 00964 void getAsImageFiltered( utils::CImage &img, bool verticalFlip = false, bool forceRGB=false) const; 00965 00966 /** Returns a 3D plane with its texture being the occupancy grid and transparency proportional to "uncertainty" (i.e. a value of 0.5 is fully transparent) 00967 */ 00968 void getAs3DObject ( mrpt::opengl::CSetOfObjectsPtr &outObj ) const; 00969 00970 /** Returns true upon map construction or after calling clear(), the return 00971 * changes to false upon successful insertObservation() or any other method to load data in the map. 00972 */ 00973 bool isEmpty() const; 00974 00975 /** Load the gridmap from a image in a file (the format can be any supported by CImage::loadFromFile). 00976 * \param file The file to be loaded. 00977 * \param resolution The size of a pixel (cell), in meters. Recall cells are always squared, so just a dimension is needed. 00978 * \param xCentralPixel The "x" coordinate (0=first) for the pixel which will be taken at coordinates origin (0,0). If not supplied, it will be used the middle of the map. 00979 * \param yCentralPixel The "y" coordinate (0=first) for the pixel which will be taken at coordinates origin (0,0). If not supplied, it will be used the middle of the map. 00980 * \return False on any error. 00981 * \sa loadFromBitmap 00982 */ 00983 bool loadFromBitmapFile(const std::string &file, float resolution, float xCentralPixel = -1, float yCentralPixel =-1 ); 00984 00985 /** Load the gridmap from a image in a file (the format can be any supported by CImage::loadFromFile). 00986 * \param img The image. Only a grayscale image will be used, so RGB components will be mixed if a color image is passed. 00987 * \param resolution The size of a pixel (cell), in meters. Recall cells are always squared, so just a dimension is needed. 00988 * \param xCentralPixel The "x" coordinate (0=first) for the pixel which will be taken at coordinates origin (0,0). If not supplied, it will be used the middle of the map. 00989 * \param yCentralPixel The "y" coordinate (0=first) for the pixel which will be taken at coordinates origin (0,0). If not supplied, it will be used the middle of the map. 00990 * \return False on any error. 00991 * \sa loadFromBitmapFile 00992 */ 00993 bool loadFromBitmap(const mrpt::utils::CImage &img, float resolution, float xCentralPixel = -1, float yCentralPixel =-1 ); 00994 00995 /** See the base class for more details: In this class it is implemented as correspondences of the passed points map to occupied cells. 00996 * NOTICE: That the "z" dimension is ignored in the points. Clip the points as appropiated if needed before calling this method. 00997 * 00998 * \sa computeMatching3DWith 00999 */ 01000 void computeMatchingWith2D( 01001 const CMetricMap *otherMap, 01002 const CPose2D &otherMapPose, 01003 float maxDistForCorrespondence, 01004 float maxAngularDistForCorrespondence, 01005 const CPose2D &angularDistPivotPoint, 01006 TMatchingPairList &correspondences, 01007 float &correspondencesRatio, 01008 float *sumSqrDist = NULL, 01009 bool onlyKeepTheClosest = false, 01010 bool onlyUniqueRobust = false, 01011 const size_t decimation_other_map_points = 1, 01012 const size_t offset_other_map_points = 0 ) const; 01013 01014 01015 /** Computes the ratio in [0,1] of correspondences between "this" and the "otherMap" map, whose 6D pose relative to "this" is "otherMapPose" 01016 * In the case of a multi-metric map, this returns the average between the maps. This method always return 0 for grid maps. 01017 * \param otherMap [IN] The other map to compute the matching with. 01018 * \param otherMapPose [IN] The 6D pose of the other map as seen from "this". 01019 * \param minDistForCorr [IN] The minimum distance between 2 non-probabilistic map elements for counting them as a correspondence. 01020 * \param minMahaDistForCorr [IN] The minimum Mahalanobis distance between 2 probabilistic map elements for counting them as a correspondence. 01021 * 01022 * \return The matching ratio [0,1] 01023 * \sa computeMatchingWith2D 01024 */ 01025 float compute3DMatchingRatio( 01026 const CMetricMap *otherMap, 01027 const CPose3D &otherMapPose, 01028 float minDistForCorr = 0.10f, 01029 float minMahaDistForCorr = 2.0f 01030 ) const; 01031 01032 /** This virtual method saves the map to a file "filNamePrefix"+< some_file_extension >, as an image or in any other applicable way (Notice that other methods to save the map may be implemented in classes implementing this virtual interface). 01033 */ 01034 void saveMetricMapRepresentationToFile( 01035 const std::string &filNamePrefix 01036 ) const; 01037 01038 /** The structure used to store the set of Voronoi diagram 01039 * critical points. 01040 * \sa findCriticalPoints 01041 */ 01042 struct MAPS_IMPEXP TCriticalPointsList 01043 { 01044 TCriticalPointsList() : x(),y(),clearance(),x_basis1(),y_basis1(),x_basis2(),y_basis2() 01045 {} 01046 01047 /** The coordinates of critical point. 01048 */ 01049 std::vector<int> x,y; 01050 /** The clearance of critical points, in 1/100 of cells. 01051 */ 01052 std::vector<int> clearance; 01053 /** Their two first basis points coordinates. 01054 */ 01055 std::vector<int> x_basis1,y_basis1, x_basis2,y_basis2; 01056 } CriticalPointsList; 01057 01058 01059 private: 01060 /** Returns a byte with the occupancy of the 8 sorrounding cells. 01061 * \param cx The cell index 01062 * \param cy The cell index 01063 * \sa direction2idx 01064 */ 01065 inline unsigned char GetNeighborhood( int cx, int cy ) const; 01066 01067 /** Used to store the 8 possible movements from a cell to the 01068 * sorrounding ones.Filled in the constructor. 01069 * \sa direction2idx 01070 */ 01071 int direccion_vecino_x[8],direccion_vecino_y[8]; 01072 01073 /** Returns the index [0,7] of the given movement, or 01074 * -1 if invalid one. 01075 * \sa direccion_vecino_x,direccion_vecino_y,GetNeighborhood 01076 */ 01077 int direction2idx(int dx, int dy); 01078 }; 01079 01080 01081 bool operator <(const COccupancyGridMap2D::TPairLikelihoodIndex &e1, const COccupancyGridMap2D::TPairLikelihoodIndex &e2); 01082 01083 } // End of namespace 01084 } // End of namespace 01085 01086 #endif
| Page generated by Doxygen 1.7.5 for MRPT 0.9.5 SVN: at Thu Oct 13 21:25:36 UTC 2011 |