80#include <visp3/core/vpUKSigmaDrawerMerwe.h>
81#include <visp3/core/vpUnscentedKalman.h>
84#include <visp3/core/vpConfig.h>
85#include <visp3/core/vpGaussRand.h>
86#ifdef VISP_HAVE_DISPLAY
87#include <visp3/gui/vpPlot.h>
90#if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
92#ifdef ENABLE_VISP_NAMESPACE
104double normalizeAngle(
const double &angle)
107 double angleInMinPiPi = angleIn0to2pi;
108 if (angleInMinPiPi > M_PI) {
110 angleInMinPiPi -= 2. * M_PI;
112 return angleInMinPiPi;
122vpColVector measurementMean(
const std::vector<vpColVector> &measurements,
const std::vector<double> &wm)
124 const unsigned int nbPoints =
static_cast<unsigned int>(measurements.size());
125 const unsigned int sizeMeasurement = measurements[0].
size();
126 const unsigned int nbLandmarks = sizeMeasurement / 2;
128 std::vector<double> sumCos(nbLandmarks, 0.);
129 std::vector<double> sumSin(nbLandmarks, 0.);
130 for (
unsigned int i = 0;
i < nbPoints; ++
i) {
132 mean[2*
j] += wm[
i] * measurements[
i][2*
j];
133 sumCos[
j] += wm[
i] * std::cos(measurements[i][(2*j)+1]);
134 sumSin[
j] += wm[
i] * std::sin(measurements[i][(2*j)+1]);
138 mean[(2*
j)+1] = std::atan2(sumSin[j], sumCos[j]);
154 unsigned int nbMeasures = res.
size();
155 for (
unsigned int i = 1;
i < nbMeasures;
i += 2) {
156 res[
i] = normalizeAngle(res[i]);
172 add[2] = normalizeAngle(add[2]);
183vpColVector stateMean(
const std::vector<vpColVector> &states,
const std::vector<double> &wm)
186 unsigned int nbPoints =
static_cast<unsigned int>(states.size());
189 for (
unsigned int i = 0;
i < nbPoints; ++
i) {
190 mean[0] += wm[
i] * states[
i][0];
191 mean[1] += wm[
i] * states[
i][1];
192 sumCos += wm[
i] * std::cos(states[i][2]);
193 sumSin += wm[
i] * std::sin(states[i][2]);
195 mean[2] = std::atan2(sumSin, sumCos);
210 res[2] = normalizeAngle(res[2]);
235std::vector<vpColVector> generateTurnCommands(
const double &v,
const double &angleStart,
const double &angleStop,
const unsigned int &nbSteps)
237 std::vector<vpColVector>
cmds;
238 double dTheta =
vpMath::rad(angleStop - angleStart) /
static_cast<double>(nbSteps - 1);
239 for (
unsigned int i = 0;
i < nbSteps; ++
i) {
240 double theta =
vpMath::rad(angleStart) + dTheta *
static_cast<double>(
i);
254std::vector<vpColVector> generateCommands()
256 std::vector<vpColVector>
cmds;
258 unsigned int nbSteps = 30;
259 double dv = (1.1 - 0.001) /
static_cast<double>(nbSteps - 1);
260 for (
unsigned int i = 0;
i < nbSteps; ++
i) {
262 cmd[0] = 0.001 +
static_cast<double>(
i) * dv;
268 double lastLinearVelocity =
cmds[
cmds.size() -1][0];
269 std::vector<vpColVector> leftTurnCmds = generateTurnCommands(lastLinearVelocity, 0, 2, 15);
270 cmds.insert(
cmds.end(), leftTurnCmds.begin(), leftTurnCmds.end());
271 for (
unsigned int i = 0;
i < 100; ++
i) {
272 cmds.push_back(cmds[
cmds.size() -1]);
276 lastLinearVelocity =
cmds[
cmds.size() -1][0];
277 std::vector<vpColVector> rightTurnCmds = generateTurnCommands(lastLinearVelocity, 2, -2, 15);
278 cmds.insert(
cmds.end(), rightTurnCmds.begin(), rightTurnCmds.end());
279 for (
unsigned int i = 0;
i < 200; ++
i) {
280 cmds.push_back(cmds[
cmds.size() -1]);
284 lastLinearVelocity =
cmds[
cmds.size() -1][0];
285 leftTurnCmds = generateTurnCommands(lastLinearVelocity, -2, 0, 15);
286 cmds.insert(
cmds.end(), leftTurnCmds.begin(), leftTurnCmds.end());
287 for (
unsigned int i = 0;
i < 150; ++
i) {
288 cmds.push_back(cmds[
cmds.size() -1]);
291 lastLinearVelocity =
cmds[
cmds.size() -1][0];
292 leftTurnCmds = generateTurnCommands(lastLinearVelocity, 0, 1, 25);
293 cmds.insert(
cmds.end(), leftTurnCmds.begin(), leftTurnCmds.end());
294 for (
unsigned int i = 0;
i < 150; ++
i) {
295 cmds.push_back(cmds[
cmds.size() -1]);
327 double heading = x[2];
329 double steeringAngle = u[1];
330 double distance = vel * dt;
332 if (std::abs(steeringAngle) > 0.001) {
334 double beta = (distance / m_w) * std::tan(steeringAngle);
335 double radius = m_w / std::tan(steeringAngle);
336 double sinh = std::sin(heading);
337 double sinhb = std::sin(heading + beta);
338 double cosh = std::cos(heading);
339 double coshb = std::cos(heading + beta);
341 motion[0] = -radius * sinh + radius * sinhb;
342 motion[1] = radius * cosh - radius * coshb;
349 motion[0] = distance * std::cos(heading);
350 motion[1] = distance * std::sin(heading);
369 newX[2] = normalizeAngle(newX[2]);
394 , m_rngRange(range_std, 0., 4224)
395 , m_rngRelativeAngle(rel_angle_std, 0., 2112)
407 double dx = m_x - chi[0];
408 double dy = m_y - chi[1];
409 meas[0] = std::sqrt(dx * dx + dy * dy);
410 meas[1] = normalizeAngle(std::atan2(dy, dx) - chi[2]);
423 double dx = m_x - pos[0];
424 double dy = m_y - pos[1];
425 double range = std::sqrt(dx * dx + dy * dy);
426 double orientation = normalizeAngle(std::atan2(dy, dx) - pos[2]);
428 measurements[0] = range;
429 measurements[1] = orientation;
444 measurementsNoisy[0] += m_rngRange();
445 measurementsNoisy[1] += m_rngRelativeAngle();
446 measurementsNoisy[1] = normalizeAngle(measurementsNoisy[1]);
447 return measurementsNoisy;
470 : m_landmarks(landmarks)
481 unsigned int nbLandmarks =
static_cast<unsigned int>(m_landmarks.size());
483 for (
unsigned int i = 0; i < nbLandmarks; ++i) {
484 vpColVector landmarkMeas = m_landmarks[i].state_to_measurement(chi);
485 measurements[2*i] = landmarkMeas[0];
486 measurements[(2*i) + 1] = landmarkMeas[1];
501 unsigned int nbLandmarks =
static_cast<unsigned int>(m_landmarks.size());
503 for (
unsigned int i = 0; i < nbLandmarks; ++i) {
504 vpColVector landmarkMeas = m_landmarks[i].measureGT(pos);
505 measurements[2*i] = landmarkMeas[0];
506 measurements[(2*i) + 1] = landmarkMeas[1];
521 unsigned int nbLandmarks =
static_cast<unsigned int>(m_landmarks.size());
523 for (
unsigned int i = 0; i < nbLandmarks; ++i) {
524 vpColVector landmarkMeas = m_landmarks[i].measureWithNoise(pos);
525 measurements[2*i] = landmarkMeas[0];
526 measurements[(2*i) + 1] = landmarkMeas[1];
532 std::vector<vpLandmarkMeasurements> m_landmarks;
535int main(
const int argc,
const char *argv[])
537 bool opt_useDisplay =
true;
538 bool opt_useUserInteraction =
true;
539 for (
int i = 1;
i < argc; ++
i) {
540 std::string arg(argv[i]);
542 opt_useDisplay =
false;
545 opt_useUserInteraction =
false;
547 else if ((arg ==
"-h") || (arg ==
"--help")) {
548 std::cout <<
"SYNOPSIS" << std::endl;
549 std::cout <<
" " << argv[0] <<
" [-d][-h]" << std::endl;
550 std::cout << std::endl << std::endl;
551 std::cout <<
"DETAILS" << std::endl;
552 std::cout <<
" -d" << std::endl;
553 std::cout <<
" Deactivate display." << std::endl;
554 std::cout <<
" -c" << std::endl;
555 std::cout <<
" Deactivate user interaction." << std::endl;
556 std::cout << std::endl;
557 std::cout <<
" -h, --help" << std::endl;
562 const double dt = 0.1;
563 const double step = 1.;
564 const double sigmaRange = 0.3;
575 std::vector<vpColVector>
cmds = generateCommands();
576 const unsigned int nbCmds =
static_cast<unsigned int>(
cmds.size());
579 std::shared_ptr<vpUKSigmaDrawerAbstract>
drawer = std::make_shared<vpUKSigmaDrawerMerwe>(3, 0.1, 2., 0, stateResidual, stateAdd);
584 vpMatrix R(2*nbLandmarks, 2 * nbLandmarks);
586 R.insert(R1landmark, 2*i, 2*i);
589 const double processVariance = 0.0001;
592 Q =
Q * processVariance;
608 using std::placeholders::_1;
609 using std::placeholders::_2;
610 using std::placeholders::_3;
617 ukf.setCommandStateFunction(bx);
618 ukf.setMeasurementMeanFunction(measurementMean);
619 ukf.setMeasurementResidualFunction(measurementResidual);
620 ukf.setStateAddFunction(stateAdd);
621 ukf.setStateMeanFunction(stateMean);
622 ukf.setStateResidualFunction(stateResidual);
624#ifdef VISP_HAVE_DISPLAY
626 if (opt_useDisplay) {
629 plot->initGraph(0, 2);
630 plot->setTitle(0,
"Position of the robot");
631 plot->setUnitX(0,
"Position along x(m)");
632 plot->setUnitY(0,
"Position along y (m)");
633 plot->setLegend(0, 0,
"GT");
634 plot->setLegend(0, 1,
"Filtered");
637 (void)opt_useDisplay;
643 for (
unsigned int i = 0;
i < nbCmds; ++
i) {
644 robot_pos = robot.move(cmds[i], robot_pos, dt / step);
645 if (i %
static_cast<int>(step) == 0) {
650 ukf.filter(z, dt, cmds[i]);
652#ifdef VISP_HAVE_DISPLAY
653 if (opt_useDisplay) {
656 plot->plot(0, 1, Xest[0], Xest[1]);
661#ifdef VISP_HAVE_DISPLAY
662 if (opt_useDisplay) {
664 plot->plot(0, 0, robot_pos[0], robot_pos[1]);
669 if (opt_useUserInteraction) {
670 std::cout <<
"Press Enter to quit..." << std::endl;
674#ifdef VISP_HAVE_DISPLAY
675 if (opt_useDisplay) {
681 const double maxError = 0.3;
683 std::cerr <<
"Error: max tolerated error = " << maxError <<
", final error = " << finalError.
frobeniusNorm() << std::endl;
691 std::cout <<
"This example is only available if you compile ViSP in C++11 standard or higher." << std::endl;
unsigned int size() const
Return the number of elements of the 2D array.
Class that approximates a 4-wheel robot using a bicycle model.
vpColVector computeMotion(const vpColVector &u, const vpColVector &x, const double &dt)
Models the effect of the command on the state model.
vpBicycleModel(const double &w)
Construct a new vpBicycleModel object.
vpColVector move(const vpColVector &u, const vpColVector &x, const double &dt)
Move the robot according to its current position and the commands.
Implementation of column vector and the associated operations.
double frobeniusNorm() const
Class for generating random number with normal probability density.
Class that permits to convert the position + heading of the 4-wheel robot into measurements from a la...
vpColVector measureGT(const vpColVector &pos)
Perfect measurement of the range and relative orientation of the robot located at pos.
vpColVector state_to_measurement(const vpColVector &chi)
Convert the prior of the UKF into the measurement space.
vpLandmarkMeasurements(const double &x, const double &y, const double &range_std, const double &rel_angle_std)
Construct a new vpLandmarkMeasurements object.
vpColVector measureWithNoise(const vpColVector &pos)
Noisy measurement of the range and relative orientation that correspond to pos.
Class that represent a grid of landmarks that measure the distance and relative orientation of the 4-...
vpColVector measureGT(const vpColVector &pos)
Perfect measurement from each landmark of the range and relative orientation of the robot located at ...
vpLandmarksGrid(const std::vector< vpLandmarkMeasurements > &landmarks)
Construct a new vpLandmarksGrid object.
vpColVector measureWithNoise(const vpColVector &pos)
Noisy measurement from each landmark of the range and relative orientation that correspond to pos.
vpColVector state_to_measurement(const vpColVector &chi)
Convert the prior of the UKF into the measurement space.
static double rad(double deg)
static float modulo(const float &value, const float &modulo)
Gives the rest of value divided by modulo when the quotient can only be an integer.
Implementation of a matrix and operations on matrices.
This class enables real time drawing of 2D or 3D graphics. An instance of the class open a window whi...
std::function< vpColVector(const vpColVector &, const vpColVector &, const double &)> vpCommandStateFunction
Command model function, which projects effect of the command on the state, when the effect of the com...
std::function< vpColVector(const vpColVector &)> vpMeasurementFunction
Measurement function, which converts the prior points in the measurement space. The argument is a poi...
std::function< vpColVector(const vpColVector &, const double &)> vpProcessFunction
Process model function, which projects the sigma points forward in time. The first argument is a sigm...
ColVector measurementResidual(ColVector meas, ColVector to_subtract)