Visual Servoing Platform  version 3.6.1 under development (2024-10-18)
vpUnscentedKalman.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  * Description:
31  * Display a point cloud using PCL library.
32  */
33 
39 #include <visp3/core/vpUnscentedKalman.h>
40 
41 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
42 BEGIN_VISP_NAMESPACE
43 vpUnscentedKalman::vpUnscentedKalman(const vpMatrix &Q, const vpMatrix &R, std::shared_ptr<vpUKSigmaDrawerAbstract> &drawer, const vpProcessFunction &f, const vpMeasurementFunction &h)
44  : m_hasUpdateBeenCalled(false)
45  , m_Q(Q)
46  , m_R(R)
47  , m_f(f)
48  , m_h(h)
49  , m_sigmaDrawer(drawer)
50  , m_b(nullptr)
51  , m_bx(nullptr)
52  , m_measMeanFunc(vpUnscentedKalman::simpleMean)
53  , m_measResFunc(vpUnscentedKalman::simpleResidual)
54  , m_stateAddFunction(vpUnscentedKalman::simpleAdd)
55  , m_stateMeanFunc(vpUnscentedKalman::simpleMean)
56  , m_stateResFunc(vpUnscentedKalman::simpleResidual)
57 { }
58 
59 void vpUnscentedKalman::init(const vpColVector &mu0, const vpMatrix &P0)
60 {
61  if ((mu0.getRows() != P0.getCols()) || (mu0.getRows() != P0.getRows())) {
62  throw(vpException(vpException::dimensionError, "Initial state X0 and process covariance matrix P0 sizes mismatch"));
63  }
64  if ((m_Q.getCols() != P0.getCols()) || (m_Q.getRows() != P0.getRows())) {
65  throw(vpException(vpException::dimensionError, "Initial process covariance matrix P0 and Q matrix sizes mismatch"));
66  }
67  m_Xest = mu0;
68  m_Pest = P0;
69  m_mu = mu0;
70  m_Ppred = P0;
71 }
72 
73 void vpUnscentedKalman::filter(const vpColVector &z, const double &dt, const vpColVector &u)
74 {
75  predict(dt, u);
76  update(z);
77 }
78 
79 void vpUnscentedKalman::predict(const double &dt, const vpColVector &u)
80 {
81  vpColVector x;
82  vpMatrix P;
83 
84  if (m_hasUpdateBeenCalled) {
85  // Update is the last function that has been called, starting from the filtered values.
86  x = m_Xest;
87  P = m_Pest;
88  m_hasUpdateBeenCalled = false;
89  }
90  else {
91  // Predict is the last function that has been called, starting from the predicted values.
92  x = m_mu;
93  P = m_Ppred;
94  }
95 
96  // Drawing the sigma points
97  m_chi = m_sigmaDrawer->drawSigmaPoints(x, P);
98 
99  // Computation of the attached weights
100  vpUKSigmaDrawerAbstract::vpSigmaPointsWeights weights = m_sigmaDrawer->computeWeights();
101  m_wm = weights.m_wm;
102  m_wc = weights.m_wc;
103 
104  // Computation of the prior based on the sigma points
105  size_t nbPoints = m_chi.size();
106  if (m_Y.size() != nbPoints) {
107  m_Y.resize(nbPoints);
108  }
109  for (size_t i = 0; i < nbPoints; ++i) {
110  vpColVector prior = m_f(m_chi[i], dt);
111  if (m_b) {
112  prior = m_stateAddFunction(prior, m_b(u, dt));
113  }
114  else if (m_bx) {
115  prior = m_stateAddFunction(prior, m_bx(u, m_chi[i], dt));
116  }
117  m_Y[i] = prior;
118  }
119 
120  // Computation of the mean and covariance of the prior
121  vpUnscentedTransformResult transformResults = unscentedTransform(m_Y, m_wm, m_wc, m_Q, m_stateResFunc, m_stateMeanFunc);
122  m_mu = transformResults.m_mu;
123  m_Ppred = transformResults.m_P;
124 }
125 
127 {
128  size_t nbPoints = m_chi.size();
129  if (m_Z.size() != nbPoints) {
130  m_Z.resize(nbPoints);
131  }
132  for (size_t i = 0; i < nbPoints; ++i) {
133  m_Z[i] = (m_h(m_Y[i]));
134  }
135 
136  // Computation of the mean and covariance of the prior expressed in the measurement space
137  vpUnscentedTransformResult transformResults = unscentedTransform(m_Z, m_wm, m_wc, m_R, m_measResFunc, m_measMeanFunc);
138  m_muz = transformResults.m_mu;
139  m_Pz = transformResults.m_P;
140 
141  // Computation of the Kalman gain
142  vpMatrix Pxz = m_wc[0] * m_stateResFunc(m_Y[0], m_mu) * m_measResFunc(m_Z[0], m_muz).transpose();
143  size_t nbPts = m_wc.size();
144  for (size_t i = 1; i < nbPts; ++i) {
145  Pxz += m_wc[i] * m_stateResFunc(m_Y[i], m_mu) * m_measResFunc(m_Z[i], m_muz).transpose();
146  }
147  m_K = Pxz * m_Pz.inverseByCholesky();
148 
149  // Updating the estimate
150  m_Xest = m_stateAddFunction(m_mu, m_K * m_measResFunc(z, m_muz));
151  m_Pest = m_Ppred - m_K * m_Pz * m_K.transpose();
152  m_hasUpdateBeenCalled = true;
153 }
154 
155 vpUnscentedKalman::vpUnscentedTransformResult vpUnscentedKalman::unscentedTransform(const std::vector<vpColVector> &sigmaPoints,
156  const std::vector<double> &wm, const std::vector<double> &wc, const vpMatrix &cov,
157  const vpAddSubFunction &resFunc, const vpMeanFunction &meanFunc
158 )
159 {
160  vpUnscentedKalman::vpUnscentedTransformResult result;
161 
162  // Computation of the mean
163  result.m_mu = meanFunc(sigmaPoints, wm);
164 
165  // Computation of the covariance
166  result.m_P = cov;
167  size_t nbSigmaPoints = sigmaPoints.size();
168  for (size_t i = 0; i < nbSigmaPoints; ++i) {
169  vpColVector e = resFunc(sigmaPoints[i], result.m_mu);
170  result.m_P += wc[i] * e*e.transpose();
171  }
172  return result;
173 }
174 END_VISP_NAMESPACE
175 #else
176 void vpUnscentedKalman_dummy()
177 {
178 
179 }
180 #endif
unsigned int getCols() const
Definition: vpArray2D.h:337
unsigned int size() const
Return the number of elements of the 2D array.
Definition: vpArray2D.h:349
unsigned int getRows() const
Definition: vpArray2D.h:347
Implementation of column vector and the associated operations.
Definition: vpColVector.h:191
vpRowVector transpose() const
error that can be emitted by ViSP classes.
Definition: vpException.h:60
@ dimensionError
Bad dimension.
Definition: vpException.h:71
Implementation of a matrix and operations on matrices.
Definition: vpMatrix.h:169
vpMatrix inverseByCholesky() const
vpMatrix transpose() const
vpUnscentedKalman(const vpMatrix &Q, const vpMatrix &R, std::shared_ptr< vpUKSigmaDrawerAbstract > &drawer, const vpProcessFunction &f, const vpMeasurementFunction &h)
Construct a new vpUnscentedKalman object.
void update(const vpColVector &z)
Update the estimate of the state based on a new measurement.
void filter(const vpColVector &z, const double &dt, const vpColVector &u=vpColVector())
Perform first the prediction step and then the filtering step.
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.
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...
void init(const vpColVector &mu0, const vpMatrix &P0)
Set the guess of the initial state and covariance.
The weights corresponding to the sigma points drawing.