00001 /** 00002 \file Robots/LoBot/tti/LoSensorModel.H 00003 \brief A data structure encapsulating the sensor model for Bayesian 00004 TTI estimation. 00005 00006 This file defines a class that encapsulates the sensor model required 00007 to make Bayesian time-to-impact estimation work. This sensor model is 00008 basically a table of probabilities that describe the causal data, 00009 i.e., P(lgmd|tti), or, the likelihoods of seeing different LGMD values 00010 given different TTI values. 00011 00012 The columns of the table discretize the LGMD "space" and its rows 00013 discretize the TTI "space." The Bayes filter works by using the column 00014 vector of the sensor model corresponding to the latest LGMD value as 00015 the P(lgmd|tti) term in the update equation. 00016 00017 The sensor model's probabilities are generated by applying the 00018 Gabbiani LGMD model on each discretized TTI value to get the 00019 corresponding LGMD spike rate. These [TTI, LGMD] pairs are then used 00020 to find the correct bin in the table and increment it. To ensure that 00021 we don't have zeros in any bin, the remaining bins in the column 00022 corresponding to the generated LGMD value are also incremented 00023 according to a Gaussian weighting formula. 00024 */ 00025 00026 // //////////////////////////////////////////////////////////////////// // 00027 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005 // 00028 // by the University of Southern California (USC) and the iLab at USC. // 00029 // See http://iLab.usc.edu for information about this project. // 00030 // //////////////////////////////////////////////////////////////////// // 00031 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00032 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00033 // in Visual Environments, and Applications'' by Christof Koch and // 00034 // Laurent Itti, California Institute of Technology, 2001 (patent // 00035 // pending; application number 09/912,225 filed July 23, 2001; see // 00036 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00037 // //////////////////////////////////////////////////////////////////// // 00038 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00039 // // 00040 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00041 // redistribute it and/or modify it under the terms of the GNU General // 00042 // Public License as published by the Free Software Foundation; either // 00043 // version 2 of the License, or (at your option) any later version. // 00044 // // 00045 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00046 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00047 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00048 // PURPOSE. See the GNU General Public License for more details. // 00049 // // 00050 // You should have received a copy of the GNU General Public License // 00051 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00052 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00053 // Boston, MA 02111-1307 USA. // 00054 // //////////////////////////////////////////////////////////////////// // 00055 // 00056 // Primary maintainer for this file: mviswana usc edu 00057 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Robots/LoBot/tti/LoSensorModel.H $ 00058 // $Id: LoSensorModel.H 13521 2010-06-06 14:23:03Z mviswana $ 00059 // 00060 00061 #ifndef LOBOT_SENSOR_MODEL_DOT_H 00062 #define LOBOT_SENSOR_MODEL_DOT_H 00063 00064 //------------------------------ HEADERS -------------------------------- 00065 00066 // lobot headers 00067 #include "Robots/LoBot/thread/LoMutex.H" 00068 #include "Robots/LoBot/misc/singleton.hh" 00069 #include "Robots/LoBot/util/range.hh" 00070 #include "Robots/LoBot/util/triple.hh" 00071 00072 // Standard C++ headers 00073 #include <string> 00074 #include <deque> 00075 #include <vector> 00076 00077 //----------------------------- NAMESPACE ------------------------------- 00078 00079 namespace lobot { 00080 00081 //------------------------- CLASS DEFINITION ---------------------------- 00082 00083 /** 00084 \class lobot::SensorModel 00085 00086 \brief Data structure encapsulating sensor model required for Bayesian 00087 time-to-impact state estimation. 00088 00089 This class implements a two-dimensional array (i.e., a table) 00090 containing P(lgmd|tti) probability values. The columns of this table 00091 correspond to different LGMD ranges; its rows to different TTI states. 00092 The array is stored in a single-dimensional array in column-major 00093 order. 00094 */ 00095 class SensorModel { 00096 // Prevent copy and assignment 00097 SensorModel(const SensorModel&) ; 00098 SensorModel& operator=(const SensorModel&) ; 00099 00100 /// The basic idea behind any sort of Bayesian state estimation is to 00101 /// determine P(x|z), i.e., the likelihood of being in state x given a 00102 /// measurement z. For this to work, we will need a sensor model, 00103 /// viz., a table containing P(z|x), i.e., the likelihood of seeing a 00104 /// measurement z given that the system is in state x. 00105 /// 00106 /// The columns of the sensor model's probability table denote the 00107 /// different ranges of values for sensor measurements. For 00108 /// Robolocust, the sensor measurements are LGMD spike rates. Thus, 00109 /// the columns of the sensor model table will be LGMD spike rate 00110 /// ranges. These ranges are read from the config file and mapped to 00111 /// column indices using a simple linear search through the range 00112 /// boundaries. 00113 /// 00114 /// For example, let's say the user configures Robolocust to use LGMD 00115 /// ranges 0, 250, 400, 600. The m_lgmd_ranges member variable will be 00116 /// a list of these numbers. There will be three LGMD ranges, viz., 00117 /// [0, 250), [250, 400) and [400, 600]. LGMD values in the range [0, 00118 /// 250) will correspond to the first column in the sensor model 00119 /// probability table; LGMD values in [250, 400) will pick the second 00120 /// column; and LGMD values in [400, 600] will pick the probability 00121 /// table's third column. 00122 std::deque<float> m_lgmd_ranges ; 00123 00124 /// As mentioned above, for the Bayesian time-to-impact estimation to 00125 /// work, we need a table containing P(lgmd|tti) values. This table is 00126 /// considered to be a collection of column vectors. Each column 00127 /// contains the so-called causal probabilities for some range of 00128 /// sensor (LGMD) values. 00129 /// 00130 /// The size of each column vector will be the same as the size of the 00131 /// posterior probability distribution, which depends on how the TTI 00132 /// space has been discretized. For instance, if the TTI range is [0, 00133 /// 3] and the discretization step size is 0.1 seconds, then the TTI 00134 /// belief consists of a distribution containing 30 probabilities. 00135 /// Therefore, the size of each column vector in the sensor model must 00136 /// also be 30 elements. 00137 /// 00138 /// This data structure is used to represent the sensor model, viz., a 00139 /// table of probabilities stored in a single-dimensional array in 00140 /// column-major order. 00141 //@{ 00142 typedef std::vector<float> Table ; 00143 Table m_prob ; 00144 //@} 00145 00146 /// The sensor model's probability table's column vectors have to be 00147 /// combined with the current TTI belief in each iteration of the 00148 /// Bayes filter. To ensure that the filter works reasonably well 00149 /// without entering pathological conditions, the sensor model's 00150 /// column vectors should avoid having zero probabilities (extremely 00151 /// low likelihoods can also cause trouble due to numerical 00152 /// instabilities). 00153 /// 00154 /// Thus, during initialization, when we apply the Gabbiani model to 00155 /// obtain [TTI, LGMD] pairs that point to bins in the probability 00156 /// table that should be incremented, we should also update the other 00157 /// bins in the columns pointed to by each of the generated LGMD 00158 /// values by some amount. The nearby bins' increment factors are 00159 /// obtained using a Gaussian weighting formula. 00160 /// 00161 /// This data member stores the standard deviation of the 00162 /// above-mentioned Gaussian weighting formula. 00163 float m_sigma ; 00164 00165 /// The LGMD spikes with increasing frequency as objects approach and 00166 /// then, right before collision, it reaches a peak and experiences a 00167 /// rapid die-off. Therefore, we can divide the LGMD's spike rate 00168 /// curve into two distinct phases, viz., LOOMING and BLANKING. 00169 /// 00170 /// It is best to have different sensor model corresponding to each of 00171 /// the above-mentioned phases of the LGMD input signal. Since each 00172 /// phase's sensor model can be parametrized differently, clients must 00173 /// specify the name of the LGMD signal's phase when instantiating a 00174 /// sensor model object. This name is used to lookup the different 00175 /// parameters in the config file. 00176 std::string m_name ; 00177 00178 /// This class is designed to allow multiple threads to access its 00179 /// instances simultaneously. Specifically, behaviours such as 00180 /// lgmd_extricate_tti, which use this class indirectly via 00181 /// lobot::TTIEstimator, will read probabilities from the sensor 00182 /// model, while others such as calibrate_let will update the sensor 00183 /// model. This allows customizations to the Bayes filter while the 00184 /// robot is online. 00185 /// 00186 /// We use a mutex to ensure that one behaviour doesn't read 00187 /// intermediate results while another is busy writing to the sensor 00188 /// model. 00189 Mutex m_mutex ; 00190 00191 public: 00192 /// As mentioned earlier, the LGMD input signal is partitioned into 00193 /// two phases, viz., LOOMING and BLANKING. Robolocust uses different 00194 /// causal likelihood profiles for these phases. Thus, when creating a 00195 /// sensor model object, the client must specify the phase 00196 /// corresponding to this instance so that it can retrieve the correct 00197 /// set of parameters from the config file. 00198 SensorModel(const std::string& lgmd_phase) ; 00199 00200 /// This method can be used to recompute the sensor model using some 00201 /// new value for the standard deviation of the Gaussian weighting 00202 /// formula used for the increment factors of each bin in the sensor 00203 /// model's probability table. 00204 /// 00205 /// NOTE: The standard deviation provided to this function is actually 00206 /// an increment factor for the current sigma value maintained 00207 /// internally by the sensor model object. Thus, the dsigma parameter 00208 /// to this function will be added to the m_sigma member variable. 00209 /// This design caters to a client behaviour that is specifically 00210 /// designed to update and visualize the available sensor models. 00211 void update(float dsigma) ; 00212 00213 private: 00214 00215 /// This method increments the bin "pointed" to by the given [TTI, 00216 /// LGMD] pair. It also increments the other bins in the row "pointed" 00217 /// to by the TTI using a Gaussian weighting formula (which is what 00218 /// the third parameter is for) to ensure that no bin in that row has 00219 /// weird likelihood values that can screw up the Bayesian TTI 00220 /// estimation. Finally, it normalizes the row to ensure that each row 00221 /// vector is a valid probability distribution. 00222 void update_row(float tti, float lgmd, float sigma) ; 00223 00224 /// This method uses the LGMD discretization to look up the column 00225 /// index in the probability table given an LGMD spike rate. 00226 int col_index(float lgmd) const ; 00227 00228 /// Given a time-to-impact, this method returns the row index in the 00229 /// probability table using the TTI discretization parameters. 00230 int row_index(float tti) const { 00231 return static_cast<int>((tti - row_min())/row_step()) ; 00232 } 00233 00234 public: 00235 /// As mentioned earlier, the sensor model is a table containing 00236 /// causal probabilities. These methods return the sizes of this 00237 /// table's rows and columns and the its total size. 00238 //@{ 00239 int column_size() const {return Params::belief_size() ;} 00240 int row_size() const {return m_lgmd_ranges.size() - 1 ;} 00241 //@} 00242 00243 /// These methods return the minimum, maximum and step values 00244 /// associated with the discretization used on the rows (i.e., TTI 00245 /// values). 00246 //@{ 00247 float row_min () const {return Params::tti_range().min() ;} 00248 float row_max () const {return Params::tti_range().max() ;} 00249 float row_step() const {return Params::tti_step() ;} 00250 //@} 00251 00252 /// Return the current value of the standard deviation used for the 00253 /// Gaussian weighting for neighbouring bins. 00254 float sigma() const {return m_sigma ;} 00255 00256 /// Return the name of LGMD phase this sensor model corresponds to. 00257 std::string name() const {return m_name ;} 00258 00259 /// This method returns an STL vector containing the column vector 00260 /// "pointed to" by the given LGMD value. 00261 std::vector<float> column_vector(float lgmd) const ; 00262 00263 /// This method returns an STL vector containing the entire table of 00264 /// probabilities. 00265 std::vector<float> table() const ; 00266 00267 /// Clean-up. 00268 ~SensorModel() ; 00269 00270 private: 00271 /// This inner class encapsulates various parameters that can be used 00272 /// to tweak different aspects of the Bayesian time-to-impact 00273 /// estimation's sensor model. 00274 class Params : public singleton<Params> { 00275 /// To get the Bayesian time-to-impact state estimation to work, we 00276 /// need to discretize both TTI and LGMD values. This setting 00277 /// specifies the TTI discretization using three parameters, viz., 00278 /// a TTI [min, max] range and a step value. For example, a triple 00279 /// containing [0 10 0.1] means that the time-to-impact estimates 00280 /// will be made in the range of zero to ten seconds in increments 00281 /// of a tenth of a second. 00282 triple<float, float, float> m_tti_discretization ; 00283 00284 /// It can be cumbersome to constantly "convert" the above triple 00285 /// into a [min, max] range and step value. Therefore, these two 00286 /// member variables are used to "split" the TTI discretization 00287 /// into its range and step components. 00288 //@{ 00289 range<float> m_tti_range ; 00290 float m_tti_step ; 00291 //@} 00292 00293 /// This member variable is used to compute (once during 00294 /// initialization) and store the number of elements in the TTI 00295 /// probability distribution. It is simply the TTI range divided by 00296 /// the TTI step size. 00297 int m_belief_size ; 00298 00299 /// Private constructor because this is a singleton. 00300 Params() ; 00301 00302 // Boilerplate code to make generic singleton design pattern work 00303 friend class singleton<Params> ; 00304 00305 public: 00306 /// Accessing the various parameters. 00307 //@{ 00308 static range<float> tti_range() {return instance().m_tti_range ;} 00309 static float tti_step() {return instance().m_tti_step ;} 00310 static int belief_size() {return instance().m_belief_size ;} 00311 //@} 00312 00313 /// Clean-up. 00314 ~Params() ; 00315 } ; 00316 } ; 00317 00318 //----------------------------------------------------------------------- 00319 00320 } // end of namespace encapsulating this file's definitions 00321 00322 #endif 00323 00324 /* So things look consistent in everyone's emacs... */ 00325 /* Local Variables: */ 00326 /* indent-tabs-mode: nil */ 00327 /* End: */