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
104 double normalizeAngle(
const double &angle)
107 double angleInMinPiPi = angleIn0to2pi;
108 if (angleInMinPiPi > M_PI) {
110 angleInMinPiPi -= 2. * M_PI;
112 return angleInMinPiPi;
122 vpColVector 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) {
131 for (
unsigned int j = 0; j < nbLandmarks; ++j) {
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]);
137 for (
unsigned int j = 0; j < nbLandmarks; ++j) {
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]);
183 vpColVector 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]);
235 std::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);
254 std::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;
535 int main(
const int argc,
const char *argv[])
537 bool opt_useDisplay =
true;
538 for (
int i = 1; i < argc; ++i) {
539 std::string arg(argv[i]);
541 opt_useDisplay =
false;
543 else if ((arg ==
"-h") || (arg ==
"--help")) {
544 std::cout <<
"SYNOPSIS" << std::endl;
545 std::cout <<
" " << argv[0] <<
" [-d][-h]" << std::endl;
546 std::cout << std::endl << std::endl;
547 std::cout <<
"DETAILS" << std::endl;
548 std::cout <<
" -d" << std::endl;
549 std::cout <<
" Deactivate display." << std::endl;
550 std::cout << std::endl;
551 std::cout <<
" -h, --help" << std::endl;
556 const double dt = 0.1;
557 const double step = 1.;
558 const double sigmaRange = 0.3;
560 const double wheelbase = 0.5;
561 const std::vector<vpLandmarkMeasurements> landmarks = {
vpLandmarkMeasurements(5, 10, sigmaRange, sigmaBearing)
568 const unsigned int nbLandmarks =
static_cast<unsigned int>(landmarks.size());
569 std::vector<vpColVector> cmds = generateCommands();
570 const unsigned int nbCmds =
static_cast<unsigned int>(cmds.size());
573 std::shared_ptr<vpUKSigmaDrawerAbstract> drawer = std::make_shared<vpUKSigmaDrawerMerwe>(3, 0.1, 2., 0, stateResidual, stateAdd);
576 R1landmark[0][0] = sigmaRange*sigmaRange;
577 R1landmark[1][1] = sigmaBearing*sigmaBearing;
578 vpMatrix R(2*nbLandmarks, 2 * nbLandmarks);
579 for (
unsigned int i = 0; i < nbLandmarks; ++i) {
580 R.insert(R1landmark, 2*i, 2*i);
583 const double processVariance = 0.0001;
586 Q = Q * processVariance;
602 using std::placeholders::_1;
603 using std::placeholders::_2;
604 using std::placeholders::_3;
611 ukf.setCommandStateFunction(bx);
612 ukf.setMeasurementMeanFunction(measurementMean);
613 ukf.setMeasurementResidualFunction(measurementResidual);
614 ukf.setStateAddFunction(stateAdd);
615 ukf.setStateMeanFunction(stateMean);
616 ukf.setStateResidualFunction(stateResidual);
618 #ifdef VISP_HAVE_DISPLAY
620 if (opt_useDisplay) {
624 plot->
setTitle(0,
"Position of the robot");
625 plot->
setUnitX(0,
"Position along x(m)");
626 plot->
setUnitY(0,
"Position along y (m)");
635 for (
unsigned int i = 0; i < nbCmds; ++i) {
636 robot_pos = robot.move(cmds[i], robot_pos, dt / step);
637 if (i %
static_cast<int>(step) == 0) {
642 ukf.filter(z, dt, cmds[i]);
644 #ifdef VISP_HAVE_DISPLAY
645 if (opt_useDisplay) {
648 plot->
plot(0, 1, Xest[0], Xest[1]);
653 #ifdef VISP_HAVE_DISPLAY
654 if (opt_useDisplay) {
656 plot->
plot(0, 0, robot_pos[0], robot_pos[1]);
661 if (opt_useDisplay) {
662 std::cout <<
"Press Enter to quit..." << std::endl;
666 #ifdef VISP_HAVE_DISPLAY
667 if (opt_useDisplay) {
672 vpColVector finalError = grid.state_to_measurement(ukf.getXest()) - grid.measureGT(robot_pos);
673 const double maxError = 0.3;
675 std::cerr <<
"Error: max tolerated error = " << maxError <<
", final error = " << finalError.
frobeniusNorm() << std::endl;
683 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...
void initGraph(unsigned int graphNum, unsigned int curveNbr)
void setUnitY(unsigned int graphNum, const std::string &unity)
void setLegend(unsigned int graphNum, unsigned int curveNum, const std::string &legend)
void plot(unsigned int graphNum, unsigned int curveNum, double x, double y)
void setUnitX(unsigned int graphNum, const std::string &unitx)
void setTitle(unsigned int graphNum, const std::string &title)
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...