34#ifndef VP_PARTICLE_FILTER_H
35#define VP_PARTICLE_FILTER_H
37#include <visp3/core/vpConfig.h>
39#if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
40#include <visp3/core/vpColVector.h>
41#include <visp3/core/vpGaussRand.h>
42#include <visp3/core/vpMatrix.h>
46#ifdef VISP_HAVE_OPENMP
52# pragma clang diagnostic push
53# pragma clang diagnostic ignored "-Wexit-time-destructors"
109template <
typename MeasurementsType>
187 VP_EXPLICIT
vpParticleFilter(
const unsigned int &N,
const std::vector<double> &stdev,
const long &seed = -1,
const int &nbThreads = -1);
267 void update(
const MeasurementsType &z);
286 m_useCommandStateFunction =
false;
287 m_useProcessFunction =
true;
300 m_useCommandStateFunction =
true;
301 m_useProcessFunction =
false;
312 m_likelihood = likelihood;
323 m_stateFilterFunc = filterFunc;
334 m_checkIfResample = resamplingCondFunc;
345 m_resampling = resamplingFunc;
395#ifdef VISP_HAVE_OPENMP
396 void predictMultithread(
const double &dt,
const vpColVector &u);
397 void updateMultithread(
const MeasurementsType &z);
400 void predictMonothread(
const double &dt,
const vpColVector &u);
401 void updateMonothread(
const MeasurementsType &z);
407 unsigned int m_nbMaxThreads;
408 std::vector<std::vector<vpGaussRand>> m_noiseGenerators;
409 std::vector<vpColVector> m_particles;
410 std::vector<double> m_w;
422 bool m_useProcessFunction;
423 bool m_useCommandStateFunction;
426template <
typename MeasurementsType>
427vpUniRand vpParticleFilter<MeasurementsType>::sampler;
429template <
typename MeasurementsType>
430vpUniRand vpParticleFilter<MeasurementsType>::samplerRandomIdx;
432template <
typename MeasurementsType>
436 , m_w(N, 1./static_cast<double>(N))
437 , m_useProcessFunction(false)
438 , m_useCommandStateFunction(false)
440#ifndef VISP_HAVE_OPENMP
443 std::cout <<
"[vpParticleFilter::vpParticleFilter] WARNING: OpenMP is not available, maximum number of threads to use clamped to 1" << std::endl;
446 int maxThreads = omp_get_max_threads();
447 if (nbThreads <= 0) {
448 m_nbMaxThreads = maxThreads;
450 else if (nbThreads > maxThreads) {
451 m_nbMaxThreads = maxThreads;
452 std::cout <<
"[vpParticleFilter::vpParticleFilter] WARNING: maximum number of threads to use clamped to "
453 << maxThreads <<
" instead of " << nbThreads <<
" due to OpenMP restrictions." << std::endl;
454 std::cout <<
"[vpParticleFilter::vpParticleFilter] If you want more, consider to use omp_set_num_threads before." << std::endl;
457 m_nbMaxThreads = nbThreads;
461 unsigned int sizeState =
static_cast<unsigned int>(stdev.size());
462 m_noiseGenerators.resize(m_nbMaxThreads);
463 unsigned long long seedForGenerator;
465 seedForGenerator = seed;
472 sampler.setSeed(seed, 0x123465789ULL);
473 samplerRandomIdx.setSeed(seed + 4224, 0x123465789ULL);
475 vpUniRand seedGenerator(seedForGenerator);
476 for (
unsigned int threadId = 0; threadId < m_nbMaxThreads; ++threadId) {
477 for (
unsigned int stateId = 0; stateId < sizeState; ++stateId) {
478 m_noiseGenerators[threadId].push_back(
vpGaussRand(stdev[stateId], 0.,
static_cast<long>(seedGenerator.
uniform(0., 1e9))));
483template <
typename MeasurementsType>
489 if (x0.
size() != m_noiseGenerators[0].size()) {
493 m_stateFilterFunc = filterFunc;
495 m_checkIfResample = checkResamplingFunc;
496 m_resampling = resamplingFunc;
497 m_stateAdd = addFunc;
498 m_useProcessFunction =
true;
499 m_useCommandStateFunction =
false;
505template <
typename MeasurementsType>
511 if (x0.
size() != m_noiseGenerators[0].size()) {
515 m_stateFilterFunc = filterFunc;
517 m_checkIfResample = checkResamplingFunc;
518 m_resampling = resamplingFunc;
519 m_stateAdd = addFunc;
520 m_useProcessFunction =
false;
521 m_useCommandStateFunction =
true;
527template <
typename MeasurementsType>
534template <
typename MeasurementsType>
537 if (m_nbMaxThreads == 1) {
538 predictMonothread(dt, u);
540#ifdef VISP_HAVE_OPENMP
542 predictMultithread(dt, u);
547template <
typename MeasurementsType>
550 if (m_nbMaxThreads == 1) {
553#ifdef VISP_HAVE_OPENMP
555 updateMultithread(z);
558 bool shouldResample = m_checkIfResample(m_N, m_w);
559 if (shouldResample) {
561 m_particles = std::move(particles_weights.
m_particles);
562 m_w = std::move(particles_weights.
m_weights);
566template <
typename MeasurementsType>
569 return m_stateFilterFunc(m_particles, m_w, m_stateAdd);
572template <
typename MeasurementsType>
575 size_t nbParticles = particles.
size();
576 if (nbParticles == 0) {
580 for (
size_t i = 1; i < nbParticles; ++i) {
581 res = addFunc(res, particles[i] * weights[i]);
586template <
typename MeasurementsType>
589 double sumSquare = 0.;
590 for (
unsigned int i = 0; i < N; ++i) {
591 sumSquare += weights[i] * weights[i];
593 if (sumSquare < std::numeric_limits<double>::epsilon()) {
597 double N_eff = 1.0 / sumSquare;
598 return (N_eff < (N / 2.0));
601template <
typename MeasurementsType>
604 unsigned int nbParticles =
static_cast<unsigned int>(particles.size());
606 double sumWeights = 0.;
607 std::vector<int> idx(nbParticles);
610 for (
unsigned int i = 0; i < nbParticles; ++i) {
613 int index = samplerRandomIdx.uniform(0, nbParticles);
614 for (
unsigned int j = 0; j < nbParticles; ++j) {
615 if (x < sumWeights + weights[j]) {
619 sumWeights += weights[j];
626 newParticlesWeights.
m_particles.resize(nbParticles);
627 for (
unsigned int i = 0; i < nbParticles; ++i) {
628 newParticlesWeights.
m_particles[i] = particles[idx[i]];
632 newParticlesWeights.
m_weights.resize(nbParticles, 1.0/
static_cast<double>(nbParticles));
633 return newParticlesWeights;
636template <
typename MeasurementsType>
637void vpParticleFilter<MeasurementsType>::initParticles(
const vpColVector &x0)
639 unsigned int sizeState = x0.
size();
640 unsigned int chunkSize = m_N / m_nbMaxThreads;
641 double uniformWeight = 1. /
static_cast<double>(m_N);
642 for (
unsigned int i = 0; i < m_nbMaxThreads; ++i) {
643 unsigned int idStart = chunkSize * i;
644 unsigned int idStop = chunkSize * (i + 1);
646 if (i == m_nbMaxThreads - 1) {
649 for (
unsigned int id = idStart;
id < idStop; ++id) {
652 for (
unsigned int idState = 0; idState < sizeState; ++idState) {
653 noise[idState] = m_noiseGenerators[
i][idState]();
656 m_particles[id] = m_stateAdd(x0, noise);
659 m_w[id] = uniformWeight;
664#ifdef VISP_HAVE_OPENMP
665template <
typename MeasurementsType>
668 int iam, nt, ipoints, istart, npoints(m_N);
669 unsigned int sizeState = m_particles[0].size();
671#pragma omp parallel default(shared) private(iam, nt, ipoints, istart) num_threads(m_nbMaxThreads)
673 iam = omp_get_thread_num();
674 nt = omp_get_num_threads();
675 ipoints = npoints / nt;
677 istart = iam * ipoints;
680 ipoints = npoints - istart;
683 for (
int i = istart;
i< istart + ipoints; ++
i) {
685 if (m_useCommandStateFunction) {
686 m_particles[
i] = m_bx(u, m_particles[i], dt);
688 else if (m_useProcessFunction) {
689 m_particles[
i] = m_f(m_particles[i], dt);
694 for (
unsigned int j = 0;
j < sizeState; ++
j) {
695 noise[
j] = m_noiseGenerators[iam][
j]();
699 m_particles[
i] = m_stateAdd(m_particles[i], noise);
704template <
typename MeasurementsType>
706 const MeasurementsType &z, std::vector<double> &w,
const int &istart,
const int &ipoints)
709 for (
int i = istart;
i< istart + ipoints; ++
i) {
710 w[
i] =
w[
i] * likelihood(v_particles[i], z);
716template <
typename MeasurementsType>
719 double sumWeights = 0.0;
720 int iam, nt, ipoints, istart, npoints(m_N);
723#pragma omp parallel default(shared) private(iam, nt, ipoints, istart) num_threads(m_nbMaxThreads)
725 iam = omp_get_thread_num();
726 nt = omp_get_num_threads();
727 ipoints = npoints / nt;
729 istart = iam * ipoints;
732 ipoints = npoints - istart;
734 tempSums[iam] = threadLikelihood<MeasurementsType>(m_likelihood, m_particles, z, m_w, istart, ipoints);
736 sumWeights = tempSums.sum();
738 if (sumWeights > std::numeric_limits<double>::epsilon()) {
739#pragma omp parallel default(shared) private(iam, nt, ipoints, istart)
741 iam = omp_get_thread_num();
742 nt = omp_get_num_threads();
743 ipoints = npoints / nt;
745 istart = iam * ipoints;
748 ipoints = npoints - istart;
752 for (
int i = istart;
i < istart + ipoints; ++
i) {
753 m_w[
i] = m_w[
i] / sumWeights;
760template <
typename MeasurementsType>
761void vpParticleFilter<MeasurementsType>::predictMonothread(
const double &dt,
const vpColVector &u)
763 unsigned int sizeState = m_particles[0].size();
764 for (
unsigned int i = 0;
i < m_N; ++
i) {
766 if (m_useCommandStateFunction) {
767 m_particles[
i] = m_bx(u, m_particles[i], dt);
769 else if (m_useProcessFunction) {
770 m_particles[
i] = m_f(m_particles[i], dt);
778 for (
unsigned int j = 0;
j < sizeState; ++
j) {
779 noise[
j] = m_noiseGenerators[0][
j]();
783 m_particles[
i] = m_stateAdd(m_particles[i], noise);
787template <
typename MeasurementsType>
788void vpParticleFilter<MeasurementsType>::updateMonothread(
const MeasurementsType &z)
790 double sumWeights = 0.;
792 for (
unsigned int i = 0;
i < m_N; ++
i) {
793 m_w[
i] = m_w[
i] * m_likelihood(m_particles[i], z);
794 sumWeights += m_w[
i];
798 if (sumWeights > std::numeric_limits<double>::epsilon()) {
799 for (
unsigned int i = 0;
i < m_N; ++
i) {
800 m_w[
i] = m_w[
i] / sumWeights;
806#if defined(__clang__)
807# pragma clang diagnostic pop
unsigned int size() const
Return the number of elements of the 2D array.
Implementation of column vector and the associated operations.
error that can be emitted by ViSP classes.
@ notInitialized
Used to indicate that a parameter is not initialized.
@ dimensionError
Bad dimension.
Class for generating random number with normal probability density.
The class permits to use a Particle Filter.
std::function< vpParticlesWithWeights(const std::vector< vpColVector > &, const std::vector< double > &)> vpResamplingFunction
Function that takes as argument the vector of particles and the vector of associated weights....
virtual ~vpParticleFilter()
void setResamplingFunction(const vpResamplingFunction &resamplingFunc)
Set the resampling function that generate new particles and associated weights when the filter starts...
void setLikelihoodFunction(const vpLikelihoodFunction &likelihood)
Set the likelihood function that updates the weights of the particles based on the new measurements.
std::function< vpColVector(const std::vector< vpColVector > &, const std::vector< double > &, const vpStateAddFunction &)> vpFilterFunction
Filter function, which computes the filtered state of the particle filter. The first argument is the ...
void filter(const MeasurementsType &z, const double &dt, const vpColVector &u=vpColVector())
Perform first the prediction step and then the update step. If needed, resampling will also be perfor...
static bool simpleResamplingCheck(const unsigned int &N, const std::vector< double > &weights)
Returns true if the following condition is fulfilled, or if all the particles diverged: .
static vpColVector weightedMean(const std::vector< vpColVector > &particles, const std::vector< double > &weights, const vpStateAddFunction &addFunc)
Simple function to compute a weighted mean, which just does .
std::function< vpColVector(const vpColVector &, const double &)> vpProcessFunction
Process model function, which projects a particle forward in time. The first argument is a particle,...
struct vpParticleFilter::vpParticlesWithWeights vpParticlesWithWeights
Structure of vectors for which the i^th element of the weights vector is associated to the i^th eleme...
void init(const vpColVector &x0, const vpProcessFunction &f, const vpLikelihoodFunction &l, const vpResamplingConditionFunction &checkResamplingFunc, const vpResamplingFunction &resamplingFunc, const vpFilterFunction &filterFunc=weightedMean, const vpStateAddFunction &addFunc=simpleAdd)
Set the guess of the initial state.
std::function< vpColVector(const vpColVector &, const vpColVector &, const double &)> vpCommandStateFunction
Command model function, which projects a particle forward in time according to the command and its pr...
static vpParticlesWithWeights simpleImportanceResampling(const std::vector< vpColVector > &particles, const std::vector< double > &weights)
Function implementing the resampling of a Simple Importance Resampling Particle Filter.
std::function< bool(const unsigned int &, const std::vector< double > &)> vpResamplingConditionFunction
Function that takes as argument the number of particles and the vector of weights associated to each ...
static vpColVector simpleAdd(const vpColVector &a, const vpColVector &toAdd)
Simple function to compute an addition, which just does .
void setCheckResamplingFunction(const vpResamplingConditionFunction &resamplingCondFunc)
Set the function that returns true when the filter starts to degenerate and false otherwise.
void setFilterFunction(const vpFilterFunction &filterFunc)
Set the filter function that compute the filtered state from the particles and their associated weigh...
VP_EXPLICIT vpParticleFilter(const unsigned int &N, const std::vector< double > &stdev, const long &seed=-1, const int &nbThreads=-1)
Construct a new vpParticleFilter object.
std::function< double(const vpColVector &, const MeasurementsType &)> vpLikelihoodFunction
Likelihood function, which evaluates the likelihood of a particle with regard to the measurements....
void predict(const double &dt, const vpColVector &u=vpColVector())
Predict the new state based on the last state and how far in time we want to predict.
void setCommandStateFunction(const vpCommandStateFunction &bx)
Set the command function to use when projecting the particles in the future.
void setProcessFunction(const vpProcessFunction &f)
Set the process function to use when projecting the particles in the future.
void update(const MeasurementsType &z)
Update the weights of the particles based on a new measurement. The weights will be normalized (i....
std::function< vpColVector(const vpColVector &, const vpColVector &)> vpStateAddFunction
Function that computes either the equivalent of an addition in the state space. The first argument is...
vpColVector computeFilteredState()
Compute the filtered state from the particles and their associated weights.
Class for generating random numbers with uniform probability density.
int uniform(int a, int b)
VISP_EXPORT double measureTimeMicros()
Structure of vectors for which the i^th element of the weights vector is associated to the i^th eleme...
std::vector< double > m_weights
std::vector< vpColVector > m_particles