ND_Navigation.C

00001 /*!@file Robots2/Beobot2/Navigation/ND_Navigation/ND_Navigation.C Ice Module to navigate indoors using LRF    */
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: Christian Siagian <siagian@usc.edu>
00033 // $HeadURL: svn://ilab.usc.edu/trunk/saliency/src/Robots/Beobot2/ND_Navigation.C
00034 // $ $Id: ND_Navigation.C 12962 2010-03-06 02:13:53Z irock $
00035 //
00036 //////////////////////////////////////////////////////////////////////////
00037 
00038 #include "Robots/Beobot2/Navigation/ND_Navigation/ND_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 "Transport/FrameInfo.H"
00046 
00047 #include "Ice/IceImageUtils.H"
00048 
00049 #define  SecurityNearnessRad 1000.0
00050 
00051 // ######################################################################
00052 ND_Navigation::ND_Navigation(OptionManager& mgr,
00053                const std::string& descrName, const std::string& tagName) :
00054   RobotBrainComponent(mgr, descrName, tagName),
00055   itsOfs(new OutputFrameSeries(mgr)),
00056   itsDispImg(512, 512, ZEROS),
00057   itsTimer(1000000)
00058 {
00059   addSubComponent(itsOfs);
00060 
00061   // goal is always in front of the robot FOR NOW
00062   // this should be set by the Localizer at some point
00063   itsGoalSector = 141;
00064 
00065   itsPrevDirection = 141;
00066   itsBigJumpCount = 0;
00067 }
00068 
00069 // ######################################################################
00070 ND_Navigation::~ND_Navigation()
00071 { }
00072 
00073 // ######################################################################
00074 void ND_Navigation::start1()
00075 {
00076 }
00077 
00078 // ######################################################################
00079 void ND_Navigation::registerTopics()
00080 {
00081   // subscribe to all sensor data
00082   this->registerSubscription("LRFMessageTopic");
00083   this->registerPublisher("MotorRequestTopic");
00084 }
00085 
00086 // ######################################################################
00087 void ND_Navigation::evolve()
00088 { }
00089 
00090 // ######################################################################
00091 Beobot2::MotorCommand ND_Navigation::computeND_Navigation()
00092 {
00093   // find gaps and regions in the input Laser Range Finder
00094   findGapsAndRegions();
00095 
00096   // find all movement information
00097   findGoalRegion();
00098 
00099   biasForSafety();
00100 
00101   // can't go too far from previous direction
00102   LINFO("Jump detected: %d %d", itsPrevDirection, itsNextDirection);
00103 
00104   if((abs(int(itsPrevDirection) - int(itsNextDirection)) > 90)
00105      && itsBigJumpCount < 3)
00106     {
00107       LINFO("BIG JUMP\n\n\n");
00108       itsNextDirection = itsPrevDirection;
00109       itsBigJumpCount++;
00110     }
00111   else itsBigJumpCount = 0;
00112 
00113   if(abs(int(itsPrevDirection) - int(itsNextDirection)) > 15)
00114     {
00115       LINFO("JUMP\n\n\n");
00116       if(itsPrevDirection > itsNextDirection)
00117         itsNextDirection = itsPrevDirection - 15;
00118       else itsNextDirection = itsPrevDirection + 15;
00119     }
00120   LINFO("Jump detected: %d %d", itsPrevDirection, itsNextDirection);
00121 
00122   // formulate the motor commands
00123   float frontIndex = 141.0; float maxAngle = 141.0;
00124   float rot = (itsNextDirection - frontIndex)/maxAngle * 2.0;
00125   if(rot >  1.0) rot =  1.0;
00126   if(rot < -1.0) rot = -1.0;
00127   if(itsNextDirection < frontIndex)
00128     {
00129       LINFO("Go RIGHT: %f", rot);
00130     }
00131   else
00132     {
00133       LINFO("Go LEFT : %f", rot);
00134     }
00135 
00136   // draw the situation
00137   drawSituation();
00138 
00139   //Raster::waitForKey();
00140 
00141   // command to be executed by the BeoPilot
00142   Beobot2::MotorCommand cmd;
00143 
00144   cmd.rotation = rot;
00145 
00146   float dist = itsDistances[120];
00147   for(uint i = 121; i < 160; i++)
00148     {
00149       if(dist < itsDistances[i]) dist = itsDistances[i];
00150     }
00151 
00152   if(dist < 600.0)
00153     cmd.translation = 0.0;
00154   else
00155     {
00156       float trans = 1.0 - 1.5 * abs(rot);
00157       if(trans < 0) trans = 0.0;
00158       trans += 0.1;
00159 
00160       if(trans > 1.0) trans = 1.0;
00161       //if(trans < -1.0) trans = -1.0;
00162 
00163       cmd.translation  = trans;
00164     }
00165 
00166   std::string ntext(sformat("T: %10.6f R: %10.6f",
00167                             cmd.translation, cmd.rotation));
00168   writeText(itsDispImg, Point2D<int>(0,400), ntext.c_str());
00169 
00170   itsOfs->writeRGB(itsDispImg, "ND_nav", FrameInfo("ND_nav",SRC_POS));
00171 
00172   itsPrevDirection = itsNextDirection;
00173   return cmd;
00174 }
00175 
00176 // ######################################################################
00177 void ND_Navigation::findGapsAndRegions()
00178 {
00179   itsGaps.clear();
00180   itsRegions.clear();
00181 
00182   uint nSectors = itsDistances.size();
00183   uint prevEnd = 0;
00184 
00185   // threshold for discontinuity
00186   double discThres = 1000.0;
00187 
00188   // find discontinuities (gaps)
00189   for(uint i = 1; i < nSectors; i++)
00190   {
00191     // ignore invalid -1.0 distances
00192     if(itsDistances[i-1] == -1.0 || itsDistances[i] == -1.0)
00193       continue;
00194 
00195     // ignore regions occupied by the killswitches
00196     if(i >=  25 && i <=  36) continue;
00197     if(i >= 243 && i <= 253) continue;
00198 
00199     if(abs(itsDistances[i-1] - itsDistances[i]) > discThres)
00200       {
00201         itsGaps.push_back(i);
00202         RegionInformation r;
00203         r.start = prevEnd;
00204         r.end   = i-1;
00205         itsRegions.push_back(r);
00206         prevEnd = i;
00207       }
00208   }
00209   RegionInformation r;
00210   r.start = prevEnd;
00211   r.end   = nSectors-1;
00212   itsRegions.push_back(r);
00213 
00214   // get the minimum distances
00215   setMinimumDistances();
00216 }
00217 
00218 // ######################################################################
00219 void ND_Navigation::drawSituation()
00220 {
00221   // draw regions
00222   float max = itsDistances[0]; float min = itsDistances[0];
00223   for(uint i = 1; i < itsDistances.size(); i++)
00224     {
00225       if(min > itsDistances[i]) min = itsDistances[i];
00226       if(max < itsDistances[i]) max = itsDistances[i];
00227     }
00228   if (min == max) max += 1.0;
00229   itsMinDistance = min;
00230   itsMaxDistance = max;
00231 
00232   itsDispImg.clear(PixRGB<byte>(0, 0, 0));
00233   for(uint i = 0; i < itsRegions.size(); i++)
00234     {
00235       PixRGB<byte> c((byte)rand()/2, (byte)rand()/2, (byte)rand()/2);
00236 
00237       for(uint j = itsRegions[i].start; j <= itsRegions[i].end; j++)
00238         {
00239           float dist = itsDistances[j];
00240           int angle  = itsAngles[j];
00241 
00242           // note drawing takes up a lot of time
00243           float rad = dist; rad = ((rad - min)/(max-min))*256;
00244           if (rad < 0) rad = 1.0;
00245 
00246           Point2D<int> pt;
00247           pt.i = 256 - int(rad*sin((double)angle*M_PI/180.0));
00248           pt.j = 256 - int(rad*cos((double)angle*M_PI/180.0));
00249 
00250           drawCircle(itsDispImg, pt, 2, PixRGB<byte>(255,0,0));
00251           drawLine(itsDispImg, Point2D<int>(256,256), pt, c);
00252 
00253           LDEBUG("[%4d][%4d] <%4d>: %10.1f mm", i, j, angle, dist);
00254         }
00255     }
00256 
00257   for(uint i = 0; i < itsRegions.size(); i++)
00258     {
00259       float dist = itsDistances[itsRegions[i].minIndex];
00260       int angle  = itsAngles[itsRegions[i].minIndex];
00261       //drawRangeLine(dist, angle, PixRGB<byte>(255,0,0));
00262 
00263       PixRGB<byte> c((byte)rand()/2, (byte)rand()/2, (byte)rand()/2);
00264       dist  = itsDistances[itsRegions[i].start];
00265       angle = itsAngles[itsRegions[i].start];
00266       //drawRangeLine(dist, angle, c);
00267 
00268       dist  = itsDistances[itsRegions[i].end];
00269       angle = itsAngles[itsRegions[i].end];
00270       //drawRangeLine(dist, angle, c);
00271 
00272       uint midInd = (itsRegions[i].start+ itsRegions[i].end) /2;
00273       dist  = itsDistances[midInd] + 1000.0;
00274       angle = itsAngles[midInd];
00275 
00276       // note drawing takes up a lot of time
00277       float rad = dist; rad = ((rad - min)/(max-min))*256;
00278       if (rad < 0) rad = 1.0;
00279       Point2D<int> pt;
00280       pt.i = 256 - int(rad*sin((double)angle*M_PI/180.0));
00281       pt.j = 256 - int(rad*cos((double)angle*M_PI/180.0));
00282 
00283       std::string ntext(sformat("%d", i));
00284       writeText(itsDispImg, pt, ntext.c_str());
00285     }
00286 
00287   // draw the next direction and navigable region boundaries
00288   int angle  = itsAngles[itsNextDirection];
00289   drawRangeLine(itsMaxDistance, angle, PixRGB<byte>(0,0,255));
00290   angle  = itsAngles[itsRegions[itsGoalRegion].start];
00291   drawRangeLine(itsMaxDistance, angle, PixRGB<byte>(255, 255, 0));
00292   angle  = itsAngles[itsRegions[itsGoalRegion].end];
00293   drawRangeLine(itsMaxDistance, angle, PixRGB<byte>(255, 255, 0));
00294 
00295   // draw the security zone
00296   float rad = ((SecurityNearnessRad - itsMinDistance)/
00297                (itsMaxDistance - itsMinDistance))*256;
00298   drawCircle(itsDispImg, Point2D<int>(256, 256) , rad,
00299              PixRGB<byte>(255,255,0));
00300   //LINFO("radius: %f", rad);
00301 
00302   // draw the hirzontal white line denoting -90 to 90 range
00303   drawLine(itsDispImg, Point2D<int>(256,0), Point2D<int>(256,512),
00304            PixRGB<byte>(255,255,255));
00305   drawLine(itsDispImg, Point2D<int>(0,256), Point2D<int>(512,256),
00306            PixRGB<byte>(255,255,255));
00307 
00308   drawRangeLine(itsMaxDistance, itsAngles[itsLeftEnd], PixRGB<byte>(255,0,0));
00309   drawRangeLine(itsMaxDistance, itsAngles[itsRightEnd], PixRGB<byte>(255,0,0));
00310 }
00311 
00312 // ######################################################################
00313 void ND_Navigation::drawRangeLine(float dist, int angle, PixRGB<byte> c)
00314 {
00315   float min = itsMinDistance;
00316   float max = itsMaxDistance;
00317   // note drawing takes up a lot of time
00318   float rad = ((dist - min)/(max-min))*256;
00319   if (rad < 0) rad = 1.0;
00320 
00321   Point2D<int> pt;
00322   pt.i = 256 - int(rad*sin((double)angle*M_PI/180.0));
00323   pt.j = 256 - int(rad*cos((double)angle*M_PI/180.0));
00324 
00325   drawLine(itsDispImg, Point2D<int>(256,256), pt, c);
00326 }
00327 
00328 
00329 // ######################################################################
00330 void ND_Navigation::setMinimumDistances()
00331 {
00332   for(uint i = 0; i < itsRegions.size(); i++)
00333     {
00334       float minDist = itsDistances[itsRegions[i].start];
00335       uint  minIndex = itsRegions[i].start;
00336       for(uint j = itsRegions[i].start +1; j <= itsRegions[i].end; j++)
00337         {
00338           // ignore invalid -1.0 distances
00339           if(itsDistances[j] == -1.0) continue;
00340 
00341           // ignore regions occupied by the killswitches
00342           if(j >=  25 && j <=  36) continue;
00343           if(j >= 243 && j <= 253) continue;
00344 
00345 
00346           if(minDist > itsDistances[j])
00347             {
00348               minDist  = itsDistances[j];
00349               minIndex = j;
00350             }
00351         }
00352 
00353       itsRegions[i].minDistance = minDist;
00354       itsRegions[i].minIndex    = minIndex;
00355       LINFO("[%4d]{%4d, %4d}  minDist: %f",
00356             itsRegions[i].minIndex,
00357             itsRegions[i].start, itsRegions[i].end,
00358             itsRegions[i].minDistance);
00359     }
00360 }
00361 
00362 // ######################################################################
00363 void ND_Navigation::findGoalRegion()
00364 {
00365   // find the Free Walking Area:
00366   //   the closest region to the goal location
00367 
00368   // check the goal region first
00369   uint goalRegion = 0;
00370   for(uint i = 0; i < itsRegions.size(); i++)
00371     {
00372       LINFO("[%d][%d %d]",
00373             i, itsRegions[i].start,itsRegions[i].end);
00374       if(itsRegions[i].start <= itsGoalSector &&
00375          itsRegions[i].end   >= itsGoalSector)
00376         {
00377           goalRegion = i;
00378           LINFO("goal: %d", i);
00379         }
00380     }
00381 
00382   LINFO("initial goal region: %d", goalRegion);
00383   if(isNavigableRegion(goalRegion))
00384       LINFO("just go straight: %d", goalRegion);
00385   else
00386     {
00387       int cRegion = goalRegion + 1;
00388       while(cRegion < int(itsRegions.size()) &&
00389             !isNavigableRegion(cRegion))
00390         {
00391           cRegion++;
00392         }
00393 
00394       // trouble no space in left
00395       if(cRegion == int(itsRegions.size()))
00396         {
00397           cRegion = goalRegion - 1;
00398           while(cRegion >= 0  &&
00399                 !isNavigableRegion(cRegion))
00400             {
00401               cRegion--;
00402             }
00403 
00404           if(cRegion == -1)
00405             {
00406               // use goal region until something good happen
00407               LINFO("get lucky");
00408             }
00409           else
00410             {
00411               goalRegion = cRegion;
00412               LINFO("to the right: %d", goalRegion);
00413             }
00414         }
00415       else
00416         {
00417           goalRegion = cRegion;
00418           LINFO("to the left: %d", goalRegion);
00419         }
00420     }
00421 
00422   itsGoalRegion = goalRegion;
00423   uint start = itsRegions[goalRegion].start;
00424   uint end   = itsRegions[goalRegion].end;
00425 
00426   float total = 0.0;
00427   for(uint i = start; i <= end; i++)
00428     total += itsDistances[i];
00429   total /= 2.0;
00430 
00431   uint index = start;
00432   while(index <= end && total > 0.0)
00433     {
00434       total -= itsDistances[index];
00435       index++;
00436     }
00437   itsNextDirection = index;
00438 
00439 
00440   //itsNextDirection =
00441   //  (itsRegions[goalRegion].start + itsRegions[goalRegion].end)/2;
00442 
00443   LINFO("Next dir: %d ", itsNextDirection);
00444 
00445 }
00446 
00447 
00448 // ######################################################################
00449 void ND_Navigation::biasForSafety()
00450 {
00451   uint biasRange = 60;
00452   // check for danger to the Right of itsNextDirection
00453   if(itsNextDirection > biasRange)
00454     itsRightEnd = itsNextDirection - biasRange;
00455   else itsRightEnd = 0;
00456 
00457   // check for danger to the Left of itsNextDirection
00458   if(itsNextDirection < itsAngles.size() - biasRange)
00459     itsLeftEnd = itsNextDirection + biasRange;
00460   else itsLeftEnd = itsAngles.size() - 1;
00461 
00462   // cut off everythings behind the robot
00463   // only consider front 180 degrees
00464   if(itsRightEnd < 50) itsRightEnd =  50;
00465   if(itsLeftEnd > 230) itsLeftEnd  = 230;
00466 
00467   if(itsNextDirection <  50) itsNextDirection =  50;
00468   if(itsNextDirection > 230) itsNextDirection = 230;
00469 
00470   if(itsRightEnd > itsLeftEnd) return;
00471 
00472   float rightDist = SecurityNearnessRad;
00473   int rightIndex = -1;
00474   for(uint i = itsRightEnd; i < itsNextDirection; i++)
00475     {
00476       // ignore invalid -1.0 distances
00477       if(itsDistances[i] == -1.0) continue;
00478 
00479       // ignore regions occupied by the killswitches
00480       if(i >=  25 && i <=  36) continue;
00481       if(i >= 243 && i <= 253) continue;
00482 
00483       if(itsDistances[i] < SecurityNearnessRad)
00484         {
00485           rightDist  = itsDistances[i];
00486           rightIndex = i;
00487         }
00488     }
00489 
00490   float leftDist = SecurityNearnessRad;
00491   int leftIndex = -1;
00492   for(uint i = itsNextDirection; i < itsLeftEnd; i++)
00493     {
00494       // ignore invalid -1.0 distances
00495       if(itsDistances[i] == -1.0) continue;
00496 
00497       // ignore regions occupied by the killswitches
00498       if(i >=  25 && i <=  36) continue;
00499       if(i >= 243 && i <= 253) continue;
00500 
00501       if(itsDistances[i] < SecurityNearnessRad)
00502         {
00503           leftDist  = itsDistances[i];
00504           leftIndex = i;
00505         }
00506     }
00507 
00508   LINFO("safety [%f %f]",
00509         itsAngles[itsRightEnd], itsAngles[itsLeftEnd]);
00510 
00511   // no obstacle
00512   if(rightIndex == -1 && leftIndex == -1)
00513     {
00514       LINFO("good to go");
00515       return;
00516     }
00517 
00518   // right obstacle, bias left
00519   if(rightIndex != -1 && leftIndex == -1)
00520     {
00521       itsNextDirection = itsNextDirection + 30;
00522       if(itsNextDirection >= itsAngles.size())
00523         itsNextDirection = itsAngles.size() - 1;
00524 
00525       LINFO("right obstacle: [%d] %f: -> %d",
00526             rightIndex, rightDist, itsNextDirection);
00527       return;
00528     }
00529 
00530   // left obstacle, bias right
00531   if(rightIndex == -1 && leftIndex != -1)
00532     {
00533       if(itsNextDirection < 30)
00534         itsNextDirection = 0;
00535       else
00536         itsNextDirection -= 30;
00537 
00538       LINFO("left obstacle: [%d] %f -> %d",
00539             leftIndex, leftDist, itsNextDirection);
00540       return;
00541     }
00542 
00543   // obstacles on both
00544   if(rightIndex != -1 && leftIndex != -1)
00545     {
00546       int bias = int((leftDist - rightDist)/SecurityNearnessRad * 10.0);
00547       if (bias > 10.0)  bias = 10.0;
00548       if (bias < -10.0) bias = -10.0;
00549 
00550       int temp = itsNextDirection + bias;
00551       if(temp < 0) temp = 0;
00552       if(temp >= int(itsAngles.size())) temp = itsAngles.size() - 1;
00553 
00554       LINFO("hopefully nothing bad happens: %d -> %d", itsNextDirection, temp);
00555       itsNextDirection = temp;
00556 
00557       return;
00558     }
00559   return;
00560 }
00561 
00562 // ######################################################################
00563 bool ND_Navigation::isNavigableRegion(uint index)
00564 {
00565   bool extreme = false;
00566   uint size = itsRegions[index].end - itsRegions[index].start;
00567   uint count = 0;
00568   for(uint i = itsRegions[index].start; i <= itsRegions[index].end; i++)
00569     {
00570       if(i > 230 || i < 50) count++;
00571     }
00572   if(count > size/2) extreme = true;
00573 
00574   uint nSector  = itsRegions[index].end - itsRegions[index].start;
00575   float minDist = itsRegions[index].minDistance;
00576 
00577   bool isNav = (((nSector >= 6 && minDist > 1250.0) ||
00578                 ((nSector >= 4 && nSector <= 5) && minDist >= 2000.0))
00579                 && !extreme);
00580 
00581   if(!isNav) LINFO("not Nav: %d %d   %f extreme: %d", index,
00582                    nSector, minDist, extreme);
00583 
00584   return isNav;
00585 }
00586 
00587 // ######################################################################
00588 void ND_Navigation::updateMessage
00589 (const RobotSimEvents::EventMessagePtr& eMsg, const Ice::Current&)
00590 {
00591   // LRF message
00592   if(eMsg->ice_isA("::BeobotEvents::LRFMessage"))
00593   {
00594     // we got LRF data
00595     BeobotEvents::LRFMessagePtr lrfMsg =
00596       BeobotEvents::LRFMessagePtr::dynamicCast(eMsg);
00597 
00598     itsDistances = lrfMsg->distances;
00599     itsAngles    = lrfMsg->angles;
00600 
00601     // compute navigation
00602     Beobot2::MotorCommand cmd = computeND_Navigation();
00603 
00604     // send to BeoPilot
00605     updateMotor(cmd.translation,cmd.rotation);
00606   }
00607 }
00608 
00609 // ######################################################################
00610 void ND_Navigation::updateMotor(double tran,double rot)
00611 {
00612     BeobotEvents::MotorRequestPtr msg = new BeobotEvents::MotorRequest;
00613     msg->transVel = tran;
00614     msg->rotVel   = rot;
00615     this->publish("MotorRequestTopic", msg);
00616 
00617 }
00618 // ######################################################################
00619 
00620 /* So things look consistent in everyone's emacs... */
00621 /* Local Variables: */
00622 /* indent-tabs-mode: nil */
00623 /* End: */
Generated on Sun May 8 08:05:37 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3