LoFastSLAM.C

00001 /**
00002    \file  Robots/LoBot/misc/LoFastSLAM.C
00003    \brief This file implements the non-inline member functions of the
00004    lobot::FastSLAM class.
00005 */
00006 
00007 // //////////////////////////////////////////////////////////////////// //
00008 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005   //
00009 // by the University of Southern California (USC) and the iLab at USC.  //
00010 // See http://iLab.usc.edu for information about this project.          //
00011 // //////////////////////////////////////////////////////////////////// //
00012 // Major portions of the iLab Neuromorphic Vision Toolkit are protected //
00013 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency //
00014 // in Visual Environments, and Applications'' by Christof Koch and      //
00015 // Laurent Itti, California Institute of Technology, 2001 (patent       //
00016 // pending; application number 09/912,225 filed July 23, 2001; see      //
00017 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status).     //
00018 // //////////////////////////////////////////////////////////////////// //
00019 // This file is part of the iLab Neuromorphic Vision C++ Toolkit.       //
00020 //                                                                      //
00021 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can   //
00022 // redistribute it and/or modify it under the terms of the GNU General  //
00023 // Public License as published by the Free Software Foundation; either  //
00024 // version 2 of the License, or (at your option) any later version.     //
00025 //                                                                      //
00026 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope  //
00027 // that it will be useful, but WITHOUT ANY WARRANTY; without even the   //
00028 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      //
00029 // PURPOSE.  See the GNU General Public License for more details.       //
00030 //                                                                      //
00031 // You should have received a copy of the GNU General Public License    //
00032 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write   //
00033 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,   //
00034 // Boston, MA 02111-1307 USA.                                           //
00035 // //////////////////////////////////////////////////////////////////// //
00036 //
00037 // Primary maintainer for this file: mviswana usc edu
00038 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Robots/LoBot/slam/LoFastSLAM.C $
00039 // $Id: LoFastSLAM.C 13575 2010-06-17 01:42:18Z mviswana $
00040 //
00041 
00042 //------------------------------ HEADERS --------------------------------
00043 
00044 // lobot headers
00045 #include "Robots/LoBot/slam/LoFastSLAM.H"
00046 #include "Robots/LoBot/slam/LoMap.H"
00047 #include "Robots/LoBot/slam/LoSlamParams.H"
00048 
00049 #include "Robots/LoBot/config/LoConfigHelpers.H"
00050 #include "Robots/LoBot/util/LoStats.H"
00051 #include "Robots/LoBot/util/LoSTL.H"
00052 #include "Robots/LoBot/util/LoString.H"
00053 #include "Robots/LoBot/util/LoMath.H"
00054 #include "Robots/LoBot/util/range.hh"
00055 #include "Robots/LoBot/util/triple.hh"
00056 #include "Robots/LoBot/misc/singleton.hh"
00057 
00058 // INVT utilities
00059 #include "Util/log.H"
00060 
00061 // Boost headers
00062 #include <boost/bind.hpp>
00063 
00064 // Standard C++ headers
00065 #include <string>
00066 #include <numeric>
00067 #include <algorithm>
00068 #include <functional>
00069 #include <iterator>
00070 #include <utility>
00071 
00072 //----------------------------- NAMESPACE -------------------------------
00073 
00074 namespace lobot {
00075 
00076 //-------------------------- INITIALIZATION -----------------------------
00077 
00078 // Generator function object to create a new particle
00079 namespace {
00080 
00081 class new_particle {
00082    float initial_weight ;
00083    std::vector<int> initial_scan ;
00084    boost::shared_ptr<OccGrid> known_map ;
00085 public:
00086    new_particle(float initial_weight, const LRFData& initial_scan) ;
00087    new_particle(float, const LRFData&, const boost::shared_ptr<OccGrid>&) ;
00088    Particle operator()() const ;
00089 } ;
00090 
00091 new_particle::new_particle(float w, const LRFData& lrf)
00092    : initial_weight(w), initial_scan(lrf.distances())
00093 {}
00094 
00095 new_particle::
00096 new_particle(float w, const LRFData& lrf, const boost::shared_ptr<OccGrid>& m)
00097    : initial_weight(w), initial_scan(lrf.distances()), known_map(m)
00098 {}
00099 
00100 Particle new_particle::operator()() const
00101 {
00102    if (known_map)
00103       return Particle(initial_weight, initial_scan, known_map) ;
00104    else
00105       return Particle(initial_weight, initial_scan) ;
00106 }
00107 
00108 } // end of local anonymous namespace encapsulating above helper
00109 
00110 // Since this implementation of FastSLAM uses laser range scan matching
00111 // to reduce the number of particles required by the algorithm, it needs
00112 // to be passed an initial scan of range readings during initialization.
00113 FastSLAM::FastSLAM(const LRFData& lrf)
00114    : m_w_slow(0), m_w_fast(0)
00115 {
00116    const int N = SlamParams::num_particles() ;
00117    m_particles.reserve(N) ;
00118    std::generate_n(std::back_inserter(m_particles), N,
00119                    new_particle(1.0f/N, lrf)) ;
00120 }
00121 
00122 FastSLAM::
00123 FastSLAM(const LRFData& lrf, const boost::shared_ptr<OccGrid>& known_map)
00124    : m_w_slow(0), m_w_fast(0)
00125 {
00126    const int N = SlamParams::num_particles() ;
00127    m_particles.reserve(N) ;
00128    std::generate_n(std::back_inserter(m_particles), N,
00129                    new_particle(1.0f/N, lrf, known_map)) ;
00130 }
00131 
00132 //------------------------ FASTSLAM ALGORITHM ---------------------------
00133 
00134 namespace {
00135 
00136 // Helper function object to update a particle's state using the current
00137 // control and data inputs.
00138 class update_particle {
00139    std::vector<int> lrf;
00140    const Odometry&  ut ; // control input at (current) time step t
00141    std::vector<int> zt ; // sensor  data  at (current) time step t
00142    mutable int n ;       // particle number (for debugging)
00143    const bool slam_mode ;// are we doing MCL only or full SLAM?
00144 public:
00145    update_particle(const Odometry&, const LRFData&) ;
00146    void operator()(Particle&) const ;
00147 } ;
00148 
00149 update_particle::update_particle(const Odometry& odometry, const LRFData& L)
00150    : lrf(L.distances()), ut(odometry), n(0), slam_mode(SlamParams::slam_mode())
00151 {
00152    const int begin = SlamParams::beam_start() ;
00153    const int end   = SlamParams::beam_end()   ;
00154    const int step  = SlamParams::beam_step()  ;
00155 
00156    const int N = (end - begin)/step + 1 ;
00157    zt.reserve(N) ;
00158    for (int angle = begin; angle <= end; angle += step)
00159       zt.push_back(L[angle]) ;
00160 }
00161 
00162 void update_particle::operator()(Particle& P) const
00163 {
00164    //LERROR("--------------- Particle #%-4d ---------------", n++) ;
00165    P.apply_motion_model(ut, lrf) ; // full LRF scan for scan matching
00166    P.apply_sensor_model(zt) ;      // limited scan for filter's
00167    if (slam_mode)                  // sensor-based correction steps
00168       P.update_map(zt) ;
00169 }
00170 
00171 // Divide each particle's weight by the sum of all their weights
00172 void normalize_weights(std::vector<Particle>& particles)
00173 {
00174    accumulator<float> sum =
00175       std::transform(particles.begin(), particles.end(), accumulator<float>(0),
00176                      std::mem_fun_ref(&Particle::weight)) ;
00177    std::for_each(particles.begin(), particles.end(),
00178                  std::bind2nd(std::mem_fun_ref(&Particle::normalize),
00179                               1/sum.value())) ;
00180 
00181    //LERROR("w_sum = %12.8f (%14.8e)", sum.value(), sum.value()) ;
00182    /*
00183    const int N = particles.size() ;
00184    for  (int i = 0; i < N; ++i) {
00185        float w = particles[i].weight() ;
00186        LERROR("particle[%4d].weight = %12.8f (%14.8e)", i, w, w) ;
00187    }
00188    // */
00189 }
00190 
00191 // This function computes the effective sample size of the particle
00192 // population by taking the reciprocal of the sum of the squares of the
00193 // normalized individual particle weights.
00194 int effective_sample_size(const std::vector<Particle>& particles)
00195 {
00196    float sum =
00197       std::accumulate(particles.begin(), particles.end(), 0.0f,
00198                       boost::bind(sum_of_squares<float>(), _1,
00199                                   boost::bind(&Particle::weight, _2))) ;
00200    int N = round(1/sum) ;
00201    //LERROR("w_sum_sqr = %12.8f, Neff = %d", sum, N) ;
00202    return N ;
00203 }
00204 
00205 } // end of local anonymous namespace encapsulating above helpers
00206 
00207 // This function implements the FastSLAM algorithm's particle update and
00208 // resampling steps. Resampling is carried out only when the effective
00209 // number of particles falls below a certain threshold. This strategy is
00210 // described in many different papers on particle filters. See, for
00211 // example, "Improving Grid-based SLAM with Rao-Blackwellized Particle
00212 // Filters by Adaptive Proposals and Selective Resampling" by Grisetti,
00213 // Stachniss and Burgard, ICRA 2005.
00214 //
00215 // NOTE: The effective sample size check can be turned off by configuring
00216 // the ESS threshold to be a negative number or to a number greater than
00217 // the number of particles. If the user has switched off the ESS check,
00218 // then resampling will occur after each and every FastSLAM update.
00219 void FastSLAM::update(const Odometry& ut, const LRFData& zt)
00220 {
00221    // First, update all particles using latest control and sensor inputs
00222    std::for_each(m_particles.begin(), m_particles.end(),
00223                  update_particle(ut, zt)) ;
00224 
00225    // Then, normalize the particle weights and resample particle
00226    // population based on their weights.
00227    //LERROR("=============== Resampling...  ===============") ;
00228    normalize_weights(m_particles) ;
00229    update_averages() ;
00230 
00231    const int N = m_particles.size() ;
00232    const int T = SlamParams::ess_threshold() ;
00233    if (T <= 0 || T > N || effective_sample_size(m_particles) < T)
00234    {
00235       // Low-variance resampling; see "Probabilistic Robotics" by Thrun,
00236       // Burgard and Fox (pg. 110).
00237       Particles particles ;
00238       particles.reserve(N) ;
00239 
00240       float n = 1.0f/N ;
00241       float r = randomf(0, n) ;
00242       float c = m_particles[0].weight() ;
00243       float w = std::max(0.0f, 1 - m_w_fast/m_w_slow);
00244       int   i = 0 ;
00245       //LERROR("n = %12.8f, r = %12.8f, w = %12.8f", n, r, w) ;
00246 
00247       float U = r ;
00248       for (int j = 0; j < N; ++j, U += n)
00249       {
00250          if (randomf(0, 1) < w)
00251          {
00252             //LERROR("randomizing particle #%-4d", i) ;
00253             Particle tmp = m_particles[i] ;
00254             tmp.randomize() ;
00255             particles.push_back(tmp) ;
00256          }
00257          else
00258          {
00259             //LERROR("U = %12.8f, c = %12.8f", U, c) ;
00260             while (U > c) {
00261                ++i ;
00262                c += m_particles[i].weight() ;
00263                //LERROR("c = %12.8f, i = %4d", c, i) ;
00264             }
00265             //LERROR("U = %12.8f, c = %12.8f, i = %4d", U, c, i) ;
00266             /*
00267             LERROR("particle[%4d] =  %-4d (w: %12.8f)",
00268                    j, i, m_particles[i].weight()) ;
00269             // */
00270             particles.push_back(m_particles[i]) ;
00271          }
00272       }
00273 
00274       m_particles = particles ;
00275    }
00276 }
00277 
00278 // This method updates the short and long-term particle weight averages,
00279 // which are used in the resampling step to determine when to insert
00280 // random particles to help deal with mislocalizations and also the
00281 // overall number of random particles that will be inserted.
00282 void FastSLAM::update_averages()
00283 {
00284    float w_avg = mean<float>(m_particles.begin(), m_particles.end(),
00285                              std::mem_fun_ref(& Particle::weight)) ;
00286    m_w_slow += SlamParams::alpha_slow() * (w_avg - m_w_slow) ;
00287    m_w_fast += SlamParams::alpha_fast() * (w_avg - m_w_fast) ;
00288 }
00289 
00290 //-------------------- PARTICLE DENSITY EXTRACTION ----------------------
00291 
00292 namespace {
00293 
00294 // This helper class implements a function object that can be used to
00295 // compute the overall error in the relevant state variables between the
00296 // particle with maximum weight and any other particle.
00297 //
00298 // The type parameters for this template are:
00299 //
00300 //    1. T -- the state variable for which we are interested in the error
00301 //    2. C -- a "converter" that returns a T given a lobot::Particle
00302 //    3. E -- a callable that makes the actual error determination
00303 //
00304 // Since this class is used in conjunction with Robolocust's
00305 // implementation of FastSLAM, the relevant state variables we're
00306 // interested in are either lobot::OccGrid or lobot::Pose. Thus, T would
00307 // be one of these two classes.
00308 //
00309 // This function object is intended to be used in conjunction with an STL
00310 // algorithm that iterates over a container of Particles. For each
00311 // particle, it will return an estimate of the error in the state of that
00312 // particle against the state of the particle with maximum weight. When
00313 // this function object is created, it will be passed the state
00314 // corresponding to the Particle with maximum weight. In order to
00315 // retrieve the state for each Particle in the sequence being iterated
00316 // over, the object will use the Particle-to-T converter (type C).
00317 //
00318 // Since the lobot::Particle class provides member functions to retrieve
00319 // that Particle's Pose and OccGrid map, type C will usually be an
00320 // adapter function object that calls the relevant member function of the
00321 // Particle class to "convert" the Particle to the state type T. An
00322 // example of such an adapter would be a function object returned by
00323 // std::mem_fun_ref.
00324 //
00325 // The E function will be passed two state variables: the first
00326 // corresponding to the Particle with maximum weight and the second
00327 // corresponding to the "current" Particle in the sequence being iterated
00328 // over. This function is expected to return a floating point number that
00329 // somehow measures the error between the current Particle's state
00330 // variable and the state of the Particle with maximum weight.
00331 //
00332 // As mentioned above, this function object is intended to be used in
00333 // conjunction with an STL algorithm that iterates over a container of
00334 // Particles. For each Particle in the sequence, it will return an
00335 // estimate of the error in the state of that Particle against the state
00336 // of the Particle with maximum weight. In addition to this error value,
00337 // it will also return the index of the Particle for which it just
00338 // performed the error evaluation. These two items are returned via an
00339 // STL pair, error value first and particle index second.
00340 template<typename T, typename C, typename E = float (*)(const T&, const T&)>
00341 class compute_error {
00342    const T& max ; // state corresponding to Particle with max weight
00343    const C& p2t ; // Particle-to-T "converter"
00344    const E& err ; // function to evaluate error between max & other Particles
00345    mutable int i; // index of current Particle in the Particle list
00346 public:
00347    compute_error(const T&, const C&, const E&) ;
00348    std::pair<float, int> operator()(const Particle&) const ;
00349 } ;
00350 
00351 // When the compute_error function object is created, it should be passed
00352 // the state of the particle with maximum weight, a function or function
00353 // object that retrieves/converts a Particle to a state variable (type T)
00354 // and a callable that can be used to estimate the error for this state
00355 // variable.
00356 template<typename T, typename C, typename E>
00357 compute_error<T,C,E>::compute_error(const T& t, const C& c, const E& e)
00358    : max(t), p2t(c), err(e), i(0)
00359 {}
00360 
00361 // As mentioned earlier, the compute_error function object is meant to be
00362 // used with a container of Particles. Each time this function object is
00363 // called, it will evaluate the error in the state of the "current"
00364 // Particle against that of the Particle with maximum weight using the
00365 // error function it was given when it was created.
00366 template<typename T, typename C, typename E>
00367 std::pair<float, int>
00368 compute_error<T,C,E>::
00369 operator()(const Particle& P) const
00370 {
00371    return std::make_pair(err(max, p2t(P)), i++) ;
00372 }
00373 
00374 // This function evaluates the error in the occupancy grids of two
00375 // particles. To measure the amount of error between two occupancy grids,
00376 // we perform a pixel/cell-wise comparison and return the overall sum of
00377 // the squares of the differences between the individual cells.
00378 float map_error(const OccGrid& m1, const OccGrid& m2)
00379 {
00380    if (&m1 == &m2) // same map ==> error will be zero
00381       return 0 ;   // so don't waste time computing the obvious
00382 
00383    using boost::bind ;
00384    accumulator<float> acc =
00385       std::transform(m1.begin(), m1.end(), m2.begin(), accumulator<float>(0),
00386                      bind(sqr<float>, bind(std::minus<float>(), _1, _2))) ;
00387    return acc.value() ; // sum of square errors
00388 }
00389 
00390 // This function evaluates the error in the poses of two Particles. The
00391 // total error in the pose of two particles is evaluated as simply the
00392 // sum of the squares of the differences between the individual pose
00393 // variables (x, y, theta).
00394 float pose_error(const Pose& p1, const Pose& p2)
00395 {
00396    // NOTE: Cannot apply (&p1 == &p2) check here to bypass useless zero
00397    // computation (as in map_error above) because Particle::pose()
00398    // returns a copy of its Pose object whereas Particle::map() returns a
00399    // reference to its OccGrid object. Therefore, the addresses of this
00400    // function's two parameters will always be different.
00401    //
00402    // DEVNOTE: We could change Particle::pose() so that it too returns a
00403    // reference to its Pose object. However, the error computation for
00404    // Pose objects is much less strenuous than the error computation for
00405    // OccGrid objects. So the object address check for bypassing the
00406    // error computation here is not as imperative as for the map case.
00407    return sqr(p2.x() - p1.x())
00408         + sqr(p2.y() - p1.y())
00409         + sqr(p2.t() - p1.t()) ;
00410 }
00411 
00412 // This helper function returns a function object for computing the map
00413 // error between the given map and the map associated with any other
00414 // Particle. It is meant to be used for the computation of the particle
00415 // filter's current best hypothesis regarding the occupancy grid map of
00416 // the robot's surroundings. Therefore, the map object passed to this
00417 // function should be the map of the Particle with maximum weight.
00418 //
00419 // Additionally, this function should be passed a "converter" (type C)
00420 // that can return a lobot::OccGrid given a lobot::Particle. Usually,
00421 // this would be a member function adapter such as the thing returned by
00422 // std::mem_fun_ref or boost::bind.
00423 //
00424 // With these two parameters in hand, this function will instantiate the
00425 // compute_error function object with the appropriate types and arguments
00426 // and return this object properly setup to perform the map error
00427 // computations.
00428 template<typename C>
00429 compute_error<OccGrid, C>
00430 error_function(const OccGrid& max_map, const C& particle_to_occgrid)
00431 {
00432    return compute_error<OccGrid, C>(max_map, particle_to_occgrid, map_error) ;
00433 }
00434 
00435 // This helper function returns a function object for computing the pose
00436 // error between the given Pose and the Pose associated with any other
00437 // Particle. It is meant to be used for the computation of the particle
00438 // filter's current best hypothesis regarding the robot's pose.
00439 // Therefore, the Pose object passed to this function should be the Pose
00440 // of the Particle with maximum weight.
00441 //
00442 // Additionally, this function should be passed a "converter" (type C)
00443 // that can return a lobot::Pose given a lobot::Particle. Usually, this
00444 // would be a member function adapter such as the thing returned by
00445 // std::mem_fun_ref or boost::bind.
00446 //
00447 // With these two parameters in hand, this function will instantiate the
00448 // compute_error function object with the appropriate types and arguments
00449 // and return this object properly setup to perform the pose error
00450 // computations.
00451 template<typename C>
00452 compute_error<Pose, C>
00453 error_function(const Pose& max_pose, const C& particle_to_pose)
00454 {
00455    return compute_error<Pose, C>(max_pose, particle_to_pose, pose_error) ;
00456 }
00457 
00458 // To construct the particle filter's current best hypothesis regarding
00459 // the robot's state (either the occupancy map or the robot pose), we use
00460 // a robust mean computed thusly:
00461 //
00462 //    1. Find particle with max weight
00463 //    2. Pick K particles whose maps/poses most closely match above particle
00464 //    3. Return average of above K maps/poses
00465 //
00466 // Since the procedure for both occupancy maps and poses is the same, we
00467 // use a template function to implement it. This function is parametrized
00468 // on the following types:
00469 //
00470 //    1. T -- the type of state extracted from the particle population
00471 //    2. C -- a "converter" function for returning a T from a Particle
00472 //
00473 // Since this function is used in conjunction with FastSLAM, the state
00474 // we're concerned with is either the occupancy grid map of the robot's
00475 // surroundings or the robot's pose. Thus, T will be either
00476 // lobot::OccGrid or lobot::Pose.
00477 //
00478 // The type C will be a function or function object that takes a Particle
00479 // and returns a T (OccGrid or Pose) from that Particle. Since the
00480 // lobot::Particle class provides member functions for returning that
00481 // particle's hypothesis regarding the map and pose, type C will usually
00482 // be a member function adapter (e.g., returned by std::mem_fun_ref).
00483 //
00484 // The parameters to this function are:
00485 //
00486 //    1. particles -- the particle filter's current list of particles
00487 //    2. p2t -- a function/object that takes a Particle and returns a T
00488 //
00489 // DEVNOTE: Since this function's argument list does not contain a T,
00490 // which only figures in its return type, automatic type deduction will
00491 // not work (as return types don't count when determining a function's
00492 // signature).
00493 template<typename T, typename C>
00494 T current_best_hypothesis(const std::vector<Particle>& particles, C p2t)
00495 {
00496    // Find particle with max weight
00497    std::vector<Particle>::const_iterator max =
00498       std::max_element(particles.begin(), particles.end(),
00499                        boost::bind(std::less<float>(),
00500                                    boost::bind(&Particle::weight, _1),
00501                                    boost::bind(&Particle::weight, _2))) ;
00502 
00503    // Special case: don't bother with finding top K matches to above
00504    // particle when K is one. If K is one, then the particle with max
00505    // weight is the thing we're interested in.
00506    const int K = SlamParams::num_matches() ;
00507    if (K == 1)
00508       return p2t(*max) ;
00509 
00510    // Compute overall errors between the relevant state variable (T) of
00511    // all other particles and the corresponding state variable of the
00512    // particle with max weight. These errors are stored along with their
00513    // corresponding particle indices.
00514    typedef std::pair<float, int> ErrIndPair ;
00515    std::vector<ErrIndPair> E ;
00516    E.reserve(particles.size()) ;
00517    std::transform(particles.begin(), particles.end(),
00518                   std::back_inserter(E), error_function(p2t(*max), p2t)) ;
00519 
00520    // Partially sort the errors so that the top K particles whose states
00521    // are closest to the state of the particle with max weight are at the
00522    // front of the error-index pair vector.
00523    std::partial_sort(E.begin(), E.begin() + K, E.end(),
00524                      boost::bind(std::less<float>(),
00525                                  boost::bind(get_first<ErrIndPair>, _1),
00526                                  boost::bind(get_first<ErrIndPair>, _2))) ;
00527 
00528    // Compute final state as average of states of top K particles closest
00529    // to particle with max weight.
00530    T t ;
00531    for (int i = 0; i < K; ++i)
00532       t += p2t(particles[E[i].second]) ;
00533    return t * (1.0f/K) ;
00534 }
00535 
00536 } // end of local anonymous namespace encapsulating above helpers
00537 
00538 // This method computes the particle filter's current best hypothesis
00539 // regarding the occupancy grid map of the robot's surroundings using a
00540 // robust mean, i.e., a mean of the maps of the K particles whose maps
00541 // most closesly resemble the map built by the particle with maximum
00542 // weight.
00543 OccGrid FastSLAM::current_map() const
00544 {
00545    return current_best_hypothesis<OccGrid>(m_particles,
00546                                            std::mem_fun_ref(&Particle::map)) ;
00547 }
00548 
00549 // This method computes the particle filter's current best hypothesis
00550 // regarding the robot's pose using a robust mean, i.e., a mean of the
00551 // poses of the K particles whose poses most closesly match the pose
00552 // estimated by the particle with maximum weight.
00553 Pose FastSLAM::current_pose() const
00554 {
00555    return current_best_hypothesis<Pose>(m_particles,
00556                                         std::mem_fun_ref(&Particle::pose)) ;
00557 }
00558 
00559 //----------------------- VISUALIZATION SUPPORT -------------------------
00560 
00561 std::vector<Particle::Viz> FastSLAM::viz() const
00562 {
00563    std::vector<Particle::Viz> v ;
00564    v.reserve(m_particles.size()) ;
00565    std::transform(m_particles.begin(), m_particles.end(),
00566                   std::back_inserter(v), std::mem_fun_ref(&Particle::viz)) ;
00567    return v ;
00568 }
00569 
00570 //----------------------------- CLEAN-UP --------------------------------
00571 
00572 FastSLAM::~FastSLAM(){}
00573 
00574 //-----------------------------------------------------------------------
00575 
00576 } // end of namespace encapsulating this file's definitions
00577 
00578 /* So things look consistent in everyone's emacs... */
00579 /* Local Variables: */
00580 /* indent-tabs-mode: nil */
00581 /* End: */
Generated on Sun May 8 08:41:31 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3