costmap_to_lines_convex_hull.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  *
3  * Software License Agreement (BSD License)
4  *
5  * Copyright (c) 2016,
6  * TU Dortmund - Institute of Control Theory and Systems Engineering.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * * Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  * * Redistributions in binary form must reproduce the above
16  * copyright notice, this list of conditions and the following
17  * disclaimer in the documentation and/or other materials provided
18  * with the distribution.
19  * * Neither the name of the institute nor the names of its
20  * contributors may be used to endorse or promote products derived
21  * from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34  * POSSIBILITY OF SUCH DAMAGE.
35  *
36  * Author: Christoph Rösmann, Otniel Rinaldo
37  *********************************************************************/
38 
40 #include <costmap_converter/misc.h>
41 #include <boost/thread.hpp>
42 #include <boost/thread/mutex.hpp>
44 
46 
47 namespace costmap_converter
48 {
49 
50 CostmapToLinesDBSMCCH::CostmapToLinesDBSMCCH() : CostmapToPolygonsDBSMCCH()
51 {
52  dynamic_recfg_ = NULL;
53 }
54 
56 {
57  if (dynamic_recfg_ != NULL)
58  delete dynamic_recfg_;
59 }
60 
62 {
63  // DB SCAN
64  nh.param("cluster_max_distance", parameter_.max_distance_, 0.4);
65  nh.param("cluster_min_pts", parameter_.min_pts_, 2);
66  nh.param("cluster_max_pts", parameter_.max_pts_, 30);
67  // convex hull
68  nh.param("convex_hull_min_pt_separation", parameter_.min_keypoint_separation_, 0.1);
70 
71  // Line extraction
72  nh.param("support_pts_max_dist", support_pts_max_dist_, 0.3);
73  nh.param("support_pts_max_dist_inbetween", support_pts_max_dist_inbetween_, 1.0);
74  nh.param("min_support_pts", min_support_pts_, 2);
75 
76  // setup dynamic reconfigure
77  dynamic_recfg_ = new dynamic_reconfigure::Server<CostmapToLinesDBSMCCHConfig>(nh);
78  dynamic_reconfigure::Server<CostmapToLinesDBSMCCHConfig>::CallbackType cb = boost::bind(&CostmapToLinesDBSMCCH::reconfigureCB, this, _1, _2);
79  dynamic_recfg_->setCallback(cb);
80 
81  // deprecated
82  if (nh.hasParam("support_pts_min_dist_") || nh.hasParam("support_pts_min_dist"))
83  ROS_WARN("CostmapToLinesDBSMCCH: Parameter 'support_pts_min_dist' is deprecated and not included anymore.");
84  if (nh.hasParam("min_support_pts_"))
85  ROS_WARN("CostmapToLinesDBSMCCH: Parameter 'min_support_pts_' is not found. Remove the underscore.");
86 }
87 
89 {
90  std::vector< std::vector<KeyPoint> > clusters;
91  dbScan(clusters);
92 
93  // Create new polygon container
94  PolygonContainerPtr polygons(new std::vector<geometry_msgs::Polygon>());
95 
96 
97  // add convex hulls to polygon container
98  for (int i = 1; i <clusters.size(); ++i) // skip first cluster, since it is just noise
99  {
100  geometry_msgs::Polygon polygon;
101  convexHull2(clusters[i], polygon );
102 
103  // now extract lines of the polygon (by searching for support points in the cluster)
104  // and add them to the polygon container
105  extractPointsAndLines(clusters[i], polygon, std::back_inserter(*polygons));
106  }
107 
108  // add our non-cluster points to the polygon container (as single points)
109  if (!clusters.empty())
110  {
111  for (int i=0; i < clusters.front().size(); ++i)
112  {
113  polygons->push_back( geometry_msgs::Polygon() );
114  convertPointToPolygon(clusters.front()[i], polygons->back());
115  }
116  }
117 
118  // replace shared polygon container
119  updatePolygonContainer(polygons);
120 }
121 
123 bool sort_keypoint_x(const std::size_t& i, const std::size_t& j, const std::vector<CL::KeyPoint>& cluster)
124 { return (cluster[i].x<cluster[j].x) || (cluster[i].x == cluster[j].x && cluster[i].y < cluster[j].y); }
125 bool sort_keypoint_y(const std::size_t& i, const std::size_t& j, const std::vector<CL::KeyPoint>& cluster)
126 { return (cluster[i].y<cluster[j].y) || (cluster[i].y == cluster[j].y && cluster[i].x < cluster[j].x); }
127 
128 void CostmapToLinesDBSMCCH::extractPointsAndLines(std::vector<KeyPoint>& cluster, const geometry_msgs::Polygon& polygon,
129  std::back_insert_iterator< std::vector<geometry_msgs::Polygon> > lines)
130 {
131  if (polygon.points.empty())
132  return;
133 
134  if (polygon.points.size() < 2)
135  {
136  lines = polygon; // our polygon is just a point, push back onto the output sequence
137  return;
138  }
139  int n = (int)polygon.points.size();
140 
141  for (int i=1; i<n; ++i) // this implemenation requires an explicitly closed polygon (start vertex = end vertex)
142  {
143  const geometry_msgs::Point32* vertex1 = &polygon.points[i-1];
144  const geometry_msgs::Point32* vertex2 = &polygon.points[i];
145 
146  // check how many vertices belong to the line (sometimes the convex hull algorithm finds multiple vertices on a line,
147  // in case of small coordinate deviations)
148  double dx = vertex2->x - vertex1->x;
149  double dy = vertex2->y - vertex1->y;
150 // double norm = std::sqrt(dx*dx + dy*dy);
151 // dx /= norm;
152 // dy /= norm;
153 // for (int j=i; j<(int)polygon.points.size() - 2; ++j)
154 // {
155 // const geometry_msgs::Point32* vertex_jp2 = &polygon.points[j+2];
156 // double dx2 = vertex_jp2->x - vertex2->x;
157 // double dy2 = vertex_jp2->y - vertex2->y;
158 // double norm2 = std::sqrt(dx2*dx2 + dy2*dy2);
159 // dx2 /= norm2;
160 // dy2 /= norm2;
161 // if (std::abs(dx*dx2 + dy*dy2) < 0.05) //~3 degree
162 // {
163 // vertex2 = &polygon.points[j+2];
164 // i = j; // DO NOT USE "i" afterwards
165 // }
166 // else break;
167 // }
168 
169  //Search support points
170  std::vector<std::size_t> support_points; // store indices of cluster
171 
172  for (std::size_t k = 0; k < cluster.size(); ++k)
173  {
174  bool is_inbetween = false;
175  double dist_line_to_point = computeDistanceToLineSegment( cluster[k], *vertex1, *vertex2, &is_inbetween );
176 
177  if(is_inbetween && dist_line_to_point <= support_pts_max_dist_)
178  {
179  support_points.push_back(k);
180  continue;
181  }
182  }
183 
184  // now check if the inlier models a line by checking the minimum distance between all support points (avoid lines over gaps)
185  // and by checking if the minium number of points is reached // deactivate by setting support_pts_max_dist_inbetween_==0
186  bool is_line=true;
188  {
189  if ((int)support_points.size() >= min_support_pts_ + 2) // +2 since start and goal are included
190  {
191  // sort points
192  if (std::abs(dx) >= std::abs(dy))
193  std::sort(support_points.begin(), support_points.end(), boost::bind(sort_keypoint_x, _1, _2, boost::cref(cluster)));
194  else
195  std::sort(support_points.begin(), support_points.end(), boost::bind(sort_keypoint_y, _1, _2, boost::cref(cluster)));
196 
197  // now calculate distances
198  for (int k = 1; k < int(support_points.size()); ++k)
199  {
200  double dist_x = cluster[support_points[k]].x - cluster[support_points[k-1]].x;
201  double dist_y = cluster[support_points[k]].y - cluster[support_points[k-1]].y;
202  double dist = std::sqrt( dist_x*dist_x + dist_y*dist_y);
204  {
205  is_line = false;
206  break;
207  }
208  }
209 
210  }
211  else
212  is_line = false;
213  }
214 
215  if (is_line)
216  {
217  // line found:
218  geometry_msgs::Polygon line;
219  line.points.push_back(*vertex1);
220  line.points.push_back(*vertex2);
221  lines = line; // back insertion
222 
223  // remove inlier from list
224  // iterate in reverse order, otherwise indices are not valid after erasing elements
225  std::vector<std::size_t>::reverse_iterator support_it = support_points.rbegin();
226  for (; support_it != support_points.rend(); ++support_it)
227  {
228  cluster.erase(cluster.begin() + *support_it);
229  }
230  }
231  else
232  {
233  // remove goal, since it will be added in the subsequent iteration
234  //support_points.pop_back();
235  // old:
236 // // add vertex 1 as single point
237 // geometry_msgs::Polygon point;
238 // point.points.push_back(*vertex1);
239 // lines = point; // back insertion
240 
241  // add complete inlier set as points
242 // for (int k = 0; k < int(support_points.size()); ++k)
243 // {
244 // geometry_msgs::Polygon polygon;
245 // convertPointToPolygon(cluster[support_points[k]], polygon);
246 // lines = polygon; // back insertion
247 // }
248 
249  // remove inlier from list and add them as point obstacles
250  // iterate in reverse order, otherwise indices are not valid after erasing elements
251  std::vector<std::size_t>::reverse_iterator support_it = support_points.rbegin();
252  for (; support_it != support_points.rend(); ++support_it)
253  {
254  geometry_msgs::Polygon polygon;
255  convertPointToPolygon(cluster[*support_it], polygon);
256  lines = polygon; // back insertion
257 
258  cluster.erase(cluster.begin() + *support_it);
259  }
260  }
261  }
262 
263  // add all remaining cluster points, that do not belong to a line
264  for (int i=0; i<(int)cluster.size();++i)
265  {
266  geometry_msgs::Polygon polygon;
267  convertPointToPolygon(cluster[i], polygon);
268  lines = polygon; // back insertion
269  }
270 
271 }
272 
273 void CostmapToLinesDBSMCCH::reconfigureCB(CostmapToLinesDBSMCCHConfig& config, uint32_t level)
274 {
275  boost::mutex::scoped_lock lock(parameter_mutex_);
276  parameter_buffered_.max_distance_ = config.cluster_max_distance;
277  parameter_buffered_.min_pts_ = config.cluster_min_pts;
278  parameter_buffered_.max_pts_ = config.cluster_max_pts;
279  parameter_buffered_.min_keypoint_separation_ = config.cluster_min_pts;
280  support_pts_max_dist_ = config.support_pts_max_dist;
281  support_pts_max_dist_inbetween_ = config.support_pts_max_dist_inbetween;
282  min_support_pts_ = config.min_support_pts;
283 }
284 
285 
286 
287 }//end namespace costmap_converter
288 
289 
virtual void compute()
This method performs the actual work (conversion of the costmap to polygons)
virtual void initialize(ros::NodeHandle nh)
Initialize the plugin.
double min_keypoint_separation_
Clear keypoints of the convex polygon that are close to each other [distance in meters] (0: keep all)...
static void convertPointToPolygon(const Point &point, geometry_msgs::Polygon &polygon)
Convert a generi point type to a geometry_msgs::Polygon.
void dbScan(std::vector< std::vector< KeyPoint > > &clusters)
DBSCAN algorithm for clustering.
This class converts the costmap_2d into a set of lines (and points)
bool sort_keypoint_x(const std::size_t &i, const std::size_t &j, const std::vector< CL::KeyPoint > &cluster)
double computeDistanceToLineSegment(const Point &point, const LinePoint &line_start, const LinePoint &line_end, bool *is_inbetween=NULL)
Calculate the distance between a point and a straight line segment.
Definition: misc.h:118
int max_pts_
Parameter for DB_Scan: maximum number of points that define a cluster (to avoid large L- and U-shapes...
PLUGINLIB_EXPORT_CLASS(BAGReader, nodelet::Nodelet)
TFSIMD_FORCE_INLINE const tfScalar & y() const
#define ROS_WARN(...)
dynamic_reconfigure::Server< CostmapToLinesDBSMCCHConfig > * dynamic_recfg_
Dynamic reconfigure server to allow config modifications at runtime.
int min_pts_
Parameter for DB_Scan: minimum number of points that define a cluster.
boost::mutex parameter_mutex_
Mutex that keeps track about the ownership of the shared polygon instance.
double max_distance_
Parameter for DB_Scan, maximum distance to neighbors [m].
bool param(const std::string &param_name, T &param_val, const T &default_val) const
bool sort_keypoint_y(const std::size_t &i, const std::size_t &j, const std::vector< CL::KeyPoint > &cluster)
This abstract class defines the interface for plugins that convert the costmap into polygon types...
TFSIMD_FORCE_INLINE const tfScalar & x() const
This class converts the costmap_2d into a set of convex polygons (and points)
void convexHull2(std::vector< KeyPoint > &cluster, geometry_msgs::Polygon &polygon)
Compute the convex hull for a single cluster.
void reconfigureCB(CostmapToLinesDBSMCCHConfig &config, uint32_t level)
Callback for the dynamic_reconfigure node.
void extractPointsAndLines(std::vector< KeyPoint > &cluster, const geometry_msgs::Polygon &polygon, std::back_insert_iterator< std::vector< geometry_msgs::Polygon > > lines)
Extract points and lines from a given cluster by checking all keypoint pairs of the convex hull for a...
bool hasParam(const std::string &key) const
void updatePolygonContainer(PolygonContainerPtr polygons)
Thread-safe update of the internal polygon container (that is shared using getPolygons() from outside...


costmap_converter
Author(s): Christoph Rösmann , Franz Albers , Otniel Rinaldo
autogenerated on Sat May 16 2020 03:19:18