Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
pf-nonlinear-example.cpp
1/*
2 * ViSP, open source Visual Servoing Platform software.
3 * Copyright (C) 2005 - 2024 by Inria. All rights reserved.
4 *
5 * This software is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 * See the file LICENSE.txt at the root directory of this source
10 * distribution for additional information about the GNU GPL.
11 *
12 * For using ViSP with software that can not be combined with the GNU
13 * GPL, please contact Inria about acquiring a ViSP Professional
14 * Edition License.
15 *
16 * See https://visp.inria.fr for more information.
17 *
18 * This software was developed at:
19 * Inria Rennes - Bretagne Atlantique
20 * Campus Universitaire de Beaulieu
21 * 35042 Rennes Cedex
22 * France
23 *
24 * If you have questions regarding the use of this file, please contact
25 * Inria at visp@inria.fr
26 *
27 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29 */
30
66
67#include <iostream>
68
69// ViSP includes
70#include <visp3/core/vpConfig.h>
71#include <visp3/core/vpGaussRand.h>
72#ifdef VISP_HAVE_DISPLAY
73#include <visp3/gui/vpPlot.h>
74#endif
75
76// PF includes
77#include <visp3/core/vpParticleFilter.h>
78
79#if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
80
81#ifdef ENABLE_VISP_NAMESPACE
82using namespace VISP_NAMESPACE_NAME;
83#endif
84
85namespace
86{
94vpColVector fx(const vpColVector &particle, const double &dt)
95{
96 vpColVector point(4);
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];
101 return point;
102}
103
113void computeCoordinatesFromMeasurement(const vpColVector &z, const double &xRadar, const double &yRadar, double &x, double &y)
114{
115 double dx = z[0] * std::cos(z[1]);
116 double dy = z[0] * std::sin(z[1]);
117 x = dx + xRadar;
118 y = dy + yRadar;
119}
120
129double computeError(const double &x0, const double &y0, const double &x1, const double &y1)
130{
131 double dx = x0 - x1;
132 double dy = y0 - y1;
133 double error = std::sqrt(dx * dx + dy * dy);
134 return error;
135}
136
144double computeStateError(const vpColVector &state, const vpColVector &gt_X)
145{
146 double error = computeError(state[0], state[2], gt_X[0], gt_X[1]);
147 return error;
148}
149
159double computeMeasurementsError(const vpColVector &z, const double &xRadar, const double &yRadar, const vpColVector &gt_X)
160{
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]);
164 return error;
165}
166}
167
172class vpRadarStation
173{
174public:
184 vpRadarStation(const double &x, const double &y, const double &range_std, const double &elev_angle_std,
185 const double &distMaxAllowed)
186 : m_x(x)
187 , m_y(y)
188 , m_rngRange(range_std, 0., 4224)
189 , m_rngElevAngle(elev_angle_std, 0., 2112)
190 {
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);
195 }
196
204 {
205 vpColVector meas(2);
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);
210 return meas;
211 }
212
222 {
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);
227 vpColVector measurements(2);
228 measurements[0] = range;
229 measurements[1] = elevAngle;
230 return measurements;
231 }
232
242 {
243 vpColVector measurementsGT = measureGT(pos);
244 vpColVector measurementsNoisy = measurementsGT;
245 measurementsNoisy[0] += m_rngRange();
246 measurementsNoisy[1] += m_rngElevAngle();
247 return measurementsNoisy;
248 }
249
260 double likelihood(const vpColVector &particle, const vpColVector &meas)
261 {
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;
268 likelihood = std::min(likelihood, 1.0); // Clamp to have likelihood <= 1.
269 likelihood = std::max(likelihood, 0.); // Clamp to have likelihood >= 0.
270 return likelihood;
271 }
272
273private:
274 double m_x; // The position on the ground of the radar
275 double m_y; // The altitude of the radar
276 vpGaussRand m_rngRange; // Noise simulator for the range measurement
277 vpGaussRand m_rngElevAngle; // Noise simulator for the elevation angle measurement
278 double m_constantDenominator; // Denominator of the Gaussian function used in the likelihood computation.
279 double m_constantExpDenominator; // Denominator of the exponential in the Gaussian function used in the likelihood computation.
280};
281
285class vpACSimulator
286{
287public:
295 vpACSimulator(const vpColVector &X0, const vpColVector &vel, const double &vel_std)
296 : m_pos(X0)
297 , m_vel(vel)
298 , m_rngVel(vel_std, 0.)
299 {
300
301 }
302
310 vpColVector update(const double &dt)
311 {
312 vpColVector dx = m_vel * dt;
313 dx[0] += m_rngVel() * dt;
314 dx[1] += m_rngVel() * dt;
315 m_pos += dx;
316 return m_pos;
317 }
318
319private:
320 vpColVector m_pos; // Position of the simulated aircraft
321 vpColVector m_vel; // Velocity of the simulated aircraft
322 vpGaussRand m_rngVel; // Random generator for slight variations of the velocity of the aircraft
323};
324
326{
327 // --- Main loop parameters---
328 static const int SOFTWARE_CONTINUE = 42;
329 bool m_useDisplay;
331 unsigned int m_nbStepsWarmUp;
332 unsigned int m_nbSteps;
333 double m_dt; // Period, expressed in seconds
334 double m_sigmaRange; // Standard deviation of the range measurement, expressed in meters.
335 double m_sigmaElevAngle; // Standard deviation of the elevation angle measurent, expressed in radians.
336 double m_stdevAircraftVelocity; // Standard deviation of the velocity of the simulated aircraft, to make it deviate a bit from the constant velocity model
337 double m_radar_X; // Radar position along the X-axis, in meters
338 double m_radar_Y; // Radar position along the Y-axis, in meters
339 double m_gt_X_init; // Ground truth initial position along the X-axis, in meters
340 double m_gt_Y_init; // Ground truth initial position along the Y-axis, in meters
341 double m_gt_vX_init; // Ground truth initial velocity along the X-axis, in meters
342 double m_gt_vY_init; // Ground truth initial velocity along the Y-axis, in meters
343 // --- PF parameters---
344 unsigned int m_N;
346 double m_ampliMaxX;
347 double m_ampliMaxY;
350 long m_seedPF;
351 int m_nbThreads;
352
354 : m_useDisplay(true)
356 , m_nbStepsWarmUp(200)
357 , m_nbSteps(300)
358 , m_dt(3.)
359 , m_sigmaRange(5)
360 , m_sigmaElevAngle(vpMath::rad(0.5))
362 , m_radar_X(0.)
363 , m_radar_Y(0.)
364 , m_gt_X_init(-500.)
365 , m_gt_Y_init(1000.)
366 , m_gt_vX_init(10.)
367 , m_gt_vY_init(5.)
368 , m_N(500)
370 , m_ampliMaxX(20.)
371 , m_ampliMaxY(200.)
372 , m_ampliMaxVx(1.)
373 , m_ampliMaxVy(0.5)
374 , m_seedPF(4224)
375 , m_nbThreads(1)
376 { }
377
378 int parseArgs(const int argc, const char *argv[])
379 {
380 int i = 1;
381 while (i < argc) {
382 std::string arg(argv[i]);
383 if ((arg == "--nb-steps-main") && ((i+1) < argc)) {
384 m_nbSteps = std::atoi(argv[i + 1]);
385 ++i;
386 }
387 else if ((arg == "--nb-steps-warmup") && ((i+1) < argc)) {
388 m_nbStepsWarmUp = std::atoi(argv[i + 1]);
389 ++i;
390 }
391 else if ((arg == "--dt") && ((i+1) < argc)) {
392 m_dt = std::atoi(argv[i + 1]);
393 ++i;
394 }
395 else if ((arg == "--stdev-range") && ((i+1) < argc)) {
396 m_sigmaRange = std::atof(argv[i + 1]);
397 ++i;
398 }
399 else if ((arg == "--stdev-elev-angle") && ((i+1) < argc)) {
400 m_sigmaElevAngle = vpMath::rad(std::atof(argv[i + 1]));
401 ++i;
402 }
403 else if ((arg == "--stdev-aircraft-vel") && ((i+1) < argc)) {
404 m_stdevAircraftVelocity = std::atof(argv[i + 1]);
405 ++i;
406 }
407 else if ((arg == "--radar-X") && ((i+1) < argc)) {
408 m_radar_X = std::atof(argv[i + 1]);
409 ++i;
410 }
411 else if ((arg == "--radar-Y") && ((i+1) < argc)) {
412 m_radar_Y = std::atof(argv[i + 1]);
413 ++i;
414 }
415 else if ((arg == "--gt-X0") && ((i+1) < argc)) {
416 m_gt_X_init = std::atof(argv[i + 1]);
417 ++i;
418 }
419 else if ((arg == "--gt-Y0") && ((i+1) < argc)) {
420 m_gt_Y_init = std::atof(argv[i + 1]);
421 ++i;
422 }
423 else if ((arg == "--gt-vX0") && ((i+1) < argc)) {
424 m_gt_vX_init = std::atof(argv[i + 1]);
425 ++i;
426 }
427 else if ((arg == "--gt-vY0") && ((i+1) < argc)) {
428 m_gt_vY_init = std::atof(argv[i + 1]);
429 ++i;
430 }
431 else if ((arg == "--max-distance-likelihood") && ((i+1) < argc)) {
432 m_maxDistanceForLikelihood = std::atof(argv[i + 1]);
433 ++i;
434 }
435 else if (((arg == "-N") || (arg == "--nb-particles")) && ((i+1) < argc)) {
436 m_N = std::atoi(argv[i + 1]);
437 ++i;
438 }
439 else if ((arg == "--seed") && ((i+1) < argc)) {
440 m_seedPF = std::atoi(argv[i + 1]);
441 ++i;
442 }
443 else if ((arg == "--nb-threads") && ((i+1) < argc)) {
444 m_nbThreads = std::atoi(argv[i + 1]);
445 ++i;
446 }
447 else if ((arg == "--ampli-max-X") && ((i+1) < argc)) {
448 m_ampliMaxX = std::atof(argv[i + 1]);
449 ++i;
450 }
451 else if ((arg == "--ampli-max-Y") && ((i+1) < argc)) {
452 m_ampliMaxY = std::atof(argv[i + 1]);
453 ++i;
454 }
455 else if ((arg == "--ampli-max-vX") && ((i+1) < argc)) {
456 m_ampliMaxVx = std::atof(argv[i + 1]);
457 ++i;
458 }
459 else if ((arg == "--ampli-max-vY") && ((i+1) < argc)) {
460 m_ampliMaxVy = std::atof(argv[i + 1]);
461 ++i;
462 }
463 else if (arg == "-d") {
464 m_useDisplay = false;
465 }
466 else if (arg == "-c" ) {
467 m_useUserInteraction = false;
468 }
469 else if ((arg == "-h") || (arg == "--help")) {
470 printUsage(std::string(argv[0]));
471 SoftwareArguments defaultArgs;
472 defaultArgs.printDetails();
473 return 0;
474 }
475 else {
476 std::cout << "WARNING: unrecognised argument \"" << arg << "\"";
477 if (i + 1 < argc) {
478 std::cout << " with associated value(s) { ";
479 int nbValues = 0;
480 int j = i + 1;
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 << " ";
486 ++nbValues;
487 }
488 else {
489 hasToRun = false;
490 }
491 ++j;
492 }
493 std::cout << "}" << std::endl;
494 i += nbValues;
495 }
496 }
497 ++i;
498 }
499 return SOFTWARE_CONTINUE;
500 }
501
502private:
503 void printUsage(const std::string &softName)
504 {
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;
514 }
515
516 void printDetails()
517 {
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;
526 std::cout << " Default: " << m_nbStepsWarmUp << 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;
534 std::cout << " Default: " << m_sigmaRange << 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;
538 std::cout << " Default: " << vpMath::deg(m_sigmaElevAngle) << 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;
542 std::cout << " Default: " << m_stdevAircraftVelocity << 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;
567 std::cout << " Default: " << m_gt_vX_init << 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;
572 std::cout << " Default: " << m_gt_vY_init << 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;
577 std::cout << " Default: " << m_maxDistanceForLikelihood << 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;
603 std::cout << " Default: " << m_ampliMaxVx << 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;
607 std::cout << " Default: " << m_ampliMaxVy << 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;
614#else
615 std::cout << "OFF" << std::endl;
616#endif
617 std::cout << std::endl;
618 std::cout << " -c" << std::endl;
619 std::cout << " Deactivate user interaction." << std::endl;
620 std::cout << " Default: user interaction enabled: " << m_useUserInteraction << 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;
625 }
626};
627
628int main(const int argc, const char *argv[])
629{
631 int returnCode = args.parseArgs(argc, argv);
632 if (returnCode != SoftwareArguments::SOFTWARE_CONTINUE) {
633 return returnCode;
634 }
635
636 // Initialize the attributes of the PF
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;
641
642 vpColVector X0(4);
643 X0[0] = 0.9 * args.m_gt_X_init; // x, i.e. 10% of error with regard to ground truth
644 X0[1] = 0.9 * args.m_gt_vX_init; // dx/dt, i.e. 10% of error with regard to ground truth
645 X0[2] = 0.9 * args.m_gt_Y_init; // y, i.e. 10% of error with regard to ground truth
646 X0[3] = 0.9 * args.m_gt_vY_init; // dy/dt, i.e. 10% of error with regard to ground truth
647
649 vpRadarStation radar(args.m_radar_X, args.m_radar_Y, args.m_sigmaRange, args.m_sigmaElevAngle, args.m_maxDistanceForLikelihood);
650 using std::placeholders::_1;
651 using std::placeholders::_2;
652 vpParticleFilter<vpColVector>::vpLikelihoodFunction likelihoodFunc = std::bind(&vpRadarStation::likelihood, &radar, _1, _2);
655
656 // Initialize the PF
657 vpParticleFilter<vpColVector> filter(nbParticles, stdevsPF, seedPF, nbThreads);
658 filter.init(X0, f, likelihoodFunc, checkResamplingFunc, resamplingFunc);
659
660#ifdef VISP_HAVE_DISPLAY
661 vpPlot *plot = nullptr;
662 if (args.m_useDisplay) {
663 // Initialize the plot
664 plot = new vpPlot(4);
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");
672 plot->setColor(0, 0, vpColor::red);
673 plot->setColor(0, 1, vpColor::blue);
674 plot->setColor(0, 2, vpColor::black);
675
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");
683 plot->setColor(1, 0, vpColor::red);
684 plot->setColor(1, 1, vpColor::blue);
685 plot->setColor(1, 2, vpColor::black);
686
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");
694 plot->setColor(2, 0, vpColor::red);
695 plot->setColor(2, 1, vpColor::blue);
696 plot->setColor(2, 2, vpColor::black);
697
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");
705 plot->setColor(3, 0, vpColor::red);
706 plot->setColor(3, 1, vpColor::blue);
707 plot->setColor(3, 2, vpColor::black);
708 }
709#endif
710
711 // Initialize the simulation
713 ac_pos[0] = args.m_gt_X_init;
714 ac_pos[1] = args.m_gt_Y_init;
716 ac_vel[0] = args.m_gt_vX_init;
717 ac_vel[1] = args.m_gt_vY_init;
718 vpACSimulator ac(ac_pos, ac_vel, args.m_stdevAircraftVelocity);
719 vpColVector gt_Xprec = ac_pos;
720 vpColVector gt_Vprec = ac_vel;
721 double averageFilteringTime = 0.;
722 double meanErrorFilter = 0., meanErrorNoise = 0.;
723 double xNoise_prec = 0., yNoise_prec = 0.;
724
725 // Warmup loop
726 const unsigned int nbStepsWarmUp = args.m_nbStepsWarmUp;
727 for (unsigned int i = 0; i < nbStepsWarmUp; ++i) {
728 // Update object pose
729 vpColVector gt_X = ac.update(args.m_dt);
730
731 // Perform the measurement
732 vpColVector z = radar.measureWithNoise(gt_X);
733
734 // Use the UKF to filter the measurement
735 double t0 = vpTime::measureTimeMicros();
736 filter.filter(z, args.m_dt);
737 averageFilteringTime += vpTime::measureTimeMicros() - t0;
738 gt_Xprec = gt_X;
739
740 // Save the noisy position
741 computeCoordinatesFromMeasurement(z, args.m_radar_X, args.m_radar_Y, xNoise_prec, yNoise_prec);
742 }
743
744 for (unsigned int i = 0; i < args.m_nbSteps; ++i) {
745 // Perform the measurement
746 vpColVector gt_X = ac.update(args.m_dt);
747 vpColVector gt_V = (gt_X - gt_Xprec) / args.m_dt;
748 vpColVector z = radar.measureWithNoise(gt_X);
749
750 // Use the PF to filter the measurement
751 double t0 = vpTime::measureTimeMicros();
752 filter.filter(z, args.m_dt);
753 averageFilteringTime += vpTime::measureTimeMicros() - t0;
754
755 // Compute the error between GT and filtered state for statistics at the end of the program
756 vpColVector Xest = filter.computeFilteredState();
757 vpColVector gtState = vpColVector({ gt_Xprec[0], gt_Vprec[0], gt_Xprec[1], gt_Vprec[1] });
758 double normErrorFilter = computeStateError(Xest, gt_X);
759 meanErrorFilter += normErrorFilter;
760
761 // Compute the error between GT and noisy measurements for statistics at the end of the program
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;
766
767#ifdef VISP_HAVE_DISPLAY
768 if (args.m_useDisplay) {
769 // Plot the ground truth, measurement and filtered state
770 plot->plot(0, 0, i, gt_X[0]);
771 plot->plot(0, 1, i, Xest[0]);
772 plot->plot(0, 2, i, xNoise);
773
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);
778
779 plot->plot(2, 0, i, gt_X[1]);
780 plot->plot(2, 1, i, Xest[2]);
781 plot->plot(2, 2, i, yNoise);
782
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);
787 }
788#endif
789
790 gt_Xprec = gt_X;
791 gt_Vprec = gt_V;
792 xNoise_prec = xNoise;
793 yNoise_prec = yNoise;
794 }
795
796 // COmpute and display the error statistics and computation time
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;
803
804 if (args.m_useUserInteraction) {
805 std::cout << "Press Enter to quit..." << std::endl;
806 std::cin.get();
807 }
808
809#ifdef VISP_HAVE_DISPLAY
810 if (args.m_useDisplay) {
811 delete plot;
812 }
813#endif
814
815 // For the unit tests that uses this program
816 const double maxError = 150.;
817 if (meanErrorFilter > maxError) {
818 std::cerr << "Error: max tolerated error = " << maxError << ", mean error = " << meanErrorFilter << std::endl;
819 return -1;
820 }
821 else if (meanErrorFilter >= meanErrorNoise) {
822 std::cerr << "Error: mean error without filter = " << meanErrorNoise << ", mean error with filter = " << meanErrorFilter << std::endl;
823 return -1;
824 }
825
826 return 0;
827}
828#else
829int main()
830{
831 std::cout << "This example is only available if you compile ViSP in C++11 standard or higher." << std::endl;
832 return 0;
833}
834#endif
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 red
Definition vpColor.h:198
static const vpColor blue
Definition vpColor.h:204
static const vpColor black
Definition vpColor.h:192
Class for generating random number with normal probability density.
Provides simple mathematics computation tools that are not available in the C mathematics library (ma...
Definition vpMath.h:111
static double rad(double deg)
Definition vpMath.h:129
static double deg(double rad)
Definition vpMath.h:119
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...
Definition vpPlot.h:117
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.
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.