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 = 150*150; // was 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   // std::cout << "Particle x=" << p.x << " y=" << p.y << " th=" << p.theta << std::endl;
00051   for ( unsigned int indexL=0; indexL < nLocals; indexL++ ) {
00052     PfRoot &landmark = *(localLms[indexL]);
00053     particleViewX[indexL] = landmark.x * cosT + landmark.y * sinT + p.x; 
00054     particleViewY[indexL] = landmark.x * negSinT + landmark.y * cosT + p.y;
00055     if ( landmark.type == lineDataType ) {
00056       const PfLine &line = static_cast<PfLine&>(landmark);
00057       particleViewX2[indexL] = line.x2 * cosT + line.y2 * sinT + p.x;
00058       particleViewY2[indexL] = line.x2 * negSinT + line.y2 * cosT + p.y;
00059     }
00060   }
00061   // Now compute match scores for the particle by finding matches between local landmarks and world landmarks.
00062   for ( unsigned int indexL = 0; indexL < nLocals; indexL++ ) {
00063     float distsq = maxDist; // distance > this is treated as a non-match; value should be < 1e10 to avoid underflows when not using log weights
00064     localMatches[indexL] = -1;  // assume no match unless we find something
00065     for ( unsigned int indexW=0; indexW<worldLms.size(); indexW++ ) {
00066       if ( localLms[indexL]->type == worldLms[indexW]->type &&
00067            localLms[indexL]->color == worldLms[indexW]->color ) {
00068         float const lx = particleViewX[indexL];
00069         float const ly = particleViewY[indexL];
00070         float const wx = worldLms[indexW]->x;
00071         float const wy = worldLms[indexW]->y;
00072         float tempDistsq;
00073 
00074         // dispatch on landmark type because lines and markers require special match handling
00075         switch ( localLms[indexL]->type ) {
00076         case lineDataType: {
00077           PfLine &localLine = *static_cast<PfLine*>(localLms[indexL]);
00078           PfLine &worldLine = *static_cast<PfLine*>(worldLms[indexW]);
00079           float tempDistsq1, tempDistsq2;
00080           // If endpoints are valid, compare distance between endpoints.
00081           // If not valid, measure perpendicular distance from the local endpoint
00082           // to the world line segment, if the projection of the endpoint onto the
00083           // segment occurs within the segment, not beyond it.  Instead of calculating
00084           // the projection we use a heuristic test: either the x or y endpoint value must
00085           // lie within the range of the line segment.
00086           if ( (localLine.valid1 && worldLine.valid1) ||
00087                !( (lx >= std::min(worldLine.x,worldLine.x2) && lx <= std::max(worldLine.x,worldLine.x2)) ||
00088                   (ly >= std::min(worldLine.y,worldLine.y2) && ly <= std::max(worldLine.y,worldLine.y2)) ) ) {
00089             tempDistsq1 = (lx-wx)*(lx-wx) + (ly-wy)*(ly-wy);
00090           } else {
00091             float const tempDist1 = distanceFromLine(lx,ly,worldLine);
00092             tempDistsq1 = tempDist1 * tempDist1;
00093           }
00094           float const lx2 = particleViewX2[indexL];
00095           float const ly2 = particleViewY2[indexL];
00096           float const wx2 = worldLine.x2;
00097           float const wy2 = worldLine.y2;
00098           if ( (localLine.valid2 && worldLine.valid2) ||
00099                !( (lx2 >= std::min(worldLine.x,worldLine.x2) && lx2 <= std::max(worldLine.x,worldLine.x2)) ||
00100                   (ly2 >= std::min(worldLine.y,worldLine.y2) && ly2 <= std::max(worldLine.y,worldLine.y2)) ) )
00101             tempDistsq2 = (lx2-wx2)*(lx2-wx2) + (ly2-wy2)*(ly2-wy2);
00102           else {
00103             float const tempDist2 = distanceFromLine(lx2,ly2,worldLine);
00104             tempDistsq2 = tempDist2 * tempDist2;
00105           }
00106           AngPi const localOrient = localLine.orientation + p.theta;
00107           AngPi odiff = worldLine.orientation - localOrient;
00108           odiff = std::min<float>(odiff, M_PI - odiff);
00109           float const odist = 500 * std::sin(odiff);
00110           float const odistsq = odist * odist;
00111           tempDistsq = tempDistsq1 + tempDistsq2 + odistsq; // plus orientation match term?
00112           if ( false && tempDistsq1 <= 10000 && tempDistsq2 <= 10000 ) {
00113             std::cout << "line " << lx << "," << ly << " : " << wx << "," << wy
00114                       << " end1valid=" << localLine.valid1 << ":" << worldLine.valid1
00115                       << " tempDistsq1=" << tempDistsq1
00116                       << " indexL:W=" << indexL << ":" << indexW << std::endl;
00117             std::cout << "     " << lx2 << "," << ly2 << " : " << wx2 << "," << wy2
00118                       << " end2valid=" << localLine.valid2 << ":" << worldLine.valid2
00119                       << " tempDistsq2=" << tempDistsq2
00120                       << " odistsq = " << odistsq << " total=" << tempDistsq << std::endl;
00121           }
00122         }
00123           break;
00124         case ellipseDataType:
00125         case blobDataType:
00126         case cylinderDataType: {
00127           tempDistsq = (lx-wx)*(lx-wx) + (ly-wy)*(ly-wy);
00128           // *** EXPERIMENTAL ***
00129           PfRoot &landmark = *(localLms[indexL]);
00130           AngTwoPi ltheta = atan2(landmark.y,landmark.x);
00131           float ldist = sqrt(landmark.x * landmark.x + landmark.y * landmark.y);
00132           AngTwoPi ptheta = atan2(wy-p.y,wx-p.x) - p.theta;
00133           float pdist = sqrt((wx-p.x)*(wx-p.x) + (wy-p.y)*(wy-p.y));
00134           float thetadiff = angdist(ptheta,ltheta);
00135           tempDistsq = (pdist-ldist)*(pdist-ldist) + 5000*thetadiff; // *pdist*pdist;
00136           if ( false && tempDistsq < 50000 )
00137             std::cout << "dists " << ldist << " " << pdist << "   thetas " << ltheta << " " << ptheta
00138                       << "  thetadiff=" << thetadiff << "   tempDistsq=" << tempDistsq << std::endl;
00139           break;
00140         }
00141         case markerDataType: {
00142           PfMarker &localMarker = *static_cast<PfMarker*>(localLms[indexL]);
00143           PfMarker &worldMarker = *static_cast<PfMarker*>(worldLms[indexW]);
00144 
00145           // check for marker "equality"
00146           if (localMarker.data->isMatchingMarker(worldMarker.data))
00147             tempDistsq = (lx-wx)*(lx-wx) + (ly-wy)*(ly-wy);
00148           else
00149             continue;
00150           break;
00151         }
00152         case aprilTagDataType: {
00153           PfAprilTag &localTag = *static_cast<PfAprilTag*>(localLms[indexL]);
00154           PfAprilTag &worldTag = *static_cast<PfAprilTag*>(worldLms[indexW]);
00155 
00156           // check for tag "equality"
00157           if ( localTag.data->getTagID() == worldTag.data->getTagID() )
00158             tempDistsq = (lx-wx)*(lx-wx) + (ly-wy)*(ly-wy);
00159           else
00160             continue;
00161           break;
00162         }
00163         case pointDataType:
00164           // don't try to match points; they're just placeholders, not landmarks
00165           tempDistsq = maxDist;
00166           break;
00167         default:
00168           std::cout << "ParticleFilter::computeMatchScore() can't match landmark type "
00169                     << localLms[indexL]->type << std::endl;
00170           return;
00171         }
00172 
00173         // if this world landmark is a closer match, accept it
00174         if ( tempDistsq < distsq ) {
00175           distsq = tempDistsq;
00176           localMatches[indexL] = indexW;
00177         }
00178       }
00179     }
00180 
00181     if ( localMatches[indexL] != -1 || ! localLms[indexL]->mobile )
00182       localScores[indexL] = distsq;
00183   }
00184 }
00185 
00186 void LocalShapeEvaluator::updateWeight(LocalizationParticle &p, 
00187                                        int const localMatches[], float const localScores[]) {
00188   for (unsigned int i=0; i < localLms.size(); i++)
00189     if ( localMatches[i] != -1 )
00190       p.weight += -localScores[i]/stdevSq;
00191 }
00192 
00193 float LocalShapeEvaluator::distanceFromLine(coordinate_t x0, coordinate_t y0, PfLine &wline) {
00194   float const &x1 = wline.x;
00195   float const &y1 = wline.y;
00196   float const &x2 = wline.x2;
00197   float const &y2 = wline.y2;
00198   float const &length = wline.length;
00199   float const result = std::fabs((x2-x1)*(y1-y0) - (x1-x0)*(y2-y1)) / length;
00200   return result;
00201 }
00202 
00203 bool projectShapeToCamera(const LocalizationParticle &particle, const ShapeRoot *shp, float& px, float& py)
00204 {
00205   //    cout << "PROJECT: marker to cam: part " << particle << " marker " << m << endl;
00206 
00207   // get centroid
00208   BaseData const &bd = shp->getData();
00209   Point worldPt = bd.getCentroid();
00210 
00211   // transform to local frame
00212   // lx = (wx - x) * cos(theta) + (wy - y) * sin(theta)
00213   // ...
00214   Point localPt;
00215   worldPt.coords[0] -= particle.x;
00216   worldPt.coords[1] -= particle.y;
00217 
00218   localPt.coords[0] = worldPt.coords[0] * std::cos(particle.theta) + worldPt.coords[1] * std::sin(particle.theta);
00219   localPt.coords[1] = worldPt.coords[0] * -std::sin(particle.theta) + worldPt.coords[1] * std::cos(particle.theta);
00220   localPt.coords[2] = worldPt.coords[2];
00221   //    cout << "PROJECT: transformed to local: " << localPt << endl;
00222 
00223   // check for shape behind the camera (bad projection)
00224   if (localPt.coords[0] < 0)
00225     return false;
00226 
00227   // transform from local to camera coordinates
00228   Point cameraPt(localPt);
00229 #ifdef TGT_HAS_CAMERA
00230   cameraPt.applyTransform(kine->baseToLink(CameraFrameOffset));
00231 #endif
00232 
00233   // compute pixels
00234   float camXnorm, camYnorm;
00235   config->vision.computePixel(cameraPt.coordX(), cameraPt.coordY(), cameraPt.coordZ(), camXnorm, camYnorm);
00236 
00237   // transform from normalized [-1,+1] coordinates to actual pixel coordinates
00238   px = (camXnorm + 1)/2*VRmixin::camSkS.getWidth();
00239   py = (camYnorm + 1)/2*VRmixin::camSkS.getHeight();
00240   return true;
00241 }
00242 
00243 void CameraShapeEvaluator::computeLikelihood(LocalizationParticle &particle)
00244 {
00245   //    cout << "SENSOR MODEL: Computing likelihood for particle " << particle << endl;
00246 
00247   // look through all markers in sensor reading (image)
00248   SHAPEROOTVEC_ITERATE(cShS, cs)
00249     switch (cs->getType()) {
00250     case markerDataType : {
00251       BaseData const &bd = cs.getData();
00252       //cout << "SENSOR MODEL: Searching for match for " << cm << endl;
00253       fmat::Column<2> original(bd.getCentroid().coords);
00254 
00255       float bestMatch = std::log(pRandom);
00256 
00257       // look for best match in world space
00258       SHAPEROOTVEC_ITERATE(wShS, wm)
00259 
00260   if (wm->isSameTypeAs(cs)) {
00261     //cout << "SENSOR MODEL: Found matching markers: " << cm << " and " << wm << endl;
00262     // try and project marker
00263     fmat::Column<2> projected;
00264     DualCoding::ShapeRoot *targetLandMark(&wm);
00265     if (projectShapeToCamera(particle, targetLandMark, projected[0], projected[1])) {
00266       float prob = std::log((1- pRandom) * (normpdf(projected[0] - original[0], xvar) *
00267                normpdf(projected[1] - original[1], yvar)) + pRandom) * alpha;
00268       if (prob > bestMatch) {
00269         bestMatch = prob;
00270       }
00271     }
00272   }
00273       END_ITERATE;
00274       // don't need to normalize here because every particle will have same
00275       // number of markers in image
00276       particle.weight += bestMatch;
00277     }
00278       break;
00279     default:
00280       break;
00281     }
00282   END_ITERATE;
00283 }   

Tekkotsu v5.1CVS
Generated Mon May 9 04:58:50 2016 by Doxygen 1.6.3