Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

ShapeSensorModel.cc

Go to the documentation of this file.
00001 #include "ShapeSensorModel.h"
00002 
00003 #include "Motion/Kinematics.h"  // for kine variable
00004 #include "Shared/Config.h"  // for config variable
00005 #include "DualCoding/VRmixin.h"
00006 #include "ShapeLandmarks.h"
00007 
00008 #include <cmath>
00009 #include <iostream>
00010 
00011 using namespace DualCoding;
00012 
00013 float const LocalShapeEvaluator::maxDist = 1e10;
00014 float const LocalShapeEvaluator::stdevSq = 60*60;
00015 
00016 LocalShapeEvaluator::LocalShapeEvaluator(ShapeSpace &localShS, ShapeSpace &worldShS) : 
00017   localLms(), worldLms() {
00018   PfRoot::loadLms(localShS.allShapes(), false, localLms);
00019   PfRoot::loadLms(worldShS.allShapes(), true, worldLms);
00020   std::cout << "LocalShapeEvaluator: " << localShS.allShapes().size() << " local shapes,   "
00021       << worldShS.allShapes().size() << " world shapes " << std::endl;
00022   if ( localLms.size()==0 || worldLms.size()==0 ) {
00023     std::cout << "ParticleFilter::loadLms found " << localLms.size() << " local and "
00024         << worldLms.size() << " world landmarks: can't localize!" << std::endl;
00025   }
00026 }
00027 
00028 void LocalShapeEvaluator::evaluate(LocalizationParticle& p) {
00029   unsigned int const nLocals = localLms.size();
00030   float particleViewX[nLocals],  particleViewY[nLocals], particleViewX2[nLocals],  particleViewY2[nLocals];
00031   int localMatches[nLocals];
00032   float localScores[nLocals];
00033   evaluateWorkhorse(p, nLocals, particleViewX, particleViewY, particleViewX2, particleViewY2,
00034         localMatches, localScores);
00035   //  for (unsigned int i=0; i<nLocals; i++)
00036   //    std::cout <<"  lm " << i << ":   " << localMatches[i] <<  " score " << localScores[i] << std::endl;
00037   //  std::cout << "    oldweight " << p.weight;
00038   updateWeight(p, localMatches, localScores);
00039   //  std::cout << "    newweight " << p.weight << std::endl;
00040 }
00041 
00042 void LocalShapeEvaluator::evaluateWorkhorse
00043 (LocalizationParticle& p, const unsigned int nLocals,
00044  float particleViewX[], float particleViewY[], float particleViewX2[], float particleViewY2[],
00045  int localMatches[], float localScores[]) {
00046   // determine position of local space landmark in world given the current particle
00047   float const cosT = std::cos(-p.theta);
00048   float const sinT = std::sin(-p.theta);
00049   float const negSinT = -sinT;
00050   for ( unsigned int indexL=0; indexL < nLocals; indexL++ ) {
00051     PfRoot &landmark = *(localLms[indexL]);
00052     particleViewX[indexL] = landmark.x * cosT + landmark.y * sinT + p.x; 
00053     particleViewY[indexL] = landmark.x * negSinT + landmark.y * cosT + p.y;
00054     if ( landmark.type == lineDataType ) {
00055       const PfLine &line = static_cast<PfLine&>(landmark);
00056       particleViewX2[indexL] = line.x2 * cosT + line.y2 * sinT + p.x;
00057       particleViewY2[indexL] = line.x2 * negSinT + line.y2 * cosT + p.y;
00058     }
00059   }
00060   // Now compute match scores for the particle by finding matches between local landmarks and world landmarks.
00061   for ( unsigned int indexL = 0; indexL < nLocals; indexL++ ) {
00062     float distsq = maxDist; // distance > this is treated as a non-match; value should be < 1e10 to avoid underflows when not using log weights
00063     localMatches[indexL] = -1;  // assume no match unless we find something
00064     for ( unsigned int indexW=0; indexW<worldLms.size(); indexW++ ) {
00065       if ( localLms[indexL]->type == worldLms[indexW]->type &&
00066      localLms[indexL]->color == worldLms[indexW]->color ) {
00067   float const lx = particleViewX[indexL];
00068   float const ly = particleViewY[indexL];
00069   float const wx = worldLms[indexW]->x;
00070   float const wy = worldLms[indexW]->y;
00071   float tempDistsq;
00072 
00073   // dispatch on landmark type because lines and markers require special match handling
00074   switch ( localLms[indexL]->type ) {
00075   case lineDataType: {
00076     PfLine &localLine = *static_cast<PfLine*>(localLms[indexL]);
00077     PfLine &worldLine = *static_cast<PfLine*>(worldLms[indexW]);
00078     float tempDistsq1, tempDistsq2;
00079     // If endpoints are valid, compare distance between endpoints.
00080     // If not valid, measure perpendicular distance from the local endpoint
00081     // to the world line segment, if the projection of the endpoint onto the
00082     // segment occurs within the segment, not beyond it.  Instead of calculating
00083     // the projection we use a heuristic test: either the x or y endpoint value must
00084     // lie within the range of the line segment.
00085     if ( (localLine.valid1 && worldLine.valid1) ||
00086          !( (lx >= std::min(worldLine.x,worldLine.x2) && lx <= std::max(worldLine.x,worldLine.x2)) ||
00087       (ly >= std::min(worldLine.y,worldLine.y2) && ly <= std::max(worldLine.y,worldLine.y2)) ) )
00088       tempDistsq1 = (lx-wx)*(lx-wx) + (ly-wy)*(ly-wy);
00089     else {
00090       float const tempDist1 = distanceFromLine(lx,ly,worldLine);
00091       tempDistsq1 = tempDist1 * tempDist1;
00092     }
00093     float const lx2 = particleViewX2[indexL];
00094     float const ly2 = particleViewY2[indexL];
00095     float const wx2 = worldLine.x2;
00096     float const wy2 = worldLine.y2;
00097     if ( (localLine.valid2 && worldLine.valid2) ||
00098          !( (lx2 >= std::min(worldLine.x,worldLine.x2) && lx2 <= std::max(worldLine.x,worldLine.x2)) ||
00099       (ly2 >= std::min(worldLine.y,worldLine.y2) && ly2 <= std::max(worldLine.y,worldLine.y2)) ) )
00100       tempDistsq2 = (lx2-wx2)*(lx2-wx2) + (ly2-wy2)*(ly2-wy2);
00101     else {
00102       float const tempDist2 = distanceFromLine(lx2,ly2,worldLine);
00103       tempDistsq2 = tempDist2 * tempDist2;
00104     }
00105     AngPi const localOrient = localLine.orientation + p.theta;
00106     AngPi const odiff = worldLine.orientation - localOrient;
00107     float const odist = 500 * std::sin(odiff);
00108     float const odistsq = odist * odist;
00109     tempDistsq = tempDistsq1 + tempDistsq2 + odistsq; // plus orientation match term?
00110   }
00111     break;
00112   case ellipseDataType:
00113   case blobDataType: {
00114     tempDistsq = (lx-wx)*(lx-wx) + (ly-wy)*(ly-wy);
00115     break;
00116   }
00117   case markerDataType: {
00118     PfMarker &localMarker = *static_cast<PfMarker*>(localLms[indexL]);
00119     PfMarker &worldMarker = *static_cast<PfMarker*>(worldLms[indexW]);
00120 
00121     // check for marker "equality"
00122     if (localMarker.data->isMatchingMarker(worldMarker.data))
00123       tempDistsq = (lx-wx)*(lx-wx) + (ly-wy)*(ly-wy);
00124     else
00125       continue;
00126     break;
00127   }
00128   case aprilTagDataType: {
00129     PfAprilTag &localTag = *static_cast<PfAprilTag*>(localLms[indexL]);
00130     PfAprilTag &worldTag = *static_cast<PfAprilTag*>(worldLms[indexW]);
00131 
00132     // check for tag "equality"
00133     if ( localTag.data->getTagID() == worldTag.data->getTagID() )
00134       tempDistsq = (lx-wx)*(lx-wx) + (ly-wy)*(ly-wy);
00135     else
00136       continue;
00137     break;
00138   }
00139   case pointDataType:
00140     // don't try to match points; they're just placeholders, not landmarks
00141     tempDistsq = maxDist;
00142     break;
00143   default:
00144     std::cout << "ParticleFilter::computeMatchScore() can't match landmark type "
00145         << localLms[indexL]->type << std::endl;
00146     return;
00147   }
00148 
00149   // if this world landmark is a closer match, accept it
00150   if ( tempDistsq < distsq ) {
00151     distsq = tempDistsq;
00152     localMatches[indexL] = indexW;
00153   }
00154       }
00155     }
00156 
00157     if ( localMatches[indexL] != -1 || ! localLms[indexL]->mobile )
00158       localScores[indexL] = distsq;
00159   }
00160 }
00161 
00162 void LocalShapeEvaluator::updateWeight(LocalizationParticle &p, 
00163                int const localMatches[], float const localScores[]) {
00164   for (unsigned int i=0; i < localLms.size(); i++)
00165     if ( localMatches[i] != -1 )
00166       p.weight += -localScores[i]/stdevSq;
00167 }
00168 
00169 float LocalShapeEvaluator::distanceFromLine(coordinate_t x0, coordinate_t y0, PfLine &wline) {
00170   float const &x1 = wline.x;
00171   float const &y1 = wline.y;
00172   float const &x2 = wline.x2;
00173   float const &y2 = wline.y2;
00174   float const &length = wline.length;
00175   float const result = std::fabs((x2-x1)*(y1-y0) - (x1-x0)*(y2-y1)) / length;
00176   return result;
00177 }
00178 
00179 bool projectShapeToCamera(const LocalizationParticle &particle, const ShapeRoot *shp, float& px, float& py)
00180 {
00181   //    cout << "PROJECT: marker to cam: part " << particle << " marker " << m << endl;
00182 
00183   // get centroid
00184   BaseData const &bd = shp->getData();
00185   Point worldPt = bd.getCentroid();
00186 
00187   // transform to local frame
00188   // lx = (wx - x) * cos(theta) + (wy - y) * sin(theta)
00189   // ...
00190   Point localPt;
00191   worldPt.coords[0] -= particle.x;
00192   worldPt.coords[1] -= particle.y;
00193 
00194   localPt.coords[0] = worldPt.coords[0] * std::cos(particle.theta) + worldPt.coords[1] * std::sin(particle.theta);
00195   localPt.coords[1] = worldPt.coords[0] * -std::sin(particle.theta) + worldPt.coords[1] * std::cos(particle.theta);
00196   localPt.coords[2] = worldPt.coords[2];
00197   //    cout << "PROJECT: transformed to local: " << localPt << endl;
00198 
00199   // check for shape behind the camera (bad projection)
00200   if (localPt.coords[0] < 0)
00201     return false;
00202 
00203   // transform from local to camera coordinates
00204   Point cameraPt(localPt);
00205 #ifdef TGT_HAS_CAMERA
00206   cameraPt.applyTransform(kine->baseToLink(CameraFrameOffset));
00207 #endif
00208 
00209   // compute pixels
00210   float camXnorm, camYnorm;
00211   config->vision.computePixel(cameraPt.coordX(), cameraPt.coordY(), cameraPt.coordZ(), camXnorm, camYnorm);
00212 
00213   // transform from normalized [-1,+1] coordinates to actual pixel coordinates
00214   px = (camXnorm + 1)/2*VRmixin::camSkS.getWidth();
00215   py = (camYnorm + 1)/2*VRmixin::camSkS.getHeight();
00216   return true;
00217 }
00218 
00219 void CameraShapeEvaluator::computeLikelihood(LocalizationParticle &particle)
00220 {
00221   //    cout << "SENSOR MODEL: Computing likelihood for particle " << particle << endl;
00222 
00223   // look through all markers in sensor reading (image)
00224   SHAPEROOTVEC_ITERATE(cShS, cs)
00225     switch (cs->getType()) {
00226     case markerDataType : {
00227       BaseData const &bd = cs.getData();
00228       //cout << "SENSOR MODEL: Searching for match for " << cm << endl;
00229       fmat::Column<2> original(bd.getCentroid().coords);
00230 
00231       float bestMatch = std::log(pRandom);
00232 
00233       // look for best match in world space
00234       SHAPEROOTVEC_ITERATE(wShS, wm)
00235 
00236   if (wm->isSameTypeAs(cs)) {
00237     //cout << "SENSOR MODEL: Found matching markers: " << cm << " and " << wm << endl;
00238     // try and project marker
00239     fmat::Column<2> projected;
00240     DualCoding::ShapeRoot *targetLandMark(&wm);
00241     if (projectShapeToCamera(particle, targetLandMark, projected[0], projected[1])) {
00242       float prob = std::log((1- pRandom) * (normpdf(projected[0] - original[0], xvar) *
00243                normpdf(projected[1] - original[1], yvar)) + pRandom) * alpha;
00244       if (prob > bestMatch) {
00245         bestMatch = prob;
00246       }
00247     }
00248   }
00249       END_ITERATE;
00250       // don't need to normalize here because every particle will have same
00251       // number of markers in image
00252       particle.weight += bestMatch;
00253     }
00254       break;
00255     default:
00256       break;
00257     }
00258   END_ITERATE;
00259 }   

Tekkotsu v5.1CVS
Generated Fri Mar 16 05:26:51 2012 by Doxygen 1.6.3