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: */