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