MRPT  2.0.4
CParticleFilterCapable.cpp
Go to the documentation of this file.
1 /* +------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | https://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2020, Individual contributors, see AUTHORS file |
6  | See: https://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See: https://www.mrpt.org/License |
8  +------------------------------------------------------------------------+ */
9 
10 #include "bayes-precomp.h" // Precompiled headers
11 
13 #include <mrpt/math/ops_vectors.h>
14 #include <mrpt/random.h>
15 #include <iostream>
16 
17 using namespace mrpt;
18 using namespace mrpt::bayes;
19 using namespace mrpt::random;
20 using namespace mrpt::math;
21 using namespace std;
22 
24  20;
25 
26 /*---------------------------------------------------------------
27  performResampling
28  ---------------------------------------------------------------*/
31  size_t out_particle_count)
32 {
34 
35  // Make a vector with the particles' log. weights:
36  const size_t in_particle_count = particlesCount();
37  ASSERT_(in_particle_count > 0);
38 
39  vector<size_t> indxs;
40  vector<double> log_ws;
41  log_ws.assign(in_particle_count, .0);
42  for (size_t i = 0; i < in_particle_count; i++) log_ws[i] = getW(i);
43 
44  // Compute the surviving indexes:
45  computeResampling(
46  PF_options.resamplingMethod, log_ws, indxs, out_particle_count);
47 
48  // And perform the particle replacement:
49  performSubstitution(indxs);
50 
51  // Finally, equal weights:
52  for (size_t i = 0; i < out_particle_count; i++)
53  setW(i, 0 /* Logarithmic weight */);
54 
55  MRPT_END
56 }
57 
58 /*---------------------------------------------------------------
59  resample
60  ---------------------------------------------------------------*/
63  const vector<double>& in_logWeights, vector<size_t>& out_indexes,
64  size_t out_particle_count)
65 {
67 
68  // Compute the normalized linear weights:
69  // The array "linW" will be the input to the actual
70  // resampling algorithms.
71  size_t i, j, M = in_logWeights.size();
72  ASSERT_(M > 0);
73 
74  if (!out_particle_count) out_particle_count = M;
75 
76  vector<double> linW(M, 0);
77  vector<double>::const_iterator inIt;
78  vector<double>::iterator outIt;
79  double linW_SUM = 0;
80 
81  // This is to avoid float point range problems:
82  double max_log_w = math::maximum(in_logWeights);
83  for (i = 0, inIt = in_logWeights.begin(), outIt = linW.begin(); i < M;
84  i++, inIt++, outIt++)
85  linW_SUM += ((*outIt) = exp((*inIt) - max_log_w));
86 
87  // Normalize weights:
88  ASSERT_(linW_SUM > 0);
89  linW *= 1.0 / linW_SUM;
90 
91  switch (method)
92  {
94  {
95  // ==============================================
96  // Select with replacement
97  // ==============================================
98  vector<double> Q;
99  mrpt::math::cumsum_tmpl<vector<double>, vector<double>>(linW, Q);
100  Q[M - 1] = 1.1;
101 
102  vector<double> T(M);
103  getRandomGenerator().drawUniformVector(T, 0.0, 0.999999);
104  T.push_back(1.0);
105 
106  // Sort:
107  // --------------------
108  sort(T.begin(), T.end());
109 
110  out_indexes.resize(out_particle_count);
111  i = j = 0;
112 
113  while (i < out_particle_count)
114  {
115  if (T[i] < Q[j])
116  {
117  out_indexes[i++] = (unsigned int)j;
118  }
119  else
120  {
121  j++;
122  if (j >= M) j = M - 1;
123  }
124  }
125  }
126  break; // end of "Select with replacement"
127 
129  {
130  // ==============================================
131  // prResidual
132  // ==============================================
133  // Repetition counts:
134  std::vector<uint32_t> N(M);
135  size_t R = 0; // Remainder or residual count
136  for (i = 0; i < M; i++)
137  {
138  N[i] = int(M * linW[i]);
139  R += N[i];
140  }
141  size_t N_rnd = out_particle_count >= R ? (out_particle_count - R)
142  : 0; // # of particles to be
143  // drawn randomly (the
144  // "residual" part)
145 
146  // Fillout the deterministic part of the resampling:
147  out_indexes.resize(out_particle_count);
148  for (i = 0, j = 0; i < out_particle_count; i++)
149  for (size_t k = 0; k < N[i]; k++) out_indexes[j++] = i;
150 
151  size_t M_fixed = j;
152 
153  // Prepare a multinomial resampling with the residual part,
154  // using the modified weights:
155  // ----------------------------------------------------------
156  if (N_rnd) // If there are "residual" part (should be virtually
157  // always!)
158  {
159  // Compute modified weights:
160  vector<double> linW_mod(M);
161  const double M_R_1 = 1.0 / N_rnd;
162  for (i = 0; i < M; i++)
163  linW_mod[i] = M_R_1 * (M * linW[i] - N[i]);
164 
165  // perform resampling:
166  vector<double> Q;
167  mrpt::math::cumsum_tmpl<vector<double>, vector<double>>(
168  linW_mod, Q);
169  Q[M - 1] = 1.1;
170 
171  vector<double> T(M);
172  getRandomGenerator().drawUniformVector(T, 0.0, 0.999999);
173  T.push_back(1.0);
174 
175  // Sort:
176  sort(T.begin(), T.end());
177 
178  i = 0;
179  j = 0;
180 
181  while (i < N_rnd)
182  {
183  if (T[i] < Q[j])
184  {
185  out_indexes[M_fixed + i++] = (unsigned int)j;
186  }
187  else
188  {
189  j++;
190  if (j >= M) j = M - 1;
191  }
192  }
193  } // end if N_rnd!=0
194  }
195  break;
197  {
198  // ==============================================
199  // prStratified
200  // ==============================================
201  vector<double> Q;
202  mrpt::math::cumsum_tmpl<vector<double>, vector<double>>(linW, Q);
203  Q[M - 1] = 1.1;
204 
205  // Stratified-uniform random vector:
206  vector<double> T(M + 1);
207  const double _1_M = 1.0 / M;
208  const double _1_M_eps = _1_M - 0.000001;
209  double T_offset = 0;
210  for (i = 0; i < M; i++)
211  {
212  T[i] =
213  T_offset + getRandomGenerator().drawUniform(0.0, _1_M_eps);
214  T_offset += _1_M;
215  }
216  T[M] = 1;
217 
218  out_indexes.resize(out_particle_count);
219  i = j = 0;
220  while (i < out_particle_count)
221  {
222  if (T[i] < Q[j])
223  out_indexes[i++] = (unsigned int)j;
224  else
225  {
226  j++;
227  if (j >= M) j = M - 1;
228  }
229  }
230  }
231  break;
233  {
234  // ==============================================
235  // prSystematic
236  // ==============================================
237  vector<double> Q;
238  mrpt::math::cumsum_tmpl<vector<double>, vector<double>>(linW, Q);
239  Q[M - 1] = 1.1;
240 
241  // Uniform random vector:
242  vector<double> T(M + 1);
243  double _1_M = 1.0 / M;
244  T[0] = getRandomGenerator().drawUniform(0.0, _1_M);
245  for (i = 1; i < M; i++) T[i] = T[i - 1] + _1_M;
246  T[M] = 1;
247 
248  out_indexes.resize(out_particle_count);
249  i = j = 0;
250  while (i < out_particle_count)
251  {
252  if (T[i] < Q[j])
253  out_indexes[i++] = (unsigned int)j;
254  else
255  {
256  j++;
257  if (j >= M) j = M - 1;
258  }
259  }
260  }
261  break;
262  default:
264  "ERROR: Unknown resampling method selected: %i", method));
265  };
266 
267  MRPT_END
268 }
269 
270 /*---------------------------------------------------------------
271  prediction_and_update
272  ---------------------------------------------------------------*/
274  const mrpt::obs::CActionCollection* action,
275  const mrpt::obs::CSensoryFrame* observation,
277 {
278  switch (PF_options.PF_algorithm)
279  {
281  prediction_and_update_pfStandardProposal(
282  action, observation, PF_options);
283  break;
285  prediction_and_update_pfAuxiliaryPFStandard(
286  action, observation, PF_options);
287  break;
289  prediction_and_update_pfOptimalProposal(
290  action, observation, PF_options);
291  break;
293  prediction_and_update_pfAuxiliaryPFOptimal(
294  action, observation, PF_options);
295  break;
296  default:
297  {
298  THROW_EXCEPTION("Invalid particle filter algorithm selection!");
299  }
300  break;
301  }
302 }
303 
304 /*---------------------------------------------------------------
305  prediction_and_update_...
306  ---------------------------------------------------------------*/
308  [[maybe_unused]] const mrpt::obs::CActionCollection* action,
309  [[maybe_unused]] const mrpt::obs::CSensoryFrame* observation,
310  [[maybe_unused]] const bayes::CParticleFilter::TParticleFilterOptions&
311  PF_options)
312 {
314  "Algorithm 'pfStandardProposal' is not implemented in inherited "
315  "class!");
316 }
317 /*---------------------------------------------------------------
318  prediction_and_update_...
319  ---------------------------------------------------------------*/
321  [[maybe_unused]] const mrpt::obs::CActionCollection* action,
322  [[maybe_unused]] const mrpt::obs::CSensoryFrame* observation,
323  [[maybe_unused]] const bayes::CParticleFilter::TParticleFilterOptions&
324  PF_options)
325 {
327  "Algorithm 'pfAuxiliaryPFStandard' is not implemented in inherited "
328  "class!");
329 }
330 /*---------------------------------------------------------------
331  prediction_and_update_...
332  ---------------------------------------------------------------*/
334  [[maybe_unused]] const mrpt::obs::CActionCollection* action,
335  [[maybe_unused]] const mrpt::obs::CSensoryFrame* observation,
336  [[maybe_unused]] const bayes::CParticleFilter::TParticleFilterOptions&
337  PF_options)
338 {
340  "Algorithm 'pfOptimalProposal' is not implemented in inherited class!");
341 }
342 /*---------------------------------------------------------------
343  prediction_and_update_...
344  ---------------------------------------------------------------*/
346  [[maybe_unused]] const mrpt::obs::CActionCollection* action,
347  [[maybe_unused]] const mrpt::obs::CSensoryFrame* observation,
348  [[maybe_unused]] const bayes::CParticleFilter::TParticleFilterOptions&
349  PF_options)
350 {
352  "Algorithm 'pfAuxiliaryPFOptimal' is not implemented in inherited "
353  "class!");
354 }
355 
356 /*---------------------------------------------------------------
357  prepareFastDrawSample
358  ---------------------------------------------------------------*/
361  TParticleProbabilityEvaluator partEvaluator, const void* action,
362  const void* observation) const
363 {
364  MRPT_START
365 
366  if (PF_options.adaptiveSampleSize)
367  {
368  // --------------------------------------------------------
369  // CASE: Dynamic number of particles:
370  // -> Use m_fastDrawAuxiliary.CDF, PDF, CDF_indexes
371  // --------------------------------------------------------
374  "resamplingMethod must be 'prMultinomial' for a dynamic number "
375  "of particles!");
376 
377  size_t i, j = 666666, M = particlesCount();
378 
379  MRPT_START
380 
381  // Prepare buffers:
382  m_fastDrawAuxiliary.CDF.resize(
383  1 + PARTICLE_FILTER_CAPABLE_FAST_DRAW_BINS, 0);
384  m_fastDrawAuxiliary.CDF_indexes.resize(
385  PARTICLE_FILTER_CAPABLE_FAST_DRAW_BINS, 0);
386 
387  // Compute the vector of each particle's probability (usually
388  // it will be simply the weight, but there are other algorithms)
389  m_fastDrawAuxiliary.PDF.resize(M, 0);
390 
391  // This is done to avoid floating point overflow!! (JLBC - SEP 2007)
392  // -------------------------------------------------------------------
393  double SUM = 0;
394  // Save the log likelihoods:
395  for (i = 0; i < M; i++)
396  m_fastDrawAuxiliary.PDF[i] =
397  partEvaluator(PF_options, this, i, action, observation);
398  // "Normalize":
399  m_fastDrawAuxiliary.PDF += -math::maximum(m_fastDrawAuxiliary.PDF);
400  for (i = 0; i < M; i++)
401  SUM += m_fastDrawAuxiliary.PDF[i] = exp(m_fastDrawAuxiliary.PDF[i]);
402  ASSERT_(SUM >= 0);
404  m_fastDrawAuxiliary.PDF *= 1.0 / SUM;
405 
406  // Compute the CDF thresholds:
407  for (i = 0; i < PARTICLE_FILTER_CAPABLE_FAST_DRAW_BINS; i++)
408  m_fastDrawAuxiliary.CDF[i] =
409  ((double)i) / ((double)PARTICLE_FILTER_CAPABLE_FAST_DRAW_BINS);
410  m_fastDrawAuxiliary.CDF[PARTICLE_FILTER_CAPABLE_FAST_DRAW_BINS] = 1.0;
411 
412  // Compute the CDF and save threshold indexes:
413  double CDF = 0; // Cumulative density func.
414  for (i = 0, j = 0; i < M && j < PARTICLE_FILTER_CAPABLE_FAST_DRAW_BINS;
415  i++)
416  {
417  double CDF_next = CDF + m_fastDrawAuxiliary.PDF[i];
418  if (i == (M - 1)) CDF_next = 1.0; // rounds fix...
419  if (CDF_next > 1.0) CDF_next = 1.0;
420 
421  while (m_fastDrawAuxiliary.CDF[j] < CDF_next)
422  m_fastDrawAuxiliary.CDF_indexes[j++] = (unsigned int)i;
423 
424  CDF = CDF_next;
425  }
426 
427  ASSERT_(j == PARTICLE_FILTER_CAPABLE_FAST_DRAW_BINS);
428 
429 // Done!
430 #if !defined(_MSC_VER) || (_MSC_VER > 1400) // <=VC2005 doesn't work with this!
431  MRPT_END_WITH_CLEAN_UP(/* Debug: */
432  cout << "j=" << j
433  << "\nm_fastDrawAuxiliary.CDF_indexes:"
434  << m_fastDrawAuxiliary.CDF_indexes << endl;
435  cout << "m_fastDrawAuxiliary.CDF:"
436  << m_fastDrawAuxiliary.CDF << endl;);
437 #else
438  MRPT_END
439 #endif
440  }
441  else
442  {
443  // ------------------------------------------------------------------------
444  // CASE: Static number of particles:
445  // -> Use m_fastDrawAuxiliary.alreadyDrawnIndexes & alreadyDrawnNextOne
446  // ------------------------------------------------------------------------
447  // Generate the vector with the "probabilities" of each particle being
448  // selected:
449  size_t i, M = particlesCount();
450  vector<double> PDF(M, 0);
451  for (i = 0; i < M; i++)
452  PDF[i] = partEvaluator(
453  PF_options, this, i, action,
454  observation); // Default evaluator: takes current weight.
455 
456  vector<size_t> idxs;
457 
458  // Generate the particle samples:
459  computeResampling(PF_options.resamplingMethod, PDF, idxs);
460 
461  vector<size_t>::iterator it;
462  std::vector<uint32_t>::iterator it2;
463  m_fastDrawAuxiliary.alreadyDrawnIndexes.resize(idxs.size());
464  for (it = idxs.begin(),
465  it2 = m_fastDrawAuxiliary.alreadyDrawnIndexes.begin();
466  it != idxs.end(); ++it, ++it2)
467  *it2 = (unsigned int)(*it);
468 
469  m_fastDrawAuxiliary.alreadyDrawnNextOne = 0;
470  }
471 
472  MRPT_END
473 }
474 
475 /*---------------------------------------------------------------
476  fastDrawSample
477  ---------------------------------------------------------------*/
479  const bayes::CParticleFilter::TParticleFilterOptions& PF_options) const
480 {
481  MRPT_START
482 
483  if (PF_options.adaptiveSampleSize)
484  {
485  // --------------------------------------------------------
486  // CASE: Dynamic number of particles:
487  // -> Use m_fastDrawAuxiliary.CDF, PDF, CDF_indexes
488  // --------------------------------------------------------
491  "resamplingMethod must be 'prMultinomial' for a dynamic number "
492  "of particles!");
493 
494  double draw = getRandomGenerator().drawUniform(0.0, 0.999999);
495  double CDF_next = -1.;
496  double CDF = -1.;
497 
498  MRPT_START
499 
500  // Use the look-up table to see the starting index we must start looking
501  // from:
502  auto j = (size_t)floor(
503  draw * ((double)PARTICLE_FILTER_CAPABLE_FAST_DRAW_BINS - 0.05));
504  CDF = m_fastDrawAuxiliary.CDF[j];
505  size_t i = m_fastDrawAuxiliary.CDF_indexes[j];
506 
507  // Find the drawn particle!
508  while (draw > (CDF_next = CDF + m_fastDrawAuxiliary.PDF[i]))
509  {
510  CDF = CDF_next;
511  i++;
512  }
513 
514  return i;
515 
517  printf(
518  "\n[CParticleFilterCapable::fastDrawSample] DEBUG: draw=%f, "
519  "CDF=%f CDF_next=%f\n",
520  draw, CDF, CDF_next););
521  }
522  else
523  {
524  // --------------------------------------------------------
525  // CASE: Static number of particles:
526  // -> Use m_fastDrawAuxiliary.alreadyDrawnIndexes & alreadyDrawnNextOne
527  // --------------------------------------------------------
528  if (m_fastDrawAuxiliary.alreadyDrawnNextOne >=
529  m_fastDrawAuxiliary.alreadyDrawnIndexes.size())
531  "Have you called 'fastDrawSample' more times than the sample "
532  "size? Did you forget calling 'prepareFastCall' before?");
533 
534  return m_fastDrawAuxiliary
535  .alreadyDrawnIndexes[m_fastDrawAuxiliary.alreadyDrawnNextOne++];
536  }
537 
538  MRPT_END
539 }
540 
541 /*---------------------------------------------------------------
542  log2linearWeights
543  ---------------------------------------------------------------*/
545  const vector<double>& in_logWeights, vector<double>& out_linWeights)
546 {
547  MRPT_START
548 
549  size_t N = in_logWeights.size();
550 
551  out_linWeights.resize(N);
552 
553  if (!N) return;
554 
555  double sumW = 0;
556  size_t i;
557  for (i = 0; i < N; i++) sumW += out_linWeights[i] = exp(in_logWeights[i]);
558 
559  ASSERT_(sumW > 0);
560 
561  for (i = 0; i < N; i++) out_linWeights[i] /= sumW;
562 
563  MRPT_END
564 }
A namespace of pseudo-random numbers generators of diferent distributions.
#define MRPT_START
Definition: exceptions.h:241
#define THROW_EXCEPTION(msg)
Definition: exceptions.h:67
std::string std::string format(std::string_view fmt, ARGS &&... args)
Definition: format.h:26
static void log2linearWeights(const std::vector< double > &in_logWeights, std::vector< double > &out_linWeights)
A static method to compute the linear, normalized (the sum the unity) weights from log-weights...
The namespace for Bayesian filtering algorithm: different particle filters and Kalman filter algorith...
virtual void prediction_and_update_pfAuxiliaryPFStandard(const mrpt::obs::CActionCollection *action, const mrpt::obs::CSensoryFrame *observation, const bayes::CParticleFilter::TParticleFilterOptions &PF_options)
Performs the particle filter prediction/update stages for the algorithm "pfAuxiliaryPFStandard" (if n...
void prepareFastDrawSample(const bayes::CParticleFilter::TParticleFilterOptions &PF_options, TParticleProbabilityEvaluator partEvaluator=defaultEvaluator, const void *action=nullptr, const void *observation=nullptr) const
Prepares data structures for calling fastDrawSample method next.
double(*)(const bayes::CParticleFilter::TParticleFilterOptions &PF_options, const CParticleFilterCapable *obj, size_t index, const void *action, const void *observation) TParticleProbabilityEvaluator
A callback function type for evaluating the probability of m_particles of being selected, used in "fastDrawSample".
STL namespace.
#define MRPT_END_WITH_CLEAN_UP(stuff)
Definition: exceptions.h:247
Declares a class for storing a collection of robot actions.
#define ASSERT_(f)
Defines an assertion mechanism.
Definition: exceptions.h:120
return_t drawUniform(const double Min, const double Max)
Generate a uniformly distributed pseudo-random number using the MT19937 algorithm, scaled to the selected range.
This base provides a set of functions for maths stuff.
TParticleResamplingAlgorithm
Defines the different resampling algorithms.
TParticleResamplingAlgorithm resamplingMethod
The resampling algorithm to use (default=prMultinomial).
#define MRPT_CHECK_NORMAL_NUMBER(v)
Throws an exception if the number is NaN, IND, or +/-INF, or return the same number otherwise...
Definition: exceptions.h:125
CONTAINER::Scalar maximum(const CONTAINER &v)
Declares a class for storing a "sensory frame", a set of "observations" taken by the robot approximat...
Definition: CSensoryFrame.h:51
virtual void prediction_and_update_pfStandardProposal(const mrpt::obs::CActionCollection *action, const mrpt::obs::CSensoryFrame *observation, const bayes::CParticleFilter::TParticleFilterOptions &PF_options)
Performs the particle filter prediction/update stages for the algorithm "pfStandardProposal" (if not ...
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
virtual void prediction_and_update_pfAuxiliaryPFOptimal(const mrpt::obs::CActionCollection *action, const mrpt::obs::CSensoryFrame *observation, const bayes::CParticleFilter::TParticleFilterOptions &PF_options)
Performs the particle filter prediction/update stages for the algorithm "pfAuxiliaryPFOptimal" (if no...
void prediction_and_update(const mrpt::obs::CActionCollection *action, const mrpt::obs::CSensoryFrame *observation, const bayes::CParticleFilter::TParticleFilterOptions &PF_options)
Performs the prediction stage of the Particle Filter.
const float R
The configuration of a particle filter.
#define MRPT_END
Definition: exceptions.h:245
void performResampling(const bayes::CParticleFilter::TParticleFilterOptions &PF_options, size_t out_particle_count=0)
Performs a resample of the m_particles, using the method selected in the constructor.
static const unsigned PARTICLE_FILTER_CAPABLE_FAST_DRAW_BINS
static void computeResampling(CParticleFilter::TParticleResamplingAlgorithm method, const std::vector< double > &in_logWeights, std::vector< size_t > &out_indexes, size_t out_particle_count=0)
A static method to perform the computation of the samples resulting from resampling a given set of pa...
CRandomGenerator & getRandomGenerator()
A static instance of a CRandomGenerator class, for use in single-thread applications.
void drawUniformVector(VEC &v, const double unif_min=0, const double unif_max=1)
Fills the given vector with independent, uniformly distributed samples.
virtual void prediction_and_update_pfOptimalProposal(const mrpt::obs::CActionCollection *action, const mrpt::obs::CSensoryFrame *observation, const bayes::CParticleFilter::TParticleFilterOptions &PF_options)
Performs the particle filter prediction/update stages for the algorithm "pfOptimalProposal" (if not i...
TParticleFilterAlgorithm PF_algorithm
The PF algorithm to use (default=pfStandardProposal) See TParticleFilterAlgorithm for the possibiliti...
size_t fastDrawSample(const bayes::CParticleFilter::TParticleFilterOptions &PF_options) const
Draws a random sample from the particle filter, in such a way that each particle has a probability pr...
bool adaptiveSampleSize
A flag that indicates whether the CParticleFilterCapable object should perform adative sample size (d...



Page generated by Doxygen 1.8.14 for MRPT 2.0.4 Git: 33de1d0ad Sat Jun 20 11:02:42 2020 +0200 at sáb jun 20 17:35:17 CEST 2020