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

ArLineFinder.cpp

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

Generated on Wed Oct 19 12:56:36 2005 for Aria by  doxygen 1.4.0