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
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;
00039 prevImpact.min_range = -1.0;
00040 corner.min_range = -1.0;
00041 }
00042
00043 ScanEventProcessor() : maxRange(-1.0), impacts(NULL) { }
00044
00045 const ScanEvent& operator=(const ScanEvent& event) {
00046
00047
00048
00049 if ((this->prevImpact.min_range >= 0.0) &&
00050 (this->prevImpact.pmax == event.pmin)) {
00051
00052 corner = SonarImpact(this->source,
00053 prevImpact.pmin,
00054 event.dmin,
00055 event.pmin,
00056 event.pmax);
00057 } else {
00058
00059 corner.min_range = -1.0;
00060 }
00061
00062
00063
00064 Geometry::Point pmin = event.pmin;
00065 Geometry::Point pmax = event.pmax;
00066 SonarImpact tmp(this->source, event.dmin, event.dmax, pmin, pmax);
00067
00068 if (tmp.min_range <= this->maxRange) {
00069
00070 this->impact = tmp;
00071 dir = event.dmax;
00072 } else {
00073
00074 this->impact.min_range = -1.0;
00075 }
00076 return event;
00077 }
00078
00079 ScanEventProcessor operator++() {
00080
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
00087 impacts->push_back(impact);
00088
00089 prevImpact = impact;
00090 }
00091 return *this;
00092 }
00093
00094 ScanEventProcessor operator++(int) {
00095 ScanEventProcessor tmp(*this);
00096 ++(*this);
00097 return tmp;
00098 }
00099 };
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
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
00144
00145
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;
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
00161 if (edges.empty()) return false;
00162
00163
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
00169
00170 se.init(vertex, min, max, *it);
00171
00172 if (!cones_intersect(min, max, se.dmin, se.dmax)) continue;
00173
00174 SonarImpact i = SonarImpact(vertex, se.dmin, se.dmax, se.pmin, se.pmax);
00175 if (i.min_range >= closest) continue;
00176
00177
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
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
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
00239
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
00248 }
00249 obsIndex = new SonarObsIndex(bd, rows, cols);
00250 #ifdef REGISTER_QUERY_POINTS
00251 std::vector<Geometry::Point> sources;
00252 #endif
00253
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
00265 for (std::vector<SonarObs*>::iterator it = obs.begin();
00266 it != obs.end(); it++)
00267 lp += updateObsLP(*(*it), true);
00268
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
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;
00305 computeSonarImpacts(c, o.source, o.min, o.max,
00306 Geometry::Kernel::FT(maxRange), impacts,
00307 &maxImpact);
00308 #endif
00309
00310
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
00317
00318
00319
00320
00321
00322
00323
00324
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
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
00335
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
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357 if (c.color(o.source) == BLACK) {
00358 o.lp = -1e10;
00359 return o.lp - oldLP;
00360 }
00361
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
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398
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
00407
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
00416 const double max_likelihood = 5.0;
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
00437
00438 Geometry::Triangle t(a, b, c);
00439 SonarObsIndex::TriangleCellIterator it(*obsIndex, t), end;
00440
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
00453 }
00454 }
00455 } while (++it != end);
00456
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