Main Page | Class Hierarchy | Alphabetical List | Class List | File List | Class Members | Related Pages | Examples

ArLineFinder.cpp

00001 /*
00002 MobileRobots Advanced Robotics Interface for Applications (ARIA)
00003 Copyright (C) 2004, 2005 ActivMedia Robotics LLC
00004 Copyright (C) 2006, 2007 MobileRobots Inc.
00005 
00006      This program is free software; you can redistribute it and/or modify
00007      it under the terms of the GNU General Public License as published by
00008      the Free Software Foundation; either version 2 of the License, or
00009      (at your option) any later version.
00010 
00011      This program is distributed in the hope that it will be useful,
00012      but WITHOUT ANY WARRANTY; without even the implied warranty of
00013      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014      GNU General Public License for more details.
00015 
00016      You should have received a copy of the GNU General Public License
00017      along with this program; if not, write to the Free Software
00018      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019 
00020 If you wish to redistribute ARIA under different terms, contact 
00021 MobileRobots for information about a commercial version of ARIA at 
00022 robots@mobilerobots.com or 
00023 MobileRobots Inc, 19 Columbia Drive, Amherst, NH 03031; 800-639-9481
00024 */
00025 
00026 #include "ArExport.h"
00027 
00028 #include "ariaOSDef.h"
00029 #include "ArLineFinder.h"
00030 
00031 AREXPORT ArLineFinder::ArLineFinder(ArRangeDevice *rangeDevice) 
00032 {
00033   myRangeDevice = rangeDevice;
00034   myPrinting = false;
00035   myPoints = NULL;
00036   myLines = NULL;  
00037   myFlippedFound = false;
00038 
00039   mySinMultiplier = (ArMath::sin(1) / ArMath::sin(5));
00040 
00041   setLineCreationParams();
00042   setLineCombiningParams();
00043   setLineFilteringParams();
00044   setLineValidParams();
00045   setMaxDistBetweenPoints();
00046 }
00047 
00048 AREXPORT ArLineFinder::~ArLineFinder()
00049 {
00050 
00051 }
00052 
00053 AREXPORT std::map<int, ArLineFinderSegment *> *ArLineFinder::getLines(void)
00054 {
00055   // fill the laser readings into myPoints
00056   fillPointsFromLaser();
00057   // make lines out of myPoints into myLines
00058   findLines();
00059   // put the lines from myLines into combined lines
00060   // this will recurse itself until there are no more
00061   combineLines();
00062   // now filter out the short lines
00063   filterLines();
00064   // combines the lines again
00065   combineLines();
00066   return myLines;
00067 }
00068 
00069 
00070 AREXPORT void ArLineFinder::fillPointsFromLaser(void)
00071 {
00072   const std::list<ArSensorReading *> *readings;
00073   std::list<ArSensorReading *>::const_iterator it;
00074   std::list<ArSensorReading *>::const_reverse_iterator rit;
00075   ArSensorReading *reading;
00076   int pointCount = 0;
00077 
00078   if (myPoints != NULL)
00079     delete myPoints;
00080 
00081   myPoints = new std::map<int, ArPose>;
00082   
00083   myRangeDevice->lockDevice();
00084   readings = myRangeDevice->getRawReadings();
00085 
00086   if (!myFlippedFound)
00087   {
00088     if (readings->begin() != readings->end())
00089     {
00090       int size;
00091       size = readings->size();
00092       it = readings->begin();
00093       // advance along 10 readings
00094       for (int i = 0; i < 10 && i < size / 2; i++)
00095     it++;
00096       // see if we're flipped
00097       if (ArMath::subAngle((*(readings->begin()))->getSensorTh(), 
00098                (*it)->getSensorTh()) > 0)
00099     myFlipped = true;
00100       else
00101     myFlipped = false;
00102       myFlippedFound = true;
00103       //printf("@@@ LINE %d %.0f\n", myFlipped, ArMath::subAngle((*(readings->begin()))->getSensorTh(), (*it)->getSensorTh()));
00104 
00105       
00106     }
00107   }
00108 
00109 
00110 
00111 
00112   if (readings->begin() == readings->end())
00113   {
00114     myRangeDevice->unlockDevice();
00115     return;
00116   }
00117   myPoseTaken = (*readings->begin())->getPoseTaken();
00118 
00119   if (myFlipped)
00120   {
00121     for (rit = readings->rbegin(); rit != readings->rend(); rit++)
00122     {
00123       reading = (*rit);
00124       if (reading->getRange() > 5000)
00125     continue;
00126       (*myPoints)[pointCount] = reading->getPose();
00127       pointCount++;
00128     }
00129   }
00130   else
00131   {
00132     for (it = readings->begin(); it != readings->end(); it++)
00133     {
00134       reading = (*it);
00135       if (reading->getRange() > 5000)
00136     continue;
00137       (*myPoints)[pointCount] = reading->getPose();
00138       pointCount++;
00139     }
00140   }
00141   myRangeDevice->unlockDevice();
00142 }
00143 
00144 AREXPORT void ArLineFinder::findLines(void)
00145 {
00146   int start = 0;
00147   int pointsLen = myPoints->size();
00148   int end;
00149 
00150   if (myLines != NULL)
00151   {
00152     ArUtil::deleteSetPairs(myLines->begin(), myLines->end());
00153     delete myLines;
00154     myLines = NULL;
00155   }
00156   myLines = new std::map<int, ArLineFinderSegment *>;
00157   int numLines = 0;
00158 
00159   FILE *lineFile = NULL;
00160   /*
00161   if ((lineFile = fopen("firstLines", "w+")) == NULL)
00162   {
00163     printf("Couldn't open 'lines' for writing\n");
00164     return;
00165   }
00166   */
00167   ArLineFinderSegment *newLine;
00168   double totalDistFromLine = 0;
00169   double dist;
00170   int i;
00171   bool maxDistTriggered;
00172 
00173   while (1)
00174   {
00175     maxDistTriggered = false;
00176     // first we try to find the first place we'll check for lines
00177     // move out from the start as far as we should for the first one
00178     for (end = start; ; end++)
00179     {
00180       // if we hit the end stop
00181       if (end >= pointsLen)
00182     break;
00183       // if we've moved at least two spots AND at least 50 mm then go
00184       if (end - start >= myMakingMinPoints && 
00185   (*myPoints)[start].findDistanceTo((*myPoints)[end]) > myMakingMinLen)
00186     break;
00187       // if the distance between any of the points is too great than
00188       // break (to try and get rid of spots where a laser spot half
00189       // way between things hurts us)
00190       if (myMaxDistBetweenPoints > 0 && end > start && 
00191       ((*myPoints)[end-1].findDistanceTo((*myPoints)[end]) > 
00192        myMaxDistBetweenPoints))
00193       {
00194     maxDistTriggered = true;
00195     break;
00196       }
00197     } 
00198     if (end < pointsLen)
00199     {
00200       // if the distance between any of the points is too great don't
00201       // make a line out of it (to try and get rid of spots where a
00202       // laser spot half way between things hurts us)
00203       if (maxDistTriggered)
00204       {
00205     if (myPrinting)
00206       ArLog::log(ArLog::Normal, "too great a distance between some points on the line %d %d", start, end);
00207       }
00208       // see if its too far between these line segments
00209       else if ((*myPoints)[start].findDistanceTo((*myPoints)[end]) <
00210            ((*myPoints)[start].findDistanceTo(myPoseTaken) * mySinMultiplier))
00211       {
00212     if (lineFile != NULL)
00213       fprintf(lineFile, "%.0f %.0f %.0f %.0f\n",
00214           (*myPoints)[start].getX(), (*myPoints)[start].getY(),
00215           (*myPoints)[end].getX() - (*myPoints)[start].getX(),
00216           (*myPoints)[end].getY() - (*myPoints)[start].getY());
00217     
00218     newLine = new ArLineFinderSegment(
00219         (*myPoints)[start].getX(), 
00220         (*myPoints)[start].getY(),
00221         (*myPoints)[end].getX(),
00222         (*myPoints)[end].getY(), 
00223         1, start, end);
00224     
00225     totalDistFromLine = 0;
00226     // Make sure none of the points are too far away from the new line
00227     for (i = newLine->getStartPoint(); i <= newLine->getEndPoint(); i++)
00228     {
00229       dist = newLine->getDistToLine((*myPoints)[i]);
00230       totalDistFromLine += dist;
00231     }
00232     newLine->setAveDistFromLine(totalDistFromLine / (end - start));
00233     
00234     (*myLines)[numLines] = newLine;
00235     numLines++;
00236       }
00237       else
00238       {
00239     if (myPrinting)
00240       ArLog::log(ArLog::Normal, "too great a distance between the two line points %d %d", start, end);
00241       }
00242     }
00243     
00244     start += 1;
00245     if (start >= pointsLen)
00246       break;
00247   }
00248   
00249   if (lineFile != NULL)
00250     fclose(lineFile);
00251 }
00252 
00253 AREXPORT bool ArLineFinder::combineLines(void)
00254 {
00255   int start = 0;
00256   int len = myLines->size();
00257   // this is the min line distance
00258   std::map<int, ArLineFinderSegment *> *newLines;
00259   int numNewLines = 0;
00260   int numNewMerges = 0;
00261   ArLineFinderSegment *newLine;
00262   
00263   newLines = new std::map<int, ArLineFinderSegment *>;
00264 
00265   if (myPrinting)
00266     ArLog::log(ArLog::Normal, "new iteration\n");
00267   
00268   bool nextMerged = false;
00269   for (start = 0; start < len; start++)
00270   {
00271     if (nextMerged)
00272     {
00273       nextMerged = false;
00274       continue;
00275     }
00276 
00277     if (start + 1 == len)
00278     {
00279       if (myPrinting)
00280     ArLog::log(ArLog::Normal, "inserted last one %g",
00281          ArPose((*myLines)[start]->getX1(), 
00282             (*myLines)[start]->getY1()).findDistanceTo(
00283           ArPose((*myLines)[start]->getX2(), (*myLines)[start]->getY2())));
00284       (*newLines)[numNewLines] = new ArLineFinderSegment(*((*myLines)[start]));
00285       numNewLines++;
00286       continue;
00287     }
00288 
00289     newLine = averageSegments((*myLines)[start], (*myLines)[start+1]);
00290     if (newLine != NULL)
00291     {
00292       
00293       if (myPrinting)
00294     ArLog::log(ArLog::Normal, "merged %g %g to %g", 
00295            (*myLines)[start]->getLength(),
00296            (*myLines)[start+1]->getLength(),
00297            newLine->getLength());
00298       (*newLines)[numNewLines] = newLine;
00299       numNewLines++;
00300       numNewMerges++;
00301       nextMerged = true;
00302     }
00303     else
00304     {
00305       if (myPrinting)
00306     ArLog::log(ArLog::Normal, "inserted anyways %g", 
00307            (*myLines)[start]->getLength());
00308       (*newLines)[numNewLines] = new ArLineFinderSegment(*((*myLines)[start]));
00309       numNewLines++;
00310     }
00311     
00312   }
00313 
00314   // move the new lines over and delete the old ones
00315   if (myLines != NULL && myLines->begin() != myLines->end())
00316   {
00317     ArUtil::deleteSetPairs(myLines->begin(), myLines->end());
00318     delete myLines;
00319     myLines = NULL;
00320   } 
00321   else if (myLines != NULL)
00322   {  
00323     delete myLines;
00324     myLines = NULL;
00325   }
00326   myLines = newLines;
00327   // if we didn't merge any just return
00328   if (numNewMerges == 0)
00329     return true;
00330   
00331   // otherwise do it again
00332   return combineLines();
00333 }
00334 
00335 AREXPORT ArLineFinderSegment *ArLineFinder::averageSegments(
00336     ArLineFinderSegment *line1,
00337     ArLineFinderSegment *line2)
00338 {
00339 
00340   // the angles can be myCombiningAngleTol diff but if its more than myCombiningAngleTol / 2
00341   // then the resulting line angle should be between the other two
00342   if (myPrinting)
00343     ArLog::log(ArLog::Normal,
00344            "%3.0f %5.0f    %3.0f %3.0f      (%5.0f %5.0f) <%d %d> (%5.0f %5.0f) <%d %d>", 
00345        ArMath::subAngle(line1->getLineAngle(),
00346                 line2->getLineAngle()),
00347            line1->getEndPoint2().findDistanceTo(line2->getEndPoint1()),
00348            line1->getLineAngle(), line2->getLineAngle(),
00349            line1->getX2(), line1->getY2(),
00350            line1->getStartPoint(), line1->getEndPoint(),
00351            line2->getX1(), line2->getY1(),
00352            line2->getStartPoint(), line2->getEndPoint());
00353 
00354   if (myMaxDistBetweenPoints > 0 && 
00355       (line1->getEndPoint2().findDistanceTo(line2->getEndPoint1()) > 
00356        myMaxDistBetweenPoints))
00357   {
00358     if (myPrinting)
00359       ArLog::log(ArLog::Normal, 
00360          "distance between the two line end points greater than maxDistBetweenPoints");
00361     return NULL;
00362   }
00363 
00364   // see if its too far between these line segments
00365   if (line1->getEndPoint2().findDistanceTo(line2->getEndPoint1()) >
00366       line1->getEndPoint2().findDistanceTo(myPoseTaken) * mySinMultiplier)
00367   {
00368     if (myPrinting)
00369       ArLog::log(ArLog::Normal, 
00370          "too great a distance between the two line points");
00371     return NULL;
00372   }
00373   // make sure they're pointing in the same direction at least
00374   double angleOff;
00375   if ((angleOff = ArMath::fabs(
00376       ArMath::subAngle(line1->getLineAngle(),
00377                line2->getLineAngle()))) > myCombiningAngleTol)
00378   {
00379     if (myPrinting)
00380       ArLog::log(ArLog::Normal, "greater than angle tolerance");
00381     return NULL;    
00382   }
00383 
00384   ArPose endPose2(line2->getX2(), line2->getY2());
00385   ArPose intersection1;
00386   ArLine line1Line(*(line1->getLine()));
00387   ArLine perpLine1;
00388 
00389   // make sure that the lines are close to each other
00390   line1Line.makeLinePerp(&endPose2, &perpLine1);
00391   if (!line1Line.intersects(&perpLine1, &intersection1) ||
00392       intersection1.findDistanceTo(endPose2) > myCombiningLinesCloseEnough)
00393   {
00394     //printf("e1 %d %.0f\n", line1Line.intersects(&perpLine1, &intersection1), intersection1.findDistanceTo(endPose2));
00395     
00396     if (myPrinting)
00397       ArLog::log(ArLog::Normal, "endPose2 too far from line1");
00398     return NULL;
00399   }
00400 
00401   ArPose endPose1(line1->getX1(), line1->getY1());
00402   ArPose intersection2;
00403   ArLine line2Line(*(line2->getLine()));
00404   ArLine perpLine2;
00405 
00406 
00407   // make sure that the lines are close to each other
00408   line2Line.makeLinePerp(&endPose1, &perpLine2);
00409   if (!line2Line.intersects(&perpLine2, &intersection2) ||
00410       intersection2.findDistanceTo(endPose1) > myCombiningLinesCloseEnough)
00411   {
00412     //printf("e2 %d %.0f\n", line2Line.intersects(&perpLine2, &intersection2),     intersection2.findDistanceTo(endPose1));
00413     if (myPrinting)
00414       ArLog::log(ArLog::Normal, "endPose1 too far from line2");
00415     return NULL;
00416   }
00417 
00418 
00419 
00420   ArLineFinderSegment *newLine;
00421   /*
00422   newLine = new ArLineFinderSegment((endPose1.getX() + intersection2.getX()) / 2,
00423                   (endPose1.getY() + intersection2.getY()) / 2,
00424                   (endPose2.getX() + intersection1.getX()) / 2,
00425                   (endPose2.getY() + intersection1.getY()) / 2,
00426                   line1->getCounter() + line2->getCounter());
00427   */
00428   // make the new line so that it averages the position based on how
00429   // many points are in each line
00430   int l1C = line1->getNumPoints();
00431   int l2C = line2->getNumPoints();
00432   newLine = new ArLineFinderSegment((endPose1.getX() * l1C +
00433                      intersection2.getX() * l2C) / (l1C + l2C),
00434                     (endPose1.getY() * l1C + 
00435                      intersection2.getY() * l2C) / (l1C + l2C),
00436                     (endPose2.getX() * l2C +
00437                      intersection1.getX() * l1C) / (l1C + l2C),
00438                     (endPose2.getY() * l2C +
00439                      intersection1.getY() * l1C) / (l1C + l2C),
00440                     (line1->getNumPoints() + 
00441                      line2->getNumPoints()),
00442                     line1->getStartPoint(), 
00443                     line2->getEndPoint());
00444 
00445   //printf("%d %d\n", newLine->getStartPoint(), newLine->getEndPoint());
00446   double totalDistFromLine = 0;
00447   double dist;
00448   int i;
00449   // Make sure none of the points are too far away from the new line
00450   for (i = newLine->getStartPoint(); i <= newLine->getEndPoint(); i++)
00451   {
00452     if ((dist = newLine->getDistToLine((*myPoints)[i])) > 
00453     myValidMaxDistFromLine && 
00454     i != newLine->getStartPoint() &&
00455     i != newLine->getEndPoint())
00456     {
00457       if (myPrinting)
00458     ArLog::log(ArLog::Normal, 
00459            "Had a point %d that was to far from our line at %.0f (max %d)",
00460            i, dist, myValidMaxDistFromLine);
00461 
00462       delete newLine;
00463       return NULL;
00464     }
00465     //printf("d %.0f\n", dist);
00466     totalDistFromLine += dist;
00467   }
00468   newLine->setAveDistFromLine(totalDistFromLine / (newLine->getEndPoint() - newLine->getStartPoint()));
00469 
00470   //printf("ave dist %.3f\n", newLine->getAveDistFromLine());
00471   if (newLine->getAveDistFromLine() > myValidMaxAveFromLine)
00472   {
00473     if (myPrinting)
00474       ArLog::log(ArLog::Normal, 
00475          "Ave dist from line was too great at %.0f (max %d)",
00476          newLine->getAveDistFromLine(), myValidMaxDistFromLine);
00477     
00478     delete newLine;
00479     return NULL;
00480   }
00481   if (newLine->getAveDistFromLine() > (line1->getAveDistFromLine() + 
00482                        line2->getAveDistFromLine()) * 1.25)
00483   {
00484     if (myPrinting)
00485       ArLog::log(ArLog::Normal, 
00486          "Ave dist from line greater than component lines at %.0f (component lines %.0f %.0f)",
00487          newLine->getAveDistFromLine(), 
00488          line1->getAveDistFromLine(), 
00489          line2->getAveDistFromLine());
00490     
00491     delete newLine;
00492     return NULL;
00493 
00494   }
00495   // if we're in myCombiningAngleTol / 2 then its close enough
00496   if (angleOff < myCombiningAngleTol / 2)
00497     return newLine;
00498 
00499   // if the new angle is in between the two lines and within myCombiningAngleTol we're ok
00500   if ((ArMath::subAngle(newLine->getLineAngle(), line2->getLineAngle()) > 0 &&
00501        ArMath::subAngle(line1->getLineAngle(), newLine->getLineAngle()) > 0) ||
00502       (ArMath::subAngle(newLine->getLineAngle(), line1->getLineAngle()) > 0 &&
00503        ArMath::subAngle(line2->getLineAngle(), newLine->getLineAngle()) > 0))
00504     return newLine;
00505   
00506   //printf("%g\n", newLine->getLineAngle());
00507   if (myPrinting)
00508     ArLog::log(ArLog::Normal, "angles wonky");
00509   // if we got down here hte line didn't work
00510   delete newLine;
00511   return NULL; 
00512 }
00513 
00514 AREXPORT void ArLineFinder::filterLines(void)
00515 {
00516   int start = 0;
00517   int len = myLines->size();
00518 
00519   // this is the min line distance
00520   std::map<int, ArLineFinderSegment *> *newLines;
00521   int numNewLines = 0;
00522 
00523   newLines = new std::map<int, ArLineFinderSegment *>;
00524 
00525   if (myPrinting)
00526     ArLog::log(ArLog::Normal, "filtering lines\n");
00527   
00528   for (start = 0; start < len; start++)
00529   {
00530     if ((*myLines)[start]->getNumPoints() >= myFilteringMinPointsInLine &&
00531     (*myLines)[start]->getEndPoint1().findDistanceTo(
00532         (*myLines)[start]->getEndPoint2()) > myFilteringMinLineLength)
00533     {
00534       if (myPrinting)
00535     ArLog::log(ArLog::Normal, "kept %g (%d points)", 
00536            (*myLines)[start]->getLength(),
00537            (*myLines)[start]->getNumPoints());
00538       (*newLines)[numNewLines] = new ArLineFinderSegment(*((*myLines)[start]));
00539       numNewLines++;
00540     }
00541     else
00542     {
00543       if (myPrinting)
00544     ArLog::log(ArLog::Normal, "Clipped %g (%d points)",
00545            (*myLines)[start]->getLength(),
00546            (*myLines)[start]->getNumPoints());
00547     }
00548     
00549   }
00550 
00551   // move the new lines over and delete the old ones
00552   if (myLines != NULL && myLines->begin() != myLines->end())
00553   {
00554     ArUtil::deleteSetPairs(myLines->begin(), myLines->end());
00555     delete myLines;
00556     myLines = NULL;
00557   } 
00558   else if (myLines != NULL)
00559   {  
00560     delete myLines;
00561     myLines = NULL;
00562   }
00563   myLines = newLines;
00564 
00565 }
00566 
00567 
00568 
00574 AREXPORT void ArLineFinder::saveLast(void)
00575 {
00576   int len = myPoints->size();
00577   int i;
00578   
00579   FILE *points;
00580   if ((points = fopen("points", "w+")) == NULL)
00581   {
00582     ArLog::log(ArLog::Terse, "ArLineFinder::log: Could not open 'points' file for output");
00583     return;
00584   }
00585   for (i = 0; i < len; i++)
00586   {
00587     fprintf(points, "%.0f %.0f\n", 
00588         (*myPoints)[i].getX(), (*myPoints)[i].getY());
00589   }
00590   fclose(points);
00591 
00592 
00593   len = myLines->size();
00594   
00595   FILE *lines;
00596   if ((lines = fopen("lines", "w+")) == NULL)
00597   {
00598     ArLog::log(ArLog::Terse, "ArLineFinder::log: Could not open 'lines' file for output");
00599     return;
00600   }
00601   for (i = 0; i < len; i++)
00602   {
00603     fprintf(lines, "%.0f %.0f %.0f %.0f\n", 
00604         (*myLines)[i]->getX1(), (*myLines)[i]->getY1(),
00605         (*myLines)[i]->getX2() - (*myLines)[i]->getX1(),
00606         (*myLines)[i]->getY2() - (*myLines)[i]->getY1());       
00607   }
00608   fclose(lines);
00609 
00610   ArLog::log(ArLog::Normal, "Saved points and lines");
00611 }
00612 
00613 AREXPORT void ArLineFinder::getLinesAndSaveThem(void)
00614 {
00615   getLines();
00616   saveLast();
00617 }
00618 

Generated on Tue Feb 20 10:51:40 2007 for Aria by  doxygen 1.4.0