filter_base.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2014, 2015, 2016, Charles River Analytics, Inc.
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  *
00009  * 1. Redistributions of source code must retain the above copyright
00010  * notice, this list of conditions and the following disclaimer.
00011  * 2. Redistributions in binary form must reproduce the above
00012  * copyright notice, this list of conditions and the following
00013  * disclaimer in the documentation and/or other materials provided
00014  * with the distribution.
00015  * 3. Neither the name of the copyright holder nor the names of its
00016  * contributors may be used to endorse or promote products derived
00017  * from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00020  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00021  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00022  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00023  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00024  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00025  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00026  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00027  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00028  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00029  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00030  * POSSIBILITY OF SUCH DAMAGE.
00031  */
00032 
00033 #include "robot_localization/filter_base.h"
00034 #include "robot_localization/filter_common.h"
00035 
00036 #include <iomanip>
00037 #include <iostream>
00038 #include <limits>
00039 #include <sstream>
00040 #include <vector>
00041 
00042 namespace RobotLocalization
00043 {
00044   FilterBase::FilterBase() :
00045     accelerationGains_(TWIST_SIZE, 0.0),
00046     accelerationLimits_(TWIST_SIZE, 0.0),
00047     decelerationGains_(TWIST_SIZE, 0.0),
00048     decelerationLimits_(TWIST_SIZE, 0.0),
00049     controlAcceleration_(TWIST_SIZE),
00050     controlTimeout_(0.0),
00051     controlUpdateVector_(TWIST_SIZE, 0),
00052     dynamicProcessNoiseCovariance_(STATE_SIZE, STATE_SIZE),
00053     latestControlTime_(0.0),
00054     state_(STATE_SIZE),
00055     predictedState_(STATE_SIZE),
00056     transferFunction_(STATE_SIZE, STATE_SIZE),
00057     transferFunctionJacobian_(STATE_SIZE, STATE_SIZE),
00058     estimateErrorCovariance_(STATE_SIZE, STATE_SIZE),
00059     covarianceEpsilon_(STATE_SIZE, STATE_SIZE),
00060     processNoiseCovariance_(STATE_SIZE, STATE_SIZE),
00061     identity_(STATE_SIZE, STATE_SIZE),
00062     debug_(false),
00063     debugStream_(NULL),
00064     useControl_(false),
00065     useDynamicProcessNoiseCovariance_(false)
00066   {
00067     reset();
00068   }
00069 
00070   FilterBase::~FilterBase()
00071   {
00072   }
00073 
00074   void FilterBase::reset()
00075   {
00076     initialized_ = false;
00077 
00078     // Clear the state and predicted state
00079     state_.setZero();
00080     predictedState_.setZero();
00081     controlAcceleration_.setZero();
00082 
00083     // Prepare the invariant parts of the transfer
00084     // function
00085     transferFunction_.setIdentity();
00086 
00087     // Clear the Jacobian
00088     transferFunctionJacobian_.setZero();
00089 
00090     // Set the estimate error covariance. We want our measurements
00091     // to be accepted rapidly when the filter starts, so we should
00092     // initialize the state's covariance with large values.
00093     estimateErrorCovariance_.setIdentity();
00094     estimateErrorCovariance_ *= 1e-9;
00095 
00096     // We need the identity for the update equations
00097     identity_.setIdentity();
00098 
00099     // Set the epsilon matrix to be a matrix with small values on the diagonal
00100     // It is used to maintain the positive-definite property of the covariance
00101     covarianceEpsilon_.setIdentity();
00102     covarianceEpsilon_ *= 0.001;
00103 
00104     // Assume 30Hz from sensor data (configurable)
00105     sensorTimeout_ = 0.033333333;
00106 
00107     // Initialize our last update and measurement times
00108     lastUpdateTime_ = 0;
00109     lastMeasurementTime_ = 0;
00110 
00111     // These can be overridden via the launch parameters,
00112     // but we need default values.
00113     processNoiseCovariance_.setZero();
00114     processNoiseCovariance_(StateMemberX, StateMemberX) = 0.05;
00115     processNoiseCovariance_(StateMemberY, StateMemberY) = 0.05;
00116     processNoiseCovariance_(StateMemberZ, StateMemberZ) = 0.06;
00117     processNoiseCovariance_(StateMemberRoll, StateMemberRoll) = 0.03;
00118     processNoiseCovariance_(StateMemberPitch, StateMemberPitch) = 0.03;
00119     processNoiseCovariance_(StateMemberYaw, StateMemberYaw) = 0.06;
00120     processNoiseCovariance_(StateMemberVx, StateMemberVx) = 0.025;
00121     processNoiseCovariance_(StateMemberVy, StateMemberVy) = 0.025;
00122     processNoiseCovariance_(StateMemberVz, StateMemberVz) = 0.04;
00123     processNoiseCovariance_(StateMemberVroll, StateMemberVroll) = 0.01;
00124     processNoiseCovariance_(StateMemberVpitch, StateMemberVpitch) = 0.01;
00125     processNoiseCovariance_(StateMemberVyaw, StateMemberVyaw) = 0.02;
00126     processNoiseCovariance_(StateMemberAx, StateMemberAx) = 0.01;
00127     processNoiseCovariance_(StateMemberAy, StateMemberAy) = 0.01;
00128     processNoiseCovariance_(StateMemberAz, StateMemberAz) = 0.015;
00129 
00130     dynamicProcessNoiseCovariance_ = processNoiseCovariance_;
00131   }
00132 
00133   void FilterBase::computeDynamicProcessNoiseCovariance(const Eigen::VectorXd &state, const double delta)
00134   {
00135     // A more principled approach would be to get the current velocity from the state, make a diagonal matrix from it,
00136     // and then rotate it to be in the world frame (i.e., the same frame as the pose data). We could then use this
00137     // rotated velocity matrix to scale the process noise covariance for the pose variables as
00138     // rotatedVelocityMatrix * poseCovariance * rotatedVelocityMatrix'
00139     // However, this presents trouble for robots that may incur rotational error as a result of linear motion (and
00140     // vice-versa). Instead, we create a diagonal matrix whose diagonal values are the vector norm of the state's
00141     // velocity. We use that to scale the process noise covariance.
00142     Eigen::MatrixXd velocityMatrix(TWIST_SIZE, TWIST_SIZE);
00143     velocityMatrix.setIdentity();
00144     velocityMatrix.diagonal() *= state.segment(POSITION_V_OFFSET, TWIST_SIZE).norm();
00145 
00146     dynamicProcessNoiseCovariance_.block<TWIST_SIZE, TWIST_SIZE>(POSITION_OFFSET, POSITION_OFFSET) =
00147       velocityMatrix *
00148       processNoiseCovariance_.block<TWIST_SIZE, TWIST_SIZE>(POSITION_OFFSET, POSITION_OFFSET) *
00149       velocityMatrix.transpose();
00150   }
00151 
00152   const Eigen::VectorXd& FilterBase::getControl()
00153   {
00154     return latestControl_;
00155   }
00156 
00157   double FilterBase::getControlTime()
00158   {
00159     return latestControlTime_;
00160   }
00161 
00162   bool FilterBase::getDebug()
00163   {
00164     return debug_;
00165   }
00166 
00167   const Eigen::MatrixXd& FilterBase::getEstimateErrorCovariance()
00168   {
00169     return estimateErrorCovariance_;
00170   }
00171 
00172   bool FilterBase::getInitializedStatus()
00173   {
00174     return initialized_;
00175   }
00176 
00177   double FilterBase::getLastMeasurementTime()
00178   {
00179     return lastMeasurementTime_;
00180   }
00181 
00182   double FilterBase::getLastUpdateTime()
00183   {
00184     return lastUpdateTime_;
00185   }
00186 
00187   const Eigen::VectorXd& FilterBase::getPredictedState()
00188   {
00189     return predictedState_;
00190   }
00191 
00192   const Eigen::MatrixXd& FilterBase::getProcessNoiseCovariance()
00193   {
00194     return processNoiseCovariance_;
00195   }
00196 
00197   double FilterBase::getSensorTimeout()
00198   {
00199     return sensorTimeout_;
00200   }
00201 
00202   const Eigen::VectorXd& FilterBase::getState()
00203   {
00204     return state_;
00205   }
00206 
00207   void FilterBase::processMeasurement(const Measurement &measurement)
00208   {
00209     FB_DEBUG("------ FilterBase::processMeasurement (" << measurement.topicName_ << ") ------\n");
00210 
00211     double delta = 0.0;
00212 
00213     // If we've had a previous reading, then go through the predict/update
00214     // cycle. Otherwise, set our state and covariance to whatever we get
00215     // from this measurement.
00216     if (initialized_)
00217     {
00218       // Determine how much time has passed since our last measurement
00219       delta = measurement.time_ - lastMeasurementTime_;
00220 
00221       FB_DEBUG("Filter is already initialized. Carrying out predict/correct loop...\n"
00222                "Measurement time is " << std::setprecision(20) << measurement.time_ <<
00223                ", last measurement time is " << lastMeasurementTime_ << ", delta is " << delta << "\n");
00224 
00225       // Only want to carry out a prediction if it's
00226       // forward in time. Otherwise, just correct.
00227       if (delta > 0)
00228       {
00229         validateDelta(delta);
00230         predict(measurement.time_, delta);
00231 
00232         // Return this to the user
00233         predictedState_ = state_;
00234       }
00235 
00236       correct(measurement);
00237     }
00238     else
00239     {
00240       FB_DEBUG("First measurement. Initializing filter.\n");
00241 
00242       // Initialize the filter, but only with the values we're using
00243       size_t measurementLength = measurement.updateVector_.size();
00244       for (size_t i = 0; i < measurementLength; ++i)
00245       {
00246         state_[i] = (measurement.updateVector_[i] ? measurement.measurement_[i] : state_[i]);
00247       }
00248 
00249       // Same for covariance
00250       for (size_t i = 0; i < measurementLength; ++i)
00251       {
00252         for (size_t j = 0; j < measurementLength; ++j)
00253         {
00254           estimateErrorCovariance_(i, j) = (measurement.updateVector_[i] && measurement.updateVector_[j] ?
00255                                             measurement.covariance_(i, j) :
00256                                             estimateErrorCovariance_(i, j));
00257         }
00258       }
00259 
00260       initialized_ = true;
00261     }
00262 
00263     if (delta >= 0.0)
00264     {
00265       // Update the last measurement and update time.
00266       // The measurement time is based on the time stamp of the
00267       // measurement, whereas the update time is based on this
00268       // node's current ROS time. The update time is used to
00269       // determine if we have a sensor timeout, whereas the
00270       // measurement time is used to calculate time deltas for
00271       // prediction and correction.
00272       lastMeasurementTime_ = measurement.time_;
00273     }
00274 
00275     FB_DEBUG("------ /FilterBase::processMeasurement (" << measurement.topicName_ << ") ------\n");
00276   }
00277 
00278   void FilterBase::setControl(const Eigen::VectorXd &control, const double controlTime)
00279   {
00280     latestControl_ = control;
00281     latestControlTime_ = controlTime;
00282   }
00283 
00284   void FilterBase::setControlParams(const std::vector<int> &updateVector, const double controlTimeout,
00285     const std::vector<double> &accelerationLimits, const std::vector<double> &accelerationGains,
00286     const std::vector<double> &decelerationLimits, const std::vector<double> &decelerationGains)
00287   {
00288     useControl_ = true;
00289     controlUpdateVector_ = updateVector;
00290     controlTimeout_ = controlTimeout;
00291     accelerationLimits_ = accelerationLimits;
00292     accelerationGains_ = accelerationGains;
00293     decelerationLimits_ = decelerationLimits;
00294     decelerationGains_ = decelerationGains;
00295   }
00296 
00297   void FilterBase::setDebug(const bool debug, std::ostream *outStream)
00298   {
00299     if (debug)
00300     {
00301       if (outStream != NULL)
00302       {
00303         debugStream_ = outStream;
00304         debug_ = true;
00305       }
00306       else
00307       {
00308         debug_ = false;
00309       }
00310     }
00311     else
00312     {
00313       debug_ = false;
00314     }
00315   }
00316 
00317   void FilterBase::setUseDynamicProcessNoiseCovariance(const bool useDynamicProcessNoiseCovariance)
00318   {
00319     useDynamicProcessNoiseCovariance_ = useDynamicProcessNoiseCovariance;
00320   }
00321 
00322   void FilterBase::setEstimateErrorCovariance(const Eigen::MatrixXd &estimateErrorCovariance)
00323   {
00324     estimateErrorCovariance_ = estimateErrorCovariance;
00325   }
00326 
00327   void FilterBase::setLastMeasurementTime(const double lastMeasurementTime)
00328   {
00329     lastMeasurementTime_ = lastMeasurementTime;
00330   }
00331 
00332   void FilterBase::setLastUpdateTime(const double lastUpdateTime)
00333   {
00334     lastUpdateTime_ = lastUpdateTime;
00335   }
00336 
00337   void FilterBase::setProcessNoiseCovariance(const Eigen::MatrixXd &processNoiseCovariance)
00338   {
00339     processNoiseCovariance_ = processNoiseCovariance;
00340     dynamicProcessNoiseCovariance_ = processNoiseCovariance_;
00341   }
00342 
00343   void FilterBase::setSensorTimeout(const double sensorTimeout)
00344   {
00345     sensorTimeout_ = sensorTimeout;
00346   }
00347 
00348   void FilterBase::setState(const Eigen::VectorXd &state)
00349   {
00350     state_ = state;
00351   }
00352 
00353   void FilterBase::validateDelta(double &delta)
00354   {
00355     // This handles issues with ROS time when use_sim_time is on and we're playing from bags.
00356     if (delta > 100000.0)
00357     {
00358       FB_DEBUG("Delta was very large. Suspect playing from bag file. Setting to 0.01\n");
00359 
00360       delta = 0.01;
00361     }
00362   }
00363 
00364 
00365   void FilterBase::prepareControl(const double referenceTime, const double predictionDelta)
00366   {
00367     controlAcceleration_.setZero();
00368 
00369     if (useControl_)
00370     {
00371       bool timedOut = ::fabs(referenceTime - latestControlTime_) >= controlTimeout_;
00372 
00373       if (timedOut)
00374       {
00375         FB_DEBUG("Control timed out. Reference time was " << referenceTime << ", latest control time was " <<
00376           latestControlTime_ << ", control timeout was " << controlTimeout_ << "\n");
00377       }
00378 
00379       for (size_t controlInd = 0; controlInd < TWIST_SIZE; ++controlInd)
00380       {
00381         if (controlUpdateVector_[controlInd])
00382         {
00383           controlAcceleration_(controlInd) = computeControlAcceleration(state_(controlInd + POSITION_V_OFFSET),
00384             (timedOut ? 0.0 : latestControl_(controlInd)), accelerationLimits_[controlInd],
00385             accelerationGains_[controlInd], decelerationLimits_[controlInd], decelerationGains_[controlInd]);
00386         }
00387       }
00388     }
00389   }
00390 
00391   void FilterBase::wrapStateAngles()
00392   {
00393     state_(StateMemberRoll)  = FilterUtilities::clampRotation(state_(StateMemberRoll));
00394     state_(StateMemberPitch) = FilterUtilities::clampRotation(state_(StateMemberPitch));
00395     state_(StateMemberYaw)   = FilterUtilities::clampRotation(state_(StateMemberYaw));
00396   }
00397 
00398   bool FilterBase::checkMahalanobisThreshold(const Eigen::VectorXd &innovation,
00399                                              const Eigen::MatrixXd &invCovariance,
00400                                              const double nsigmas)
00401   {
00402     double sqMahalanobis = innovation.dot(invCovariance * innovation);
00403     double threshold = nsigmas * nsigmas;
00404 
00405     if (sqMahalanobis >= threshold)
00406     {
00407       FB_DEBUG("Innovation mahalanobis distance test failed. Squared Mahalanobis is: " << sqMahalanobis << "\n" <<
00408                "Threshold is: " << threshold << "\n" <<
00409                "Innovation is: " << innovation << "\n" <<
00410                "Innovation covariance is:\n" << invCovariance << "\n");
00411 
00412       return false;
00413     }
00414 
00415     return true;
00416   }
00417 }  // namespace RobotLocalization


robot_localization
Author(s): Tom Moore
autogenerated on Thu Jun 6 2019 21:01:48