Homepage Demos Overview Downloads Tutorials Reference
Credits

ParticleFilter.cc

Go to the documentation of this file.
00001 //-*-c++-*-
00002 
00003 #include <iostream>
00004 #include <vector>
00005 
00006 #include <math.h>
00007 
00008 #include "BaseData.h" // for BoundingBox
00009 #include "Particle.h"
00010 #include "ParticleShapes.h"
00011 #include "ParticleFilter.h"
00012 
00013 namespace DualCoding {
00014 
00015 const float ParticleFilter::INITIAL_XY_NOISE;
00016 const float ParticleFilter::INITIAL_THETA_NOISE;
00017 const float ParticleFilter::NOISE_REDUCTION_XY;
00018 const float ParticleFilter::NOISE_REDUCTION_T;
00019 const float ParticleFilter::INFINITE_DISTANCE;
00020 const float ParticleFilter::STDEV;
00021 const float ParticleFilter::ADDITION_PENALTY;
00022 const float ParticleFilter::PERCENT_RANDOM;
00023   
00024   
00025 void ParticleFilter::reinitialize() {
00026   delete curParticles;
00027   delete newParticles;
00028   curParticles=NULL;
00029   newParticles=NULL;
00030   numParticles = NUM_PARTICLES;
00031   numParticles = NUM_PARTICLES;
00032   numGenerations = NUM_GENERATIONS;
00033   numTries = NUM_TRIES;
00034   noiseFactorXY = INITIAL_XY_NOISE;
00035   noiseFactorT = INITIAL_THETA_NOISE;
00036   worldBounds = Shape<PolygonData>();
00037   bestIndex = -1;
00038   localScores.clear();
00039   localMatches.clear();
00040 }
00041 
00042 void ParticleFilter::makeParticles() {
00043   delete curParticles;
00044   delete newParticles;
00045   curParticles = new vector<Particle>(numParticles);
00046   newParticles = new vector<Particle>(numParticles);
00047 }
00048 
00049 void ParticleFilter::resizeParticles() {
00050   for ( int i=0; i<numParticles; i++ ) {
00051     (*curParticles)[i].addLocal.resize(nlocal);
00052     (*newParticles)[i].addLocal.resize(nlocal);
00053   }
00054   particleViewX.resize(nlocal);
00055   particleViewY.resize(nlocal);
00056   particleViewX2.resize(nlocal);
00057   particleViewY2.resize(nlocal);
00058   localScores.resize(nlocal);
00059   localMatches.resize(nlocal);
00060 }
00061 
00062 void ParticleFilter::loadLms() {
00063   PfRoot::deleteLms(localLms);
00064   PfRoot::deleteLms(worldLms);
00065   localLms = PfRoot::loadLms(localShS.allShapes(), false);
00066   worldLms = PfRoot::loadLms(worldShS.allShapes(), true);
00067   nlocal = localLms->size();
00068   nworld = worldLms->size();
00069   if ( nlocal==0 || nworld==0 ) {
00070     cout << "ParticleFilter::loadLMs found " << nlocal << " local and "
00071    << nworld << " world landmarks: can't localize!" << endl;
00072     return;
00073   }
00074 
00075   // Determine x,y bounds for resampling.  If there is a world bounding
00076   // polygon, use its bounding box.
00077   if ( worldBounds.isValid() ) {
00078     BoundingBox b(worldBounds->getBoundingBox());
00079     xmin = b.xmin;
00080     xmax = b.xmax;
00081     ymin = b.ymin;
00082     ymax = b.ymax;
00083       }
00084   else {
00085     coordinate_t localXmin, localYmin, localXmax, localYmax;
00086     coordinate_t worldXmin, worldYmin, worldXmax, worldYmax;
00087     PfRoot::findBounds(*localLms, localXmin, localYmin, localXmax, localYmax);
00088     PfRoot::findBounds(*worldLms, worldXmin, worldYmin, worldXmax, worldYmax);
00089     const coordinate_t localMax = max(fabs(localXmin),
00090               max(fabs(localYmin),
00091             max(fabs(localXmax), fabs(localYmax))));
00092     xmin = worldXmin - localMax;
00093     xmax = worldXmax + localMax;
00094     ymin = worldYmin - localMax;
00095     ymax = worldYmax + localMax;
00096   }
00097   xrange = xmax - xmin;
00098   yrange = ymax - ymin;
00099 
00100   // create or resize particles if necessary
00101   if ( curParticles == NULL ) {
00102     makeParticles();
00103     uniformlyDistribute();
00104   }
00105   if ( (int)((*curParticles)[0].addLocal.size()) != nlocal )
00106     resizeParticles();
00107 }
00108 
00109 void ParticleFilter::uniformlyDistribute() {
00110   // Determine space to generate random samples
00111   loadLms();
00112   for (int i = 0; i < numParticles; i++)
00113     randomizeNewParticle(i);
00114   vector<Particle> * const oldParticles = curParticles;
00115   curParticles = newParticles;
00116   newParticles = oldParticles;
00117 
00118 }
00119 
00120 void ParticleFilter::randomizeNewParticle(int const i) {
00121     Particle &part = (*newParticles)[i];
00122     while (1) {   // loop until particle is acceptable
00123       part.dx = float(rand())/RAND_MAX * xrange + xmin;
00124       part.dy = float(rand())/RAND_MAX * yrange + ymin;
00125       if ( !worldBounds.isValid() ||
00126      worldBounds->isInside(Point(part.dx,part.dy)) )
00127   break;
00128     }
00129     part.theta = float(rand())/RAND_MAX * 2 * M_PI;
00130 }
00131 
00132 int ParticleFilter::localize() {
00133   loadLms();
00134   if ( nlocal == 0 || nworld == 0 )
00135     return -1;
00136   getInitialGuess();
00137   noiseFactorXY = INITIAL_XY_NOISE;
00138   noiseFactorT = INITIAL_THETA_NOISE;
00139   for ( int gen = 0; gen < numGenerations; gen++ ) {
00140     resample();
00141     noiseFactorXY = noiseFactorXY * NOISE_REDUCTION_XY;
00142     noiseFactorT = noiseFactorT * NOISE_REDUCTION_T;
00143     /*
00144     cout << "Generation " << gen << ":" << endl;
00145     for ( int i=0; i<40; i++ ) {
00146       Particle &part = (*curParticles)[i];
00147       printf("  %3d:  %7.2f , %7.2f @ %6.4f = %6.4f\n", i, part.dx, part.dy, (float)part.theta, part.prob);
00148     }
00149     */
00150   }
00151   return bestIndex;
00152 }
00153 
00154 void ParticleFilter::moveBy(float const xdelta, float const ydelta, AngPi const tdelta) {
00155   for ( int i = 0; i<numParticles; i++ ) {
00156    Particle &part = (*curParticles)[i];
00157    part.dx += xdelta;
00158    part.dy += ydelta;
00159    part.theta = part.theta + tdelta;
00160   }
00161   computeParticleScores();
00162 }
00163 
00164 
00165 void ParticleFilter::getInitialGuess() {
00166   float const minAcceptableProb = pow(((double)0.3),((double)nlocal)); 
00167   for ( int tryCounter = 0; tryCounter < numTries; tryCounter++ ) {
00168     computeParticleScores();
00169     if ( (*curParticles)[bestIndex].prob >= minAcceptableProb )
00170       break;
00171     else
00172       uniformlyDistribute();    // no decent match, so randomize all particles and try another pass
00173   }
00174 }
00175 
00176 void ParticleFilter::computeParticleScores() {
00177   float bestProb = -1;
00178   bestIndex = -1;
00179   for ( int i = 0; i<numParticles; i++ ) {
00180     setParticleView(i);
00181     (*curParticles)[i].addLocal.assign(nlocal,false);
00182     for ( int j = 0; j<nlocal; j++ )
00183       computeLocalMatch(i,j);
00184     determineAdditions(i);
00185     // determineDeletions(i);
00186     float s = localScores[0];
00187     for ( int j=1; j<nlocal; j++ )
00188       s *= localScores[j];
00189     (*curParticles)[i].prob = s;
00190     if ( s > bestProb ) {
00191       bestProb = s;
00192       bestIndex = i;
00193     }
00194   }
00195   // cout << "computeParticleScores(): best particle = " << bestIndex
00196   // << ": " << bestProb << endl;
00197 }
00198 
00199 void ParticleFilter::setParticleView(int const i) {
00200   Particle const &part = (*curParticles)[i];
00201   float const partDx = part.dx;
00202   float const partDy = part.dy;
00203   float const cosT = cos(-part.theta);
00204   float const sinT = sin(-part.theta);
00205   float const negSinT = -sinT;
00206   for ( int j=0; j < nlocal; j++ ) {
00207     PfRoot &landmark = *((*localLms)[j]);
00208     particleViewX[j] = landmark.x * cosT + landmark.y * sinT + partDx; 
00209     particleViewY[j] = landmark.x * negSinT + landmark.y * cosT + partDy;
00210     if ( landmark.type == lineDataType ) {
00211       const PfLine &line = dynamic_cast<PfLine&>(landmark);
00212       particleViewX2[j] = line.x2 * cosT + line.y2 * sinT + partDx; 
00213       particleViewY2[j] = line.x2 * negSinT + line.y2 * cosT + partDy;
00214     }
00215   }
00216 }
00217 
00218 void ParticleFilter::computeLocalMatch (int const i, int const j) {
00219   float distsq = INFINITE_DISTANCE;
00220   int worldMatch = -1;
00221   for ( int k=0; k<nworld; k++ ) {
00222     if ( (*localLms)[j]->type == (*worldLms)[k]->type &&
00223    (*localLms)[j]->color == (*worldLms)[k]->color ) {
00224       float const lx = particleViewX[j];
00225       float const ly = particleViewY[j];
00226       float const wx = (*worldLms)[k]->x;
00227       float const wy = (*worldLms)[k]->y;
00228       float tempDistsq;
00229       switch ( (*localLms)[j]->type ) {
00230       case lineDataType: {
00231   PfLine &localLine = *dynamic_cast<PfLine*>((*localLms)[j]);
00232   PfLine &worldLine = *dynamic_cast<PfLine*>((*worldLms)[k]);
00233   float tempDistsq1, tempDistsq2;
00234   // If endpoints are valid, compare distance between endpoints.
00235   // If not valid, measure perpendicular distance from the local endpoint
00236   // to the world line segment, if the projection of the endpoint onto the
00237   // segment occurs within the segment, not beyond it.  Instead of calculating
00238   // the projection we use a heuristic test: either the x or y endpoint value must
00239   // lie within the range of the line segment.
00240   if ( (localLine.valid1 && worldLine.valid1) ||
00241       !( lx >= min(worldLine.x,worldLine.x2) &&
00242         lx <= max(worldLine.x,worldLine.x2) ||
00243         ly >= min(worldLine.y,worldLine.y2) &&
00244         ly <= max(worldLine.y,worldLine.y2) ) )
00245     tempDistsq1 = (lx-wx)*(lx-wx) + (ly-wy)*(ly-wy);
00246   else {
00247     float const tempDist1 = distanceFromLine(lx,ly,worldLine);
00248     tempDistsq1 = tempDist1 * tempDist1;
00249   }
00250   float const lx2 = particleViewX2[j];
00251   float const ly2 = particleViewY2[j];
00252   float const wx2 = worldLine.x2;
00253   float const wy2 = worldLine.y2;
00254   if ( (localLine.valid2 && worldLine.valid2) ||
00255       !( lx2 >= min(worldLine.x,worldLine.x2) &&
00256         lx2 <= max(worldLine.x,worldLine.x2) ||
00257         ly2 >= min(worldLine.y,worldLine.y2) &&
00258         ly2 <= max(worldLine.y,worldLine.y2) ) )
00259     tempDistsq2 = (lx2-wx2)*(lx2-wx2) + (ly2-wy2)*(ly2-wy2);
00260   else {
00261     float const tempDist2 = distanceFromLine(lx2,ly2,worldLine);
00262     tempDistsq2 = tempDist2 * tempDist2;
00263   }
00264   AngPi const localOrient = localLine.orientation + (*curParticles)[i].theta;
00265   AngPi const odiff = worldLine.orientation - localOrient;
00266   float const odist = 500 * sin(odiff);
00267   float const odistsq = odist * odist;
00268   tempDistsq = tempDistsq1 + tempDistsq2 + odistsq; // plus orientation match term?
00269   }
00270   break;
00271       case ellipseDataType:
00272       case pointDataType:
00273       case blobDataType: {
00274   tempDistsq = (lx-wx)*(lx-wx) + (ly-wy)*(ly-wy);
00275   break;
00276       }
00277       default:
00278   std::cout << "ParticleFilter::computeMatchScore() can't match landmark type "
00279       << (*localLms)[j]->type << std::endl;
00280   return;
00281       }
00282       if ( tempDistsq < distsq ) {
00283   distsq = tempDistsq;
00284   worldMatch = k;
00285       }
00286     }
00287   }
00288   localMatches[j] = worldMatch;
00289   if ( worldMatch == -1 ) {
00290     (*curParticles)[i].addLocal[j] = true;
00291     localScores[j] = 1.0;
00292   } 
00293   else
00294     localScores[j] = normpdf(distsq);
00295 }
00296 
00297 float ParticleFilter::distanceFromLine(coordinate_t x0, coordinate_t y0, PfLine &wline) {
00298   float const &x1 = wline.x;
00299   float const &y1 = wline.y;
00300   float const &x2 = wline.x2;
00301   float const &y2 = wline.y2;
00302   float const &length = wline.length;
00303   float const result = fabs((x2-x1)*(y1-y0) - (x1-x0)*(y2-y1)) / length;
00304   return result;
00305 }
00306 
00307 void ParticleFilter::resample() {
00308   float const normFactor = (*curParticles)[bestIndex].prob;
00309   // cout << "resample():  normFactor = " << normFactor << endl;
00310   int newParticleCount = 0;
00311   (*newParticles)[newParticleCount++] = (*curParticles)[bestIndex];
00312   while (newParticleCount < numParticles) {
00313     for ( int i=0; i<numParticles; i++ ) {
00314       float spawnChance = float(rand())/RAND_MAX;
00315       if ( spawnChance <= 0.8 * (*curParticles)[i].prob / normFactor ) {
00316       spawnInto(i, newParticleCount++);
00317       if ( newParticleCount == numParticles )
00318        break;
00319       }
00320     if ( float(rand())/RAND_MAX < PERCENT_RANDOM ) {
00321       randomizeNewParticle(newParticleCount++); 
00322       if ( newParticleCount == numParticles )
00323        break;
00324     }
00325     }
00326   }
00327   vector<Particle> * const oldParticles = curParticles;
00328   curParticles = newParticles;
00329   newParticles = oldParticles;
00330   computeParticleScores();
00331 }
00332 
00333 void ParticleFilter::spawnInto(int const i, int const j) {
00334   Particle &parent = (*curParticles)[i];
00335   Particle &spawned = (*newParticles)[j];
00336 
00337   while (1) { // loop until we generate an acceptable particle
00338     float const xRand = 2*float(rand())/RAND_MAX - 1;
00339     float const yRand = 2*float(rand())/RAND_MAX - 1;
00340     spawned.dx = parent.dx + xRand * noiseFactorXY;
00341     spawned.dy = parent.dy + yRand * noiseFactorXY;
00342     if ( !worldBounds.isValid() ||
00343    worldBounds->isInside(Point(spawned.dx, spawned.dy)) ) {
00344       float const tRand = 2*float(rand())/RAND_MAX - 1;
00345       spawned.theta = parent.theta + AngTwoPi(tRand * noiseFactorT);
00346       break;
00347     }
00348   }
00349 }
00350 
00351 void ParticleFilter::determineAdditions(int const i) {
00352   Particle &part = (*curParticles)[i];
00353   for (int j = 0; j<nlocal; j++) {
00354     float const randval = float(rand()) / (RAND_MAX*6);
00355     if (randval >= localScores[j]) {
00356       part.addLocal[j] = true;
00357       localScores[j] = ADDITION_PENALTY;
00358       localMatches[j] = -1;
00359     } 
00360     else
00361       for (int j2 = (j+1); j2<nlocal; j2++)
00362   if (localMatches[j2] == localMatches[j] && localMatches[j2] != -1)
00363     if (localScores[j2] > localScores[j]) {
00364       part.addLocal[j] = true;
00365       localScores[j] = ADDITION_PENALTY;
00366      localMatches[j] = -1;
00367     } else {
00368       part.addLocal[j2] = true;
00369       localScores[j2] = ADDITION_PENALTY;
00370      localMatches[j2] = -1;
00371     }
00372   }
00373 }
00374 
00375   /*
00376 void ParticleFilter::determineDeletions (int const i) {
00377   Particle &part = (*curParticles)[i];
00378   part.deleteWorld.assign(nworld,true);
00379 
00380   float minXLoc = INFINITEDISTANCE;
00381   float minYLoc = INFINITEDISTANCE;
00382   float maxXLoc = 0;
00383   float maxYLoc = 0;
00384   for (int j = 0; j<nlocal; j++) {
00385     if ( localMatches[j] != -1 )
00386       part.deleteWorld[localMatches[j]] = false;  // don't delete world LM if it matches
00387     if ( particleViewX[j] < minXLoc )
00388       minXLoc = particleViewX[j];
00389     else if (particleViewX[j] > maxXLoc)
00390       maxXLoc = particleViewX[j];
00391     if (particleViewY[j] < minYLoc)
00392       minYLoc = particleViewY[j];
00393     else if (particleViewY[j] > maxYLoc)
00394       maxYLoc = particleViewY[j];
00395   }
00396   
00397   for (int k = 0; k<nworld; k++)
00398     if ( !( worldLms[k]->x >= minXLoc && 
00399       worldLms[k]->x <= maxXLoc &&
00400       worldLms[k]->y >= minYLoc &&
00401       worldLms[k]->y <= maxYLoc ) )
00402       part.deleteWorld[k] == false; // don't delete world LM if it was outside local view
00403 }
00404   */
00405 
00406 } // namespace

DualCoding 3.0beta
Generated Wed Oct 4 00:01:54 2006 by Doxygen 1.4.7