70#include <visp3/core/vpConfig.h>
71#include <visp3/core/vpGaussRand.h>
72#ifdef VISP_HAVE_DISPLAY
73#include <visp3/gui/vpPlot.h>
77#include <visp3/core/vpParticleFilter.h>
79#if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
81#ifdef ENABLE_VISP_NAMESPACE
97 point[0] = particle[1] *
dt + particle[0];
98 point[1] = particle[1];
99 point[2] = particle[3] *
dt + particle[2];
100 point[3] = particle[3];
113void computeCoordinatesFromMeasurement(
const vpColVector &z,
const double &xRadar,
const double &yRadar,
double &x,
double &y)
115 double dx =
z[0] * std::cos(z[1]);
116 double dy =
z[0] * std::sin(z[1]);
129double computeError(
const double &x0,
const double &y0,
const double &x1,
const double &y1)
133 double error = std::sqrt(dx * dx + dy * dy);
146 double error = computeError(state[0], state[2], gt_X[0], gt_X[1]);
159double computeMeasurementsError(
const vpColVector &z,
const double &xRadar,
const double &yRadar,
const vpColVector >_X)
161 double xMeas = 0., yMeas = 0.;
162 computeCoordinatesFromMeasurement(z, xRadar, yRadar, xMeas, yMeas);
163 double error = computeError(xMeas, yMeas, gt_X[0], gt_X[1]);
184 vpRadarStation(
const double &x,
const double &y,
const double &range_std,
const double &elev_angle_std,
185 const double &distMaxAllowed)
188 , m_rngRange(range_std, 0., 4224)
189 , m_rngElevAngle(elev_angle_std, 0., 2112)
191 double sigmaDistance = distMaxAllowed / 3.;
192 double sigmaDistanceSquared = sigmaDistance * sigmaDistance;
193 m_constantDenominator = 1. / std::sqrt(2. * M_PI * sigmaDistanceSquared);
194 m_constantExpDenominator = -1. / (2. * sigmaDistanceSquared);
206 double dx = particle[0] - m_x;
207 double dy = particle[2] - m_y;
208 meas[0] = std::sqrt(dx * dx + dy * dy);
209 meas[1] = std::atan2(dy, dx);
223 double dx = pos[0] - m_x;
224 double dy = pos[1] - m_y;
225 double range = std::sqrt(dx * dx + dy * dy);
226 double elevAngle = std::atan2(dy, dx);
228 measurements[0] = range;
229 measurements[1] = elevAngle;
245 measurementsNoisy[0] += m_rngRange();
246 measurementsNoisy[1] += m_rngElevAngle();
247 return measurementsNoisy;
262 double xParticle = particle[0];
263 double yParticle = particle[2];
264 double xMeas = 0., yMeas = 0.;
265 computeCoordinatesFromMeasurement(meas, m_x, m_y, xMeas, yMeas);
266 double dist = computeError(xParticle, yParticle, xMeas, yMeas);
267 double likelihood = std::exp(m_constantExpDenominator * dist) * m_constantDenominator;
278 double m_constantDenominator;
279 double m_constantExpDenominator;
298 , m_rngVel(vel_std, 0.)
313 dx[0] += m_rngVel() * dt;
314 dx[1] += m_rngVel() * dt;
382 std::string arg(argv[i]);
383 if ((arg ==
"--nb-steps-main") && ((i+1) < argc)) {
387 else if ((arg ==
"--nb-steps-warmup") && ((i+1) < argc)) {
391 else if ((arg ==
"--dt") && ((i+1) < argc)) {
392 m_dt = std::atoi(argv[i + 1]);
395 else if ((arg ==
"--stdev-range") && ((i+1) < argc)) {
399 else if ((arg ==
"--stdev-elev-angle") && ((i+1) < argc)) {
403 else if ((arg ==
"--stdev-aircraft-vel") && ((i+1) < argc)) {
407 else if ((arg ==
"--radar-X") && ((i+1) < argc)) {
411 else if ((arg ==
"--radar-Y") && ((i+1) < argc)) {
415 else if ((arg ==
"--gt-X0") && ((i+1) < argc)) {
419 else if ((arg ==
"--gt-Y0") && ((i+1) < argc)) {
423 else if ((arg ==
"--gt-vX0") && ((i+1) < argc)) {
427 else if ((arg ==
"--gt-vY0") && ((i+1) < argc)) {
431 else if ((arg ==
"--max-distance-likelihood") && ((i+1) < argc)) {
435 else if (((arg ==
"-N") || (arg ==
"--nb-particles")) && ((i+1) < argc)) {
436 m_N = std::atoi(argv[i + 1]);
439 else if ((arg ==
"--seed") && ((i+1) < argc)) {
443 else if ((arg ==
"--nb-threads") && ((i+1) < argc)) {
447 else if ((arg ==
"--ampli-max-X") && ((i+1) < argc)) {
451 else if ((arg ==
"--ampli-max-Y") && ((i+1) < argc)) {
455 else if ((arg ==
"--ampli-max-vX") && ((i+1) < argc)) {
459 else if ((arg ==
"--ampli-max-vY") && ((i+1) < argc)) {
463 else if (arg ==
"-d") {
466 else if (arg ==
"-c" ) {
469 else if ((arg ==
"-h") || (arg ==
"--help")) {
470 printUsage(std::string(argv[0]));
472 defaultArgs.printDetails();
476 std::cout <<
"WARNING: unrecognised argument \"" << arg <<
"\"";
478 std::cout <<
" with associated value(s) { ";
481 bool hasToRun =
true;
482 while ((j < argc) && hasToRun) {
483 std::string nextValue(argv[j]);
484 if (nextValue.find(
"--") == std::string::npos) {
485 std::cout << nextValue <<
" ";
493 std::cout <<
"}" << std::endl;
503 void printUsage(
const std::string &softName)
505 std::cout <<
"SYNOPSIS" << std::endl;
506 std::cout <<
" " << softName <<
" [--nb-steps-main <uint>] [--nb-steps-warmup <uint>]" << std::endl;
507 std::cout <<
" [--dt <double>] [--stdev-range <double>] [--stdev-elev-angle <double>] [--stdev-aircraft-vel <double>]" << std::endl;
508 std::cout <<
" [--radar-X <double>] [--radar-Y <double>]" << std::endl;
509 std::cout <<
" [--gt-X0 <double>] [--gt-Y0 <double>] [--gt-vX0 <double>] [--gt-vY0 <double>]" << std::endl;
510 std::cout <<
" [--max-distance-likelihood <double>] [-N, --nb-particles <uint>] [--seed <int>] [--nb-threads <int>]" << std::endl;
511 std::cout <<
" [--ampli-max-X <double>] [--ampli-max-Y <double>] [--ampli-max-vX <double>] [--ampli-max-vY <double>]" << std::endl;
512 std::cout <<
" [-d, --no-display] [-h]" << std::endl;
513 std::cout <<
" [-c] [-h]" << std::endl;
518 std::cout << std::endl << std::endl;
519 std::cout <<
"DETAILS" << std::endl;
520 std::cout <<
" --nb-steps-main" << std::endl;
521 std::cout <<
" Number of steps in the main loop." << std::endl;
522 std::cout <<
" Default: " <<
m_nbSteps << std::endl;
523 std::cout << std::endl;
524 std::cout <<
" --nb-steps-warmup" << std::endl;
525 std::cout <<
" Number of steps in the warmup loop." << std::endl;
527 std::cout << std::endl;
528 std::cout <<
" --dt" << std::endl;
529 std::cout <<
" Timestep of the simulation, in seconds." << std::endl;
530 std::cout <<
" Default: " <<
m_dt << std::endl;
531 std::cout << std::endl;
532 std::cout <<
" --stdev-range" << std::endl;
533 std::cout <<
" Standard deviation of the range measurements, in meters." << std::endl;
535 std::cout << std::endl;
536 std::cout <<
" --stdev-elev-angle" << std::endl;
537 std::cout <<
" Standard deviation of the elevation angle measurements, in degrees." << std::endl;
539 std::cout << std::endl;
540 std::cout <<
" --stdev-aircraft-vel" << std::endl;
541 std::cout <<
" Standard deviation of the aircraft velocity, in m/s." << std::endl;
543 std::cout << std::endl;
544 std::cout <<
" --radar-X" << std::endl;
545 std::cout <<
" Position along the X-axis of the radar, in meters." << std::endl;
546 std::cout <<
" Be careful, because singularities happen if the aircraft flies above the radar." << std::endl;
547 std::cout <<
" Default: " <<
m_radar_X << std::endl;
548 std::cout << std::endl;
549 std::cout <<
" --radar-Y" << std::endl;
550 std::cout <<
" Position along the Y-axis of the radar, in meters." << std::endl;
551 std::cout <<
" Be careful, because singularities happen if the aircraft flies above the radar." << std::endl;
552 std::cout <<
" Default: " <<
m_radar_Y << std::endl;
553 std::cout << std::endl;
554 std::cout <<
" --gt-X0" << std::endl;
555 std::cout <<
" Initial position along the X-axis of the aircraft, in meters." << std::endl;
556 std::cout <<
" Be careful, because singularities happen if the aircraft flies above the radar." << std::endl;
557 std::cout <<
" Default: " <<
m_gt_X_init << std::endl;
558 std::cout << std::endl;
559 std::cout <<
" --gt-Y0" << std::endl;
560 std::cout <<
" Initial position along the Y-axis of the aircraft, in meters." << std::endl;
561 std::cout <<
" Be careful, because singularities happen if the aircraft flies above the radar." << std::endl;
562 std::cout <<
" Default: " <<
m_gt_Y_init << std::endl;
563 std::cout << std::endl;
564 std::cout <<
" --gt-vX0" << std::endl;
565 std::cout <<
" Initial velocity along the X-axis of the aircraft, in m/s." << std::endl;
566 std::cout <<
" Be careful, because singularities happen if the aircraft flies above the radar." << std::endl;
568 std::cout << std::endl;
569 std::cout <<
" --gt-vY0" << std::endl;
570 std::cout <<
" Initial velocity along the Y-axis of the aircraft, in m/s." << std::endl;
571 std::cout <<
" Be careful, because singularities happen if the aircraft flies above the radar." << std::endl;
573 std::cout << std::endl;
574 std::cout <<
" --max-distance-likelihood" << std::endl;
575 std::cout <<
" Maximum tolerated distance between a particle and the measurements." << std::endl;
576 std::cout <<
" Above this value, the likelihood of the particle is 0." << std::endl;
578 std::cout << std::endl;
579 std::cout <<
" -N, --nb-particles" << std::endl;
580 std::cout <<
" Number of particles of the Particle Filter." << std::endl;
581 std::cout <<
" Default: " <<
m_N << std::endl;
582 std::cout << std::endl;
583 std::cout <<
" --seed" << std::endl;
584 std::cout <<
" Seed to initialize the Particle Filter." << std::endl;
585 std::cout <<
" Use a negative value makes to use the current timestamp instead." << std::endl;
586 std::cout <<
" Default: " <<
m_seedPF << std::endl;
587 std::cout << std::endl;
588 std::cout <<
" --nb-threads" << std::endl;
589 std::cout <<
" Set the number of threads to use in the Particle Filter (only if OpenMP is available)." << std::endl;
590 std::cout <<
" Use a negative value to use the maximum number of threads instead." << std::endl;
591 std::cout <<
" Default: " <<
m_nbThreads << std::endl;
592 std::cout << std::endl;
593 std::cout <<
" --ampli-max-X" << std::endl;
594 std::cout <<
" Maximum amplitude of the noise added to a particle along the X-axis." << std::endl;
595 std::cout <<
" Default: " <<
m_ampliMaxX << std::endl;
596 std::cout << std::endl;
597 std::cout <<
" --ampli-max-Y" << std::endl;
598 std::cout <<
" Maximum amplitude of the noise added to a particle along the Y-axis." << std::endl;
599 std::cout <<
" Default: " <<
m_ampliMaxY << std::endl;
600 std::cout << std::endl;
601 std::cout <<
" --ampli-max-vX" << std::endl;
602 std::cout <<
" Maximum amplitude of the noise added to a particle to the velocity along the X-axis component." << std::endl;
604 std::cout << std::endl;
605 std::cout <<
" --ampli-max-vY" << std::endl;
606 std::cout <<
" Maximum amplitude of the noise added to a particle to the velocity along the Y-axis component." << std::endl;
608 std::cout << std::endl;
609 std::cout <<
" -d, --no-display" << std::endl;
610 std::cout <<
" Deactivate display." << std::endl;
611 std::cout <<
" Default: display is ";
612#ifdef VISP_HAVE_DISPLAY
613 std::cout <<
"ON" << std::endl;
615 std::cout <<
"OFF" << std::endl;
617 std::cout << std::endl;
618 std::cout <<
" -c" << std::endl;
619 std::cout <<
" Deactivate user interaction." << std::endl;
621 std::cout << std::endl;
622 std::cout <<
" -h, --help" << std::endl;
623 std::cout <<
" Display this help." << std::endl;
624 std::cout << std::endl;
628int main(
const int argc,
const char *argv[])
631 int returnCode =
args.parseArgs(argc, argv);
637 std::vector<double> stdevsPF = {
args.m_ampliMaxX /3.,
args.m_ampliMaxVx /3.,
args.m_ampliMaxY /3. ,
args.m_ampliMaxVy /3. };
638 int seedPF =
args.m_seedPF;
639 unsigned int nbParticles =
args.m_N;
640 int nbThreads =
args.m_nbThreads;
643 X0[0] = 0.9 *
args.m_gt_X_init;
644 X0[1] = 0.9 *
args.m_gt_vX_init;
645 X0[2] = 0.9 *
args.m_gt_Y_init;
646 X0[3] = 0.9 *
args.m_gt_vY_init;
650 using std::placeholders::_1;
651 using std::placeholders::_2;
658 filter.init(X0, f, likelihoodFunc, checkResamplingFunc, resamplingFunc);
660#ifdef VISP_HAVE_DISPLAY
662 if (
args.m_useDisplay) {
665 plot->initGraph(0, 3);
666 plot->setTitle(0,
"Position along X-axis");
667 plot->setUnitX(0,
"Time (s)");
668 plot->setUnitY(0,
"Position (m)");
669 plot->setLegend(0, 0,
"GT");
670 plot->setLegend(0, 1,
"Filtered");
671 plot->setLegend(0, 2,
"Measure");
676 plot->initGraph(1, 3);
677 plot->setTitle(1,
"Velocity along X-axis");
678 plot->setUnitX(1,
"Time (s)");
679 plot->setUnitY(1,
"Velocity (m/s)");
680 plot->setLegend(1, 0,
"GT");
681 plot->setLegend(1, 1,
"Filtered");
682 plot->setLegend(1, 2,
"Measure");
687 plot->initGraph(2, 3);
688 plot->setTitle(2,
"Position along Y-axis");
689 plot->setUnitX(2,
"Time (s)");
690 plot->setUnitY(2,
"Position (m)");
691 plot->setLegend(2, 0,
"GT");
692 plot->setLegend(2, 1,
"Filtered");
693 plot->setLegend(2, 2,
"Measure");
698 plot->initGraph(3, 3);
699 plot->setTitle(3,
"Velocity along Y-axis");
700 plot->setUnitX(3,
"Time (s)");
701 plot->setUnitY(3,
"Velocity (m/s)");
702 plot->setLegend(3, 0,
"GT");
703 plot->setLegend(3, 1,
"Filtered");
704 plot->setLegend(3, 2,
"Measure");
721 double averageFilteringTime = 0.;
722 double meanErrorFilter = 0., meanErrorNoise = 0.;
723 double xNoise_prec = 0., yNoise_prec = 0.;
726 const unsigned int nbStepsWarmUp =
args.m_nbStepsWarmUp;
727 for (
unsigned int i = 0;
i < nbStepsWarmUp; ++
i) {
736 filter.filter(z,
args.m_dt);
741 computeCoordinatesFromMeasurement(z,
args.m_radar_X,
args.m_radar_Y, xNoise_prec, yNoise_prec);
744 for (
unsigned int i = 0;
i <
args.m_nbSteps; ++
i) {
752 filter.filter(z,
args.m_dt);
758 double normErrorFilter = computeStateError(Xest, gt_X);
759 meanErrorFilter += normErrorFilter;
762 double xNoise = 0., yNoise = 0.;
763 computeCoordinatesFromMeasurement(z,
args.m_radar_X,
args.m_radar_Y, xNoise, yNoise);
764 double normErrorNoise = computeMeasurementsError(z,
args.m_radar_X,
args.m_radar_Y, gt_X);
765 meanErrorNoise += normErrorNoise;
767#ifdef VISP_HAVE_DISPLAY
768 if (
args.m_useDisplay) {
770 plot->plot(0, 0, i, gt_X[0]);
771 plot->plot(0, 1, i, Xest[0]);
772 plot->plot(0, 2, i, xNoise);
774 double vxNoise = (xNoise - xNoise_prec) /
args.m_dt;
775 plot->plot(1, 0, i, gt_V[0]);
776 plot->plot(1, 1, i, Xest[1]);
777 plot->plot(1, 2, i, vxNoise);
779 plot->plot(2, 0, i, gt_X[1]);
780 plot->plot(2, 1, i, Xest[2]);
781 plot->plot(2, 2, i, yNoise);
783 double vyNoise = (yNoise - yNoise_prec) /
args.m_dt;
784 plot->plot(3, 0, i, gt_V[1]);
785 plot->plot(3, 1, i, Xest[3]);
786 plot->plot(3, 2, i, vyNoise);
792 xNoise_prec = xNoise;
793 yNoise_prec = yNoise;
797 meanErrorFilter /=
static_cast<double>(
args.m_nbSteps);
798 meanErrorNoise /=
static_cast<double>(
args.m_nbSteps);
799 averageFilteringTime = averageFilteringTime / (
static_cast<double>(
args.m_nbSteps) +
static_cast<double>(nbStepsWarmUp));
800 std::cout <<
"Mean error filter = " << meanErrorFilter <<
"m" << std::endl;
801 std::cout <<
"Mean error noise = " << meanErrorNoise <<
"m" << std::endl;
802 std::cout <<
"Mean filtering time = " << averageFilteringTime <<
"us" << std::endl;
804 if (
args.m_useUserInteraction) {
805 std::cout <<
"Press Enter to quit..." << std::endl;
809#ifdef VISP_HAVE_DISPLAY
810 if (
args.m_useDisplay) {
816 const double maxError = 150.;
817 if (meanErrorFilter > maxError) {
818 std::cerr <<
"Error: max tolerated error = " << maxError <<
", mean error = " << meanErrorFilter << std::endl;
821 else if (meanErrorFilter >= meanErrorNoise) {
822 std::cerr <<
"Error: mean error without filter = " << meanErrorNoise <<
", mean error with filter = " << meanErrorFilter << std::endl;
831 std::cout <<
"This example is only available if you compile ViSP in C++11 standard or higher." << std::endl;
Class to simulate a flying aircraft.
vpColVector update(const double &dt)
Compute the new position of the aircraft after dt seconds have passed since the last update.
vpACSimulator(const vpColVector &X0, const vpColVector &vel, const double &vel_std)
Construct a new vpACSimulator object.
Implementation of column vector and the associated operations.
static const vpColor blue
static const vpColor black
Class for generating random number with normal probability density.
Provides simple mathematics computation tools that are not available in the C mathematics library (ma...
static double rad(double deg)
static double deg(double rad)
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....
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: .
std::function< vpColVector(const vpColVector &, const double &)> vpProcessFunction
Process model function, which projects a particle forward in time. The first argument is a particle,...
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 ...
std::function< double(const vpColVector &, const MeasurementsType &)> vpLikelihoodFunction
Likelihood function, which evaluates the likelihood of a particle with regard to the measurements....
This class enables real time drawing of 2D or 3D graphics. An instance of the class open a window whi...
Class that permits to convert the position of the aircraft into range and elevation angle measurement...
vpRadarStation(const double &x, const double &y, const double &range_std, const double &elev_angle_std, const double &distMaxAllowed)
Construct a new vpRadarStation object.
double likelihood(const vpColVector &particle, const vpColVector &meas)
Compute the likelihood of a particle (value between 0. and 1.) knowing the measurements....
vpColVector measureWithNoise(const vpColVector &pos)
Noisy measurement of the range and elevation angle that correspond to pos.
vpColVector state_to_measurement(const vpColVector &particle)
Convert a particle of the Particle Filter into the measurement space.
vpColVector measureGT(const vpColVector &pos)
Perfect measurement of the range and elevation angle that correspond to pos.
VISP_EXPORT double measureTimeMicros()
bool m_useDisplay
If true, activate the plot and the renderer if VISP_HAVE_DISPLAY is defined.
int m_nbThreads
Number of thread to use in the Particle Filter.
unsigned int m_nbSteps
Number of steps for the main loop.
static const int SOFTWARE_CONTINUE
double m_stdevAircraftVelocity
double m_ampliMaxVx
Amplitude max of the noise for the state component corresponding to the velocity along the X-axis.
double m_ampliMaxY
Amplitude max of the noise for the state component corresponding to the Y coordinate.
unsigned int m_N
The number of particles.
double m_ampliMaxVy
Amplitude max of the noise for the state component corresponding to the velocity along the Y-axis.
double m_maxDistanceForLikelihood
The maximum allowed distance between a particle and the measurement, leading to a likelihood equal to...
int parseArgs(const int argc, const char *argv[])
unsigned int m_nbStepsWarmUp
Number of steps for the warmup phase.
bool m_useUserInteraction
If true, program will require some user inputs.
double m_ampliMaxX
Amplitude max of the noise for the state component corresponding to the X coordinate.
long m_seedPF
Seed for the random generators of the PF.