crossing_detector.cpp
Go to the documentation of this file.
00001 #include <crossing_detector/crossing_detector.h>
00002 
00003 /* #define DEBUG_CROSSDETECT */ 
00004 
00005 #ifdef DEBUG_CROSSDETECT
00006 #include <fstream>
00007 #include <cassert>
00008 #endif
00009 
00010 #define SIMPLIFY_PROFILE 0
00011 
00012 namespace crossing_detector
00013 {
00014 
00015 #ifdef DEBUG_CROSSDETECT
00016 
00019 void points_output(const std::string& filename, const std::vector<geometry_msgs::Point32>& points)
00020 {
00021   std::ofstream ofs(filename.c_str());
00022   if (!ofs.is_open())
00023   {
00024     ROS_WARN("Cannot open %s for writing", filename.c_str());
00025     return;
00026   }
00027   for (size_t i = 0; i < points.size(); ++i)
00028   {
00029     ofs << points[i].x << " " << points[i].y << "\n";
00030   }
00031   ofs.close();
00032 }
00033 
00036 void points_output(const std::string& filename, const std::vector<Point>& points)
00037 {
00038   std::ofstream ofs(filename.c_str());
00039   if (!ofs.is_open())
00040   {
00041     ROS_WARN("Cannot open %s for writing", filename.c_str());
00042     return;
00043   }
00044   for (size_t i = 0; i < points.size(); ++i)
00045   {
00046     ofs << points[i].x() << " " << points[i].y() << "\n";
00047   }
00048   ofs.close();
00049 }
00050 
00053 void edges_output(const std::string& filename, const Delaunay& dt)
00054 {
00055   std::ofstream ofs(filename.c_str());
00056   if (!ofs.is_open())
00057   {
00058     ROS_WARN("Cannot open %s for writing", filename.c_str());
00059     return;
00060   }
00061   Delaunay::Edge_iterator edge_it = dt.finite_edges_begin();
00062   for (; edge_it != dt.finite_edges_end(); ++edge_it)
00063   {
00064     const Delaunay::Face_handle face = edge_it->first;
00065     const int vertex_num = edge_it->second;
00066 
00067     ofs <<
00068       face->vertex(face->cw(vertex_num))->point().x() << " " <<
00069       face->vertex(face->cw(vertex_num))->point().y() << " " <<
00070       face->vertex(face->ccw(vertex_num))->point().x() << " " <<
00071       face->vertex(face->ccw(vertex_num))->point().y() << "\n";
00072   }
00073   ofs.close();
00074 }
00075 
00078 void candidates_output(const std::string& filename, const Delaunay& dt, const Polygon& polygon, const bool rejected=false)
00079 {
00080   std::ofstream ofs(filename.c_str());
00081   if (!ofs.is_open())
00082   {
00083     ROS_WARN("Cannot open %s for writing", filename.c_str());
00084     return;
00085   }
00086   for (Face_iterator face = dt.finite_faces_begin(); face != dt.finite_faces_end(); ++face)
00087   {
00088     const Point c = dt.circumcenter(face);
00089     if ((polygon.bounded_side(c) == CGAL::ON_BOUNDED_SIDE) ^ rejected)
00090     {
00091       // The circumcenter lies inside place_profile_.
00092       const Point& p = face->vertex(0)->point();
00093       const double circle_radius = std::sqrt((c[0] - p.x()) * (c[0] - p.x()) + (c[1] - p.y()) * (c[1] - p.y()));
00094       ofs << c[0] << " " << c[1] << " " << circle_radius << "\n";
00095     }
00096   }
00097   ofs.close();
00098 }
00099 
00100 #endif
00101 
00102 CrossingDetector::CrossingDetector(const double frontier_width, const double max_frontier_angle) :
00103   frontier_width_(frontier_width),
00104   max_frontier_angle_(max_frontier_angle),
00105   min_relevance_(0.01)
00106 {
00107   ros::NodeHandle private_nh("~");
00108   private_nh.getParam("max_frontier_angle", max_frontier_angle_);
00109   private_nh.getParam("frontier_width", frontier_width_);
00110 }
00111 
00117 Crossing CrossingDetector::crossingDescriptor(const PlaceProfile& profile, const bool normalize)
00118 {
00119   Crossing crossing;
00120 
00121   if (profile.polygon.points.empty())
00122   {
00123     ROS_ERROR("PlaceProfile message must have at least 2 points");
00124     return crossing;
00125   }
00126 
00127   if (normalize)
00128   {
00129     place_profile_ = lama_common::normalizedPlaceProfile(profile);
00130   }
00131   else
00132   {
00133     place_profile_ = profile;
00134   }
00135   vector<Point> inputPoints = delaunayInput(place_profile_);
00136 
00137   ROS_DEBUG("%zu PlaceProfile points", place_profile_.polygon.points.size());
00138   ROS_DEBUG("%zu Delaunay input points", inputPoints.size());
00139 
00140   // Insert points and compute the Delaunay triangulation.
00141   Delaunay triangulation;
00142   triangulation.insert(inputPoints.begin(), inputPoints.end());
00143 
00144   // Build a polygon to test if the circumcenter lies inside place_profile_.
00145   Polygon polygon(inputPoints.begin(), inputPoints.end());
00146 
00147   ROS_DEBUG("Delaunay triangulation is %svalid", triangulation.is_valid() ? "" : "not ");
00148   // check if the polygon is simple (i.e., e.g., if points are sorted according to their angle).
00149   ROS_DEBUG("The polygon is %ssimple", polygon.is_simple() ? "" : "not ");
00150 
00151   for (Face_iterator face = triangulation.finite_faces_begin(); face != triangulation.finite_faces_end(); ++face)
00152   {
00153     const Point c = triangulation.circumcenter(face);
00154     if (polygon.bounded_side(c) == CGAL::ON_BOUNDED_SIDE)
00155     {
00156       // The circumcenter lies inside place_profile_.
00157       const Point& p = face->vertex(0)->point();
00158       const double circle_radius = std::sqrt((c[0] - p.x()) * (c[0] - p.x()) + (c[1] - p.y()) * (c[1] - p.y()));
00159       if (circle_radius > crossing.radius)
00160       {
00161         crossing.center.x = c[0];
00162         crossing.center.y = c[1];
00163         crossing.radius = circle_radius;
00164       }
00165     }
00166   }
00167 
00168   crossing.frontiers = frontiers_();
00169 
00170 #ifdef DEBUG_CROSSDETECT
00171   // Cf. tests/debug_plots.py
00172   // "python $(rospack find crossing_detector)/tests/debug_plots.py"
00173 
00174   // Output place profile.
00175   std::string filename = "/tmp/" + ros::this_node::getName() + "_place_profile.dat";
00176   points_output(filename, place_profile_.polygon.points); 
00177   // Output Delaunay input points.
00178   filename = "/tmp/" + ros::this_node::getName() + "_delaunay_input.dat";
00179   points_output(filename, inputPoints);
00180   // Output Delaunay edges.
00181   filename = "/tmp/" + ros::this_node::getName() + "_delaunay_edges.dat";
00182   edges_output(filename, triangulation);
00183   // Output crossing center candidates.
00184   filename = "/tmp/" + ros::this_node::getName() + "_candidates.dat";
00185   candidates_output(filename, triangulation, polygon);
00186   // Output excluded crossing center.
00187   filename = "/tmp/" + ros::this_node::getName() + "_rejected.dat";
00188   candidates_output(filename, triangulation, polygon, true);
00189 #endif
00190 
00191   return crossing;
00192 }
00193 
00199 vector<Frontier> CrossingDetector::frontiers_() const
00200 {
00201   vector<Frontier> frontiers;
00202 
00203   size_t size = place_profile_.polygon.points.size();
00204 
00205   if (size < 2)
00206   {
00207     ROS_ERROR("PlaceProfile message must have at least 2 points");
00208     return frontiers;
00209   }
00210 
00211   double min_frontier_width2 = frontier_width_ * frontier_width_;
00212 
00213   Frontier frontier;
00214   for(size_t i = 0; i < size; ++i)
00215   {
00216     geometry_msgs::Point32 a(place_profile_.polygon.points[i]);
00217     geometry_msgs::Point32 b(place_profile_.polygon.points[(i + 1) % size]);
00218 
00219     const double width2 = (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
00220 
00221     if (width2 > min_frontier_width2)
00222     {
00223       // Frontier middle.
00224       const double sx = (a.x + b.x) / 2.0;
00225       const double sy = (a.y + b.y) / 2.0;
00226 
00227       const double dist_to_frontier_center = std::sqrt(sx * sx + sy * sy);
00228       const double dot_product_frontier_sx_sy = (b.x - a.x) * sx + (b.y - a.y) * sy;
00229       const double width = std::sqrt(width2);
00230       double frontier_angle = max_frontier_angle_;
00231       if ((width > 0) && (dist_to_frontier_center > 0))
00232       {
00233         frontier_angle = M_PI_2 - std::acos(std::abs(dot_product_frontier_sx_sy / width / dist_to_frontier_center));
00234       }
00235       if (frontier_angle < max_frontier_angle_)
00236       {
00237         frontier.p1.x = a.x;
00238         frontier.p1.y = a.y;
00239         frontier.p2.x = b.x;
00240         frontier.p2.y = b.y;
00241         frontier.width = width;
00242         frontier.angle = std::atan2(sy, sx);
00243         frontiers.push_back(frontier);
00244       }
00245     }
00246     a = b;
00247   }
00248   return frontiers;
00249 }
00250 
00258 vector<Frontier> CrossingDetector::frontiers(const PlaceProfile& profile, const bool normalize)
00259 {
00260   if (normalize)
00261   {
00262     place_profile_ = lama_common::normalizedPlaceProfile(profile);
00263   }
00264   else
00265   {
00266     place_profile_ = profile;
00267   }
00268   return frontiers_();
00269 }
00270 
00274 vector<Point> CrossingDetector::delaunayInput(const PlaceProfile& profile) const
00275 {
00276   PlaceProfile delaunayProfile = profile;
00277 
00278 #if SIMPLIFY_PROFILE
00279   /*
00280    * Two extra operations are realized:
00281    * - reduce the number of points with a relevance filter (less points on a single "line segment").
00282    * - fill frontiers so that the crossing center will not be found at frontiers.
00283    */
00284   lama_common::simplifyPlaceProfile(delaunayProfileprofile, min_relevance_);
00285   ROS_DEBUG("%zu relevant points in the PlaceProfile", delaunayProfile.polygon.points.size());
00286   lama_common::closePlaceProfile(delaunayProfile, frontier_width_ / 2);
00287 #endif
00288 
00289   vector<Point> points;
00290   points.reserve(delaunayProfile.polygon.points.size());
00291   for (size_t i = 0; i < delaunayProfile.polygon.points.size(); ++i)
00292   {
00293     points.push_back(Point(delaunayProfile.polygon.points[i].x, delaunayProfile.polygon.points[i].y));
00294   }
00295   return points;
00296 }
00297 
00298 } // namespace crossing_detector
00299 


crossing_detector
Author(s): Gaël Ecorchard , Karel Košnar , Vojtěch Vonásek
autogenerated on Thu Jun 6 2019 22:02:06