Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

ParticleFilter.h

Go to the documentation of this file.
00001 #ifndef INCLUDED_ParticleFilter_h
00002 #define INCLUDED_ParticleFilter_h
00003 
00004 #include <vector>
00005 #include <algorithm>
00006 #include <iostream>
00007 #include <cfloat>
00008 #include <cmath>
00009 
00010 //! Provides a common base class for particles used by the ParticleFilter
00011 /*! Each particle represents a hypothesis regarding a position in state space.
00012  *  The state space being modeled is defined by the fields tracked by
00013  *  the particles, a sensor model which evaluates the 'health' of each particle
00014  *  based on incoming information from the world, and a motion model
00015  *  which updates particles based on the robot's own changes to the world.
00016  *
00017  *  For a common example, see the LocalizationParticle for 
00018  *  tracking a robot's position and orientation in a 2D world.
00019  *
00020  *  The default LowVarianceResamplingPolicy has two requirements for
00021  *  particles.  One requirement is that all particle implementations must
00022  *  define a 'DistributionPolicy' (usually via typedef within your class) so
00023  *  that the resampler can create randomly generated particles and
00024  *  modify existing ones. (see ParticleFilter::DistributionPolicy)
00025  *
00026  *  The second requirement is that all particles provide a public 'weight'
00027  *  field so that particles can be compared.  The recommended way to do
00028  *  this is to inherit from this ParticleBase base class.  However, since
00029  *  templates are used to specify the particle type to the particle filter,
00030  *  you can use an unaffiliated class as long as it provides a weight member.
00031  *  (However, inheritance is still recommended so you'll automatically pick up
00032  *  any changes or new requirements made to this base class.)
00033  *
00034  *  The final requirement of the ParticleFilter itself is to provide a
00035  *  sumSqErr() function so that a confidence interval can be computed.
00036  *  However, the meaning of the value returned by this function is entirely
00037  *  up to you.  The base class provides a prototype for the function, but
00038  *  its implementation is abstract.*/
00039 
00040 template<class ParticleT> class ParticleBase {
00041  public:
00042   //! constructor
00043   ParticleBase() : weight(0) {}
00044     //! destructor
00045     virtual ~ParticleBase() {};
00046   
00047     //! returns the sum squared error between this particle and @a p
00048     /*! This is only used to compute the confidence of the particle filter,
00049      *  you may want to weight some dimensions differently if they tend
00050      *  to have smaller values or are more important.  How you interpret
00051      *  ParticleFilter::confidence() depends on how this function is implemented. */
00052     virtual float sumSqErr(const ParticleT& p) const=0;
00053   
00054     //! indicates the 'health' of the particle -- the bigger the value, the better this particle
00055     /*! Generally weights are indicative of probability, but are often unnormalized
00056      *  since the normalization factor is constant across particles and thus doesn't
00057      *  affect matters of relative comparison.
00058      *
00059      *  Further, weights tend to be very small as the accumulation of a number of
00060      *  sensor values tend to be each somewhat unlikely, and taken together
00061      *  the particle's weight shrinks exponentially.  Thus it useful to work in
00062      *  log space to avoid numeric underflow on the range of a floating point value.
00063      *  This also has the advantage of transforming multiplication operations to
00064      *  slightly quicker addition operations.  The default LowVarianceResamplingPolicy
00065      *  has a logWeights member so you can indicate whether weight values
00066      *  should be interpreted logarithmically (i.e. negative values)
00067      *  or linearly (e.g. positive (and generally very small) values). (default is logarithmic) */
00068     float weight;
00069 };
00070 
00071 //! Implements a particle filter with support for a variety of applications through the usage of arbitrary combination of a variety of models and policies
00072 /*! The particle type is passed as a template parameter, which provides the implementation
00073  *  advantage of being able to directly store arrays of particles as contiguous blocks in memory.  This allows
00074  *  better cache coherency and enables platform-specific acceleration tricks, such as SIMD calls.
00075  *
00076  *  There are a number of embedded classes which together form the implementation of the
00077  *  particle filter.  The advantage of this architecture is that you can mix and match any
00078  *  combination of modules to get the features needed for your application.
00079  *  - SensorModel: pass one of these to updateSensors in order to evaluate the particles
00080  *    as new information is discovered.  You may have several different sensors at the same
00081  *    time, simply create a model for each type of sensor, and pass it to the filter when updated.
00082  *  - MotionModel: modifies particle states based on the expected outcome of
00083  *    any controls you might have over the system.  See DeadReckoningBehavior for an example.
00084  *    Generally, you will install one motion model, and this model will be given a opportunity
00085  *    to update expected particle state before each sensor update.  (unless you pass 'false'
00086  *    to updateSensors()).  MotionModel can be NULL if you have no control over the system.
00087  *  - DistributionPolicy: defines how to generate random particles and "jiggle" existing ones.
00088  *    The default DistributionPolicy is usually specified via a typedef in the particle itself, and
00089  *    is stored as a property of the ResamplingPolicy (next item) since that is what will use it.
00090  *  - ResamplingPolicy: Following a sensor update, you may wish to re-evaluate the particles
00091  *    in use, making copies of the "good" particles, and dropping those which are not matching
00092  *    sensor values.  If you receive a group of different sensor readings, you may want to
00093  *    hold off on resampling until they have all been applied for better evaluation of the particles
00094  *    before selecting which to replicate.  Similarly, if your sensors are noisy, you may want to
00095  *    take several readings before allowing resampling so you don't kill off all the "good" particles
00096  *    based on a bad reading.  Pass 'false' to updateSensors() or use the delayCount parameter of
00097  *    LowVarianceResamplingPolicy.  The resampling policy can be 'NULL' if you never want to
00098  *    resample, but it defaults to an instance of LowVarianceResamlingPolicy.
00099  *
00100  *  Generally, preparing to use a particle filter requires these prerequisites:
00101  *  - write your particle class and its associated distribution policy
00102  *    (see LocalizationParticle and LocalizationParticleDistributionPolicy)
00103  *  - write a sensor model to evaluate particles using sensors you'll have available
00104  *    (see DualCoding::ShapeSensorModel)
00105  *  - write a motion model if you have any knowledge of modifications to the state
00106  *    (see HolonomicMotionModel and DeadReckoningBehavior)
00107  *
00108  *  Once these are available, usage goes something like this:
00109  *  -# create particle filter, optionally passing motion model and/or resampling policy
00110  *  -# customize parameters for resampling and distribution policies
00111  *  -# while active (note these are all "as needed", in no particular order):
00112  *     - update motion model whenever there's a change in controls (e.g. call setVelocity() on
00113  *        a HolonomicMotionModel)
00114  *     - create/pass SensorModel(s) for any measurements obtained (e.g. call updateSensors()
00115  *        on the particle filter)
00116  *     - call getEstimate() on the particle filter to obtain current state estimate
00117  *
00118  *  Remember that the particle filter takes responsibility for deallocating all policies and the
00119  *  motion model when they are removed.  Do not attempt to reuse them between particle
00120  *  filters. SensorModels are the only exception -- they are @e not retained between calls
00121  *  to updateSensors, so you can reuse them.
00122  */
00123 
00124 template<typename ParticleT>
00125 class ParticleFilter {
00126  public:
00127   typedef ParticleT particle_type; //!< redefinition here allows reference to the particle type even if the template parameter may be abstracted away due to a typedef
00128   typedef typename std::vector<particle_type> particle_collection; //!< the collection type we'll be using to store the particles
00129   typedef typename particle_collection::size_type index_t; //!< index type for refering to particles within the collection
00130   
00131   //! A sensor model is used to update particle weights to account based on each particle's ability to explain observations taken from the system
00132   class SensorModel {
00133   public:
00134     typedef ParticleT particle_type; //!< redefinition here allows reference to the particle type even if the template parameter may be abstracted away due to a typedef
00135     typedef typename std::vector<particle_type> particle_collection; //!< the collection type we'll be using to store the particles
00136     typedef typename particle_collection::size_type index_t; //!< index type for refering to particles within the collection
00137     virtual ~SensorModel() {} //!< destructor (no-op for base class)
00138     
00139     //! once passed to the particle filter's updateSensors(), this will be called to allow the sensor model to update the 'weight' member of each particle
00140     /*! @param particles the current set of particles to be evaluated
00141      *  @param[out] estimate the weighted mean of the particle values
00142      *
00143      *  Remember to @e update each particle's weight, don't overwrite it.  In other words,
00144      *  you want to combine (e.g. add or multiply) the weight from the current sensor evaluation
00145      *  with the weight currently stored in each particle, don't just replace it.  This is because
00146      *  there may be several sensor updates between resamplings so that particles can be
00147      *  more accurately evaluated. */
00148     virtual void evaluate(particle_collection& particles, particle_type& estimate)=0;
00149   };
00150 
00151   //! A motion model is retained by the particle filter to query before evaluating sensor measurements so all known influences are accounted for before testing the particles
00152   /*! It's a good idea to apply noise to the motion model depending on the precision of the model.
00153    *  This allows the particle cluster to spread over time until new information is obtained to
00154    *  to evaluate how accurate the motion really was, at which point resampling will collapse
00155    *  the cluster back down again. */
00156   class MotionModel {
00157   public:
00158     typedef ParticleT particle_type; //!< redefinition here allows reference to the particle type even if the template parameter may be abstracted away due to a typedef
00159     typedef typename std::vector<particle_type> particle_collection; //!< the collection type we'll be using to store the particles
00160     typedef typename particle_collection::size_type index_t; //!< index type for refering to particles within the collection
00161     virtual ~MotionModel() {} //!< destructor
00162     
00163     //! The particle filter will call these when it wants to update particle state values to account for known influences
00164     /*! See the class notes regarding the usefulness of adding noise to the control parameters (or their effects) */
00165     virtual void updateMotion(particle_collection& particles, particle_type &estimate)=0;
00166   };
00167 
00168   //! A distribution policy provides the ability to randomize ("redistribute") or tweak the values of a group of particles
00169   /*! Unlike the other particle filter helper classes, the functions for the distribution policy
00170    *  operate on a subset of the particles at a time.
00171    *  You may wonder why the randomize() and jiggle() functions aren't simply made methods
00172    *  of the ParticleBase class.  The main reason is that these functions may need additional 
00173    *  parameters, such as specification of how large an area to distribute over, and these
00174    *  parameters are static across particles.  However, if they were actually static members
00175    *  of the particle class, then the values would be shared by all particle filters.  By making
00176    *  a separate class to hold the parameters and apply the one-to-many relationship, you
00177    *  can have multiple particle filters with the same type of particle, and each filter can have
00178    *  different parameter values controlling distribution of its particles.
00179    *
00180    *  Note that the DistributionPolicy is actually a property of the resampling policy, not
00181    *  directly of the particle filter itself. */
00182   class DistributionPolicy {
00183   public:
00184     typedef ParticleT particle_type; //!< redefinition here allows reference to the particle type even if the template parameter may be abstracted away due to a typedef
00185     typedef typename std::vector<particle_type> particle_collection; //!< the collection type we'll be using to store the particles
00186     typedef typename particle_collection::size_type index_t; //!< index type for refering to particles within the collection
00187     virtual ~DistributionPolicy() {} //!< destructor
00188     
00189     //! This should redistribute the particles over a large area, independently of the particle's current value
00190     /*! Randomization occurs whenever the particle filter doesn't have any usable particles for
00191      *  replication, either because the particle filter has just been created and doesn't have any
00192      *  information yet, or because new sensor readings have invalidated all of the current particles. */
00193     virtual void randomize(particle_type* begin, index_t num)=0;// { particle_type* end=begin+num; while(begin!=end) (begin++)->randomize(); }
00194     
00195     //! This should slightly modify the particles' state values
00196     /*! @param var indicates the scale of the variance desired -- multiply whatever variance you use for modifying each state parameter by this value
00197      *  @param begin the first particle in the array
00198      *  @param num the number of particles to apply the operation to
00199      *
00200      *  This function is called on particles which have been replicated from an existing
00201      *  particle to explore the space around that particle.  The more accurate your
00202      *  sensors and particle evaluation, the smaller the jiggle variance can be. */
00203     virtual void jiggle(float var, particle_type* begin, index_t num)=0;// { particle_type* end=begin+num; while(begin!=end) (begin++)->jiggle(var); }
00204   };
00205   
00206   //! The resampling policy focuses the particle filter on those particles which are performing well, and dropping those which are poorly rated
00207   /*! Resampling should replicate particles proportionally to how well their weights compare
00208    *  to other particles in the filter.  The process is similar to a genetic algorithm.
00209    *  This process typically does not vary between applications,
00210    *  so you will probably want to use the supplied LowVarianceResamplingPolicy, and
00211    *  simply tweak parameters as needed.
00212    *
00213    *  The ResamplingPolicy interface includes a DistributionPolicy so particles can be
00214    *  randomly generated or modified in an abstract manner. */
00215   class ResamplingPolicy {
00216   public:
00217     typedef ParticleT particle_type; //!< redefinition here allows reference to the particle type even if the template parameter may be abstracted away due to a typedef
00218     typedef typename std::vector<particle_type> particle_collection; //!< the collection type we'll be using to store the particles
00219     typedef typename particle_collection::size_type index_t; //!< index type for refering to particles within the collection
00220     
00221     //! constructor, creates a DistributionPolicy based on the particle_type's own DistributionPolicy typedef
00222     ResamplingPolicy() : dist(new typename particle_type::DistributionPolicy) {}
00223       //! constructor, pass your own custom distribution policy (responsibility for deallocation is assumed by the ResamplingPolicy)
00224       explicit ResamplingPolicy(DistributionPolicy * distPolicy) : dist(distPolicy) {}
00225   //! destructor
00226   virtual ~ResamplingPolicy() { delete dist; };
00227   //! the particle filter will call resample() when the particles have been evaluated and are ready to be selected
00228   virtual void resample(particle_collection& particles)=0;
00229   //! replaces #dist with a new distribution policy.  If you pass NULL, #dist will be reset to the particle_type's default distribution policy, as specified by a 'DistributionPolicy' typedef within the particle class
00230   virtual void setDistributionPolicy(DistributionPolicy * distPolicy) {
00231     delete dist;
00232     dist = (distPolicy!=NULL) ? distPolicy : new typename particle_type::DistributionPolicy;
00233   }
00234   //! returns the currently active distribution policy (#dist)
00235   virtual DistributionPolicy& getDistributionPolicy() const { return *dist; }
00236   protected:
00237   //! a pointer to the current distribution policy, which cannot be NULL
00238   DistributionPolicy * dist;
00239   private:
00240   ResamplingPolicy(const ResamplingPolicy& rp); //!< copy unsupported
00241   ResamplingPolicy& operator=(const ResamplingPolicy& rp); //!< assignment unsupported
00242   };
00243   
00244   //! This class provides a generic, default ResamplingPolicy.  It is based on the low variance resampling policy algorithm found in "Probabilistic Robotics" by Sebastian Thrun, Wolfram Burgard, Dieter Fox
00245   /*! This class is called "low variance" because it will maintain particle modalities in the face of
00246    *  uniform weighting.  This means that if resamples are triggered when no new information
00247    *  is available, every particle is resampled for the next generation.  This prevents the eventual
00248    *  convergence of particle clusters over time.
00249    *
00250    *  However, this implementation provides a #varianceScale parameter for adding variance
00251    *  to the particle's state on each generation, which can be useful for more rapidly exploring
00252    *  the state space around a "winning" particle.  Ideally, it is better to use a very low resampling
00253    *  variance, and rely on noise in the motion model and a broad probability distribution in
00254    *  the sensor model to allow particles to spread.  #varianceScale is really a crutch to manage
00255    *  an overconfident sensor model (one which weights "correct" particles with sharply higher values).
00256    *  
00257    *  The #varianceScale parameter defaults to a negative value, which indicates the
00258    *  resampling variance will be scaled with particle weight to provide broader sampling when
00259    *  particle weights are poor, and tighten sampling when particles are tracking accurately.  This
00260    *  requires setting a value for #minAcceptableWeight, described next.
00261    *
00262    *  The other trick this implementation provides is specification of a minimum acceptable
00263    *  particle weight (#minAcceptableWeight).  If the best particle's weight is below this value,
00264    *  new, randomly generated particles will be created, up to #maxRedistribute percent of
00265    *  the particles on a round of resampling.  This handles situations where the actual state
00266    *  has somehow jumped out of the region being sampled by the particles, and the filter is "lost".
00267    *  Without some further information (i.e. fixing the MotionModel to predict the "kidnapping"),
00268    *  this can provide automatic re-acquisition of the position in state space (at the cost of
00269    *  spawning new modalities).
00270    *
00271    *  Finally, #resampleDelay is provided to limit actual resampling to one in every #resampleDelay
00272    *  attempts.  This allows you to only resample at a lower frequency than the sensor model,
00273    *  without having to manually track the number of sensor samples and pass a parameter to
00274    *  the ParticleFilter::updateSensors() to limit resampling yourself.  The reason you would
00275    *  want to limit the resampling frequency is to better evaluate the particles before selecting
00276    *  them for replication or pruning -- if your sensors are noisy and you resample too often,
00277    *  bad values will kill off good particles on a regular basis, causing the filter to continually be "lost".
00278    *
00279    *  This policy can interpret weights in either "log space" or "linear space".  It defaults to "log space",
00280    *  but if your sensor model is providing linear weights, set #logWeights to false.
00281    */
00282   class LowVarianceResamplingPolicy : public ResamplingPolicy {
00283   public:
00284     //! constructor
00285     LowVarianceResamplingPolicy()
00286       : varianceScale(-2), maxRedistribute(1/2.f), minAcceptableWeight(-30),
00287       logWeights(true), resampleDelay(0), newParticles(), resampleCount(0)
00288       {}
00289       virtual void resample(particle_collection& particles);
00290     
00291       //! returns true if the next call to resample will trigger a "real" resampling (is #resampleCount greater than #resampleDelay?)
00292       bool nextResampleIsFull() { return resampleCount>=resampleDelay; }
00293     
00294       //! If non-negative, passed to the DistributionPolicy's jiggle() for replicated particles; otherwise an "automatic" value is used which inversely scales with the best particle weight
00295       /*! A negative value is still used to control the maximum magnitude of the resampling variance.
00296        *  It's better to keep this small (or zero) and rely on the sensor and motion model's noise parameters */
00297       float varianceScale;
00298       //! A percentage (0-1) of the particles which can be randomly re-distributed on a single resampling if the best particle's weight is below #minAcceptableWeight
00299       float maxRedistribute;
00300       //! The lowest weight per resample attempt to consider "acceptable"
00301       /*! This is scaled by resampleDelay when being compared to particle weights, so that
00302        *  you don't have to adjust this parameter when you increase resampleDelay.
00303        *  As the best particle weight drops below this value, more particles will be randomly
00304        *  redistributed, up to #maxRedistribute. */
00305       float minAcceptableWeight; 
00306       //! This controls the interpretation of particle weights.  If true, they are interpreted as "log space", otherwise "linear space"
00307       bool logWeights;
00308       //! This indicates how many resampling attempts should be skipped before actually doing it.  See class notes for rationale.
00309       unsigned int resampleDelay;
00310   protected:
00311       particle_collection newParticles; //!< temporary scratch space as particles are created
00312       unsigned int resampleCount; //!< the number of resampling attempts which have occurred.
00313   };
00314   
00315   
00316   //! Constructor for the particle filter, specify number of particles and optionally pass a motion model and resampling policy
00317   /*! The particle filter assumes responsibility for eventual deallocation of the motion model and resampling policy */
00318   explicit ParticleFilter(unsigned int numParticles, MotionModel* mm=NULL, ResamplingPolicy* rs=new LowVarianceResamplingPolicy)
00319     : particles(numParticles), estimate(), variance(), varianceValid(false), motion(mm), resampler(rs), hasEvaluation(false)
00320     {
00321       if(numParticles>0)
00322   resetFilter(particles.front().weight);
00323     }
00324   
00325     //! Destructor
00326     virtual ~ParticleFilter() { delete motion; delete resampler; }
00327   
00328     //! Returns the current motion model (#motion)
00329     virtual MotionModel * getMotionModel() const { return motion; }
00330     //! Reassigns the motion model, deleting the old one; motion model can be NULL
00331     virtual void installMotionModel(MotionModel* mm) { delete motion; motion=mm; }
00332   
00333     //! Returns the current resampling policy (#resampler)
00334     virtual ResamplingPolicy * getResamplingPolicy() const { return resampler; }
00335     //! Reassigns the resampling policy, deleting the old one; resampling policy can be NULL (although not recommended...)
00336     virtual void installResamplingPolicy(ResamplingPolicy* rs) { delete resampler; resampler=rs; }
00337   
00338     //! Sets the resampling policy's resampleDelay, which controls how many sensor updates to process before resampling the particles; policy must be a LowVarianceResamplingPolicy
00339     virtual void setResampleDelay(unsigned int d) {
00340       LowVarianceResamplingPolicy* p = dynamic_cast<LowVarianceResamplingPolicy*>(getResamplingPolicy());
00341       if ( p )
00342   p->resampleDelay = d;
00343       else
00344   std::cout << "Warning: setResampleDelay found getResamplingPolicy() returns wrong type policy; resampleDelay not set." << std::endl;
00345     }
00346   
00347     //! Sets the resampling policy's minimum acceptable weight for a particle; policy must be a LowVarianceResamplingPolicy
00348     virtual void setMinAcceptableWeight(float w) {
00349       LowVarianceResamplingPolicy* p = dynamic_cast<LowVarianceResamplingPolicy*>(getResamplingPolicy());
00350       if ( p )
00351   p->minAcceptableWeight = w;
00352       else
00353   std::cout << "Warning: setMinAcceptableWeight found getResamplingPolicy() returns wrong type policy; minAcceptableWeight not set." << std::endl;
00354     }
00355   
00356     //! If getResamplingPolicy() returns a LowVarianceResamplingPolicy instance, this will set LowVarianceResamplingPolicy::maxRedistribute; otherwise will display a warning
00357     virtual void setMaxRedistribute(float r) {
00358       LowVarianceResamplingPolicy* p = dynamic_cast<LowVarianceResamplingPolicy*>(getResamplingPolicy());
00359       if ( p )
00360   p->maxRedistribute = r;
00361       else
00362   std::cout << "Warning: setMaxRedistribute found getResamplingPolicy() returns wrong type policy; maxRedistribute not set." << std::endl;
00363     }
00364   
00365     //! If getResamplingPolicy() returns a LowVarianceResamplingPolicy instance, this will set LowVarianceResamplingPolicy::varianceScale; otherwise will display a warning
00366     virtual void setVarianceScale(float s) {
00367       LowVarianceResamplingPolicy* p = dynamic_cast<LowVarianceResamplingPolicy*>(getResamplingPolicy());
00368       if ( p )
00369   p->varianceScale = s;
00370       else
00371   std::cout << "Warning: setVarianceScale found getResamplingPolicy() returns wrong type policy; varianceScale not set." << std::endl;
00372     }
00373   
00374 
00375     //! Allows you to manually request a position update -- you might want to call this before using getEstimate's state information
00376     virtual void updateMotion() {
00377       if(motion!=NULL)
00378   motion->updateMotion(particles,estimate);
00379       varianceValid = false;
00380     }
00381   
00382     //! Applies the sensor model's evaluation to the particles, optionally updating the motion model and resampling first
00383     /*! If you are applying a group of sensor readings, you probably only want to update motion for the first one
00384      *  (since no motion is occuring between the readings if they were taken at the same time), and may
00385      *  want to hold off on resampling until the end (so the particles are better evaluated).
00386      *  If using the default LowVarianceResamplingPolicy, see also LowVarianceResamplingPolicy::resampleDelay. */
00387     virtual void updateSensors(SensorModel& sm, bool updateMot=true, bool doResample=true) {
00388       if(updateMot)
00389   updateMotion();
00390       if(doResample && hasEvaluation)
00391   resample();
00392       sm.evaluate(particles, estimate);
00393       hasEvaluation=true;
00394       varianceValid = false;
00395     }
00396   
00397     //! A manual call to trigger resampling
00398     virtual void resample() {
00399       if(resampler!=NULL)
00400   resampler->resample(particles);
00401       hasEvaluation=false;
00402     }
00403   
00404     //! Assigns the specified weight value to all of the particles
00405     virtual void resetWeights(float w) {
00406       for(typename particle_collection::iterator it=particles.begin(); it!=particles.end(); ++it)
00407   it->weight=w;
00408       hasEvaluation=false;
00409     }
00410 
00411     //! Requests that the resampler's distribution policy randomly distribute all of the particles, and reset weights to @a w.
00412     /*! You might want to do this if you believe you have been "kidnapped" by some unmodeled motion
00413      *  to a new area of state space, and need to restart the filter to determine the new location. */
00414     virtual void resetFilter(float w) {
00415       if(resampler!=NULL)
00416         resampler->getDistributionPolicy().randomize(&particles[0],particles.size());
00417       resetWeights(w);
00418     }
00419 
00420     virtual const particle_type& getEstimate() const { return estimate; } //!< Returns the weighted mean of all the #particles
00421 
00422     //! Returns the variance of the #particles
00423     virtual const particle_type& getVariance() {
00424       if ( !varianceValid )
00425   computeVariance();
00426       return variance;
00427     }
00428 
00429     //! Computes the variance of the particles; no-op here: override in subclass
00430     virtual void computeVariance() { varianceValid = true; }
00431 
00432     virtual particle_collection& getParticles() { return particles; } //!< Returns a reference to #particles itself (if you want to modify the particles, generally better to formulate it in terms of a sensor model or motion model for consistency)
00433     virtual const particle_collection& getParticles() const { return particles; } //!< Returns a reference to #particles itself (if you want to modify the particles, generally better to formulate it in terms of a sensor model or motion model for consistency)
00434 
00435     //! if you know the position in state space, pass it here, along with a positive varianceScale if you want some jiggle from the distribution policy
00436     virtual void setPosition(const particle_type& pos, float jiggleVariance=0) {
00437       particles.assign(particles.size(),pos);
00438       if ( jiggleVariance > 0 )
00439   resampler->getDistributionPolicy().jiggle(jiggleVariance,&particles.front(),particles.size());
00440     }
00441   
00442     //! Returns a confidence interval based on the particle_type's sumSqErr implementation (see ParticleBase::sumSqErr())
00443     virtual float getConfidenceInterval() const {
00444       float d=0;
00445       for(typename particle_collection::const_iterator it=particles.begin(); it!=particles.end(); ++it)
00446   d += it->sumSqErr(estimate);
00447       return std::sqrt(d/(particles.size()-1));
00448     }
00449     //! Adjusts the size of the particle collection -- more particles gives better coverage, but more computation
00450     /*! You may wish to shrink the number of particles when the confidence interval is small or
00451      *  particle weights are high, and increase particles when the filter is getting "lost". */
00452     virtual void resizeParticles(unsigned int numParticles) {
00453       std::cerr << "Resizing particles from " << particles.size() << " to " << numParticles << std::endl;
00454       if(numParticles > particles.size()) {
00455   index_t oldsize=particles.size();
00456   particles.resize(numParticles);
00457   if(resampler!=NULL)
00458     resampler->getDistributionPolicy().randomize(&particles[oldsize],numParticles-oldsize);
00459       
00460       } else if(numParticles < particles.size()) {
00461   std::vector<particle_type*> sorted(particles.size());
00462   for(unsigned int i=0; i<particles.size(); ++i)
00463     sorted[i]=&particles[i];
00464   std::sort(sorted.begin(),sorted.end(),weightLess);
00465   particle_collection newParticles;
00466   newParticles.reserve(numParticles);
00467   for(unsigned int i=sorted.size()-numParticles-1; i<sorted.size(); ++i)
00468     newParticles.push_back(*sorted[i]);
00469 
00470   // The swap below will mess up the particle shapes in the sketchGUI
00471 
00472   particles.swap(newParticles);
00473       }
00474     }
00475   
00476  protected:
00477  public:// for debugging
00478   //!< used for sorting particles in resizeParticles() to drop the least weighted particles first
00479     static bool weightLess(const particle_type* a, const particle_type* b) { return a->weight < b->weight; }
00480   
00481     particle_collection particles; //!< Storage of the particles (no particular order)
00482     particle_type estimate; //!< Weighted mean of all the particles; weight is the weight of the best particle
00483     particle_type variance; //!< variance of the particles
00484     bool varianceValid; //!< True if the particles haven't changed since the variance was last computed
00485     MotionModel * motion; //!< motion model, can be NULL if you have no control or knowledge of changes in the system
00486     ResamplingPolicy * resampler; //!< resampling policy refocuses filter on "good" particles, can be NULL but filter won't work well without a resampler
00487     bool hasEvaluation; //!< set to true following each call to updateSensors, and false following resample() or resetWeights(); avoids repeated resamplings
00488   
00489  private:
00490     ParticleFilter(const ParticleFilter&); //!< don't call (copy constructor)
00491     ParticleFilter& operator=(const ParticleFilter&); //!< don't call (assignment operator)
00492 };
00493 
00494 template<typename ParticleT>
00495 void ParticleFilter<ParticleT>::LowVarianceResamplingPolicy::resample(particle_collection& particles) {
00496   if ( resampleCount++ < resampleDelay )
00497     return;
00498   resampleCount=0;
00499   // std::cerr << "RESAMPLE UNDERWAY" << std::endl;
00500   // std::cerr << "Best particle is " << bestIndex << " @ " << particles[bestIndex].weight << std::endl;
00501   
00502   // we reuse newParticles each time, doing an STL O(1) swap to quickly exchange contents
00503   // have to make sure it's still the right size though...
00504   if(newParticles.size()<particles.size() || newParticles.size()>particles.size()*2)
00505     newParticles.resize(particles.size());
00506   if(particles.size()==0)
00507     return;
00508   
00509   // This part figures out how many particles we're going to globally redistribute,
00510   // leaving the rest for resampling
00511   float bestWeight = -FLT_MAX;
00512   for (size_t i=0; i<particles.size(); i++)
00513     if ( particles[i].weight > bestWeight )
00514       bestWeight = particles[i].weight;
00515   float redistributeRatio = 1;
00516   if ( logWeights ) {
00517     float min=minAcceptableWeight*(resampleDelay+1);
00518     if ( bestWeight > min ) {
00519       redistributeRatio = 1 - (min-bestWeight)/min;
00520       if ( redistributeRatio > 1 )
00521   redistributeRatio=1;
00522     }
00523   }
00524   else { // not logWeights
00525     float min=std::pow(minAcceptableWeight,(float)(resampleDelay+1));
00526     if ( bestWeight > min )
00527       redistributeRatio = (1-bestWeight/min);
00528   }
00529   unsigned int numRedistribute = (unsigned int)(particles.size() * redistributeRatio * maxRedistribute);
00530   std::cout << "Particle filter resampling: minAcceptable=" << minAcceptableWeight << "  best=" << bestWeight
00531             << "  redistRatio=" << redistributeRatio << "  numRedist=" << numRedistribute << std::endl;
00532   
00533   // now do resampling, writing into newParticles
00534   const unsigned int numResample=newParticles.size()-numRedistribute;
00535   //std::cerr << "best " << bestIndex << " @ " << bestWeight << " numRedist. " << numRedistribute << " of " << particles.size() << std::endl;
00536   if(numResample>0) {
00537     // add up the cumulative weights for each particle...
00538     std::vector<float> weights(particles.size());
00539     if(logWeights) {
00540       weights[0]=std::exp(particles.front().weight-bestWeight);
00541       for (unsigned int i=1; i < particles.size(); i++)
00542   weights[i] = weights[i-1] + std::exp(particles[i].weight-bestWeight);
00543     } else {
00544       weights[0]=particles.front().weight/bestWeight;
00545       for (unsigned int i=1; i < particles.size(); i++)
00546   weights[i] = weights[i-1] + particles[i].weight/bestWeight;
00547     }
00548     if(weights.back()<=0) {
00549       std::cerr << "Warning particle filter attempted resampling with weight total " << weights.back() << std::endl;
00550       return;
00551     }
00552     
00553     float r = weights.back() / numResample; // last element of weights/number of particles
00554     float offset = r*float(rand())/RAND_MAX;
00555     unsigned int pos = 0;
00556     
00557     for (unsigned int i=0; i < numResample; i++){
00558       float target = offset+r*i; // multiply instead of repeatedly adding to avoid rounding issues
00559       while (target >= weights[pos])
00560         pos++;
00561       // copy over particle (we'll "jiggle" it later if desired)
00562       newParticles[i]=particles[pos];
00563     }
00564     
00565     // now jiggle all of the particles we've resampled
00566     if(varianceScale!=0) {
00567       float vs;
00568       if(varianceScale>=0)
00569   vs=varianceScale; // use the user's setting
00570       // otherwise varianceScale is negative, we'll try to pick a variance scale based on how well we're doing
00571       else if(redistributeRatio>0)
00572   vs=1+redistributeRatio*(-varianceScale-1);
00573       else {
00574   if(logWeights) {
00575     float min=minAcceptableWeight*(resampleDelay+1);
00576     vs=bestWeight/min;
00577   } else {
00578     float min=std::pow(minAcceptableWeight,(float)(resampleDelay+1));
00579     vs=(1-bestWeight)/(1-min);
00580   }
00581   //vs=std::pow(vs,4.f);
00582       }
00583       ResamplingPolicy::dist->jiggle(vs,&newParticles[0],numResample);
00584     }
00585   }
00586   
00587   // finished resampling, redistribute the remaining particles (if needed due to falling below minAcceptableWeight)
00588   ResamplingPolicy::dist->randomize(&newParticles[numResample],numRedistribute);
00589   
00590   // reset weights
00591   float const initialWeight = logWeights ? 0 : 1;
00592   for(typename particle_collection::iterator it=newParticles.begin(); it!=newParticles.end(); ++it)
00593     it->weight = initialWeight;
00594   
00595   particles.swap(newParticles); // all done!  swap the particle lists
00596 }
00597 
00598 /*! @file
00599  * @brief 
00600  * @author ejt (Creator)
00601  */
00602 
00603 #endif

Tekkotsu v5.1CVS
Generated Mon May 9 04:58:45 2016 by Doxygen 1.6.3