00001 /*!@file Robots2/Beobot2/Navigation/QT_Navigation/QT_Navigation.C */ 00002 // //////////////////////////////////////////////////////////////////// // 00003 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the // 00004 // University of Southern California (USC) and the iLab at USC. // 00005 // See http://iLab.usc.edu for information about this project. // 00006 // //////////////////////////////////////////////////////////////////// // 00007 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00008 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00009 // in Visual Environments, and Applications'' by Christof Koch and // 00010 // Laurent Itti, California Institute of Technology, 2001 (patent // 00011 // pending; application number 09/912,225 filed July 23, 2001; see // 00012 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00013 // //////////////////////////////////////////////////////////////////// // 00014 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00015 // // 00016 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00017 // redistribute it and/or modify it under the terms of the GNU General // 00018 // Public License as published by the Free Software Foundation; either // 00019 // version 2 of the License, or (at your option) any later version. // 00020 // // 00021 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00022 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00023 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00024 // PURPOSE. See the GNU General Public License for more details. // 00025 // // 00026 // You should have received a copy of the GNU General Public License // 00027 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00028 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00029 // Boston, MA 02111-1307 USA. // 00030 // //////////////////////////////////////////////////////////////////// // 00031 // 00032 // Primary maintainer for this file: Chin-Kai Chang <chinkaic@usc.edu> 00033 // $HeadURL: svn://ilab.usc.edu/trunk/saliency/src/Robots/Beobot2/QT_Navigation.C 00034 // $ $Id: QT_Navigation.C 13084 2010-03-30 02:42:00Z kai $ 00035 // 00036 ////////////////////////////////////////////////////////////////////////// 00037 00038 #include "Robots/Beobot2/Navigation/QT_Navigation/QT_Navigation.H" 00039 #include "Ice/BeobotEvents.ice.H" 00040 00041 #include "Raster/Raster.H" 00042 #include "Util/sformat.H" 00043 #include "Image/Image.H" 00044 #include "Image/DrawOps.H" 00045 #include "Image/ColorOps.H" // for luminance() 00046 #include "Image/ShapeOps.H" // for rescale() 00047 #include "Image/MathOps.H" // for stdev() 00048 #include "Image/MatrixOps.H" // for matrixMult() 00049 #include "Image/CutPaste.H" // for inplacePaste() 00050 00051 #include "Util/Timer.H" 00052 00053 #include "SIFT/Keypoint.H" 00054 #include "SIFT/VisualObject.H" 00055 #include "SIFT/VisualObjectMatch.H" 00056 #include "Transport/FrameInfo.H" 00057 00058 #include "Ice/IceImageUtils.H" 00059 00060 #include "Component/ModelParam.H" 00061 #include "Component/ModelOptionDef.H" 00062 00063 00064 #define TEACH_FOLDER "../data/logs/2010_02_26__18_32_21/image_0000000000" 00065 #define TEACH_START_NUMBER 14363 00066 #define TEACH_END_NUMBER 15038 00067 00068 00069 //#define TEACH_FOLDER "../data/logs/2009_11_12__17_30_33/image_0000000000" 00070 //#define TEACH_START_NUMBER 71449 00071 //#define TEACH_END_NUMBER 75900 00072 #define REPLAY_FOLDER "../data/logs/2010_02_26__18_33_26/image_0000000000" 00073 #define REPLAY_START_NUMBER 16298 00074 #define REPLAY_END_NUMBER 17136 00075 00076 #define DFRAME (TEACH_START_NUMBER - REPLAY_START_NUMBER) 00077 #define IMAX 1.0 00078 #define IMIN 0.0 00079 00080 const ModelOptionCateg MOC_QT_Navigation = 00081 { MOC_SORTPRI_3, "QT Navigation Related Options" }; 00082 const ModelOptionDef OPT_RealCamera = 00083 { MODOPT_ARG(bool), "RealCamera", &MOC_QT_Navigation, OPTEXP_CORE, 00084 "Do we want use real camera from BeoCamera? Default will load from image files.", 00085 "camera", '\0', "true | false", "false" }; 00086 00087 // ###################################################################### 00088 QT_Navigation::QT_Navigation(OptionManager& mgr, 00089 const std::string& descrName, const std::string& tagName) : 00090 RobotBrainComponent(mgr, descrName, tagName), 00091 itsFftComputer(new GistEstimatorFFT(mgr)), 00092 itsOfs(new OutputFrameSeries(mgr)), 00093 itsMapImg(320,240,ZEROS), 00094 itsVisualObjectDB(new VisualObjectDB()), 00095 itsVisualObjectDB2(new VisualObjectDB()), 00096 itsFfn(new FeedForwardNetwork()), 00097 itsTimer(1000000), 00098 itsCurrImgID(-1), 00099 itsPrevProcImgID(-1), 00100 itsTeachImgID(TEACH_START_NUMBER +5 +300),//FIXX 00101 itsReplayImgID(REPLAY_START_NUMBER +300),//FIXX 00102 itsTransSpeed(0.0), 00103 itsRotSpeed(0.0), 00104 itsDirVector(0.0), 00105 itsDir(0), 00106 itsMilestoneErr(999.99), 00107 itsFalseErrRate(0.0), 00108 itsDirCount(0), 00109 itsPosition(0.0, 0.0, 0.0), 00110 itsError(0.0, 0.0, 0.0), 00111 itsRealCamera(&OPT_RealCamera, this, 0), 00112 itsEstop(true), 00113 itsReplayDBLoaded(false), 00114 itsIState(0.0), 00115 itsDState(0.0), 00116 itsPGain(1.0), 00117 itsIGain(0.01), 00118 itsDGain(0.0) 00119 { 00120 addSubComponent(itsOfs); 00121 00122 // load the teach Visual Object Database 00123 loadDB(sformat("%s.sift",TEACH_FOLDER)); 00124 } 00125 00126 // ###################################################################### 00127 void QT_Navigation::loadReplayDB() 00128 { 00129 if(!itsReplayDBLoaded) 00130 { 00131 if(!itsRealCamera.getVal()) 00132 { 00133 00134 if(itsVisualObjectDB2->loadFrom 00135 (sformat("%s.sift",REPLAY_FOLDER), false)) 00136 LINFO("Load Replay Database"); 00137 else 00138 { 00139 LINFO("Can't load database"); 00140 saveDB(REPLAY_START_NUMBER, 00141 REPLAY_END_NUMBER, 00142 REPLAY_FOLDER); 00143 } 00144 } 00145 itsReplayDBLoaded = true; 00146 } 00147 } 00148 00149 // ###################################################################### 00150 void QT_Navigation::initFFN() 00151 { } 00152 00153 // ###################################################################### 00154 QT_Navigation::~QT_Navigation() 00155 { } 00156 00157 // ###################################################################### 00158 void QT_Navigation::start1() 00159 { } 00160 00161 // ###################################################################### 00162 void QT_Navigation::registerTopics() 00163 { 00164 // subscribe to all sensor data 00165 this->registerSubscription("CameraMessageTopic"); 00166 this->registerSubscription("MotorMessageTopic"); 00167 this->registerPublisher("MotorRequestTopic"); 00168 } 00169 00170 // ###################################################################### 00171 void QT_Navigation::evolve() 00172 { 00173 // if simulation 00174 if(!itsRealCamera.getVal()) 00175 { 00176 navigation(); 00177 } 00178 00179 // if real camera 00180 else 00181 { 00182 // check if the current image is updated 00183 its_Curr_Img_mutex.lock(); 00184 bool newImageFlag = (itsPrevProcImgID < itsCurrImgID); 00185 its_Curr_Img_mutex.unlock(); 00186 00187 // if so, process 00188 if(newImageFlag) 00189 { 00190 itsTimer.reset(); 00191 its_Curr_Img_mutex.lock(); 00192 itsProcImg = itsCurrImg; 00193 itsPrevProcImgID = itsCurrImgID; 00194 its_Curr_Img_mutex.unlock(); 00195 navigation(); 00196 } 00197 } 00198 00199 drawState(); 00200 itsOfs->writeRGB(itsDispImg, "QT_nav", FrameInfo("QT_nav",SRC_POS)); 00201 if(itsEstop == false) 00202 { 00203 //Raster::waitForKey(); 00204 //itsEstop = true; 00205 } 00206 } 00207 00208 // ###################################################################### 00209 void QT_Navigation::recovery() 00210 { 00211 int trytimes = 0; 00212 do{ 00213 loadFrame(); 00214 std::vector< rutz::shared_ptr<VisualObjectMatch> > matches; 00215 int nmatches = 00216 itsVisualObjectDB->getObjectMatches 00217 (itsReplayVo,matches,VOMA_SIMPLE, 00218 10U, 00219 0.5F, //keypoint distance score default 0.5F 00220 0.5F, //affine distance score default 0.5F 00221 1.0F, //minscore default 1.0F 00222 4U, //min # of keypoint match 00223 100U, //keypoint selection thershold 00224 false //sort by preattentive 00225 ); 00226 std::string objName; 00227 int closestFrame = 999999999; 00228 int tmpFrameID = itsTeachImgID; 00229 LINFO("Searching For Nearest Key Frame in the Database"); 00230 if (nmatches > 0 ) 00231 { 00232 for(int i = 0; i < nmatches; i++) 00233 { 00234 LINFO("Found [%d] frames matches Frame[%d]", nmatches, i); 00235 00236 // so that we will have a ref to the last matches obj 00237 rutz::shared_ptr<VisualObject> obj; 00238 rutz::shared_ptr<VisualObjectMatch> vom; 00239 vom = matches[i]; 00240 obj = vom->getVoTest(); 00241 // score = vom->getScore(); 00242 // nkeyp = vom->size(); 00243 // avgScore = vom->getKeypointAvgDist(); 00244 // affineAvgDist = vom->getAffineAvgDist(); 00245 objName = obj->getName(); 00246 int tmpID = atoi(objName.c_str()); 00247 int dx = tmpID - itsTeachImgID; 00248 00249 LINFO("Dist. from current teach frame[%d]=======new ID[%d]", 00250 dx,tmpID); 00251 // find closest frame that matches 00252 //FIXX: we don't consider the match rate yet. 00253 if(dx < closestFrame) 00254 { 00255 closestFrame = dx; 00256 tmpFrameID = tmpID; 00257 } 00258 } 00259 LINFO("Recovery Found:TID[%d]RID[%d] Match[%d]", 00260 itsTeachImgID,itsReplayImgID,tmpFrameID); 00261 itsTeachImgID = tmpFrameID; 00262 itsEstop = true; 00263 itsTransSpeed = 0.8; 00264 loadFrame(); 00265 computeSIFT(); 00266 } 00267 else 00268 { 00269 LINFO("No Match Point,Try get new frame and try again"); 00270 loadFrame(); 00271 trytimes++; 00272 } 00273 } 00274 while(!itsEstop && trytimes < 10); 00275 } 00276 00277 // ###################################################################### 00278 // The main function of navigation 00279 void QT_Navigation::navigation() 00280 { 00281 00282 // load key frame from database 00283 // compute SIFT keypoint and matching 00284 loadFrame(); 00285 //computeSIFT(); 00286 00287 // apply QT algo for direction 00288 double error = computeDirection2(itsTeachImgID); 00289 00290 //When the mean error and stderr have too much difference 00291 //Then ignore the SIFT bogus match 00292 if(error != 0.0 && itsMatchList->size()!=0 && 00293 (itsMatchList->checkSIFTaffine() || itsFalseErrRate > 1.0) 00294 ){ 00295 updateMotorPID(itsTransSpeed, itsRotSpeed,error); 00296 updateMotor(itsTransSpeed, itsRotSpeed); 00297 updateKeyframe(error); 00298 itsEstop = true; 00299 00300 // update keyframe when reaching milestone 00301 updateKeyframe(error); 00302 itsEstop = true; 00303 } 00304 00305 // when the mean error and stderr have too much difference 00306 // then ignore the SIFT bogus match 00307 else 00308 { 00309 itsEstop = false; 00310 LINFO("lost matching; start recover mechanism"); 00311 itsTransSpeed = 0.0; 00312 itsRotSpeed = 0.0; 00313 updateMotor(itsTransSpeed, itsRotSpeed); 00314 //Raster::waitForKey(); 00315 //recovery(); 00316 //itsEstop = true; 00317 } 00318 } 00319 00320 // ###################################################################### 00321 void QT_Navigation::loadDB(const std::string& path) 00322 { 00323 // load the actual VisualObjectDatabase but don't load images 00324 if(itsVisualObjectDB->loadFrom(path,false)) 00325 { 00326 LINFO("Load SIFT Database"); 00327 } 00328 else 00329 { 00330 LINFO("SIFT Database %s not exist, create new", path.c_str()); 00331 saveDB(TEACH_START_NUMBER,TEACH_END_NUMBER,TEACH_FOLDER); 00332 } 00333 } 00334 00335 // ###################################################################### 00336 void QT_Navigation::saveDB(int start,int end,const std::string& path) 00337 { 00338 itsVisualObjectDB.reset(new VisualObjectDB()); 00339 for(int i = start; i < end; i++) 00340 { 00341 itsTimer.reset(); 00342 std::string teachFileName(sformat("%s%05d.ppm",path.c_str(),i)); 00343 Image< PixRGB<byte> > img = Raster::ReadRGB(teachFileName); 00344 rutz::shared_ptr<VisualObject> vo1 00345 (new VisualObject(sformat("%d",i), 00346 sformat("%s%05d.png",path.c_str(),i), 00347 rescale(img,img.getWidth()/2, img.getHeight()/2))); 00348 itsVisualObjectDB->addObject(vo1); 00349 LINFO("Time[%f] Compute SIFT for Frame %d/%d", 00350 itsTimer.get()/1000.0,i-start+1,end-start+1); 00351 } 00352 std::string teachDB(sformat("%s.sift",path.c_str())); 00353 // itsVisualObjectDB->setName(); 00354 itsVisualObjectDB->saveTo(teachDB); 00355 } 00356 00357 // ###################################################################### 00358 void QT_Navigation::loadFrame() 00359 { 00360 // load teach image data 00361 itsTimer.reset(); 00362 std::string teachFileName 00363 (sformat("%s%05d.ppm", TEACH_FOLDER, itsTeachImgID)); 00364 itsTeachImg = Raster::ReadRGB(teachFileName); 00365 itsTeachVo = itsVisualObjectDB->getObject 00366 (itsTeachImgID - TEACH_START_NUMBER); 00367 LINFO("Time for Computing SIFT on TeachImg %f", 00368 itsTimer.get()/1000.0); 00369 itsTimer.reset(); 00370 00371 // Load Current Replay image data 00372 00373 // running from image files 00374 if(!itsRealCamera.getVal()) 00375 { 00376 loadReplayDB(); 00377 std::string replayFileName 00378 (sformat("%s%05d.ppm", REPLAY_FOLDER, itsReplayImgID)); 00379 itsReplayImg = Raster::ReadRGB(replayFileName); 00380 00381 itsReplayVo = itsVisualObjectDB2-> 00382 getObject(itsReplayImgID - REPLAY_START_NUMBER); 00383 itsReplayImgID++; 00384 } 00385 00386 // running from real camera 00387 else 00388 { 00389 itsReplayImg = itsProcImg; 00390 itsReplayImgID = itsPrevProcImgID; 00391 // LINFO("TeachVo Width = %d",itsTeachVo->getKeypointImage(1.0).getWidth()); 00392 // if(itsProcImg.getWidth()==itsTeachVo->getKeypointImage(1.0).getWidth()){ 00393 // itsReplayImg = itsProcImg; 00394 // }else 00395 // { 00396 // itsReplayImg = rescale(itsProcImg,itsTeachVo->getKeypointImage(1.0).getWidth(), 00397 // itsTeachVo->getKeypointImage(1.0).getHeight()); 00398 // } 00399 rutz::shared_ptr<VisualObject> vo2 00400 (new VisualObject 00401 ("1", "", 00402 rescale(itsReplayImg,itsReplayImg.getWidth()/2, 00403 itsReplayImg.getHeight()/2))); 00404 itsReplayVo = vo2; 00405 } 00406 LINFO("Time for Computing SIFT on ReplayImg %f", 00407 itsTimer.get()/1000.0); 00408 } 00409 00410 // ###################################################################### 00411 bool QT_Navigation::computeSIFT() 00412 { 00413 itsTimer.reset(); 00414 VisualObjectMatchAlgo voma(VOMA_SIMPLE); 00415 itsMatchList.reset(new VisualObjectMatch(itsTeachVo, itsReplayVo, voma)); 00416 LINFO("========================================="); 00417 //LINFO("Found %u matches between Teach and Replay", match.size()); 00418 00419 // let's prune the matches: 00420 //uint np = 00421 itsMatchList->prune(); 00422 //LINFO("Pruned %u outlier matches.", np); 00423 LINFO("Match size now is %u ", itsMatchList->size()); 00424 00425 // show our final affine transform: 00426 // std::cerr<<match.getSIFTaffine(); 00427 00428 //LINFO("getKeypointAvgDist = %f", match.getKeypointAvgDist()); 00429 //LINFO("getAffineAvgDist = %f", match.getAffineAvgDist()); 00430 //LINFO("getScore = %f", match.getScore()); 00431 if (itsMatchList->checkSIFTaffine() == false) 00432 LINFO("### Affine is too weird -- BOGUS MATCH"); 00433 00434 // record the time 00435 LINFO("Time for Matching SIFT %f",itsTimer.get()/1000.0); 00436 00437 // if the sift point is valid, return true 00438 return (itsMatchList->size()!=0 && itsMatchList->checkSIFTaffine()); 00439 } 00440 00441 // ###################################################################### 00442 double QT_Navigation::computeDirection2(int teachID) 00443 { 00444 int dirF = 0,dirL = 0,dirR = 0; 00445 rutz::shared_ptr<VisualObjectMatch> bestMatch; 00446 int bestMatchSize = -1; 00447 int bestID = 0; 00448 LINFO("Direction2 Start"); 00449 00450 // boundary condition 00451 if((teachID-5) < TEACH_START_NUMBER) teachID = TEACH_START_NUMBER+2; 00452 00453 // looking 10 frame ahead to find best match frame 00454 for(int id = teachID-2; id < teachID+10; id++) 00455 { 00456 std::string teachFileName(sformat("%s%05d.ppm",TEACH_FOLDER,id)); 00457 rutz::shared_ptr<VisualObject> vo1 = 00458 itsVisualObjectDB->getObject(id - TEACH_START_NUMBER); 00459 00460 VisualObjectMatchAlgo voma(VOMA_SIMPLE); 00461 rutz::shared_ptr<VisualObjectMatch> 00462 match(new VisualObjectMatch(vo1, itsReplayVo, voma)); 00463 match->prune(); 00464 00465 // ========================= 00466 // find the best Keyframe 00467 float theta = 2.0, sx = 2.0, sy = 2.0, str = 0; 00468 00469 // check if the match is valid 00470 if (match->checkSIFTaffine()) 00471 { 00472 match->getSIFTaffine().decompose(theta, sx, sy, str); 00473 } 00474 int msize = match->size(); 00475 if((msize > bestMatchSize && sx < 1.0 && sy < 1.0) || 00476 (bestMatchSize == -1 && id==teachID)) 00477 { 00478 bestMatchSize = msize; 00479 bestMatch = match; 00480 bestID = id; 00481 } 00482 LINFO("Best Match Size %d,",bestMatchSize); 00483 itsMatchList = bestMatch; 00484 00485 int dirGain = 1; if(id == bestID) dirGain = 2; 00486 00487 // compute the direction and find mean of error 00488 for (int i = 0; i < msize; i ++) 00489 { 00490 rutz::shared_ptr<Keypoint> refk = match->getKeypointMatch(i).refkp; 00491 rutz::shared_ptr<Keypoint> tstk = match->getKeypointMatch(i).tstkp; 00492 00493 double ut = tstk->getX() - itsTeachImg.getWidth()/4; 00494 double ud = refk->getX() - itsTeachImg.getWidth()/4; 00495 00496 if(ut > 0.0 && ud < 0.0) 00497 { 00498 dirR += dirGain; 00499 } 00500 else if(ut < 0.0 && ud > 0.0) 00501 { 00502 dirL += dirGain; 00503 } 00504 else if(ut > 0.0 && ut > ud) 00505 { 00506 dirR += dirGain; 00507 } 00508 else if(ut < 0.0 && ut < ud) 00509 { 00510 dirL += dirGain; 00511 } 00512 else 00513 { 00514 dirF +=dirGain; 00515 } 00516 } 00517 }//end for 00518 00519 //========================= 00520 double stdx = 0.0,stdy = 0.0; 00521 double avgdx = 0.0,avgdy = 0.0; 00522 00523 // compute the std error 00524 for (int i = 0; i < bestMatchSize; i ++) 00525 { 00526 // LINFO("Loading best match point %d",i); 00527 rutz::shared_ptr<Keypoint> refk = bestMatch->getKeypointMatch(i).refkp; 00528 rutz::shared_ptr<Keypoint> tstk = bestMatch->getKeypointMatch(i).tstkp; 00529 double dx = fabs(tstk->getX() - refk->getX())+ 0.000001; 00530 stdx += dx * dx; 00531 avgdx+= dx; 00532 00533 double dy = fabs(tstk->getY() - refk->getY())+ 0.000001; 00534 stdy += dy * dy; 00535 avgdy+= dy; 00536 LINFO("Error! i=[%d] BestMatchSize[%d] " 00537 "avgdx[%f] avgdy[%f],dx[%f], dy[%f]", 00538 i,bestMatchSize,avgdx,avgdy,dx,dy); 00539 } 00540 if(bestMatchSize != 0) 00541 { 00542 avgdx/= bestMatchSize; 00543 avgdy/= bestMatchSize; 00544 stdx -= (avgdx*avgdx); 00545 stdy -= (avgdy*avgdy); 00546 stdx /= bestMatchSize; 00547 stdx = sqrt(stdx); 00548 stdy /= bestMatchSize; 00549 stdy = sqrt(stdy); 00550 LINFO("Error! BestMatchSize[%d] avgdx[%f] avgdy[%f]", 00551 bestMatchSize,avgdx,avgdy); 00552 } 00553 else 00554 { 00555 avgdx = 0; 00556 avgdy = 0; 00557 stdx = 0; 00558 stdy = 0; 00559 } 00560 00561 LINFO("stdx %f stdy %f",stdx,stdy); 00562 uint stdcount = 0; 00563 double newAvgX = 0.0,newAvgY = 0.0; 00564 00565 // Pick one stddev error as the new error data 00566 for (int i = 0; i < bestMatchSize; i ++) 00567 { 00568 rutz::shared_ptr<Keypoint> refk = bestMatch->getKeypointMatch(i).refkp; 00569 rutz::shared_ptr<Keypoint> tstk = bestMatch->getKeypointMatch(i).tstkp; 00570 double dx = fabs(tstk->getX() - refk->getX())+ 0.000001; 00571 double dy = fabs(tstk->getY() - refk->getY())+ 0.000001 ; 00572 double ddx = fabs(dx - avgdx); 00573 double ddy = fabs(dy - avgdy); 00574 00575 if(ddx < stdx && ddy < stdy) 00576 { 00577 newAvgX += dx; 00578 newAvgY += dy; 00579 stdcount++; 00580 } 00581 } 00582 00583 if(stdcount != 0) 00584 { 00585 newAvgX/= stdcount; 00586 newAvgY/= stdcount; 00587 } 00588 double newAvgerr = sqrt(newAvgX*newAvgX + newAvgY*newAvgY); 00589 double avgerr = sqrt(avgdx*avgdx + avgdy*avgdy); 00590 double angle = (newAvgX*(16.0/33.0)); 00591 itsError.x = newAvgX; 00592 itsError.y = newAvgY; 00593 itsError.z = newAvgerr; 00594 //FIXXX 00595 if(newAvgerr == 0.0) 00596 { 00597 //Raster::waitForKey(); 00598 newAvgerr = avgerr; 00599 itsFalseErrRate = 0; 00600 } 00601 else 00602 { 00603 itsFalseErrRate = avgerr/newAvgerr; 00604 } 00605 LINFO("False Error Rate %1.3f",itsFalseErrRate); 00606 if(itsFalseErrRate == 0.0) 00607 { 00608 LINFO("Error!!!! stdcount[%d] avgerr[%f]", 00609 stdcount,avgerr); 00610 LINFO("Error!!!! bestMatchSize[%d] stdx[%f] stdy[%f]", 00611 bestMatchSize,stdx,stdy); 00612 //Raster::waitForKey(); 00613 } 00614 00615 // reset target keyframe if we found better match 00616 if(bestID > itsTeachImgID && newAvgerr < 10.0) 00617 itsTeachImgID = bestID; 00618 00619 // compute the forward speed based on scale 00620 if (bestMatch->checkSIFTaffine())//Check if the match is valid 00621 { 00622 float theta = 0.0 , sx = 0.0, sy = 0.0, str = 0.0 ; 00623 bestMatch->getSIFTaffine().decompose(theta, sx, sy, str); 00624 //if the size difference is large(sx/sy << 1.0),we can speed up 00625 double rms = 1.0 -(sqrt(sx*sx+sy*sy)/sqrt(2)); 00626 00627 double gain = 1.0; 00628 if(rms > 0.0) 00629 itsTransSpeed = 0.8 + rms * gain; 00630 if(itsTransSpeed > 1.0)itsTransSpeed = 1.0; 00631 if(itsTransSpeed < 0.0)itsTransSpeed = 0.0; 00632 } 00633 else 00634 { 00635 itsTransSpeed = 0.8; 00636 } 00637 00638 // compute turning speed 00639 if(dirF >= abs(dirR + dirL)) 00640 { 00641 updatePosition(0); 00642 itsRotSpeed = 0.4; 00643 if(itsDir == 0)//same heading since last time 00644 { 00645 itsDirCount++; 00646 } 00647 else 00648 { 00649 itsDirCount = 0;//reset direction count 00650 itsDir = 0;//reset direction 00651 } 00652 } 00653 else if(dirR > dirL) 00654 { 00655 updatePosition(angle); 00656 //Average the speed 00657 itsRotSpeed -= newAvgX/100.0; 00658 itsRotSpeed /= 2.0; 00659 if(itsDir == 1)//same heading since last time 00660 { 00661 itsDirCount++; 00662 } 00663 else 00664 { 00665 itsDirCount = 0;//reset direction count 00666 itsDir = 1;//reset direction 00667 } 00668 } 00669 else 00670 { 00671 updatePosition(-1*angle); 00672 //Average the speed 00673 itsRotSpeed += newAvgX/100.0+1.0; 00674 itsRotSpeed /= 2.0; 00675 if(itsDir == -1)//same heading since last time 00676 { 00677 itsDirCount++; 00678 }else{ 00679 itsDirCount = 0;//reset direction count 00680 itsDir = -1;//reset direction 00681 } 00682 } 00683 return newAvgerr; 00684 } 00685 00686 // ###################################################################### 00687 double QT_Navigation::computeDirection() 00688 { 00689 itsTimer.reset(); 00690 int dirF = 0,dirL = 0,dirR = 0; 00691 double avgdx = 0.0,avgdy = 0.0; 00692 uint msize = itsMatchList->size(); 00693 00694 // compute the direction and find mean of error 00695 for (uint i = 0; i < msize; i ++) 00696 { 00697 rutz::shared_ptr<Keypoint> refk = 00698 itsMatchList->getKeypointMatch(i).refkp; 00699 rutz::shared_ptr<Keypoint> tstk = 00700 itsMatchList->getKeypointMatch(i).tstkp; 00701 00702 double ut = tstk->getX() - itsTeachImg.getWidth()/4; 00703 double ud = refk->getX() - itsTeachImg.getWidth()/4; 00704 double dx = fabs(ut - ud); 00705 double dy = fabs(tstk->getY() - refk->getY()); 00706 avgdx += dx; 00707 avgdy += dy; 00708 std::string dir; 00709 00710 if(ut > 0.0 && ud < 0.0) 00711 { 00712 dir = std::string("RIGHT"); 00713 dirR ++; 00714 } 00715 else if(ut < 0.0 && ud > 0.0) 00716 { 00717 dir = std::string("LEFT"); 00718 dirL ++; 00719 } 00720 else if(ut > 0.0 && ut > ud ) 00721 { 00722 dir = std::string("RIGHT"); 00723 dirR ++; 00724 } 00725 else if(ut < 0.0 && ut < ud) 00726 { 00727 dir = std::string("LEFT"); 00728 dirL ++; 00729 } 00730 else 00731 { 00732 dir = std::string("Forward"); 00733 dirF ++; 00734 } 00735 LDEBUG("MatchPoint[%d] Teach(%f,%f) Replay(%f,%f), " 00736 "ud:[%f],ut:[%f],dx:[%f],dy:[%f] Turn %s", 00737 i, 00738 (refk->getX()), 00739 (refk->getY()), 00740 (tstk->getX()), 00741 (tstk->getY()), 00742 ud,ut,dx,dy,dir.c_str() 00743 ); 00744 } 00745 avgdx/= msize; 00746 avgdy/= msize; 00747 double stdx = 0.0,stdy = 0.0; 00748 00749 // compute the std error 00750 for (uint i = 0; i < msize; i ++) 00751 { 00752 rutz::shared_ptr<Keypoint> refk = 00753 itsMatchList->getKeypointMatch(i).refkp; 00754 rutz::shared_ptr<Keypoint> tstk = 00755 itsMatchList->getKeypointMatch(i).tstkp; 00756 double ddx = fabs(tstk->getX() - refk->getX()) - avgdx; 00757 stdx += ddx * ddx; 00758 double ddy = fabs(tstk->getY() - refk->getY()) - avgdy; 00759 stdy += ddy * ddy; 00760 } 00761 stdx /= msize; 00762 //stdx = sqrt(stdx); 00763 stdy /= msize; 00764 //stdy = sqrt(stdy); 00765 uint stdcount = 0; 00766 double newAvgX = 0.0,newAvgY = 0.0; 00767 00768 // pick one std error as the new error data 00769 for (uint i = 0; i < msize; i ++) 00770 { 00771 rutz::shared_ptr<Keypoint> refk = 00772 itsMatchList->getKeypointMatch(i).refkp; 00773 rutz::shared_ptr<Keypoint> tstk = 00774 itsMatchList->getKeypointMatch(i).tstkp; 00775 double dx = fabs(tstk->getX() - refk->getX()) ; 00776 double dy = fabs(tstk->getY() - refk->getY()) ; 00777 double ddx = fabs(dx - avgdx); 00778 double ddy = fabs(dy - avgdy); 00779 00780 if(ddx < sqrt(stdx) && ddy < sqrt(stdy)) 00781 { 00782 newAvgX += dx; 00783 newAvgY += dy; 00784 stdcount++; 00785 } 00786 } 00787 if(stdcount != 0) 00788 { 00789 newAvgX/=stdcount; 00790 newAvgY/=stdcount; 00791 } 00792 00793 double stdeverr = sqrt(stdx+stdy); 00794 double avgerr = sqrt(avgdx*avgdx + avgdy*avgdy); 00795 double newAvgerr = sqrt(newAvgX*newAvgX + newAvgY*newAvgY); 00796 double df = DFRAME - (itsTeachImgID - itsReplayImgID ); 00797 double angle = (newAvgX*(56.0/33.0))/df; 00798 //double angle = (newAvgX*(16.0/33.0)); 00799 LINFO("StdError[%f],StdErrX[%f],StdErrY[%f],Frame[%1.0f]", 00800 stdeverr,sqrt(stdx),sqrt(stdy),df); 00801 LINFO("AvgErr[%f],AvgX[%f],AvgY[%f]",avgerr,avgdx,avgdy); 00802 LINFO("StdAvgErr[%f],StdAvgX[%f],StdAvgY[%f]",newAvgerr,newAvgX,newAvgY); 00803 LINFO("Forward[%d], Right[%d], Left[%d] Angle[%f]",dirF,dirR,dirL,angle); 00804 itsError.x = newAvgX; 00805 itsError.y = newAvgY; 00806 itsError.z = newAvgerr; 00807 //FIXXX 00808 if(newAvgerr == 0.0) 00809 { 00810 //Raster::waitForKey(); 00811 newAvgerr = avgerr; 00812 } 00813 00814 itsFalseErrRate = avgerr/newAvgerr; 00815 00816 LINFO("Time for QT Navigation %f",itsTimer.get()/1000.0); 00817 return newAvgerr; 00818 } 00819 00820 // ###################################################################### 00821 void QT_Navigation::updateKeyframe(double error) 00822 { 00823 LINFO("itsMerror %f,new error %f", itsMilestoneErr, error); 00824 00825 // check if the match is valid 00826 if (itsMatchList->checkSIFTaffine()) 00827 { 00828 float theta, sx, sy, str; 00829 itsMatchList->getSIFTaffine().decompose(theta, sx, sy, str); 00830 LINFO("SIFT SCALE SX[%f] SY[%f] Theta[%f] Str[%f]",sx,sy,theta,str); 00831 00832 //Check the frame reach the milestone image or not 00833 //if(itsMilestoneErr > error && (sx <1.0&&sy <1.0)) 00834 // itsMilestoneErr = error; 00835 //else 00836 //{ 00837 //FIXXX: Hack,Single threshold will not work in all environment 00838 if(((sx >=0.9 && sy >=0.9) && error <50.0 ) && itsMatchList->size()>2){ 00839 itsMilestoneErr = 999.99; 00840 itsTeachImgID += 20;//FIXXX when turning sharp,slow down the forward rate 00841 LINFO("*********=========Switch Keyframe=========**********"); 00842 } 00843 //} 00844 } 00845 } 00846 00847 //Given Current image frame,it will try to find proper keyframe for navation 00848 // 00849 // ###################################################################### 00850 int QT_Navigation::computeEntropy() 00851 { 00852 return 1; 00853 } 00854 00855 // ###################################################################### 00856 void QT_Navigation::drawState() 00857 { 00858 uint w = 320, h = 240; 00859 00860 itsDispImg.resize(w*2, 3*h, NO_INIT); 00861 // original image 00862 inplacePaste(itsDispImg, itsProcImg, Point2D<int>(0, 0)); 00863 00864 its_Curr_Img_mutex.lock(); 00865 bool newImageFlag = (itsPrevProcImgID < itsCurrImgID); 00866 its_Curr_Img_mutex.unlock(); 00867 if(!itsRealCamera.getVal() || newImageFlag){ 00868 char buffer[128]; 00869 sprintf(buffer,"img%5d",itsTeachImgID); 00870 Image< PixRGB<byte> > tmpImg = itsTeachImg; 00871 writeText(tmpImg, Point2D<int>(5,5), 00872 buffer, PixRGB<byte>(255,255,255), 00873 PixRGB<byte>(0,0,0), SimpleFont::FIXED(8)); 00874 inplacePaste(itsDispImg, tmpImg, Point2D<int>(0, h)); 00875 00876 //Check the both input image have same size 00877 // if(itsReplayImg.getWidth() == w) 00878 // { 00879 tmpImg = itsReplayImg; 00880 // }else{ 00881 // tmpImg = rescale(itsReplayImg,w,h); 00882 // } 00883 sprintf(buffer,"img%5d",itsReplayImgID); 00884 writeText(itsReplayImg, Point2D<int>(5,5), 00885 buffer, PixRGB<byte>(255,255,255), 00886 PixRGB<byte>(0,0,0),SimpleFont::FIXED(8)); 00887 00888 inplacePaste(itsDispImg, tmpImg, Point2D<int>(0, 2*h)); 00889 inplacePaste(itsDispImg, itsTeachVo->getKeypointImage(1.0) , 00890 Point2D<int>(w, h)); 00891 inplacePaste(itsDispImg, itsReplayVo->getKeypointImage(1.0), 00892 Point2D<int>(w, h*1.5)); 00893 if(itsEstop) 00894 { 00895 Image< PixRGB<byte> > mimg = itsMatchList->getMatchImage(1.0F); 00896 inplacePaste(itsDispImg, mimg, Point2D<int>(w*1.5, h)); 00897 } 00898 double mapScale = 0.20; 00899 Point2D<int> drawPos 00900 (int(itsPosition.x*mapScale + itsMapImg.getWidth()/2), 00901 int(itsPosition.y*mapScale + itsMapImg.getHeight()/2)); 00902 if(itsMapImg.coordsOk(drawPos)) 00903 itsMapImg.setVal(drawPos, PixRGB<byte>(0,255,0)); 00904 inplacePaste(itsDispImg, itsMapImg, Point2D<int>(w, h*2)); 00905 inplacePaste(itsDispImg, drawInfoImg(), Point2D<int>(w, 0)); 00906 } 00907 } 00908 00909 // ###################################################################### 00910 // Only for display current heading 00911 Image<PixRGB<byte> > QT_Navigation::drawInfoImg() 00912 { 00913 char buffer[128]; 00914 Image<PixRGB<byte> > dirImg(320,240,ZEROS); 00915 00916 if(itsDir == 0) 00917 { 00918 sprintf(buffer, "|||"); 00919 } 00920 else if(itsDir > 0) 00921 { 00922 sprintf(buffer, "--> "); 00923 } 00924 else 00925 { 00926 sprintf(buffer, "<-- "); 00927 } 00928 writeText(dirImg, Point2D<int>(dirImg.getWidth()/2-20, 00929 dirImg.getHeight()/2-20), 00930 buffer, PixRGB<byte>(255,255,255), PixRGB<byte>(0,0,0), 00931 SimpleFont::FIXED(20)); 00932 00933 sprintf(buffer, "[%d]",itsDirCount); 00934 writeText(dirImg, Point2D<int>(dirImg.getWidth()/2-10, 00935 dirImg.getHeight()/2+40), 00936 buffer, PixRGB<byte>(255,255,255), PixRGB<byte>(0,0,0), 00937 SimpleFont::FIXED(20)); 00938 00939 sprintf(buffer, "False Error Rate:[%1.3f]",itsFalseErrRate); 00940 writeText(dirImg, Point2D<int>(10,10), 00941 buffer, PixRGB<byte>(255,255,255), PixRGB<byte>(0,0,0), 00942 SimpleFont::FIXED(8)); 00943 if(itsEstop) 00944 { 00945 sprintf(buffer, "Match:[%d]",itsMatchList->size()); 00946 writeText(dirImg, Point2D<int>(210,10), 00947 buffer, PixRGB<byte>(255,255,255), PixRGB<byte>(0,0,0), 00948 SimpleFont::FIXED(8)); 00949 } 00950 sprintf(buffer, "Error:[%1.3f] X:[%1.3f] Y:[%1.3f]", 00951 itsError.z,itsError.x,itsError.y); 00952 writeText(dirImg, Point2D<int>(10,25), 00953 buffer, PixRGB<byte>(255,255,255), PixRGB<byte>(0,0,0), 00954 SimpleFont::FIXED(8)); 00955 00956 // check if the match is valid 00957 if (itsEstop && itsMatchList->checkSIFTaffine()) 00958 { 00959 float theta, sx, sy, str; 00960 itsMatchList->getSIFTaffine().decompose(theta, sx, sy, str); 00961 sprintf(buffer, "Affine T:[%1.3f] X:[%1.3f] Y:[%1.3f]",theta,sx,sy); 00962 writeText(dirImg, Point2D<int>(10,40), 00963 buffer, PixRGB<byte>(255,255,255), PixRGB<byte>(0,0,0), 00964 SimpleFont::FIXED(8)); 00965 } 00966 sprintf(buffer, "Motor Speed :[%1.3f] Motor Rot:[%1.3f]", 00967 itsTransSpeed, itsRotSpeed); 00968 writeText(dirImg, Point2D<int>(10,55), 00969 buffer, PixRGB<byte>(255,255,255), PixRGB<byte>(0,0,0), 00970 SimpleFont::FIXED(8)); 00971 return dirImg; 00972 } 00973 00974 // ###################################################################### 00975 void QT_Navigation::updatePosition(double turn) 00976 { 00977 itsDirVector += turn; 00978 if(itsDirVector > 360.0) itsDirVector = 0.0; 00979 if(itsDirVector < 0.0) itsDirVector = 360.0; 00980 00981 itsPosition.x -= sin(itsDirVector*(M_PI/180.0)); 00982 itsPosition.y -= cos(itsDirVector*(M_PI/180.0)); 00983 } 00984 00985 // ###################################################################### 00986 void QT_Navigation::updateMessage 00987 (const RobotSimEvents::EventMessagePtr& eMsg, const Ice::Current&) 00988 { 00989 // camera message 00990 if(eMsg->ice_isA("::BeobotEvents::CameraMessage")) 00991 { 00992 // store the image 00993 BeobotEvents::CameraMessagePtr cameraMsg = 00994 BeobotEvents::CameraMessagePtr::dynamicCast(eMsg); 00995 00996 int currRequestID = cameraMsg->RequestID; 00997 Image<PixRGB<byte> > img = Ice2Image<PixRGB<byte> >(cameraMsg->image); 00998 00999 LDEBUG("Got a CameraMessage with Request ID = %d", currRequestID); 01000 01001 its_Curr_Img_mutex.lock(); 01002 itsCurrImg = img; 01003 itsCurrImgID = cameraMsg->RequestID; 01004 its_Curr_Img_mutex.unlock(); 01005 } 01006 01007 // motor message 01008 else if(eMsg->ice_isA("::BeobotEvents::MotorMessage")) 01009 { 01010 BeobotEvents::MotorMessagePtr mtrMsg = 01011 BeobotEvents::MotorMessagePtr::dynamicCast(eMsg); 01012 LDEBUG("Got a MotorMessage with Request ID = %d: RC Trans %f, Rot %f", 01013 mtrMsg->RequestID, itsRcTransSpeed, itsRcRotSpeed); 01014 its_Curr_Mtr_mutex.lock(); 01015 itsRemoteMode = mtrMsg->rcMode; 01016 itsRcTransSpeed = mtrMsg->rcTransVel; 01017 itsRcRotSpeed = mtrMsg->rcRotVel; 01018 its_Curr_Mtr_mutex.unlock(); 01019 } 01020 // LINFO("updateMessage"); 01021 } 01022 01023 // ###################################################################### 01024 void QT_Navigation::updateMotorPID(double tran, double rot,double error) 01025 { 01026 error/=100.0; 01027 if(itsDir == 1) 01028 error *= -1.0; 01029 double pTerm,iTerm,dTerm; 01030 pTerm = itsPGain * error; 01031 itsIState += error; 01032 if(itsIState > IMAX) 01033 itsIState = IMAX; 01034 else if(itsIState < IMIN) 01035 itsIState = IMIN; 01036 iTerm = itsIGain * itsIState; 01037 dTerm = itsDGain * (rot - itsDState); 01038 itsDState = rot; 01039 double pid = pTerm + iTerm - dTerm; 01040 01041 LINFO("P[%1.2f] I[%1.2f] D[%1.2f], Istate[%1.2f] DState[%1.2f]", 01042 pTerm,iTerm,dTerm,itsIState,itsDState); 01043 LINFO("pid[%1.2f],rot[%1.2f]",pid,rot); 01044 updateMotor(tran,pid); 01045 } 01046 01047 // ###################################################################### 01048 void QT_Navigation::updateMotor(double tran, double rot) 01049 { 01050 BeobotEvents::MotorRequestPtr msg = new BeobotEvents::MotorRequest; 01051 msg->transVel = tran; 01052 msg->rotVel = rot; 01053 this->publish("MotorRequestTopic", msg); 01054 LINFO("[%d] Publish motor request Trans %f Rotation %f", 01055 itsPrevProcImgID,tran,rot); 01056 } 01057 01058 // ###################################################################### 01059 /* So things look consistent in everyone's emacs... */ 01060 /* Local Variables: */ 01061 /* indent-tabs-mode: nil */ 01062 /* End: */