ekf.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014, 2015, 2016, Charles River Analytics, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following
13  * disclaimer in the documentation and/or other materials provided
14  * with the distribution.
15  * 3. Neither the name of the copyright holder nor the names of its
16  * contributors may be used to endorse or promote products derived
17  * from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include "robot_localization/ekf.h"
35 
36 #include <XmlRpcException.h>
37 
38 #include <iomanip>
39 #include <limits>
40 #include <sstream>
41 #include <vector>
42 
43 namespace RobotLocalization
44 {
45  Ekf::Ekf(std::vector<double>) :
46  FilterBase() // Must initialize filter base!
47  {
48  }
49 
51  {
52  }
53 
54  void Ekf::correct(const Measurement &measurement)
55  {
56  FB_DEBUG("---------------------- Ekf::correct ----------------------\n" <<
57  "State is:\n" << state_ << "\n"
58  "Topic is:\n" << measurement.topicName_ << "\n"
59  "Measurement is:\n" << measurement.measurement_ << "\n"
60  "Measurement topic name is:\n" << measurement.topicName_ << "\n\n"
61  "Measurement covariance is:\n" << measurement.covariance_ << "\n");
62 
63  // We don't want to update everything, so we need to build matrices that only update
64  // the measured parts of our state vector. Throughout prediction and correction, we
65  // attempt to maximize efficiency in Eigen.
66 
67  // First, determine how many state vector values we're updating
68  std::vector<size_t> updateIndices;
69  for (size_t i = 0; i < measurement.updateVector_.size(); ++i)
70  {
71  if (measurement.updateVector_[i])
72  {
73  // Handle nan and inf values in measurements
74  if (std::isnan(measurement.measurement_(i)))
75  {
76  FB_DEBUG("Value at index " << i << " was nan. Excluding from update.\n");
77  }
78  else if (std::isinf(measurement.measurement_(i)))
79  {
80  FB_DEBUG("Value at index " << i << " was inf. Excluding from update.\n");
81  }
82  else
83  {
84  updateIndices.push_back(i);
85  }
86  }
87  }
88 
89  FB_DEBUG("Update indices are:\n" << updateIndices << "\n");
90 
91  size_t updateSize = updateIndices.size();
92 
93  // Now set up the relevant matrices
94  Eigen::VectorXd stateSubset(updateSize); // x (in most literature)
95  Eigen::VectorXd measurementSubset(updateSize); // z
96  Eigen::MatrixXd measurementCovarianceSubset(updateSize, updateSize); // R
97  Eigen::MatrixXd stateToMeasurementSubset(updateSize, state_.rows()); // H
98  Eigen::MatrixXd kalmanGainSubset(state_.rows(), updateSize); // K
99  Eigen::VectorXd innovationSubset(updateSize); // z - Hx
100 
101  stateSubset.setZero();
102  measurementSubset.setZero();
103  measurementCovarianceSubset.setZero();
104  stateToMeasurementSubset.setZero();
105  kalmanGainSubset.setZero();
106  innovationSubset.setZero();
107 
108  // Now build the sub-matrices from the full-sized matrices
109  for (size_t i = 0; i < updateSize; ++i)
110  {
111  measurementSubset(i) = measurement.measurement_(updateIndices[i]);
112  stateSubset(i) = state_(updateIndices[i]);
113 
114  for (size_t j = 0; j < updateSize; ++j)
115  {
116  measurementCovarianceSubset(i, j) = measurement.covariance_(updateIndices[i], updateIndices[j]);
117  }
118 
119  // Handle negative (read: bad) covariances in the measurement. Rather
120  // than exclude the measurement or make up a covariance, just take
121  // the absolute value.
122  if (measurementCovarianceSubset(i, i) < 0)
123  {
124  FB_DEBUG("WARNING: Negative covariance for index " << i <<
125  " of measurement (value is" << measurementCovarianceSubset(i, i) <<
126  "). Using absolute value...\n");
127 
128  measurementCovarianceSubset(i, i) = ::fabs(measurementCovarianceSubset(i, i));
129  }
130 
131  // If the measurement variance for a given variable is very
132  // near 0 (as in e-50 or so) and the variance for that
133  // variable in the covariance matrix is also near zero, then
134  // the Kalman gain computation will blow up. Really, no
135  // measurement can be completely without error, so add a small
136  // amount in that case.
137  if (measurementCovarianceSubset(i, i) < 1e-9)
138  {
139  FB_DEBUG("WARNING: measurement had very small error covariance for index " << updateIndices[i] <<
140  ". Adding some noise to maintain filter stability.\n");
141 
142  measurementCovarianceSubset(i, i) = 1e-9;
143  }
144  }
145 
146  // The state-to-measurement function, h, will now be a measurement_size x full_state_size
147  // matrix, with ones in the (i, i) locations of the values to be updated
148  for (size_t i = 0; i < updateSize; ++i)
149  {
150  stateToMeasurementSubset(i, updateIndices[i]) = 1;
151  }
152 
153  FB_DEBUG("Current state subset is:\n" << stateSubset <<
154  "\nMeasurement subset is:\n" << measurementSubset <<
155  "\nMeasurement covariance subset is:\n" << measurementCovarianceSubset <<
156  "\nState-to-measurement subset is:\n" << stateToMeasurementSubset << "\n");
157 
158  // (1) Compute the Kalman gain: K = (PH') / (HPH' + R)
159  Eigen::MatrixXd pht = estimateErrorCovariance_ * stateToMeasurementSubset.transpose();
160  Eigen::MatrixXd hphrInv = (stateToMeasurementSubset * pht + measurementCovarianceSubset).inverse();
161  kalmanGainSubset.noalias() = pht * hphrInv;
162 
163  innovationSubset = (measurementSubset - stateSubset);
164 
165  // Wrap angles in the innovation
166  for (size_t i = 0; i < updateSize; ++i)
167  {
168  if (updateIndices[i] == StateMemberRoll ||
169  updateIndices[i] == StateMemberPitch ||
170  updateIndices[i] == StateMemberYaw)
171  {
172  while (innovationSubset(i) < -PI)
173  {
174  innovationSubset(i) += TAU;
175  }
176 
177  while (innovationSubset(i) > PI)
178  {
179  innovationSubset(i) -= TAU;
180  }
181  }
182  }
183 
184  // (2) Check Mahalanobis distance between mapped measurement and state.
185  if (checkMahalanobisThreshold(innovationSubset, hphrInv, measurement.mahalanobisThresh_))
186  {
187  // (3) Apply the gain to the difference between the state and measurement: x = x + K(z - Hx)
188  state_.noalias() += kalmanGainSubset * innovationSubset;
189 
190  // (4) Update the estimate error covariance using the Joseph form: (I - KH)P(I - KH)' + KRK'
191  Eigen::MatrixXd gainResidual = identity_;
192  gainResidual.noalias() -= kalmanGainSubset * stateToMeasurementSubset;
193  estimateErrorCovariance_ = gainResidual * estimateErrorCovariance_ * gainResidual.transpose();
194  estimateErrorCovariance_.noalias() += kalmanGainSubset *
195  measurementCovarianceSubset *
196  kalmanGainSubset.transpose();
197 
198  // Handle wrapping of angles
199  wrapStateAngles();
200 
201  FB_DEBUG("Kalman gain subset is:\n" << kalmanGainSubset <<
202  "\nInnovation is:\n" << innovationSubset <<
203  "\nCorrected full state is:\n" << state_ <<
204  "\nCorrected full estimate error covariance is:\n" << estimateErrorCovariance_ <<
205  "\n\n---------------------- /Ekf::correct ----------------------\n");
206  }
207  }
208 
209  void Ekf::predict(const double referenceTime, const double delta)
210  {
211  FB_DEBUG("---------------------- Ekf::predict ----------------------\n" <<
212  "delta is " << delta << "\n" <<
213  "state is " << state_ << "\n");
214 
215  double roll = state_(StateMemberRoll);
216  double pitch = state_(StateMemberPitch);
217  double yaw = state_(StateMemberYaw);
218  double xVel = state_(StateMemberVx);
219  double yVel = state_(StateMemberVy);
220  double zVel = state_(StateMemberVz);
221  double pitchVel = state_(StateMemberVpitch);
222  double yawVel = state_(StateMemberVyaw);
223  double xAcc = state_(StateMemberAx);
224  double yAcc = state_(StateMemberAy);
225  double zAcc = state_(StateMemberAz);
226 
227  // We'll need these trig calculations a lot.
228  double sp = ::sin(pitch);
229  double cp = ::cos(pitch);
230  double cpi = 1.0 / cp;
231  double tp = sp * cpi;
232 
233  double sr = ::sin(roll);
234  double cr = ::cos(roll);
235 
236  double sy = ::sin(yaw);
237  double cy = ::cos(yaw);
238 
239  prepareControl(referenceTime, delta);
240 
241  // Prepare the transfer function
242  transferFunction_(StateMemberX, StateMemberVx) = cy * cp * delta;
243  transferFunction_(StateMemberX, StateMemberVy) = (cy * sp * sr - sy * cr) * delta;
244  transferFunction_(StateMemberX, StateMemberVz) = (cy * sp * cr + sy * sr) * delta;
248  transferFunction_(StateMemberY, StateMemberVx) = sy * cp * delta;
249  transferFunction_(StateMemberY, StateMemberVy) = (sy * sp * sr + cy * cr) * delta;
250  transferFunction_(StateMemberY, StateMemberVz) = (sy * sp * cr - cy * sr) * delta;
255  transferFunction_(StateMemberZ, StateMemberVy) = cp * sr * delta;
256  transferFunction_(StateMemberZ, StateMemberVz) = cp * cr * delta;
266  transferFunction_(StateMemberYaw, StateMemberVyaw) = cr * cpi * delta;
270 
271  // Prepare the transfer function Jacobian. This function is analytically derived from the
272  // transfer function.
273  double xCoeff = 0.0;
274  double yCoeff = 0.0;
275  double zCoeff = 0.0;
276  double oneHalfATSquared = 0.5 * delta * delta;
277 
278  yCoeff = cy * sp * cr + sy * sr;
279  zCoeff = -cy * sp * sr + sy * cr;
280  double dFx_dR = (yCoeff * yVel + zCoeff * zVel) * delta +
281  (yCoeff * yAcc + zCoeff * zAcc) * oneHalfATSquared;
282  double dFR_dR = 1.0 + (cr * tp * pitchVel - sr * tp * yawVel) * delta;
283 
284  xCoeff = -cy * sp;
285  yCoeff = cy * cp * sr;
286  zCoeff = cy * cp * cr;
287  double dFx_dP = (xCoeff * xVel + yCoeff * yVel + zCoeff * zVel) * delta +
288  (xCoeff * xAcc + yCoeff * yAcc + zCoeff * zAcc) * oneHalfATSquared;
289  double dFR_dP = (cpi * cpi * sr * pitchVel + cpi * cpi * cr * yawVel) * delta;
290 
291  xCoeff = -sy * cp;
292  yCoeff = -sy * sp * sr - cy * cr;
293  zCoeff = -sy * sp * cr + cy * sr;
294  double dFx_dY = (xCoeff * xVel + yCoeff * yVel + zCoeff * zVel) * delta +
295  (xCoeff * xAcc + yCoeff * yAcc + zCoeff * zAcc) * oneHalfATSquared;
296 
297  yCoeff = sy * sp * cr - cy * sr;
298  zCoeff = -sy * sp * sr - cy * cr;
299  double dFy_dR = (yCoeff * yVel + zCoeff * zVel) * delta +
300  (yCoeff * yAcc + zCoeff * zAcc) * oneHalfATSquared;
301  double dFP_dR = (-sr * pitchVel - cr * yawVel) * delta;
302 
303  xCoeff = -sy * sp;
304  yCoeff = sy * cp * sr;
305  zCoeff = sy * cp * cr;
306  double dFy_dP = (xCoeff * xVel + yCoeff * yVel + zCoeff * zVel) * delta +
307  (xCoeff * xAcc + yCoeff * yAcc + zCoeff * zAcc) * oneHalfATSquared;
308 
309  xCoeff = cy * cp;
310  yCoeff = cy * sp * sr - sy * cr;
311  zCoeff = cy * sp * cr + sy * sr;
312  double dFy_dY = (xCoeff * xVel + yCoeff * yVel + zCoeff * zVel) * delta +
313  (xCoeff * xAcc + yCoeff * yAcc + zCoeff * zAcc) * oneHalfATSquared;
314 
315  yCoeff = cp * cr;
316  zCoeff = -cp * sr;
317  double dFz_dR = (yCoeff * yVel + zCoeff * zVel) * delta +
318  (yCoeff * yAcc + zCoeff * zAcc) * oneHalfATSquared;
319  double dFY_dR = (cr * cpi * pitchVel - sr * cpi * yawVel) * delta;
320 
321  xCoeff = -cp;
322  yCoeff = -sp * sr;
323  zCoeff = -sp * cr;
324  double dFz_dP = (xCoeff * xVel + yCoeff * yVel + zCoeff * zVel) * delta +
325  (xCoeff * xAcc + yCoeff * yAcc + zCoeff * zAcc) * oneHalfATSquared;
326  double dFY_dP = (sr * tp * cpi * pitchVel - cr * tp * cpi * yawVel) * delta;
327 
328  // Much of the transfer function Jacobian is identical to the transfer function
343 
344  FB_DEBUG("Transfer function is:\n" << transferFunction_ <<
345  "\nTransfer function Jacobian is:\n" << transferFunctionJacobian_ <<
346  "\nProcess noise covariance is:\n" << processNoiseCovariance_ <<
347  "\nCurrent state is:\n" << state_ << "\n");
348 
349  Eigen::MatrixXd *processNoiseCovariance = &processNoiseCovariance_;
350 
352  {
354  processNoiseCovariance = &dynamicProcessNoiseCovariance_;
355  }
356 
357  // (1) Apply control terms, which are actually accelerations
361 
368 
369  // (2) Project the state forward: x = Ax + Bu (really, x = f(x, u))
371 
372  // Handle wrapping
373  wrapStateAngles();
374 
375  FB_DEBUG("Predicted state is:\n" << state_ <<
376  "\nCurrent estimate error covariance is:\n" << estimateErrorCovariance_ << "\n");
377 
378  // (3) Project the error forward: P = J * P * J' + Q
381  transferFunctionJacobian_.transpose());
382  estimateErrorCovariance_.noalias() += delta * (*processNoiseCovariance);
383 
384  FB_DEBUG("Predicted estimate error covariance is:\n" << estimateErrorCovariance_ <<
385  "\n\n--------------------- /Ekf::predict ----------------------\n");
386  }
387 
388 } // namespace RobotLocalization
void correct(const Measurement &measurement)
Carries out the correct step in the predict/update cycle.
Definition: ekf.cpp:54
Eigen::MatrixXd transferFunctionJacobian_
The Kalman filter transfer function Jacobian.
Definition: filter_base.h:519
std::vector< int > controlUpdateVector_
Which control variables are being used (e.g., not every vehicle is controllable in Y or Z) ...
Definition: filter_base.h:424
Eigen::MatrixXd estimateErrorCovariance_
This matrix stores the total error in our position estimate (the state_ variable).
Definition: filter_base.h:447
Structure used for storing and comparing measurements (for priority queues)
Definition: filter_base.h:61
Eigen::MatrixXd transferFunction_
The Kalman filter transfer function.
Definition: filter_base.h:509
#define FB_DEBUG(msg)
Eigen::MatrixXd identity_
We need the identity for a few operations. Better to store it.
Definition: filter_base.h:451
void prepareControl(const double referenceTime, const double predictionDelta)
Converts the control term to an acceleration to be applied in the prediction step.
TF2SIMD_FORCE_INLINE Quaternion inverse(const Quaternion &q)
Eigen::MatrixXd dynamicProcessNoiseCovariance_
Gets updated when useDynamicProcessNoise_ is true.
Definition: filter_base.h:442
virtual void wrapStateAngles()
Keeps the state Euler angles in the range [-pi, pi].
const double TAU
Definition: filter_common.h:92
virtual bool checkMahalanobisThreshold(const Eigen::VectorXd &innovation, const Eigen::MatrixXd &invCovariance, const double nsigmas)
Tests if innovation is within N-sigmas of covariance. Returns true if passed the test.
~Ekf()
Destructor for the Ekf class.
Definition: ekf.cpp:50
bool useDynamicProcessNoiseCovariance_
If true, uses the robot&#39;s vehicle state and the static process noise covariance matrix to generate a ...
Definition: filter_base.h:528
Eigen::VectorXd measurement_
Definition: filter_base.h:69
void computeDynamicProcessNoiseCovariance(const Eigen::VectorXd &state, const double delta)
Computes a dynamic process noise covariance matrix using the parameterized state. ...
void predict(const double referenceTime, const double delta)
Carries out the predict step in the predict/update cycle.
Definition: ekf.cpp:209
Eigen::VectorXd controlAcceleration_
Variable that gets updated every time we process a measurement and we have a valid control...
Definition: filter_base.h:408
Eigen::VectorXd state_
This is the robot&#39;s state vector, which is what we are trying to filter. The values in this vector ar...
Definition: filter_base.h:494
Eigen::MatrixXd processNoiseCovariance_
As we move through the world, we follow a predict/update cycle. If one were to imagine a scenario whe...
Definition: filter_base.h:482
std::vector< int > updateVector_
Definition: filter_base.h:75
const double PI
Common variables.
Definition: filter_common.h:91
Ekf(std::vector< double > args=std::vector< double >())
Constructor for the Ekf class.
Definition: ekf.cpp:45


robot_localization
Author(s): Tom Moore
autogenerated on Wed Feb 3 2021 03:34:02