00001 /*!@file Image/KalmanFilter.C implementation of a 2nd order linear Kalman Filter 00002 */ 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2002 // 00005 // by the University of Southern California (USC) and the iLab at USC. // 00006 // See http://iLab.usc.edu for information about this project. // 00007 // //////////////////////////////////////////////////////////////////// // 00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00010 // in Visual Environments, and Applications'' by Christof Koch and // 00011 // Laurent Itti, California Institute of Technology, 2001 (patent // 00012 // pending; application number 09/912,225 filed July 23, 2001; see // 00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00014 // //////////////////////////////////////////////////////////////////// // 00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00016 // // 00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00018 // redistribute it and/or modify it under the terms of the GNU General // 00019 // Public License as published by the Free Software Foundation; either // 00020 // version 2 of the License, or (at your option) any later version. // 00021 // // 00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00025 // PURPOSE. See the GNU General Public License for more details. // 00026 // // 00027 // You should have received a copy of the GNU General Public License // 00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00030 // Boston, MA 02111-1307 USA. // 00031 // //////////////////////////////////////////////////////////////////// // 00032 // 00033 // Primary maintainer for this file: Dirk Walther <walther@caltech.edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Image/KalmanFilter.C $ 00035 // $Id: KalmanFilter.C 4899 2005-07-12 20:21:11Z itti $ 00036 // 00037 00038 #include "Image/KalmanFilter.H" 00039 00040 #include "Image/IO.H" 00041 #include "Image/MathOps.H" 00042 #include "Image/MatrixOps.H" 00043 #include "Util/Assert.H" 00044 #include "Util/log.H" 00045 #include <cmath> 00046 #include <iostream> 00047 00048 // ###################################################################### 00049 inline float sqr(float x) { 00050 return (x * x); 00051 } 00052 00053 // ###################################################################### 00054 KalmanFilter::KalmanFilter() 00055 : initialized(false) 00056 {} 00057 00058 // ###################################################################### 00059 KalmanFilter::KalmanFilter(float initialM, float pNoise, 00060 float mNoise, float timeStep) 00061 : initialized(true) 00062 { 00063 init(initialM, pNoise, mNoise, timeStep); 00064 } 00065 00066 00067 // ###################################################################### 00068 void KalmanFilter::init(float initialM, float pNoise, 00069 float mNoise, float timeStep) 00070 { 00071 // set the noise 00072 itsPNoise = pNoise; 00073 itsMNoise2 = mNoise * mNoise; 00074 00075 // populate H 00076 H.resize(3,1,ZEROS); 00077 H.setVal(0,0,1.0F); 00078 H.setVal(1,0,0.0F); 00079 H.setVal(2,0,0.0F); 00080 HT = transpose(H); 00081 00082 00083 // set the inner state to the initial measurement 00084 //x = HT * initialM; 00085 x.resize(1,3,ZEROS); 00086 x.setVal(0,0,initialM); 00087 x.setVal(0,1,0.0F); 00088 x.setVal(0,2,0.0F); 00089 00090 // set the identity matrix 00091 I = eye<float>(3); 00092 00093 // fill P with some large numbers on the diagonal 00094 P = I * 1000.0F; 00095 00096 // generate powers of timeStep 00097 std::vector<float> ts(6); 00098 ts[0] = 1.0F; 00099 for (uint i = 1; i <= 5; ++i) 00100 ts[i] = ts[i-1] * timeStep; 00101 00102 // populate Phi 00103 Phi = I; 00104 Phi.setVal(1,0,ts[1]); Phi.setVal(2,0,ts[2]/2.0F); Phi.setVal(2,1,ts[1]); 00105 PhiT = transpose(Phi); 00106 00107 // populate Q 00108 Q.resize(3,3,NO_INIT); 00109 Q.setVal(0,0,ts[5]/20.0F); Q.setVal(1,0,ts[4]/8.0F); Q.setVal(2,0,ts[3]/6.0F); 00110 Q.setVal(0,1,ts[4]/ 8.0F); Q.setVal(1,1,ts[3]/3.0F); Q.setVal(2,1,ts[2]/2.0F); 00111 Q.setVal(0,2,ts[3]/ 6.0F); Q.setVal(1,2,ts[2]/2.0F); Q.setVal(2,2,ts[1]); 00112 Q *= pNoise; 00113 00114 // run a first update for M, K, and P 00115 updateFilter(); 00116 00117 // LINFO("x = [%g %g %g]; 1st prediction: %g; Phi(0,0) = %g", 00118 // x.getVal(0,0),x.getVal(0,1),x.getVal(0,2), 00119 // getXEstimate().getVal(0,0),Phi.getVal(0,0)); 00120 00121 initialized = true; 00122 } 00123 00124 // ###################################################################### 00125 void KalmanFilter::updateFilter() 00126 { 00127 //writeImageToStream(std::cout,H); 00128 00129 // first, update the covariance matrix before update M 00130 // M = Phi*P*PhiT + Q 00131 M = matrixMult(matrixMult(Phi,P),PhiT) + Q; 00132 00133 // now update the Kalman marix itself 00134 // K = M*HT*(H*M*HT + R)^(-1) 00135 Image<float> HMHT = matrixMult(matrixMult(H,M),HT); 00136 ASSERT(HMHT.getSize() == 1); 00137 K = matrixMult(M,HT) / (HMHT.getVal(0,0) + itsMNoise2); 00138 00139 // finally, update the covariance matrix after update P 00140 // P = (I-K*H)*M 00141 P = matrixMult(I - matrixMult(K,H),M); 00142 } 00143 00144 // ###################################################################### 00145 void KalmanFilter::writeToStream(std::ostream& os) const 00146 { 00147 if (initialized) os << "1\n"; 00148 else os << "0\n"; 00149 00150 if (initialized) 00151 { 00152 os << itsPNoise << ' ' << itsMNoise2 << '\n'; 00153 00154 writeImageToStream(os,x); 00155 writeImageToStream(os,I); 00156 writeImageToStream(os,M); 00157 writeImageToStream(os,K); 00158 writeImageToStream(os,P); 00159 writeImageToStream(os,H); 00160 writeImageToStream(os,HT); 00161 writeImageToStream(os,Phi); 00162 writeImageToStream(os,PhiT); 00163 writeImageToStream(os,Q); 00164 } 00165 } 00166 00167 // ###################################################################### 00168 void KalmanFilter::readFromStream(std::istream& is) 00169 { 00170 int i; is >> i; 00171 initialized = (i == 1); 00172 00173 if (initialized) 00174 { 00175 is >> itsPNoise; 00176 is >> itsMNoise2; 00177 00178 readImageFromStream(is,x); 00179 readImageFromStream(is,I); 00180 readImageFromStream(is,M); 00181 readImageFromStream(is,K); 00182 readImageFromStream(is,P); 00183 readImageFromStream(is,H); 00184 readImageFromStream(is,HT); 00185 readImageFromStream(is,Phi); 00186 readImageFromStream(is,PhiT); 00187 readImageFromStream(is,Q); 00188 } 00189 } 00190 00191 // ###################################################################### 00192 Image<float> KalmanFilter::getXEstimate() const 00193 { 00194 return matrixMult(Phi,x); 00195 } 00196 00197 // ###################################################################### 00198 Image<float> KalmanFilter::getXEstimate(float z) const 00199 { 00200 Image<float> Px = getXEstimate(); 00201 return (Px + K * (z - matrixMult(H,Px).getVal(0,0))); 00202 } 00203 00204 // ###################################################################### 00205 float KalmanFilter::getEstimate() const 00206 { 00207 ASSERT(initialized); 00208 float est = getXEstimate().getVal(0,0); 00209 //LINFO("currVal = %g; est = %g",x.getVal(0,0),est); 00210 return est; 00211 } 00212 00213 // ###################################################################### 00214 float KalmanFilter::getEstimate(float measurement) const 00215 { 00216 ASSERT(initialized); 00217 float est = getXEstimate(measurement).getVal(0,0); 00218 //LINFO("currVal = %g; est = %g",x.getVal(0,0),est); 00219 return est; 00220 } 00221 00222 // ###################################################################### 00223 float KalmanFilter::getSpeed() const 00224 { 00225 ASSERT(initialized); 00226 return x.getVal(0,1); 00227 } 00228 00229 // ###################################################################### 00230 float KalmanFilter::getCost(float measurement) const 00231 { 00232 ASSERT(initialized); 00233 00234 float cost = sqr(getEstimate(measurement) - measurement); 00235 00236 if (!(cost == cost)) 00237 { 00238 writeToStream(std::cout); 00239 LFATAL("Kalman Filter error"); 00240 } 00241 00242 return cost; 00243 } 00244 00245 // ###################################################################### 00246 float KalmanFilter::update() 00247 { 00248 ASSERT(initialized); 00249 x = getXEstimate(); 00250 updateFilter(); 00251 00252 if (!(x.getVal(0,0) == x.getVal(0,0))) 00253 { 00254 writeToStream(std::cout); 00255 LFATAL("Kalman Filter error"); 00256 } 00257 00258 return x.getVal(0,0); 00259 } 00260 00261 // ###################################################################### 00262 float KalmanFilter::update(float measurement) 00263 { 00264 ASSERT(initialized); 00265 x = getXEstimate(measurement); 00266 updateFilter(); 00267 00268 if (!(x.getVal(0,0) == x.getVal(0,0))) 00269 { 00270 writeToStream(std::cout); 00271 LFATAL("Kalman Filter error"); 00272 } 00273 00274 return x.getVal(0,0); 00275 } 00276 00277 // ###################################################################### 00278 Image<float> KalmanFilter::getStateVector() const 00279 { 00280 ASSERT(initialized); 00281 return x; 00282 } 00283 00284 // ###################################################################### 00285 Image<float> KalmanFilter::getCovariances() const 00286 { 00287 ASSERT(initialized); 00288 return P; 00289 } 00290 00291 // ###################################################################### 00292 bool KalmanFilter::isInitialized() const 00293 { 00294 return initialized; 00295 } 00296 00297 // ###################################################################### 00298 /* So things look consistent in everyone's emacs... */ 00299 /* Local Variables: */ 00300 /* indent-tabs-mode: nil */ 00301 /* End: */