kalman_filter.h
Go to the documentation of this file.
1 /*****************************************************************************
2  *
3  * Copyright 2016 Intelligent Industrial Robotics (IIROB) Group,
4  * Institute for Anthropomatics and Robotics (IAR) -
5  * Intelligent Process Control and Robotics (IPR),
6  * Karlsruhe Institute of Technology (KIT)
7  *
8  * Author: Denis Štogl, email: denis.stogl@kit.edu
9  *
10  * Date of creation: 2016
11  *
12  * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
13  *
14  * Redistribution and use in source and binary forms,
15  * with or without modification, are permitted provided
16  * that the following conditions are met:
17  *
18  * * Redistributions of source code must retain the above copyright
19  * notice, this list of conditions and the following disclaimer.
20  * * Redistributions in binary form must reproduce the above copyright
21  * notice, this list of conditions and the following disclaimer in the
22  * documentation and/or other materials provided with the distribution.
23  * * Neither the name of the copyright holder nor the names of its
24  * contributors may be used to endorse or promote products derived from
25  * this software without specific prior written permission.
26  *
27  * This package is free software: you can redistribute it and/or modify
28  * it under the terms of the GNU Lesser General Public License as published by
29  * the Free Software Foundation, either version 3 of the License, or
30  * (at your option) any later version.
31  *
32  * This package is distributed in the hope that it will be useful,
33  * but WITHOUT ANY WARRANTY; without even the implied warranty of
34  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35  * GNU Lesser General Public License for more details.
36  *
37  * You should have received a copy of the GNU Lesser General Public License
38  * along with this package. If not, see <http://www.gnu.org/licenses/>.
39 *****************************************************************************/
40 
41 #ifndef IIROB_FILTERS_KALMAN_FILTER_H
42 #define IIROB_FILTERS_KALMAN_FILTER_H
43 
44 #include <filters/filter_base.h>
45 #include <Eigen/Dense>
46 #include <iirob_filters/KalmanFilterParameters.h>
47 #include <ros/ros.h>
48 #include <math.h>
49 
50 
51 namespace iirob_filters {
52 template <typename T>
54 {
55 public:
58  virtual bool configure();
59  bool configure(const std::vector<T>& init_state_vector, const std::string param_namespace = "");
60  bool configure(const std::string& param_namespace);
61  virtual bool update(const std::vector<T>& data_in, std::vector<T>& data_out);
62  bool update(const std::vector<T>& data_in, std::vector<T>& data_out, const double& delta_t, bool update_Q_matrix = false, const Eigen::MatrixXd& R_loc = Eigen::MatrixXd::Zero(1,1));
63  bool predict(std::vector<T>& data_out);
64  bool computePrediction(std::vector<T>& data_out);
65  bool computePrediction(std::vector<T>& data_out, const double& delta_t);
66  bool isInitializated();
67  bool getCurrentState(std::vector<T>& data_out);
68  bool getErrorCovarianceMatrix(Eigen::MatrixXd& data_out);
69  bool resetErrorCovAndState();
70  bool getGatingMatrix(Eigen::MatrixXd& data_out);
71  bool likelihood(const std::vector<T>& data_in, double& data_out);
72 
73 private:
74  // Matrices for computation
75  Eigen::MatrixXd A, At, C, Q, Q_coeff, Q_exponent, R, P, K, P0, gatingMatrix;
76 
77  // System dimensions
78  int m, n;
79 
80  // Discrete time step
81  double dt;
82 
83  // Is the filter initialized?
85 
86  // Variance of process noise (for a time dependent Q)
87  double Q_variance;
88 
89  // n-size identity
90  Eigen::MatrixXd I;
91 
92  // Estimated states
93  Eigen::VectorXd x_hat, x_hat_new;
94 
96 
98 
99  bool fromStdVectorToEigenMatrix(std::vector<double>& in, Eigen::MatrixXd& out, int rows, int columns, std::string matrix_name);
100  bool fromStdVectorToEigenVector(std::vector<double>& in, Eigen::VectorXd& out, int rows, std::string vector_name);
101 
102  double fac(double x);
103 
104  bool getParams(iirob_filters::KalmanFilterParameters&, const std::string&);
105 
107 };
108 
109 template <typename T>
111  double f;
112  if (x==0 || x==1) {
113  f = 1;
114  }
115  else{
116  f = fac(x-1) * x;
117  }
118  return f;
119 }
120 
121 template <typename T>
123 {
125 }
126 
127 template <typename T>
128 bool MultiChannelKalmanFilter<T>::fromStdVectorToEigenMatrix(std::vector<double>& in, Eigen::MatrixXd& out, int rows,
129  int columns, std::string matrix_name) {
130  if (in.size() != rows * columns || in.size() == 0) { ROS_ERROR("%s is not valid!", matrix_name.c_str()); return false; }
131  out.resize(rows, columns);
132  std::vector<double>::iterator it = in.begin();
133  for (int i = 0; i < rows; ++i)
134  {
135  for (int j = 0; j < columns; ++j)
136  {
137  out(i, j) = *it;
138  ++it;
139  }
140  }
141  return true;
142 }
143 
144 template <typename T>
146 {
147  return initialized;
148 }
149 
150 template <typename T>
151 bool MultiChannelKalmanFilter<T>::fromStdVectorToEigenVector(std::vector<double>& in, Eigen::VectorXd& out, int rows, std::string vector_name) {
152  if (in.size() != rows) { ROS_ERROR("%s vector is not valid!", vector_name.c_str()); return false; }
153  out.resize(rows);
154  for (int i = 0; i < rows; ++i)
155  {
156  out(i) = in[i];
157  }
158  return true;
159 }
160 
161 template <typename T>
162 bool MultiChannelKalmanFilter<T>::configure(const std::vector<T>& init_state_vector, const std::string param_namespace) {
163 
164  if (!configure(param_namespace)) { return false; }
165 
166  if (init_state_vector.size() != n) { ROS_ERROR("Kalman: Not valid init state vector!"); return false; }
167 
168  for (int i = 0; i < init_state_vector.size(); i++)
169  {
170  x_hat(i) = init_state_vector[i];
171  }
172  return true;
173 }
174 
175 template <typename T>
177  return configure("");
178 }
179 
180 template <typename T>
181 bool MultiChannelKalmanFilter<T>::configure(const std::string& param_namespace) {
182  std::vector<double> temp;
183  std::string full_namespace = nh.getNamespace() + "/";
184  if (param_namespace != "")
185  {
186  full_namespace += param_namespace;
187  iirob_filters::KalmanFilterParameters dynamic_params{full_namespace};
188  dynamic_params.fromParamServer();
189  if (!getParams(dynamic_params, full_namespace)) { return false; }
190  }
191  else
192  {
193  full_namespace += "KalmanFilter";
194  iirob_filters::KalmanFilterParameters default_namespace_params{full_namespace};
195  default_namespace_params.fromParamServer();
196  if (!getParams(default_namespace_params, full_namespace)) { return false; }
197  }
198 
199  I = Eigen::MatrixXd::Zero(n, n);
200  I.setIdentity();
201 
202  x_hat_new = Eigen::VectorXd::Zero(n);
203  P = P0;
204 
205  gatingMatrix = C * P * C.transpose() + R;
206 
207  initialized = true;
208  return true;
209 }
210 
211 template <typename T>
212 bool MultiChannelKalmanFilter<T>::getParams(iirob_filters::KalmanFilterParameters& parameters, const std::string& param_namespace)
213 {
214  if(ros::param::has(std::string(param_namespace + "/" + "dt")))
215  {
216  dt = parameters.dt;
217  }
218  else
219  {
220  ROS_ERROR("Kalman: dt is not available!");
221  return false;
222  }
223 
224  if(ros::param::has(std::string(param_namespace + "/" + "n")))
225  {
226  n = parameters.n;
227  }
228  else
229  {
230  ROS_ERROR("Kalman: n is not available!");
231  return false;
232  }
233 
234  if(ros::param::has(std::string(param_namespace + "/" + "m")))
235  {
236  m = parameters.m;
237  }
238  else
239  {
240  ROS_ERROR("Kalman: m is not available!");
241  return false;
242  }
243 
244  if (dt <= 0 || n <= 0 || m <= 0)
245  {
246  ROS_ERROR("Kalman: dt, n or m is not valid! (dt <= 0 or n <= 0 or m <= 0)");
247  return false;
248  }
249 
250  std::vector<double> temp;
251 
252  if(ros::param::has(std::string(param_namespace + "/" + "A")))
253  {
254  temp = parameters.A;
255  if (!fromStdVectorToEigenMatrix(temp, A, n, n, "State matrix")) { return false; }
256  }
257  else
258  {
259  ROS_ERROR("A is not available!");
260  return false;
261  }
262 
263  if(ros::param::has(std::string(param_namespace + "/" + "At")))
264  {
265  temp = parameters.At;
266  if (temp.size() == 0)
267  {
268  ROS_DEBUG("At is not used!");
269  }
270  else if (fromStdVectorToEigenMatrix(temp, At, n, n, "Dynamic part of state matrix"))
271  {
272  isDynamicUpdate = true;
273  }
274  }
275  else
276  {
277  ROS_DEBUG("At is not available!");
278  }
279 
280  if(ros::param::has(std::string(param_namespace + "/" + "C")))
281  {
282  temp = parameters.C;
283  if (!fromStdVectorToEigenMatrix(temp, C, m, n, "Output matrix")) { return false; }
284  }
285  else
286  {
287  ROS_ERROR("C is not available!");
288  return false;
289  }
290 
291  if(ros::param::has(std::string(param_namespace + "/" + "Q")))
292  {
293  temp = parameters.Q;
294  if (!fromStdVectorToEigenMatrix(temp, Q, n, n, "Process noise covariance")) { return false; }
295  }
296  else
297  {
298  ROS_ERROR("Q is not available!");
299  return false;
300  }
301 
302  bool skipDynamicPartQ = false;
303  if(isDynamicUpdate && ros::param::has(std::string(param_namespace + "/" + "Q_coeff")))
304  {
305  temp = parameters.Q_coeff;
306  if (!fromStdVectorToEigenMatrix(temp, Q_coeff, n, n,
307  "Process noise covariance (coefficients of dynamic part of Q)"))
308  {
309  skipDynamicPartQ = true;
310  }
311  }
312  else
313  {
314  ROS_DEBUG("Q_coeff is not available!");
315  skipDynamicPartQ = true;
316  }
317 
318  if(isDynamicUpdate && !skipDynamicPartQ &&
319  ros::param::has(std::string(param_namespace + "/" + "Q_exponent")))
320  {
321  temp = parameters.Q_exponent;
323  "Process noise covariance (exponents of the time difference)"))
324  {
325  skipDynamicPartQ = true;
326  }
327  }
328  else
329  {
330  ROS_DEBUG("Q_exponent is not available!");
331  skipDynamicPartQ = true;
332  }
333 
334  if(isDynamicUpdate && !skipDynamicPartQ &&
335  ros::param::has(std::string(param_namespace + "/" + "Q_variance")))
336  {
337  Q_variance = parameters.Q_variance;
338  can_update_Q_matrix = true;
339  }
340  else
341  {
342  ROS_DEBUG("Q_variance is not available!");
343  }
344 
345  if(ros::param::has(std::string(param_namespace + "/" + "R")))
346  {
347  temp = parameters.R;
348  if (!fromStdVectorToEigenMatrix(temp, R, m, m, "Measurement noise covariance")) { return false; }
349  }
350  else
351  {
352  ROS_ERROR("R is not available!");
353  return false;
354  }
355 
356  if(ros::param::has(std::string(param_namespace + "/" + "P")))
357  {
358  temp = parameters.P;
359  if (!fromStdVectorToEigenMatrix(temp, P0, n, n, "Estimate error covariance")) { return false; }
360  }
361  else
362  {
363  ROS_ERROR("P is not available!");
364  return false;
365  }
366 
367  if(ros::param::has(std::string(param_namespace + "/" + "x0")))
368  {
369  temp = parameters.x0;
370  if (!fromStdVectorToEigenVector(temp, x_hat, n, "Start state vector")) { return false; }
371  isDynamicUpdate = true;
372  }
373  else
374  {
375  ROS_DEBUG("x0 is not available!");
376  x_hat = Eigen::VectorXd::Zero(n);
377  }
378 
379  return true;
380 }
381 
382 template <typename T>
384 {}
385 
386 template <typename T>
388 {
389  if(!initialized) { ROS_ERROR("Kalman: Filter is not initialized!"); return false; }
390 
391  Eigen::VectorXd v = Eigen::VectorXd::Zero(n);
392  v = A * x_hat;
393 
394  data_out.resize(n);
395  for (int i = 0; i < data_out.size(); ++i) {
396  data_out[i] = v(i);
397  }
398 
399  return true;
400 }
401 
402 template <typename T>
403 bool MultiChannelKalmanFilter<T>::computePrediction(std::vector<T>& data_out, const double& delta_t)
404 {
405  if(!initialized) { ROS_ERROR("Kalman: Filter is not initialized!"); return false; }
406 
407  Eigen::MatrixXd A_t = At;
408 
409  for(int i = 0; i < n; i++){
410  for(int j = 0; j < n; j++){
411  if( A_t(i,j) ){
412  A_t(i,j) = pow( delta_t , A_t(i,j) )/fac( A_t(i,j) );
413  }
414  }
415  }
416 
417  Eigen::VectorXd v = Eigen::VectorXd::Zero(n);
418  v = (A_t+A) * x_hat;
419 
420  data_out.resize(n);
421  for (int i = 0; i < data_out.size(); ++i) {
422  data_out[i] = v(i);
423  }
424 
425  return true;
426 }
427 
428 template <typename T>
429 bool MultiChannelKalmanFilter<T>::predict(std::vector<T>& data_out)
430 {
431  if(!initialized) { ROS_ERROR("Kalman: Filter is not initialized!"); return false; }
432 
433  x_hat = A * x_hat;
434  P = A*P*A.transpose() + Q;
435 
436  data_out.resize(n);
437  for (int i = 0; i < data_out.size(); ++i) {
438  data_out[i] = x_hat(i);
439  }
440 
441  return true;
442 }
443 
444 template <typename T>
445 bool MultiChannelKalmanFilter<T>::update(const std::vector<T>& data_in, std::vector<T>& data_out)
446 {
447  if(!initialized) { ROS_ERROR("Kalman: Filter is not initialized!"); return false; }
448  if(data_in.size() != m) { ROS_ERROR("Kalman: Not valid measurement vector!"); return false; }
449 
450  Eigen::VectorXd y(m);
451  for (int i = 0; i < m; ++i) {
452  y(i) = data_in[i];
453  }
454 
455  x_hat_new = A * x_hat;
456  P = A*P*A.transpose() + Q;
457  gatingMatrix = C * P * C.transpose() + R;
458  K = P*C.transpose()*gatingMatrix.inverse(); // nxm
459  x_hat_new += K * (y - C*x_hat_new);
460  P = (I - K*C)*P;
461  x_hat = x_hat_new;
462 
463  data_out.resize(n);
464  for (int i = 0; i < data_out.size(); ++i) {
465  data_out[i] = x_hat(i);
466  }
467 
468  return true;
469 }
470 
471 template <typename T>
472 bool MultiChannelKalmanFilter<T>::getGatingMatrix(Eigen::MatrixXd& data_out)
473 {
474  if(!initialized) { ROS_ERROR("Kalman: Filter is not initialized!"); return false; }
475  data_out = gatingMatrix;
476  return true;
477 }
478 
479 template <typename T>
481 {
482  if(!initialized) { ROS_ERROR("Kalman: Filter is not initialized!"); return false; }
483  data_out = P;
484  return true;
485 }
486 
487 template <typename T>
488 bool MultiChannelKalmanFilter<T>::update(const std::vector<T>& data_in, std::vector<T>& data_out,
489  const double& delta_t, bool update_Q_matrix, const Eigen::MatrixXd& R_loc)
490 {
491  if(!initialized) { ROS_ERROR("Kalman: Filter is not initialized!"); return false; }
492  if(!isDynamicUpdate) { ROS_ERROR("Kalman: Dynamic update is not available!"); return false; }
493  if(data_in.size() != m) { ROS_ERROR("Kalman: Not valid measurement vector!"); return false; }
494 
495  Eigen::VectorXd y(m);
496  for (int i = 0; i < m; ++i) {
497  y(i) = data_in[i];
498  }
499 
500  Eigen::MatrixXd A_t = At;
501 
502  for(int i = 0; i < n; i++){
503  for(int j = 0; j < n; j++){
504  if( A_t(i,j) ){
505  A_t(i,j) = pow( delta_t , A_t(i,j) )/fac( A_t(i,j) );
506  }
507  }
508  }
509 
510  //update Q matrix if requested
511  Eigen::MatrixXd Q_updated = Q;
512  if (can_update_Q_matrix && update_Q_matrix)
513  {
514  for(int i = 0; i < n; i++){
515  for(int j = 0; j < n; j++){
516  // adds x*delta_t^y to entry in Q
517  Q_updated(i,j) = Q(i,j) + Q_variance * Q_coeff(i,j)*pow(delta_t, Q_exponent(i,j));
518  }
519  }
520  }
521 
522  Eigen::MatrixXd R_used = R;
523  if(!R_loc.isZero() && R_loc.cols() == R.cols() && R_loc.rows() == R.rows())
524  R_used = R_loc;
525 
526 
527  x_hat_new = (A_t+A) * x_hat;
528  P = (A_t+A)*P*((A_t+A).transpose()) + Q_updated;
529  gatingMatrix = C * P * C.transpose() + R_used;
530  K = P*C.transpose()*gatingMatrix.inverse(); // nxm
531  x_hat_new += K * (y - C*x_hat_new);
532  P = (I - K*C)*P;
533  x_hat = x_hat_new;
534 
535  data_out.resize(n);
536  for (int i = 0; i < n; ++i) {
537  data_out[i] = x_hat(i);
538  }
539  return true;
540 }
541 
542 template <typename T>
543 bool MultiChannelKalmanFilter<T>::getCurrentState(std::vector<T>& data_out)
544 {
545  if(!initialized) { ROS_ERROR("Kalman: Filter is not initialized!"); return false; }
546  data_out.resize(n);
547  for (int i = 0; i < n; ++i) {
548  data_out[i] = x_hat(i);
549  }
550  return true;
551 }
552 
553 template <typename T>
554 bool MultiChannelKalmanFilter<T>::likelihood(const std::vector<T>& data_in, double& data_out)
555 {
556  if(!initialized) { ROS_ERROR("Kalman: Filter is not initialized!"); return false; }
557 
558  if(data_in.size() != m) { ROS_ERROR("Kalman: Not valid measurement vector!"); return false; }
559 
560  Eigen::VectorXd measurement(m);
561  for (int i = 0; i < m; ++i) {
562  measurement(i) = data_in[i];
563  }
564 
565  // convert prediction to measurement space
566  Eigen::VectorXd prediction = C * A * x_hat;
567 
568  // vector of prediction (origin = current state)
569  Eigen::VectorXd continuousPrediction = prediction - C * x_hat;
570 
571  // assumed interpolated prediction = current state + dt * prediction
572  Eigen::VectorXd timeShiftedPrediction = ( C * x_hat ) + ( dt * continuousPrediction );
573 
574  Eigen::VectorXd d = timeShiftedPrediction - measurement;
575 
576  // calculate exponent
577  const double e = -0.5 * d.transpose() * gatingMatrix.inverse() * d;
578 
579  // get normal distribution value
580  data_out = (1. / (std::pow(2. * M_PI, (double) m * .5)
581  * std::sqrt(gatingMatrix.determinant()))) * std::exp(e);
582 
583  return true;
584 }
585 
586 template <typename T>
588 {
589  if(!initialized) { ROS_ERROR("Kalman: Filter is not initialized!"); return false; }
590 
591  for (int i = m; i < n; i++)
592  {
593  x_hat(i) = 0.0;
594  }
595  P = P0;
596  return true;
597 }
598 
599 
600 }
601 #endif
d
bool getErrorCovarianceMatrix(Eigen::MatrixXd &data_out)
f
bool predict(std::vector< T > &data_out)
bool getGatingMatrix(Eigen::MatrixXd &data_out)
bool fromStdVectorToEigenVector(std::vector< double > &in, Eigen::VectorXd &out, int rows, std::string vector_name)
bool getCurrentState(std::vector< T > &data_out)
bool fromStdVectorToEigenMatrix(std::vector< double > &in, Eigen::MatrixXd &out, int rows, int columns, std::string matrix_name)
const std::string & getNamespace() const
ROSCPP_DECL bool has(const std::string &key)
bool computePrediction(std::vector< T > &data_out)
bool getParams(iirob_filters::KalmanFilterParameters &, const std::string &)
INLINE Rall1d< T, V, S > pow(const Rall1d< T, V, S > &arg, double m)
virtual bool update(const std::vector< T > &data_in, std::vector< T > &data_out)
#define ROS_ERROR(...)
bool likelihood(const std::vector< T > &data_in, double &data_out)
#define ROS_DEBUG(...)


iirob_filters
Author(s): Denis Štogl
autogenerated on Fri Sep 18 2020 03:32:19