Tekkotsu Homepage | Demos | Overview | Downloads | Dev. Resources | Reference | Credits |
ParticleFilter.hGo 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 |