Go to the documentation of this file.00001 #include <crossing_detector/crossing_detector.h>
00002
00003
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
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
00141 Delaunay triangulation;
00142 triangulation.insert(inputPoints.begin(), inputPoints.end());
00143
00144
00145 Polygon polygon(inputPoints.begin(), inputPoints.end());
00146
00147 ROS_DEBUG("Delaunay triangulation is %svalid", triangulation.is_valid() ? "" : "not ");
00148
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
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
00172
00173
00174
00175 std::string filename = "/tmp/" + ros::this_node::getName() + "_place_profile.dat";
00176 points_output(filename, place_profile_.polygon.points);
00177
00178 filename = "/tmp/" + ros::this_node::getName() + "_delaunay_input.dat";
00179 points_output(filename, inputPoints);
00180
00181 filename = "/tmp/" + ros::this_node::getName() + "_delaunay_edges.dat";
00182 edges_output(filename, triangulation);
00183
00184 filename = "/tmp/" + ros::this_node::getName() + "_candidates.dat";
00185 candidates_output(filename, triangulation, polygon);
00186
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
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
00281
00282
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 }
00299