Main Page | Modules | Namespace List | Class Hierarchy | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

sonar_obs.cpp

Go to the documentation of this file.
00001 #include <set>
00002 #include <fstream>
00003 #include <CGAL/IO/Qt_widget.h>
00004 #include "sonar_obs.hpp"
00005 #include "cone_tracing.hpp"
00006 #include "jet.hpp"
00007 
00008 using namespace Arak;
00009 using namespace Arak::Geometry;
00010 
00011 #define REGISTER_QUERY_POINTS
00012 
00013 // #define FIRST_IMPACT_ONLY
00014 
00019 class ScanEventProcessor {
00020 
00021 protected:
00022 
00023   SonarImpact impact;
00024   SonarImpact prevImpact;
00025   SonarImpact corner;
00026   Geometry::Point source;
00027   const double maxRange;
00028   std::vector<SonarImpact>* impacts;
00029   Geometry::Direction dir;
00030 
00031 public:
00032 
00033   ScanEventProcessor(const Geometry::Point& source,
00034          const Geometry::Direction& min,
00035          const double maxRange,
00036          std::vector<SonarImpact>& impacts)
00037     : source(source), maxRange(maxRange), impacts(&impacts), dir(min) { 
00038     impact.min_range = -1.0; // invalid marker
00039     prevImpact.min_range = -1.0; // invalid marker
00040     corner.min_range = -1.0; // invalid marker
00041   }
00042 
00043   ScanEventProcessor() : maxRange(-1.0), impacts(NULL) { }
00044 
00045   const ScanEvent& operator=(const ScanEvent& event) {
00046     // First check for a corner.  We view a corner when there is a
00047     // previous impact and its max point is the same as the new
00048     // event's min point.
00049     if ((this->prevImpact.min_range >= 0.0) &&
00050   (this->prevImpact.pmax == event.pmin)) {
00051       // Populate the cached corner impact.
00052       corner = SonarImpact(this->source, 
00053          prevImpact.pmin, 
00054          event.dmin,
00055          event.pmin, 
00056          event.pmax);
00057     } else {
00058       // Invalidate the cached corner impact.
00059       corner.min_range = -1.0;
00060     }
00061     // Construct the sonar impact from the event information.  TODO:
00062     // pmin and pmax must be trimmed to lie inside the depth-truncated
00063     // sonar cone!
00064     Geometry::Point pmin = event.pmin;
00065     Geometry::Point pmax = event.pmax;
00066     SonarImpact tmp(this->source, event.dmin, event.dmax, pmin, pmax);
00067     // If the impact is close enough, update this iterator.
00068     if (tmp.min_range <= this->maxRange) {
00069       // Update the iterator.
00070       this->impact = tmp;
00071       dir = event.dmax;
00072     } else {
00073       // Invalidate the current impact.
00074       this->impact.min_range = -1.0;
00075     }
00076     return event;
00077   }
00078 
00079   ScanEventProcessor operator++() {
00080     // If there is a valid corner impact, record it first.
00081     if (corner.min_range >= 0.0) {
00082       impacts->push_back(corner);
00083       corner.min_range = -1.0;
00084     }
00085     if (impact.min_range >= 0.0) {
00086       // Record the current impact.
00087       impacts->push_back(impact);
00088       // Cache it as the previous impact.
00089       prevImpact = impact;
00090     }
00091     return *this;
00092   }
00093 
00094   ScanEventProcessor operator++(int) {
00095     ScanEventProcessor tmp(*this);
00096     ++(*this);
00097     return tmp;
00098   }
00099 }; // End of class: ScanEventProcessor
00100 
00101 void Arak::computeSonarImpacts(const Coloring& coloring,
00102              const Geometry::Point& vertex,
00103              const Geometry::Direction& dmin,
00104              const Geometry::Direction& dmax,
00105              const Geometry::Kernel::FT maxRange,
00106              std::vector<SonarImpact>& impacts,
00107              double* maxImpactRange) {
00108   impacts.clear();
00109   ScanEventProcessor processor(vertex, dmin, CGAL::to_double(maxRange), impacts);
00110   coneTrace(coloring, vertex, dmin, dmax, maxRange, processor);
00111   std::sort(impacts.begin(), impacts.end());
00112   // If required, compute the maximum impact range.
00113   if (maxImpactRange != NULL) {
00114     Geometry::Direction dir = dmin;
00115     typedef std::vector<SonarImpact>::const_iterator Iterator;
00116     *maxImpactRange = 0.0;
00117     for (Iterator it = impacts.begin(); it != impacts.end(); ++it) {
00118       if (it->dmin != dir) {
00119   *maxImpactRange = std::numeric_limits<double>::infinity();
00120   break;
00121       }
00122       *maxImpactRange = 
00123   std::max(*maxImpactRange, 
00124      CGAL::to_double(CGAL::squared_distance(vertex, it->pmax)));
00125       *maxImpactRange = 
00126   std::max(*maxImpactRange,
00127      CGAL::to_double(CGAL::squared_distance(vertex, it->pmin)));
00128       dir = it->dmax;
00129     }
00130     if (dir != dmax)
00131       *maxImpactRange = std::numeric_limits<double>::infinity();
00132     if (*maxImpactRange != std::numeric_limits<double>::infinity())
00133       *maxImpactRange = sqrt(*maxImpactRange);
00134   }
00135 }
00136 
00137 bool Arak::computeFirstSonarImpact(const Coloring& coloring,
00138            const Geometry::Point& vertex,
00139            const Geometry::Direction& min,
00140            const Geometry::Direction& max,
00141            const Geometry::Kernel::FT maxRange,
00142            SonarImpact& impact) {
00143   // TODO: this is hacked! clean it up.
00144 
00145   // Compute the set of edges that intersect the sonar cone.
00146   Geometry::Triangle outer = outerApprox(vertex, min, max, maxRange);
00147   const Coloring::InteriorEdgeIndex& index = 
00148     coloring.getInteriorEdgeIndex();
00149   Coloring::InteriorEdgeIndex::ConstTriangleCellIterator 
00150     it(index, outer), end;
00151   typedef std::set<Coloring::IntEdgeHandle> EdgeHandleSet;
00152   static EdgeHandleSet edges; // Warning: this is not thread-safe
00153   edges.clear();
00154   while (it != end) {
00155     const Coloring::InteriorEdgeIndex::Cell::ItemList& obsList = 
00156       it->getItemList();
00157     edges.insert(obsList.begin(), obsList.end());
00158     ++it;
00159   }
00160   // If there are no edges, we are done.
00161   if (edges.empty()) return false;
00162   // Otherwise, scan through the edges, looking for the closest
00163   // impact.
00164   bool found = false;
00165   ScanEdge se;
00166   double closest = std::numeric_limits<double>::infinity();
00167   for (EdgeHandleSet::iterator it = edges.begin(); it != edges.end(); it++) {
00168     // Create a scan edge object. TODO: this object should be moved
00169     // here from cone_tracing.hpp.
00170     se.init(vertex, min, max, *it);
00171     // If the scan edge does not intersect the scan cone, ignore it.
00172     if (!cones_intersect(min, max, se.dmin, se.dmax)) continue;
00173     // Construct a sonar impact from the scan edge.
00174     SonarImpact i = SonarImpact(vertex, se.dmin, se.dmax, se.pmin, se.pmax);
00175     if (i.min_range >= closest) continue;
00176     // This is the closest impact so far.  First transform it to a
00177     // corner if the projection point is an endpoint of the (full) face.
00178     if ((i.projection == se.pmin) &&
00179   ((se.pmin == (*it)->getNextVertex()->point()) ||
00180    (se.pmin == (*it)->getPrevVertex()->point()))) {
00181       i.pmax = i.pmin;
00182       i.dmax = i.dmin;
00183       i.visible_angle = 0.0;
00184     } else if ((i.projection == se.pmax) &&
00185          ((se.pmax == (*it)->getNextVertex()->point()) ||
00186     (se.pmax == (*it)->getPrevVertex()->point()))) {
00187       i.pmin = i.pmax;
00188       i.dmin = i.dmax;
00189       i.visible_angle = 0.0;
00190     }
00191     impact = i;
00192     closest = impact.min_range;
00193     found = true;
00194   }
00195   return found;
00196 }
00197 
00198 ArakPosteriorSonarObs::ArakPosteriorSonarObs(const ArakProcess& prior, 
00199                const Arak::Util::PropertyMap& props)
00200   : ArakProcess(prior.getColoring()), prior(prior), updateId(0), lp(0.0) {
00201   using namespace Arak::Util;
00202   assert(parse(getp(props, "arak.sonar_obs.aperture"), aperture));
00203   assert(parse(getp(props, "arak.sonar_obs.max_range"), maxRange));
00204   assert(parse(getp(props, "arak.sonar_obs.prob_max_range"), 
00205          prob_max_range));
00206   assert(parse(getp(props, "arak.sonar_obs.unif_outlier_weight"), 
00207          unif_outlier_weight));
00208   assert(parse(getp(props, "arak.sonar_obs.exp_outlier_coef"), 
00209          exp_outlier_coef));
00210   assert(parse(getp(props, "arak.sonar_obs.face_rel_err_std_dev"), faceRelErrStDev));
00211   assert(parse(getp(props, "arak.sonar_obs.corner_rel_err_std_dev"), cornerRelErrStDev));
00212   assert(parse(getp(props, "arak.sonar_obs.return_model.face.const_coef"), 
00213          face_const_coef));
00214   assert(parse(getp(props, "arak.sonar_obs.return_model.face.proj_angle_coef"), 
00215          face_proj_angle_coef));
00216   assert(parse(getp(props, "arak.sonar_obs.return_model.face.min_range_coef"), 
00217          face_min_range_coef));
00218   assert(parse(getp(props, "arak.sonar_obs.return_model.face.vis_angle_coef"), 
00219          face_vis_angle_coef));
00220   assert(parse(getp(props, "arak.sonar_obs.return_model.corner.const_coef"), 
00221          corner_const_coef));
00222   assert(parse(getp(props, "arak.sonar_obs.return_model.corner.range_coef"), 
00223          corner_range_coef));
00224   // Read in the number of observations.
00225   const std::string& datafile_path = getp(props, "arak.sonar_obs.data_file");
00226   std::ifstream in(datafile_path.data());
00227   assert(in.good());
00228   int n;
00229   in >> n;
00230   // Build the index.
00231   int rows, cols;
00232   Geometry::Rectangle bd = getColoring().boundary();
00233   if (hasp(props, "arak.sonar_obs.rows") &&
00234       hasp(props, "arak.sonar_obs.cols")) {
00235     assert(parse(getp(props, "arak.sonar_obs.rows"), rows));
00236     assert(parse(getp(props, "arak.sonar_obs.cols"), cols));
00237   } else {
00238     // Choose the size of the grid based upon the number of
00239     // measurements and the dimensions of the boundary.
00240     const double obsPerCell = 3.0;
00241     const double numCells = double(n) / obsPerCell;
00242     const double ar = 
00243       CGAL::to_double((bd.xmax() - bd.xmin()) /
00244           (bd.ymax() - bd.ymin()));
00245     rows = int(sqrt(numCells / ar));
00246     cols = int(numCells) / rows;
00247     // std::cerr << "Using " << rows << "x" << cols << " grid." << std::endl;
00248   }
00249   obsIndex = new SonarObsIndex(bd, rows, cols);
00250 #ifdef REGISTER_QUERY_POINTS
00251   std::vector<Geometry::Point> sources;
00252 #endif
00253   // Read in the observations.
00254   for (int i = 0; i < n; i++) {
00255     Geometry::Point source;
00256     double angle, range;
00257     in >> source >> angle >> range;
00258 #ifdef REGISTER_QUERY_POINTS
00259     sources.push_back(source);
00260 #endif
00261     obs.push_back(new SonarObs(source, angle, range, aperture, maxRange));
00262   }
00263   assert(!in.bad());
00264   // Initialize the likelihood.
00265   for (std::vector<SonarObs*>::iterator it = obs.begin(); 
00266        it != obs.end(); it++)
00267     lp += updateObsLP(*(*it), true);
00268   // Register with the coloring for updates.
00269   getColoring().addListener(*this);
00270 #ifdef REGISTER_QUERY_POINTS
00271   QueryPointIndex index(sources, obs);
00272   c.addQueryPoints(index);
00273 #endif
00274 }
00275 
00276 ArakPosteriorSonarObs::SonarObs::SonarObs(const Geometry::Point& source, 
00277             const double angle,
00278             const double range,
00279             const double aperture,
00280             const double maxRange) {
00281   this->source = source;
00282   double minAngle = angle - aperture / 2.0;
00283   double maxAngle = angle + aperture / 2.0;
00284   this->min = Geometry::Direction(cos(minAngle), sin(minAngle));
00285   this->max = Geometry::Direction(cos(maxAngle), sin(maxAngle));
00286   this->range = range;
00287   lp = 0.0;
00288   updateId = 0;
00289 }
00290 
00291 double ArakPosteriorSonarObs::updateObsLP(SonarObs& o, bool init) const {
00292   const double oldLP = o.lp;
00293   // Update the sequence number.
00294   o.updateId = updateId;
00295   double maxImpact;
00296 #ifdef FIRST_IMPACT_ONLY
00297   SonarImpact impact;
00298   bool isImpact = computeFirstSonarImpact(c, o.source, o.min, o.max, 
00299             Geometry::Kernel::FT(maxRange),
00300             impact);
00301   maxImpact = 
00302     isImpact ? impact.min_range : std::numeric_limits<double>::infinity();
00303 #else
00304   static std::vector<SonarImpact> impacts; // WARNING: static is not threadsafe
00305   computeSonarImpacts(c, o.source, o.min, o.max, 
00306           Geometry::Kernel::FT(maxRange), impacts,
00307           &maxImpact);
00308 #endif
00309   // std::cerr << impacts.size() << " " << maxImpact << std::endl;
00310   // Remove the observation's old entries in the index.
00311   for (SonarObs::CellEntryList::iterator it = o.entries.begin();
00312        it != o.entries.end(); it++)
00313     it->remove();
00314   o.unused.splice(o.unused.begin(), o.entries, 
00315       o.entries.begin(), o.entries.end());
00316   // Use a triangle cell iterator to find all cells the cone currently
00317   // intersects.  First determine the maximum range to which the
00318   // observation is sensitive.  This is the least of the following
00319   // quantities:
00320   // (1) the maximum range measurement
00321   // (2) the maximum impact obtained in the previous scan (which 
00322   //     may coincide with (1))
00323   // (3) the range at which the likelihood of the observation is 
00324   //     effectively zero.
00325   double sensitiveRange = std::min(maxImpact, maxRange);
00326   double effMaxRange = o.range + 5.0 * std::max(cornerRelErrStDev, 
00327             faceRelErrStDev);
00328   sensitiveRange = std::min(sensitiveRange, effMaxRange);
00329   // std::cerr << sensitiveRange << std::endl;
00330   o.relevantRegion = outerApprox(o.source, o.min, o.max, sensitiveRange);
00331   SonarObsIndex::TriangleCellIterator it(*obsIndex, o.relevantRegion), end;
00332   while (it != end) {
00333     SonarObsIndex::Cell::Entry entry = it->add(SonarObsHandle(o));
00334     // Add the entry to this observation's list, using an unused
00335     // list entry if possible.
00336     if (o.unused.empty())
00337       o.entries.push_front(entry);
00338     else {
00339       SonarObs::CellEntryList::iterator jt = o.unused.begin();
00340       *jt = entry;
00341       o.entries.splice(o.entries.begin(), o.unused, jt);
00342     }
00343     ++it;
00344   }
00345   /*
00346   if (impacts.size() > 0) {
00347     std::cerr << "*** " << o.source << std::endl;
00348     for (unsigned int i = 0; i < impacts.size(); i++) {
00349       std::cerr << "(" << impacts[i].proj_angle << ", " 
00350     << impacts[i].min_range << ", " 
00351     << impacts[i].visible_angle << ") ";
00352     }
00353     std::cerr << std::endl;
00354   }
00355   */
00356   // First check that the source is not colored black.
00357   if (c.color(o.source) == BLACK) {
00358     o.lp = -1e10;
00359     return o.lp - oldLP;
00360   }
00361   // Compute the likelihood of the range measurement.
00362 #ifdef FIRST_IMPACT_ONLY
00363   o.lp = ln(likelihood(o.range, &impact, 
00364            isImpact ? &impact + 1 : &impact));
00365 #else
00366   o.lp = ln(likelihood(o.range, impacts.begin(), impacts.end()));
00367 #endif
00368   return o.lp - oldLP;
00369 }
00370 
00371 double ArakPosteriorSonarObs::logMeasure() const {
00372   return prior.logMeasure();
00373 }
00374 
00375 double ArakPosteriorSonarObs::potential() const {
00376   return prior.potential() - lp;
00377 }
00378 
00379 ArakPosteriorSonarObs::~ArakPosteriorSonarObs() { 
00380   for (std::vector<SonarObs*>::iterator it = obs.begin(); 
00381        it != obs.end(); it++)
00382     delete *it;
00383   delete obsIndex;
00384 }
00385 
00386 void ArakPosteriorSonarObs::visualize(CGAL::Qt_widget& widget) const {
00387   /*
00388   // Visualize the density of the index.
00389   const int maxSize = 500;
00390   for (int i = 0; i < obsIndex->numRows(); i++) 
00391     for (int j = 0; j < obsIndex->numCols(); j++) {
00392       const SonarObsIndex::Cell& cell = obsIndex->getCell(i, j);
00393       int size = cell.getItemList().size();
00394       // std::cerr << size << std::endl;
00395       unsigned char intensity = 
00396   int(std::min(double(size) / double(maxSize), 1.0) * 255.0);
00397       CGAL::Color fill(intensity, intensity, intensity);
00398       widget << fill << CGAL::FillColor(fill) << cell;
00399     }
00400   */
00401   for (std::vector<SonarObs*>::const_iterator it = obs.begin(); 
00402        it != obs.end(); it++) {
00403     const SonarObs* o = *it;
00404     if (o->range >= maxRange) continue;
00405     /*
00406     // Visualize the sensitive regions.
00407     widget << CGAL::noFill << CGAL::PURPLE << o->relevantRegion;
00408     */
00409     Geometry::Point p = o->source;
00410     const double one_deg = (M_PI / 180.0); 
00411     const double min_angle = atan2(CGAL::to_double(o->min.dy()), 
00412            CGAL::to_double(o->min.dx()));
00413     const double max_angle = atan2(CGAL::to_double(o->max.dy()), 
00414            CGAL::to_double(o->max.dx()));
00415     // CGAL::Color color = (o->range >= maxRange) ? CGAL::ORANGE : CGAL::RED;
00416     const double max_likelihood = 5.0; // likelihoods can be greater than 1
00417     double normalized = std::min(exp(o->lp), max_likelihood) / max_likelihood;
00418     int n = int(double(jet_size - 1) * normalized);
00419     CGAL::Color color = jet_colors[n];
00420     for (double angle = min_angle + one_deg; 
00421    angle < max_angle; angle += one_deg) {
00422       Geometry::Point p = 
00423   o->source + Geometry::Vector(o->range * cos(angle - one_deg),
00424              o->range * sin(angle - one_deg));
00425       Geometry::Point q = 
00426   o->source + Geometry::Vector(o->range * cos(angle),
00427              o->range * sin(angle));
00428       widget << color << Geometry::Segment(p, q);
00429     }
00430   }
00431 }
00432 
00433 void ArakPosteriorSonarObs::update(const Geometry::Point& a,
00434            const Geometry::Point& b,
00435            const Geometry::Point& c) {
00436   // Compute a set of observations that contains all observations
00437   // sensitive to the coloring in the supplied triangle.
00438   Geometry::Triangle t(a, b, c);
00439   SonarObsIndex::TriangleCellIterator it(*obsIndex, t), end;
00440   // int count = 0;
00441   do {
00442     SonarObsIndex::Cell::ItemList& obsList = it->getItemList();
00443     typedef SonarObsIndex::Cell::ItemList::iterator Iterator;
00444     Iterator jt = obsList.begin();
00445     bool done = (jt == obsList.end());
00446     while (!done) {
00447       SonarObsHandle o = *jt;
00448       done = (++jt == obsList.end());
00449       if ((o->updateId != updateId) &&
00450     (CGAL::do_intersect(t, o->relevantRegion))) {
00451   lp += updateObsLP(*o);
00452   // count++;
00453       }
00454     }
00455   } while (++it != end);
00456   // std::cerr << "Updated " << count << " sonar likelihoods." << std::endl;
00457 }
00458 
00459 void ArakPosteriorSonarObs::recolored(const Geometry::Point& a,
00460               const Geometry::Point& b,
00461               const Geometry::Point& c) {
00462   updateId++;
00463   update(a, b, c);
00464 }
00465 
00466 void ArakPosteriorSonarObs::recolored(const Geometry::Point& a,
00467               const Geometry::Point& b,
00468               const Geometry::Point& c,
00469               const Geometry::Point& d) {
00470   updateId++;
00471   update(a, b, c);
00472   update(a, d, c);
00473 }
00474 

Generated on Wed May 25 14:39:18 2005 for Arak by doxygen 1.3.6