UsTK : Ultrasound ToolKit  version 2.0.1 under development (2024-05-17)
usUnscentedKalmanFilter.cpp
1 /****************************************************************************
2  *
3  * This file is part of the ustk software.
4  * Copyright (C) 2016 - 2017 by Inria. All rights reserved.
5  *
6  * This software is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * ("GPL") version 2 as published by the Free Software Foundation.
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 ustk 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  * This software was developed at:
17  * Inria Rennes - Bretagne Atlantique
18  * Campus Universitaire de Beaulieu
19  * 35042 Rennes Cedex
20  * France
21  *
22  * If you have questions regarding the use of this file, please contact
23  * Inria at ustk@inria.fr
24  *
25  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
26  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27  *
28  * Author:
29  * Jason Chevrie
30  *
31  *****************************************************************************/
32 
33 #include <visp3/ustk_needle_modeling/usUnscentedKalmanFilter.h>
34 
35 #include <iostream>
36 
37 #if defined(VISP_HAVE_EIGEN3)
38 #include <eigen3/Eigen/Cholesky>
39 #endif
40 
41 
42 vpMatrix root(const vpMatrix &M)
43 {
44  if(M.getCols() != M.getRows()) throw vpException(vpException::dimensionError, "NeedleUpdate::root(const vpMatrix&): input matrix should be square");
45 
46  vpMatrix sqrtM(M.getRows(), M.getCols());
47 
48 #if defined(VISP_HAVE_EIGEN3)
49  Eigen::MatrixXd Meigen(M.getRows(),M.getCols());
50 
51  for(unsigned int i =0 ; i<M.getRows() ; i++)
52  {
53  for(unsigned int j=0 ; j<M.getCols() ; j++)
54  {
55  Meigen(i,j) = M[i][j];
56  }
57  }
58 
59  Eigen::MatrixXd sqrtMeigen(Meigen.llt().matrixL());
60 
61  for(unsigned int i =0 ; i<M.getRows() ; i++)
62  {
63  for(unsigned int j=0 ; j<M.getCols() ; j++)
64  {
65  sqrtM[i][j] = sqrtMeigen(i,j);
66  }
67  }
68 #else
69  vpMatrix A(M);
70  vpColVector w;
71  vpMatrix V;
72  try
73  {
74  A.svd(w,V);
75  }
76  catch(std::exception &e)
77  {
78  vpMatrix I;
79  I.eye(M.getCols());
80  A = M + std::numeric_limits<double>::epsilon() * I;
81  A.svd(w,V);
82  }
83  for(unsigned int i=0 ; i<w.size() ; i++)
84  {
85  if(w[i] > 1e-8 * w[0]) sqrtM.insert(sqrt(w[i])*A.getCol(i), 0,i);
86  }
87 #endif
88  return sqrtM;
89 }
90 
92  m_stateDimension(0),
93  m_measureDimension(0),
94  m_processNoiseDimension(0),
95  m_measureNoiseDimension(0),
96 
97  m_state(),
98  m_stateCovarianceMatrix(),
99 
100  m_processNoiseType(usUnscentedKalmanFilter::NoiseType::ADDITIVE_NOISE),
101  m_processNoiseCovarianceMatrix(),
102  m_computeProcessNoiseCovarianceMatrixAutomatically(false),
103 
104  m_measureNoiseType(usUnscentedKalmanFilter::NoiseType::ADDITIVE_NOISE),
105  m_measureNoiseCovarianceMatrix(),
106  m_computeMeasureNoiseCovarianceMatrixAutomatically(false),
107 
108  m_measure(),
109 
110  m_sigmaPointsGenerationType(usUnscentedKalmanFilter::STANDARD_COVARIANCE),
111  m_sigmaPointsInit(),
112  m_sigmaPointsPropagated(),
113  m_sigmaPointsMeasure(),
114  m_sigmaPointsMeanWeights(),
115  m_sigmaPointsCovarianceWeights(),
116  m_sigmaPointsScalingFactor(1),
117  m_sigmaPointsSpreadThreshold(std::numeric_limits<double>::max()),
118 
119  m_stateSigmaMean(),
120  m_measureSigmaMean(),
121  m_stateSigmaCovarianceMatrix(),
122  m_stateMeasureSigmaCovarianceMatrix(),
123  m_measureSigmaCovarianceMatrix()
124 {
125 
126 }
127 
129 {
130 
131 }
132 
134 {
135  return m_stateDimension;
136 }
137 
139 {
140  if(dim <= 0) throw vpException(vpException::dimensionError, "usUnscentedKalmanFilter::setStateDimension: state dimension should be positive");
141 
142  m_stateDimension = dim;
143  m_state.resize(dim);
144  m_stateCovarianceMatrix.resize(dim,dim);
145  m_stateSigmaMean.resize(dim);
146  m_stateSigmaCovarianceMatrix.resize(dim,dim);
148 }
149 
151 {
152  return m_measureDimension;
153 }
154 
156 {
157  if(dim <= 0) throw vpException(vpException::dimensionError, "usUnscentedKalmanFilter::setMeasureDimension: measure dimension should be positive");
158 
159  m_measureDimension = dim;
160  m_measureSigmaMean.resize(dim);
161  m_measureSigmaCovarianceMatrix.resize(dim,dim);
163 }
164 
166 {
167  return m_processNoiseType;
168 }
169 
171 {
172  m_processNoiseType = type;
173 }
174 
176 {
178 }
179 
181 {
182  if(dim <= 0) throw vpException(vpException::dimensionError, "usUnscentedKalmanFilter::setProcessNoiseDimension: noise dimension should be positive");
183 
185  m_processNoiseCovarianceMatrix.resize(dim,dim);
186 }
187 
189 {
190  return m_measureNoiseType;
191 }
192 
194 {
195  m_measureNoiseType = type;
196 }
197 
199 {
201 }
202 
204 {
205  if(dim <= 0) throw vpException(vpException::dimensionError, "usUnscentedKalmanFilter::setMeasureNoiseDimension: noise dimension should be positive");
206 
208  m_measureNoiseCovarianceMatrix.resize(dim,dim);
209 }
210 
212 {
214 }
215 
217 {
219 }
220 
222 {
224 }
225 
227 {
229 }
230 
232 {
234 }
235 
237 {
238  m_sigmaPointsSpreadThreshold = threshold;
239 }
240 
241 
243 {
244  return m_state;
245 }
246 
247 void usUnscentedKalmanFilter::setState(const vpColVector &state)
248 {
249  if(state.getRows() != m_stateDimension) throw vpException(vpException::dimensionError, "usUnscentedKalmanFilter::setState: invalid state dimension");
250 
251  m_state = state;
252 }
253 
255 {
257 }
258 
260 {
261  if(mat.getRows() != m_stateDimension || mat.getCols() != m_stateDimension) throw vpException(vpException::dimensionError, "usUnscentedKalmanFilter::setStateCovarianceMatrix: invalid matrix dimension");
262 
264 }
265 
267 {
269 }
270 
272 {
273  if(cov.getCols() != m_processNoiseDimension || cov.getRows() != m_processNoiseDimension) throw vpException(vpException::dimensionError, "usUnscentedKalmanFilter::setProcessNoiseCovarianceMatrix: invalid matrix dimension");
274 
276 }
277 
279 {
281 }
282 
284 {
286 }
287 
289 {
291 }
292 
294 {
295  if(cov.getCols() != m_measureNoiseDimension || cov.getRows() != m_measureNoiseDimension) throw vpException(vpException::dimensionError, "usUnscentedKalmanFilter::setMeasureNoiseCovarianceMatrix: invalid matrix dimension");
296 
298 }
299 
301 {
303 }
304 
306 {
308 }
309 
310 bool usUnscentedKalmanFilter::checkConsistency(const vpColVector &measure)
311 {
312  if(!m_computeProcessNoiseCovarianceMatrixAutomatically && (m_processNoiseType == usUnscentedKalmanFilter::ADDITIVE_NOISE) && (m_stateDimension != m_processNoiseDimension)) throw vpException(vpException::dimensionError, "usUnscentedKalmanFilter::checkConsistency: additive process noise is set, but the noise dimension (%d) does not match the state dimension (%d)", m_processNoiseDimension, m_stateDimension);
313  if(!m_computeMeasureNoiseCovarianceMatrixAutomatically && (m_measureNoiseType == usUnscentedKalmanFilter::ADDITIVE_NOISE) && (m_measureDimension != m_measureNoiseDimension)) throw vpException(vpException::dimensionError, "usUnscentedKalmanFilter::checkConsistency: additive measure noise is set, but the noise dimension (%d) does not match the measure dimension (%d)", m_measureNoiseDimension, m_measureDimension);
314  if(measure.size() != m_measureDimension) throw vpException(vpException::dimensionError, "usUnscentedKalmanFilter::checkConsistency: invamid input measure dimension (%d), should be %d", measure.size(), m_measureDimension);
315 
316  return true;
317 }
318 
320 {
321 
322 }
323 
325 {
326 
327 }
328 
330 {
331  int augmentedStateDimension = 0;
332  vpMatrix augmentedStateCovarianceMatrix;
333 
334  switch(m_processNoiseType)
335  {
336  case NoiseType::ADDITIVE_NOISE:
337  {
338  switch(m_measureNoiseType)
339  {
340  case NoiseType::ADDITIVE_NOISE:
341  {
342  augmentedStateDimension = m_stateDimension;
343  augmentedStateCovarianceMatrix = m_stateCovarianceMatrix;
344  break;
345  }
346  case NoiseType::GENERIC_NOISE:
347  {
348  augmentedStateDimension = m_stateDimension + m_measureNoiseDimension;
349  augmentedStateCovarianceMatrix.resize(augmentedStateDimension,augmentedStateDimension);
350  augmentedStateCovarianceMatrix.insert(m_stateCovarianceMatrix, 0,0);
351  augmentedStateCovarianceMatrix.insert(m_measureNoiseCovarianceMatrix, m_stateDimension,m_stateDimension);
352  break;
353  }
354  }
355  break;
356  }
357  case NoiseType::GENERIC_NOISE:
358  {
359  switch(m_measureNoiseType)
360  {
361  case NoiseType::ADDITIVE_NOISE:
362  {
363  augmentedStateDimension = m_stateDimension + m_processNoiseDimension;
364  augmentedStateCovarianceMatrix.resize(augmentedStateDimension,augmentedStateDimension);
365  augmentedStateCovarianceMatrix.insert(m_stateCovarianceMatrix, 0,0);
366  augmentedStateCovarianceMatrix.insert(m_processNoiseCovarianceMatrix, m_stateDimension,m_stateDimension);
367  break;
368  }
369  case NoiseType::GENERIC_NOISE:
370  {
372  augmentedStateCovarianceMatrix.resize(augmentedStateDimension,augmentedStateDimension);
373  augmentedStateCovarianceMatrix.insert(m_stateCovarianceMatrix, 0,0);
374  augmentedStateCovarianceMatrix.insert(m_processNoiseCovarianceMatrix, m_stateDimension,m_stateDimension);
376  break;
377  }
378  }
379  break;
380  }
381  }
382 
383  vpMatrix rootP;
384 
385  try
386  {
387  rootP = root(augmentedStateCovarianceMatrix);
388  }
389  catch (std::exception &e)
390  {
391  std::cout << "usUnscentedKalmanFilter::generateSigmaPoints failed: " << e.what() << std::endl;
392  return false;
393  }
394 
395  double alpha = 0;
396  const double beta = 2;
397  const double k = 0; // 3 - augmentedStateDimension
399  {
400  case SigmaPointGenerationType::STANDARD_COVARIANCE:
401  {
402  alpha = 1;
403  break;
404  }
405  case SigmaPointGenerationType::FIXED_SCALING_FACTOR:
406  {
408  break;
409  }
410  case SigmaPointGenerationType::LIMITED_SPREAD:
411  {
412  double max = 0;
413  for(unsigned int i=0 ; i<m_stateDimension ; i++)
414  {
415  double val = stateNorm(rootP.getCol(i));
416  if(val > max) max = val;
417  }
418  double thresholdForMax = m_sigmaPointsSpreadThreshold/(m_sigmaPointsScalingFactor*sqrt(augmentedStateDimension+k));
419  alpha = m_sigmaPointsScalingFactor * thresholdForMax / (max + thresholdForMax);
420  break;
421  }
422  }
423 
424  double lambda = alpha*alpha*(augmentedStateDimension+k)-augmentedStateDimension;
425 
426  vpMatrix sqrtP = sqrt(fabs(augmentedStateDimension+lambda))*rootP;
427 
428  m_sigmaPointsInit.resize(augmentedStateDimension, 1+2*augmentedStateDimension);
429 
430  vpColVector sigmaCenter(augmentedStateDimension, 0);
431  sigmaCenter.insert(0, this->stateLog(m_state, m_state));
432 
433  for(int i=0 ; i<augmentedStateDimension ; i++)
434  {
435  m_sigmaPointsInit[i][0] = sigmaCenter[i];
436  for(int j=0 ; j<augmentedStateDimension ; j++) m_sigmaPointsInit[i][j+1] = sigmaCenter[i] + sqrtP[i][j];
437  for(int j=0 ; j<augmentedStateDimension ; j++) m_sigmaPointsInit[i][j+augmentedStateDimension+1] = sigmaCenter[i] - sqrtP[i][j];
438  }
439 
440  m_sigmaPointsMeanWeights.resize(1+2*augmentedStateDimension);
441  m_sigmaPointsCovarianceWeights.resize(1+2*augmentedStateDimension);
442 
443  m_sigmaPointsMeanWeights[0] = lambda / (augmentedStateDimension+lambda);
444  m_sigmaPointsCovarianceWeights[0] = lambda / (augmentedStateDimension+lambda) + 1 - alpha*alpha + beta;
445  m_sigmaPointsMeanWeights[1] = 1.0/(2*(augmentedStateDimension+lambda));
446  m_sigmaPointsCovarianceWeights[1] = 1.0/(2*(augmentedStateDimension+lambda));
447 
448  for(unsigned int i=2 ; i<m_sigmaPointsMeanWeights.size() ; i++)
449  {
452  }
453 
454  return true;
455 }
456 
458 {
459  m_sigmaPointsPropagated.resize(m_sigmaPointsInit.getRows(), m_sigmaPointsInit.getCols());
460  for(unsigned int i=0 ; i<m_sigmaPointsInit.getCols() ; i++)
461  {
462  vpColVector propagatedSigmaPoint;
463  try
464  {
465  propagatedSigmaPoint = this->propagateSigmaPoint(m_sigmaPointsInit.getCol(i));
466  }
467  catch(std::exception &e)
468  {
469  std::cout << "usUnscentedKalmanFilter::computePropagatedSigmaPoints failed: " << e.what() << std::endl;
470  return false;
471  }
472  m_sigmaPointsPropagated.insert(propagatedSigmaPoint, 0,i);
473  }
474  return true;
475 }
476 
478 {
480  vpColVector measureCenter;
481  try
482  {
483  measureCenter = this->computeMeasureFromSigmaPoint(this->stateLog(m_state, m_state));
484  }
485  catch(std::exception &e)
486  {
487  std::cout << "usUnscentedKalmanFilter::computeSigmaMeasures failed: " << e.what() << std::endl;
488  return false;
489  }
490 
491  for(unsigned int i=0 ; i<m_sigmaPointsPropagated.getCols() ; i++)
492  {
493  vpColVector sigmaMeasure;
494  try
495  {
496  sigmaMeasure = this->computeMeasureFromSigmaPoint(m_sigmaPointsPropagated.getCol(i));
497  }
498  catch(std::exception &e)
499  {
500  std::cout << "usUnscentedKalmanFilter::computeSigmaMeasures failed: " << e.what() << std::endl;
501  return false;
502  }
503  m_sigmaPointsMeasure.insert(this->measureLog(sigmaMeasure, measureCenter), 0,i);
504  }
505  return true;
506 }
507 
509 {
510  int nbSigmaPoints = m_sigmaPointsPropagated.getCols();
511 
512  m_stateSigmaMean = 0;
513  for(int i=0 ; i<nbSigmaPoints ; i++) m_stateSigmaMean += m_sigmaPointsMeanWeights[i] * m_sigmaPointsPropagated.getCol(i,0,m_stateDimension);
514 
517 
518  switch(m_processNoiseType)
519  {
520  case NoiseType::ADDITIVE_NOISE:
521  {
523  break;
524  }
525  case NoiseType::GENERIC_NOISE:
526  {
527  break;
528  }
529  }
530 
531  m_measureSigmaMean = 0;
532  for(int i=0 ; i<nbSigmaPoints ; i++) m_measureSigmaMean += m_sigmaPointsMeanWeights[i] * m_sigmaPointsMeasure.getCol(i);
533 
536 
539 
540  switch(m_measureNoiseType)
541  {
542  case NoiseType::ADDITIVE_NOISE:
543  {
545  break;
546  }
547  case NoiseType::GENERIC_NOISE:
548  {
549  break;
550  }
551  }
552 }
553 
555 {
556  try
557  {
558  vpMatrix kalmanGain = m_stateMeasureSigmaCovarianceMatrix * m_measureSigmaCovarianceMatrix.pseudoInverse();
559 
560  vpColVector Innov = this->measureLog(m_measure, this->computeMeasureFromSigmaPoint(this->stateLog(m_state, m_state))) - m_measureSigmaMean;
561 
562  m_state = this->stateExp(m_stateSigmaMean + kalmanGain * Innov, m_state);
563 
565  }
566  catch (std::exception &e)
567  {
568  std::cout << "usUnscentedKalmanFilter::updateState failed: " << e.what() << std::endl;
569  return false;
570  }
571  return true;
572 }
573 
574 bool usUnscentedKalmanFilter::filter(const vpColVector &measure)
575 {
576  if(!this->checkConsistency(measure)) return false;
577 
578  m_measure = measure;
579 
582  if(!this->generateSigmaPoints()) return false;
583  if(!this->computePropagatedSigmaPoints()) return false;
584  if(!this->computeSigmaMeasures()) return false;
586 
587  if(!this->updateState()) return false;
588 
589  return true;
590 }
591 
592 double usUnscentedKalmanFilter::stateNorm(const vpColVector &state) const
593 {
594  return state.frobeniusNorm();
595 }
596 
597 vpColVector usUnscentedKalmanFilter::measureLog(const vpColVector& measure, const vpColVector &measureCenter) const
598 {
599  (void)measureCenter; // unused variable
600  return measure;
601 }
602 
603 vpColVector usUnscentedKalmanFilter::stateLog(const vpColVector &state, const vpColVector &stateCenter) const
604 {
605  (void)stateCenter; // unused variable
606  return state;
607 }
608 
609 vpColVector usUnscentedKalmanFilter::stateExp(const vpColVector &state, const vpColVector &stateCenter) const
610 {
611  (void)stateCenter; // unused variable
612  return state;
613 }
614 
bool computeProcessNoiseCovarianceMatrixAutomatically() const
virtual void computeProcessNoiseCovarianceMatrix()
virtual vpColVector measureLog(const vpColVector &measure, const vpColVector &measureCenter) const
void setProcessNoiseCovarianceMatrix(const vpMatrix &cov)
void setMeasureNoiseCovarianceMatrix(const vpMatrix &cov)
virtual double stateNorm(const vpColVector &state) const
void setSigmaPointSpreadThreshold(double threshold)
void setProcessNoiseType(NoiseType type)
void setStateCovarianceMatrix(const vpMatrix &mat)
void setMeasureNoiseType(NoiseType type)
virtual vpColVector stateLog(const vpColVector &state, const vpColVector &stateCenter) const
vpMatrix getMeasureNoiseCovarianceMatrix() const
bool filter(const vpColVector &measure)
void setState(const vpColVector &state)
vpMatrix getProcessNoiseCovarianceMatrix() const
void setSigmaPointScalingFactor(double factor)
SigmaPointGenerationType getSigmaPointGenerationType() const
virtual vpColVector computeMeasureFromSigmaPoint(const vpColVector &sigmaPoint)=0
bool computeMeasureNoiseCovarianceMatrixAutomatically() const
virtual bool checkConsistency(const vpColVector &measure)
virtual vpColVector propagateSigmaPoint(const vpColVector &sigmaPoint)=0
virtual void computeMeasureNoiseCovarianceMatrix()
void setSigmaPointGenerationType(SigmaPointGenerationType type)
SigmaPointGenerationType m_sigmaPointsGenerationType
virtual vpColVector stateExp(const vpColVector &state, const vpColVector &stateCenter) const