SaccadeControllers.C

Go to the documentation of this file.
00001 /*!@file Neuro/SaccadeControllers.C Derived classes for saccade generation */
00002 
00003 // //////////////////////////////////////////////////////////////////// //
00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2003   //
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: Laurent Itti <itti@usc.edu>
00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Neuro/SaccadeControllers.C $
00035 // $Id: SaccadeControllers.C 10710 2009-02-01 03:33:35Z itti $
00036 //
00037 
00038 #include "Neuro/SaccadeControllers.H"
00039 
00040 #include "Component/GlobalOpts.H"
00041 #include "Component/OptionManager.H"
00042 #include "Image/Pixels.H"
00043 #include "Media/MediaOpts.H"
00044 #include "Neuro/NeuroOpts.H"
00045 #include "Neuro/NeuroSimEvents.H"
00046 #include "Neuro/Retina.H"
00047 #include "Neuro/SpatialMetrics.H"
00048 #include "Simulation/SimulationOpts.H"
00049 #include "Simulation/SimEventQueue.H"
00050 #include "Util/MathFunctions.H"
00051 #include "Util/log.H"
00052 #include "rutz/compat_cmath.h"
00053 
00054 #include <fstream>
00055 
00056 // ######################################################################
00057 // ######################################################################
00058 // ######################################################################
00059 StubSaccadeController::StubSaccadeController(OptionManager& mgr,
00060                                              const SaccadeBodyPart bodypart) :
00061   SaccadeController(mgr, "Stub Saccade Controller",
00062                     "StubSaccadeController", bodypart, 2, 2)
00063 {  }
00064 
00065 // ######################################################################
00066 StubSaccadeController::~StubSaccadeController()
00067 {  }
00068 
00069 // ######################################################################
00070 void StubSaccadeController::doEvolve(SimEventQueue& q)
00071 {  }
00072 
00073 // ######################################################################
00074 void StubSaccadeController::computeWhenNewPercept(SimEventQueue& q)
00075 {  }
00076 
00077 // ######################################################################
00078 void StubSaccadeController::computeWhenResetPos(SimEventQueue& q)
00079 {  }
00080 
00081 // ######################################################################
00082 Point2D<int> StubSaccadeController::
00083 computeWhenNewDecision(SaccadeState& sacstate, bool& blinkstate,
00084                        SimEventQueue& q)
00085 {
00086   sacstate = SACSTATE_UNK;
00087   blinkstate = false;
00088   return Point2D<int>(-1,-1);
00089 }
00090 
00091 // ######################################################################
00092 // ######################################################################
00093 // ######################################################################
00094 TrivialSaccadeController::
00095 TrivialSaccadeController(OptionManager& mgr, const SaccadeBodyPart bodypart) :
00096   SaccadeController(mgr, "Trivial Saccade Controller",
00097                     "TrivialSaccadeController", bodypart, 2, 2),
00098   itsMinSacLen(bodypart == SaccadeBodyPartEye
00099                ? &OPT_SCeyeMinSacLen
00100                : &OPT_SCheadMinSacLen,
00101                this)
00102 {  }
00103 
00104 // ######################################################################
00105 TrivialSaccadeController::~TrivialSaccadeController()
00106 {  }
00107 
00108 // ######################################################################
00109 void TrivialSaccadeController::doEvolve(SimEventQueue& q)
00110 {  }
00111 
00112 // ######################################################################
00113 void TrivialSaccadeController::computeWhenNewPercept(SimEventQueue& q)
00114 {  }
00115 
00116 // ######################################################################
00117 void TrivialSaccadeController::computeWhenResetPos(SimEventQueue& q)
00118 {  }
00119 
00120 // ######################################################################
00121 Point2D<int> TrivialSaccadeController::
00122 computeWhenNewDecision(SaccadeState& sacstate, bool& blinkstate,
00123                        SimEventQueue& q)
00124 {
00125   // this controller never blinks:
00126   blinkstate = false;
00127 
00128   // just return last percept received (or (-1,-1) if no percept yet):
00129   Point2D<int> p = getPreviousPercept(0).p;
00130 
00131   if (p.isValid())
00132     {
00133       // if this is far from our last decision, let's put ourselve in saccade:
00134       Point2D<int> pp = getPreviousDecision(0).p;
00135       if (pp.isValid() && p.distance(pp) >= itsMinSacLen.getVal())
00136         sacstate = SACSTATE_SAC;
00137       else
00138         sacstate = SACSTATE_FIX;
00139     }
00140   else sacstate = SACSTATE_UNK;
00141 
00142   return p;
00143 }
00144 
00145 // ######################################################################
00146 // ######################################################################
00147 // ######################################################################
00148 FixedSaccadeController::
00149 FixedSaccadeController(OptionManager& mgr, const SaccadeBodyPart bodypart) :
00150   SaccadeController(mgr, "Fixed Saccade Controller",
00151                     "FixedSaccadeController", bodypart, 2, 2),
00152   itsPos(-1, -1)
00153 {  }
00154 
00155 // ######################################################################
00156 FixedSaccadeController::~FixedSaccadeController()
00157 {  }
00158 
00159 // ######################################################################
00160 void FixedSaccadeController::doEvolve(SimEventQueue& q)
00161 {  }
00162 
00163 // ######################################################################
00164 void FixedSaccadeController::computeWhenNewPercept(SimEventQueue& q)
00165 {  }
00166 
00167 // ######################################################################
00168 void FixedSaccadeController::computeWhenResetPos(SimEventQueue& q)
00169 {
00170   // during start/reset, an eye position should have been set
00171   // depending on command-line options for initial position:
00172   itsPos = getPreviousPercept(0).p;
00173 }
00174 
00175 // ######################################################################
00176 Point2D<int> FixedSaccadeController::
00177 computeWhenNewDecision(SaccadeState& sacstate, bool& blinkstate,
00178                        SimEventQueue& q)
00179 {
00180   if (itsPos.isValid()) sacstate = SACSTATE_FIX;
00181   else sacstate = SACSTATE_UNK;
00182 
00183   // this controller never blinks:
00184   blinkstate = false;
00185 
00186   // just return our current fixed position:
00187   return itsPos;
00188 }
00189 
00190 // ######################################################################
00191 // ######################################################################
00192 // ######################################################################
00193 FrictionSaccadeController::
00194 FrictionSaccadeController(OptionManager& mgr, const SaccadeBodyPart part)
00195   :
00196   SaccadeController(mgr, "Friction Saccade Controller",
00197                     "FrictionSaccadeController",
00198                     part, 2, 2),  // queue length 2 for current and previous
00199   itsSpringK(part == SaccadeBodyPartEye
00200              ? &OPT_SCeyeSpringK
00201              : &OPT_SCheadSpringK,
00202              this),
00203   itsFrictionMu(part == SaccadeBodyPartEye
00204                 ? &OPT_SCeyeFrictionMu
00205                 : &OPT_SCheadFrictionMu,
00206                 this),
00207   itsDims(&OPT_InputFrameDims, this),
00208   itsTimeStep(&OPT_SimulationTimeStep, this),
00209   x(0.0), y(0.0), oldx(0.0), oldy(0.0),
00210   oldt(SimTime::SECS(-1.0))
00211 {  }
00212 
00213 // ######################################################################
00214 FrictionSaccadeController::~FrictionSaccadeController()
00215 {  }
00216 
00217 // ######################################################################
00218 void FrictionSaccadeController::doEvolve(SimEventQueue& q)
00219 {
00220   const SimTime t = q.now();
00221 
00222   if (oldt.secs() < 0.0) return;  // not simulating yet
00223   if (havePercepts() == false) { oldt = t; return; } // no percept yet
00224 
00225   // get FoA coordinates:
00226   WTAwinner last = getPreviousPercept(0);   // get latest percept
00227   double foax = last.p.i + 0.49999;
00228   double foay = last.p.j + 0.49999;
00229 
00230   // For stability of our difference equations, we need to run them at
00231   // the given simulation time step. Here, we however support for us
00232   // being called at much coarser time intervals.
00233   const SimTime dt =
00234     SimTime::computeDeltaT((t - oldt), itsTimeStep.getVal());
00235   double ox = oldx, oy = oldy;
00236   oldx = x; oldy = y; // keep track of how much we move this step
00237 
00238   for (SimTime tt = oldt; tt < t; tt += dt)
00239     {
00240       // compute force:
00241       double k = itsSpringK.getVal(), mu = itsFrictionMu.getVal();
00242       double fx = k * (foax - x);
00243       double fy = k * (foay - y);
00244 
00245       // update position:
00246       double dts = dt.secs();
00247       double newx = 2.0 * x - ox + dts * (dts * fx - mu * (x - ox));
00248       double newy = 2.0 * y - oy + dts * (dts * fy - mu * (y - oy));
00249       //LINFO("curr=(%.2f, %.2f) foa=(%.2f, %.2f) new=(%.2f, %.2f) "
00250       //"f=(%.2f, %.2f)", x, y, foax, foay, newx, newy, fx, fy);
00251 
00252       // keep track of the time step and current/old position:
00253       ox = x; oy = y; x = newx; y = newy;
00254     }
00255 
00256   oldt = t;
00257 }
00258 
00259 // ######################################################################
00260 void FrictionSaccadeController::computeWhenNewPercept(SimEventQueue& q)
00261 {
00262   // if this is our first percept, let's start the mass/spring now and here:
00263   if (oldt.secs() < 0.0)
00264     {
00265       WTAwinner last = getPreviousPercept(0);
00266       x = oldx = last.p.i + 0.49999;
00267       y = oldy = last.p.j + 0.49999;
00268       oldt = last.t;
00269     }
00270   // otherwise, the percept will simply be used as anchor point in our
00271   // next evolve()
00272 }
00273 
00274 // ######################################################################
00275 void FrictionSaccadeController::computeWhenResetPos(SimEventQueue& q)
00276 {
00277   // get desired new fixation location:
00278   Point2D<int> newloc = getPreviousPercept(0).p;
00279 
00280   // just shift overt attention coordinates to the new location,
00281   // disregarding physics of friction and the spring:
00282   x = newloc.i + 0.49999;
00283   y = newloc.j + 0.49999;
00284   oldx = x; oldy = y; oldt = getPreviousPercept(0).t;
00285 }
00286 
00287 // ######################################################################
00288 Point2D<int> FrictionSaccadeController::
00289 computeWhenNewDecision(SaccadeState& sacstate, bool& blinkstate,
00290                        SimEventQueue& q)
00291 {
00292   // this controller never blinks:
00293   blinkstate = false;
00294 
00295   if (oldt.secs() < 0.0 || havePercepts() == false)
00296     { sacstate = SACSTATE_UNK; return Point2D<int>(-1, -1); } // not started yet
00297 
00298   // if we did move during last evolve(), then we are in smooth
00299   // pursuit, otherwise fixation. This controller never saccades:
00300   if (sqrt((x-oldx)*(x-oldx) + (y-oldy)*(y-oldy)) > 0.001)
00301     sacstate = SACSTATE_SMO; else sacstate = SACSTATE_FIX;
00302 
00303   // round current coordinates to pixels:
00304   Point2D<int> fi = Point2D<int>(int(x), int(y));
00305 
00306   // possibly force fixation to inside image:
00307   fi.clampToDims(itsDims.getVal());
00308 
00309   // return our current rounded & clamped coordinates:
00310   return fi;
00311 }
00312 
00313 // ######################################################################
00314 // ######################################################################
00315 // ######################################################################
00316 ThresholdSaccadeController::
00317 ThresholdSaccadeController(OptionManager& mgr, SaccadeBodyPart part)
00318   :
00319   SaccadeController(mgr, "Threshold Saccade Controller",
00320                     "ThresholdSaccadeController", part, 2, 2),
00321   itsOdist(part == SaccadeBodyPartEye
00322            ? &OPT_SCeyeThreshMinOvert
00323            : &OPT_SCheadThreshMinOvert,
00324            this), // see ModelOptionDefs.C
00325   itsCdist(part == SaccadeBodyPartEye
00326            ? &OPT_SCeyeThreshMaxCovert
00327            : &OPT_SCheadThreshMaxCovert,
00328            this),  // see ModelOptionDefs.C
00329   itsMinNum(part == SaccadeBodyPartEye
00330             ? &OPT_SCeyeThreshMinNum
00331             : &OPT_SCheadThreshMinNum,
00332             this),  // see ModelOptionDefs.C
00333   itsSalWeight(part == SaccadeBodyPartEye
00334                ? &OPT_SCeyeThreshSalWeigh
00335                : &OPT_SCheadThreshSalWeigh,
00336                this),  // see ModelOptionDefs.C
00337   itsMetrics(new SpatialMetrics(mgr))
00338 {
00339   this->addSubComponent(itsMetrics);
00340 }
00341 
00342 // ######################################################################
00343 void ThresholdSaccadeController::start1()
00344 {
00345   resetPqlen(itsMinNum.getVal());
00346   odistsq = float(itsMetrics->getFoveaRadius()) * itsOdist.getVal();
00347   odistsq *= odistsq;
00348   cdistsq = float(itsMetrics->getFoveaRadius()) * itsCdist.getVal();
00349   cdistsq *= cdistsq;
00350   didresetpos = false;
00351 
00352   SaccadeController::start1();
00353 }
00354 
00355 // ######################################################################
00356 ThresholdSaccadeController::~ThresholdSaccadeController()
00357 {  }
00358 
00359 // ######################################################################
00360 void ThresholdSaccadeController::doEvolve(SimEventQueue& q)
00361 {  }
00362 
00363 // ######################################################################
00364 void ThresholdSaccadeController::reset1()
00365 {
00366   // reset some stuff for ThresholdSaccadeController
00367   didresetpos = false;
00368 
00369   // propagate to our base class:
00370   SaccadeController::reset1();
00371 }
00372 
00373 // ######################################################################
00374 void ThresholdSaccadeController::checkPercepts(bool& areclose, Point2D<int>& avgp)
00375 {
00376   // compute average location of queued percepts:
00377   float ii = 0.0f, jj = 0.0f, w = 0.0f; int idx = 0;
00378   while(1) {
00379     WTAwinner win = getPreviousPercept(idx);
00380     if (win.isValid() == false) break; // reached end of percept queue
00381     float weight = itsSalWeight.getVal() ? win.sv : 1.0f;
00382     ii += float(win.p.i) * weight;
00383     jj += float(win.p.j) * weight;
00384     w += weight; ++ idx;
00385   }
00386   if (w) { avgp.i = int(ii / w + 0.4999f); avgp.j = int(jj / w + 0.4999f); }
00387   else { avgp.i = -1; avgp.j = -1; }
00388 
00389   // test whether all of our percepts are within cdistsq of the average:
00390   idx = 0; areclose = true;
00391   while(1) {
00392     WTAwinner win = getPreviousPercept(idx);
00393     if (win.isValid() == false) break; // reached end of percept queue
00394     if (avgp.squdist(win.p) > cdistsq) { areclose = false; break; }
00395     ++ idx;
00396   }
00397 }
00398 
00399 // ######################################################################
00400 void ThresholdSaccadeController::computeWhenNewPercept(SimEventQueue& q)
00401 {  }
00402 
00403 // ######################################################################
00404 void ThresholdSaccadeController::computeWhenResetPos(SimEventQueue& q)
00405 {
00406   // get desired new fixation location:
00407   resetloc = getPreviousPercept(0).p;
00408   didresetpos = true;
00409 
00410   // NOTE that this is not a legit saccade, hence we do not switch our
00411   // state to SACSTATE_SAC. Our resetPos() wrapper will set our state
00412   // to fixation (or unknown if resetloc is invalid).
00413 }
00414 
00415 // ######################################################################
00416 Point2D<int> ThresholdSaccadeController::
00417 computeWhenNewDecision(SaccadeState& sacstate, bool& blinkstate,
00418                        SimEventQueue& q)
00419 {
00420   // this controller never blinks:
00421   blinkstate = false;
00422 
00423   // if we just did a reset, return the reset location:
00424   if (didresetpos)
00425     { didresetpos = false; sacstate = SACSTATE_FIX; return resetloc; }
00426 
00427   // if no percept yet, return invalid position:
00428   if (havePercepts() == false)
00429     { sacstate = SACSTATE_UNK; return Point2D<int>(-1, -1); }
00430 
00431   // are our queued-up percepts close to each other and get their average:
00432   bool areclose; Point2D<int> avgp;
00433   checkPercepts(areclose, avgp);
00434   const Point2D<int> prev = getPreviousDecision(0).p;
00435 
00436   // if the covert shifts are nicely clustered, check that the average
00437   // if sufficiently far from our current overt location (if we have
00438   // made an overt shift already; otherwise make our first overt
00439   // shift if areclose is true):
00440   if (areclose && haveDecisions() == false)
00441     { sacstate = SACSTATE_SAC; return avgp; } // first overt shift
00442   if (areclose && avgp.squdist(prev) >= odistsq)
00443     { sacstate = SACSTATE_SAC; return avgp; } // overt shift to avg covert loc
00444 
00445   // either the covert shifts are not close to each other, or they are
00446   // too close to our current overt fixation. Don't move overt
00447   // attention yet and return whatever previous decision we have made
00448   // (if any):
00449   if (prev.isValid()) sacstate = SACSTATE_FIX; else sacstate = SACSTATE_UNK;
00450   return prev;
00451 }
00452 
00453 // ######################################################################
00454 // ######################################################################
00455 // ######################################################################
00456 ThresholdFrictionSaccadeController::
00457 ThresholdFrictionSaccadeController(OptionManager& mgr,
00458                                    SaccadeBodyPart part)
00459   :
00460   SaccadeController(mgr,
00461                     "Threshold-Friction Saccade Controller",
00462                     "ThresholdFrictionSaccadeController",
00463                     part, 2, 2),  // queue length 2 for current and previous
00464   itsMaxIdle(part == SaccadeBodyPartEye
00465              ? &OPT_SCeyeMaxIdleSecs
00466              : &OPT_SCheadMaxIdleSecs,
00467              this), // see ModelOptionDefs.C
00468   itsMetrics(new SpatialMetrics(mgr)),
00469   tsc(new ThresholdSaccadeController(mgr, part)),
00470   fsc(new FrictionSaccadeController(mgr, part))
00471 {
00472   this->addSubComponent(itsMetrics);
00473   addSubComponent(tsc);
00474   addSubComponent(fsc);
00475 }
00476 
00477 // ######################################################################
00478 ThresholdFrictionSaccadeController::~ThresholdFrictionSaccadeController()
00479 {  }
00480 
00481 // ######################################################################
00482 void ThresholdFrictionSaccadeController::doEvolve(SimEventQueue& q)
00483 {
00484   // evolve the friction part of our business:
00485   fsc->evolve(q);
00486 
00487   // evolve the threshold part of our business:
00488   tsc->evolve(q);
00489 }
00490 
00491 // ######################################################################
00492 void ThresholdFrictionSaccadeController::
00493 computeWhenNewPercept(SimEventQueue& q)
00494 {
00495   // get the percept we just received:
00496   WTAwinner win = getPreviousPercept(0);
00497 
00498   // Feed that new percept directly to the ThresholdSaccadeController:
00499   tsc->setPercept(win, q);
00500 
00501   // feed the average covert location as new percept to the friction
00502   // controller, only if the queued-up percepts are close to each
00503   // other (so that the average represents a fairly stable covert
00504   // fixation), and if that new percept is different from the last one
00505   // we gave to the FrictionController:
00506   bool areclose; Point2D<int> avgp; tsc->checkPercepts(areclose, avgp);
00507   if (areclose)
00508     {
00509       const Point2D<int> flast = fsc->getPreviousPercept(0).p;
00510       if (avgp.isValid() && avgp != flast)
00511         {
00512           win.p = avgp;  // replace covert location by average covert location
00513           fsc->setPercept(win, q);
00514         }
00515     }
00516 }
00517 
00518 // ######################################################################
00519 void ThresholdFrictionSaccadeController::computeWhenResetPos(SimEventQueue& q)
00520 {
00521   // get the percept we just received:
00522   const WTAwinner win = getPreviousPercept(0);
00523 
00524   // reset friction:
00525   fsc->resetPos(win.p, q);
00526 
00527   // reset threhold:
00528   tsc->resetPos(win.p, q);
00529 }
00530 
00531 // ######################################################################
00532 Point2D<int> ThresholdFrictionSaccadeController::
00533 computeWhenNewDecision(SaccadeState& sacstate, bool& blinkstate,
00534                        SimEventQueue& q)
00535 {
00536   const SimTime t = q.now();
00537 
00538   // this controller never blinks:
00539   blinkstate = false;
00540 
00541   // what does the FrictionSaccadeController recommend?
00542   Point2D<int> pf = fsc->getDecision(q);
00543 
00544   // what does the ThresholdSaccadeController recommend?
00545   Point2D<int> pt = tsc->getDecision(q);
00546 
00547   // if we have a saccade recommendation from the threshold
00548   // controller, override the slow drift recommended by the friction
00549   // controller. Otherwise, if the friction controller is not
00550   // recommending a move either, see if we have been idle for too
00551   // long, in which case let's home to the center of the image:
00552   if (pt.isValid() ||            // thresh controller has a new move, -- OR --
00553       (pt.isValid() == false &&  // no new threshold move
00554        pf.isValid() == false &&  // no new friction move
00555        haveDecisions() &&        // but we have moved in the past
00556        t - getPreviousDecision(0).t >
00557        itsMaxIdle.getVal()))    // and it's been a long time
00558     {
00559       // if homing, let's get the coords:
00560       if (pt.isValid() == false)
00561         {
00562           if (SeC<SimEventRetinaImage> e =
00563               q.check<SimEventRetinaImage>(this, SEQ_ANY))
00564             pt = e->center();
00565           else CLFATAL("Cannot home to center without a retina image!");
00566 
00567           // reset tsc to this homing position:
00568           tsc->resetPos(pt, q);
00569         }
00570 
00571       // reset the mass/spring simulation of the friction controller
00572       // to new overt fixation:
00573       fsc->resetPos(pt, q);
00574 
00575       // we are saccading:
00576       sacstate = SACSTATE_SAC;
00577 
00578       // do overt shift:
00579       return pt;
00580     }
00581 
00582   // otherwise, follow the recommendation of the friction controller,
00583   // if any (damped drifting of overt attention in between two
00584   // saccades). This will not yield chaotic damped movements, since
00585   // the friction controller is only fed with stable percepts
00586   // (queued-up percepts that are close to each other):
00587   if (pf.isValid())
00588     {
00589       // are we fixating or in smooth pursuit? we'll just do whatever fsc says:
00590       sacstate = fsc->getState();
00591       return pf;
00592     }
00593 
00594   // if friction is not moving, we could be unknown, in saccade, or in
00595   // fixation; if tsc is in saccade, then that's what we are;
00596   // otherwise, we are just like fsc:
00597   if (tsc->getState() == SACSTATE_SAC) sacstate = SACSTATE_SAC;
00598   else sacstate = fsc->getState();
00599   return getPreviousDecision(0).p;
00600 }
00601 
00602 // ######################################################################
00603 // ######################################################################
00604 // ######################################################################
00605 MonkeySaccadeController::MonkeySaccadeController(OptionManager& mgr,
00606                                                  SaccadeBodyPart part)
00607   :
00608   SaccadeController(mgr, "Monkey Saccade Controller",
00609                     "MonkeySaccadeController", part, 2, 2),
00610   fsc(new FrictionSaccadeController(mgr, part)),
00611   itsMetrics(new SpatialMetrics(mgr))
00612 {
00613   oldt = SimTime::ZERO();
00614   addSubComponent(fsc);
00615   addSubComponent(itsMetrics);
00616 }
00617 
00618 // ######################################################################
00619 MonkeySaccadeController::~MonkeySaccadeController()
00620 {  }
00621 
00622 // ######################################################################
00623 /*
00624 void MonkeySaccadeController::saccade(const Point2DT& target)
00625 {
00626   // our starting point is whatever fsc is currently at:
00627   Point2D<int> currfsc = fsc->getPreviousDecision(0).p;
00628   itsMetrics->pix2deg(currfsc, currx, curry);
00629 
00630   // initialize simulation:
00631   startt = oldt = target.t;
00632   double ampX, ampY; itsMetrics->pix2deg(target.p, ampX, ampY);
00633   ampX -= currx; ampY -= curry;  // amplitude of displacement from current
00634 
00635   double amplitude = sqrt(ampX * ampX + ampY * ampY);
00636   theta = atan(ampY / ampX); if (ampX < 0.0) theta += M_PI;
00637 
00638   // If amplitude too small, don't even bother starting the saccade:
00639   if (amplitude < 0.0001) return;
00640 
00641   if (SaccadeBodyPartEye == bodyPart())
00642     {
00643       // set us up for a realistic eye saccade:
00644       vmax = 473.0 * (1.0 - exp(-amplitude / 7.8)); // degrees/ms
00645       duration = 2.0 * amplitude / vmax;
00646       accel = 2.0 * vmax / duration; // degrees/s^2
00647     }
00648   else
00649     {
00650       // set us up for a realistic head saccade:
00651       vmax = (3.9 * amplitude + 16.0); // degrees/ms
00652       duration = 2.0 * amplitude / vmax;
00653       accel = 2.0 * vmax / duration; // degrees/ms^2
00654     }
00655 
00656   LINFO("%s: Ampl=%.2fdeg vMax=%.2f dur=%.2fms theta=%.2fdeg start=%.2fms",
00657         (SaccadeBodyPartEye == bodyPart()) ? "Eye " : "Head",
00658         amplitude, vmax, duration * 1000.0,
00659         theta * 180.0 / M_PI, startt.msecs());
00660 }
00661 */
00662 // ######################################################################
00663 void MonkeySaccadeController::computeWhenNewPercept(SimEventQueue& q)
00664 {
00665   // get our new percept:
00666   WTAwinner win = getPreviousPercept(0);
00667 
00668   // just pass it on to fsc; that's our new spring anchor point:
00669   fsc->setPercept(win, q);
00670 }
00671 
00672 // ######################################################################
00673 void MonkeySaccadeController::doEvolve(SimEventQueue& q)
00674 {
00675   const SimTime t = q.now();
00676   /*
00677   // are we executing a realistic saccade?
00678   if (getState() == SACSTATE_SAC)
00679     {
00680       // do the realistic simulation:
00681       double deltaT = (t - oldt).secs(), elapsedT = (t - startt).secs();
00682 
00683       // Get the average velocity during this interval, that's the
00684       // velocity at mid-point of the interval:
00685       double v;
00686       if (elapsedT > 0.5 * duration)
00687         v = vmax - accel * (elapsedT + 0.5 * deltaT - 0.5 * duration);
00688       else
00689         v = accel * (elapsedT + 0.5 * deltaT);
00690 
00691       // update current position:
00692       currx += v * cos(theta) * deltaT;
00693       curry += v * sin(theta) * deltaT;
00694 
00695       // drive the friction controller with our current coordinates:
00696       Point2D<int> currpt; itsMetrics->deg2pix(currx, curry, currpt);
00697       fsc->resetPos(currpt, q);
00698 
00699       // if reached the end, finish the saccade:
00700       if (elapsedT >= duration) {
00701         LINFO("=== %s: end of saccade at (%d, %d) at %.2fms ===",
00702               (SaccadeBodyPartEye == bodyPart()) ? "Eye " : "Head",
00703               currpt.i, currpt.j, t.msecs());
00704         sacstate = SACSTATE_FIX;
00705       }
00706     }
00707   else
00708     {
00709       // let the frictioncontroller evolve and inherit its state:
00710       fsc->evolve(q);
00711       sacstate = fsc->getState();
00712     }
00713   */
00714   // get ready for next time step:
00715   oldt = t;
00716 }
00717 
00718 // ######################################################################
00719 void MonkeySaccadeController::computeWhenResetPos(SimEventQueue& q)
00720 {
00721   // get our reset target:
00722   WTAwinner win = getPreviousPercept(0);
00723 
00724   // feed it to fsc:
00725   fsc->resetPos(win.p, q);
00726 
00727   // abort any saccade we may be executing:
00728   //////////setState(SACSTATE_FIX);
00729 }
00730 
00731 // ######################################################################
00732 Point2D<int> MonkeySaccadeController::
00733 computeWhenNewDecision(SaccadeState& sacstate, bool& blinkstate,
00734                        SimEventQueue& q)
00735 {
00736   // since our realistic simulation directly drives the
00737   // FrictionController, it is always up to date, so return its
00738   // current position, or our current if fsc has nothing new for us:
00739   Point2D<int> p = fsc->getDecision(q);
00740   if (p.isValid()) return p; else return getPreviousDecision(0).p;
00741 }
00742 
00743 
00744 // ######################################################################
00745 /* So things look consistent in everyone's emacs... */
00746 /* Local Variables: */
00747 /* indent-tabs-mode: nil */
00748 /* End: */
Generated on Sun May 8 08:05:25 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3