costmap_to_polygons.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 <boost/thread.hpp>
41 #include <boost/thread/mutex.hpp>
43 
45 
46 namespace costmap_converter
47 {
48 
49 CostmapToPolygonsDBSMCCH::CostmapToPolygonsDBSMCCH() : BaseCostmapToPolygons()
50 {
51  costmap_ = NULL;
52  dynamic_recfg_ = NULL;
53 }
54 
56 {
57  if (dynamic_recfg_ != NULL)
58  delete dynamic_recfg_;
59 }
60 
62 {
63  costmap_ = NULL;
64 
65  max_distance_ = 0.4;
66  nh.param("cluster_max_distance", max_distance_, max_distance_);
67 
68  min_pts_ = 2;
69  nh.param("cluster_min_pts", min_pts_, min_pts_);
70 
71  max_pts_ = 30;
72  nh.param("cluster_max_pts", max_pts_, max_pts_);
73 
75  nh.param("convex_hull_min_pt_separation", min_keypoint_separation_, min_keypoint_separation_);
76 
77  // setup dynamic reconfigure
78  dynamic_recfg_ = new dynamic_reconfigure::Server<CostmapToPolygonsDBSMCCHConfig>(nh);
79  dynamic_reconfigure::Server<CostmapToPolygonsDBSMCCHConfig>::CallbackType cb = boost::bind(&CostmapToPolygonsDBSMCCH::reconfigureCB, this, _1, _2);
80  dynamic_recfg_->setCallback(cb);
81 }
82 
83 
85 {
86  std::vector< std::vector<KeyPoint> > clusters;
87  dbScan(occupied_cells_, clusters);
88 
89  // Create new polygon container
90  PolygonContainerPtr polygons(new std::vector<geometry_msgs::Polygon>());
91 
92 
93  // add convex hulls to polygon container
94  for (std::size_t i = 1; i <clusters.size(); ++i) // skip first cluster, since it is just noise
95  {
96  polygons->push_back( geometry_msgs::Polygon() );
97  convexHull2(clusters[i], polygons->back() );
98  }
99 
100  // add our non-cluster points to the polygon container (as single points)
101  if (!clusters.empty())
102  {
103  for (std::size_t i=0; i < clusters.front().size(); ++i)
104  {
105  polygons->push_back( geometry_msgs::Polygon() );
106  convertPointToPolygon(clusters.front()[i], polygons->back());
107  }
108  }
109 
110  // replace shared polygon container
111  updatePolygonContainer(polygons);
112 }
113 
115 {
116  if (!costmap)
117  return;
118 
119  costmap_ = costmap;
120 
121  updateCostmap2D();
122 }
123 
125 {
126  occupied_cells_.clear();
127 
128  if (!costmap_->getMutex())
129  {
130  ROS_ERROR("Cannot update costmap since the mutex pointer is null");
131  return;
132  }
133 
134  int idx = 0;
135 
136  costmap_2d::Costmap2D::mutex_t::scoped_lock lock(*costmap_->getMutex());
137 
138  // get indices of obstacle cells
139  for(std::size_t i = 0; i < costmap_->getSizeInCellsX(); i++)
140  {
141  for(std::size_t j = 0; j < costmap_->getSizeInCellsY(); j++)
142  {
143  int value = costmap_->getCost(i,j);
144  if(value >= costmap_2d::LETHAL_OBSTACLE)
145  {
146  double x, y;
147  costmap_->mapToWorld((unsigned int)i, (unsigned int)j, x, y);
148  occupied_cells_.push_back( KeyPoint( x, y ) );
149  }
150  ++idx;
151  }
152  }
153 }
154 
155 
156 void CostmapToPolygonsDBSMCCH::dbScan(const std::vector<KeyPoint>& occupied_cells, std::vector< std::vector<KeyPoint> >& clusters)
157 {
158  std::vector<bool> visited(occupied_cells.size(), false);
159 
160  clusters.clear();
161 
162  //DB Scan Algorithm
163  int cluster_id = 0; // current cluster_id
164  clusters.push_back(std::vector<KeyPoint>());
165  for(int i = 0; i< (int)occupied_cells.size(); i++)
166  {
167  if(!visited[i]) //keypoint has not been visited before
168  {
169  visited[i] = true; // mark as visited
170  std::vector<int> neighbors;
171  regionQuery(occupied_cells, i, neighbors); //Find neighbors around the keypoint
172  if((int)neighbors.size() < min_pts_) //If not enough neighbors are found, mark as noise
173  {
174  clusters[0].push_back(occupied_cells[i]);
175  }
176  else
177  {
178  ++cluster_id; // increment current cluster_id
179  clusters.push_back(std::vector<KeyPoint>());
180 
181  // Expand the cluster
182  clusters[cluster_id].push_back(occupied_cells[i]);
183  for(int j = 0; j<(int)neighbors.size(); j++)
184  {
185  if ((int)clusters[cluster_id].size() == max_pts_)
186  break;
187 
188  if(!visited[neighbors[j]]) //keypoint has not been visited before
189  {
190  visited[neighbors[j]] = true; // mark as visited
191  std::vector<int> further_neighbors;
192  regionQuery(occupied_cells, neighbors[j], further_neighbors); //Find more neighbors around the new keypoint
193 // if(further_neighbors.size() < min_pts_)
194 // {
195 // clusters[0].push_back(occupied_cells[neighbors[j]]);
196 // }
197 // else
198  if ((int)further_neighbors.size() >= min_pts_)
199  {
200  // neighbors found
201  neighbors.insert(neighbors.end(), further_neighbors.begin(), further_neighbors.end()); //Add these newfound P' neighbour to P neighbour vector "nb_indeces"
202  clusters[cluster_id].push_back(occupied_cells[neighbors[j]]);
203  }
204  }
205  }
206  }
207  }
208  }
209 }
210 
211 void CostmapToPolygonsDBSMCCH::regionQuery(const std::vector<KeyPoint>& occupied_cells, int curr_index, std::vector<int>& neighbors)
212 {
213  neighbors.clear();
214  double curr_index_x = occupied_cells[curr_index].x;
215  double curr_index_y = occupied_cells[curr_index].y;
216 
217  for(int i = 0; i < (int)occupied_cells.size(); i++)
218  {
219  double neighbor_x = occupied_cells[i].x;
220  double neighbor_y = occupied_cells[i].y;
221  double dist = sqrt(pow((curr_index_x - neighbor_x),2)+pow((curr_index_y - neighbor_y),2)); //euclidean distance between two points // TODO map resolution
222  if(dist <= max_distance_ && dist != 0.0f)
223  neighbors.push_back(i);
224  }
225 }
226 
228 {
229  return p1.x < p2.x || (p1.x == p2.x && p1.y < p2.y);
230 }
231 
232 void CostmapToPolygonsDBSMCCH::convexHull(std::vector<KeyPoint>& cluster, geometry_msgs::Polygon& polygon)
233 {
234  //Monotone Chain ConvexHull Algorithm source from http://www.algorithmist.com/index.php/Monotone_Chain_Convex_Hull
235 
236  int k = 0;
237  int n = cluster.size();
238 
239  // sort points according to x coordinate (TODO. is it already sorted due to the map representation?)
240  std::sort(cluster.begin(), cluster.end(), isXCoordinateSmaller);
241 
242  polygon.points.resize(2*n);
243 
244  // lower hull
245  for (int i = 0; i < n; ++i)
246  {
247  while (k >= 2 && cross(polygon.points[k-2], polygon.points[k-1], cluster[i]) <= 0)
248  {
249  --k;
250  }
251  cluster[i].toPointMsg(polygon.points[k]);
252  ++k;
253  }
254 
255  // upper hull
256  for (int i = n-2, t = k+1; i >= 0; --i)
257  {
258  while (k >= t && cross(polygon.points[k-2], polygon.points[k-1], cluster[i]) <= 0)
259  {
260  --k;
261  }
262  cluster[i].toPointMsg(polygon.points[k]);
263  ++k;
264  }
265 
266 
267  polygon.points.resize(k); // original
268  // TEST we skip the last point, since in our definition the polygon vertices do not contain the start/end vertex twice.
269 // polygon.points.resize(k-1); // TODO remove last point from the algorithm above to reduce computational cost
270 
271 
272 
273  if (min_keypoint_separation_>0) // TODO maybe migrate to algorithm above to speed up computation
274  {
275  for (int i=0; i < (int) polygon.points.size() - 1; ++i)
276  {
277  if ( std::sqrt(std::pow((polygon.points[i].x - polygon.points[i+1].x),2) + std::pow((polygon.points[i].y - polygon.points[i+1].y),2)) < min_keypoint_separation_ )
278  polygon.points.erase(polygon.points.begin()+i+1);
279  }
280  }
281 }
282 
283 
284 
285 void CostmapToPolygonsDBSMCCH::convexHull2(std::vector<KeyPoint>& cluster, geometry_msgs::Polygon& polygon)
286 {
287  std::vector<KeyPoint> P = cluster;
288  std::vector<geometry_msgs::Point32>& points = polygon.points;
289 
290  // Sort P by x and y
291  for (int i = 0; i < (int)P.size(); i++)
292  {
293  for (int j = i + 1; j < (int)P.size(); j++)
294  {
295  if (P[j].x < P[i].x || (P[j].x == P[i].x && P[j].y < P[i].y))
296  {
297  KeyPoint tmp = P[i];
298  P[i] = P[j];
299  P[j] = tmp;
300  }
301  }
302  }
303 
304  // the output array H[] will be used as the stack
305  int i; // array scan index
306 
307  // Get the indices of points with min x-coord and min|max y-coord
308  int minmin = 0, minmax;
309  double xmin = P[0].x;
310  for (i = 1; i < (int)P.size(); i++)
311  if (P[i].x != xmin) break;
312  minmax = i - 1;
313  if (minmax == (int)P.size() - 1)
314  { // degenerate case: all x-coords == xmin
315  points.push_back(geometry_msgs::Point32());
316  P[minmin].toPointMsg(points.back());
317  if (P[minmax].y != P[minmin].y) // a nontrivial segment
318  {
319  points.push_back(geometry_msgs::Point32());
320  P[minmax].toPointMsg(points.back());
321  }
322  // add polygon endpoint
323  points.push_back(geometry_msgs::Point32());
324  P[minmin].toPointMsg(points.back());
325  return;
326  }
327 
328  // Get the indices of points with max x-coord and min|max y-coord
329  int maxmin, maxmax = (int)P.size() - 1;
330  double xmax = P.back().x;
331  for (i = P.size() - 2; i >= 0; i--)
332  if (P[i].x != xmax) break;
333  maxmin = i+1;
334 
335  // Compute the lower hull on the stack H
336  // push minmin point onto stack
337  points.push_back(geometry_msgs::Point32());
338  P[minmin].toPointMsg(points.back());
339  i = minmax;
340  while (++i <= maxmin)
341  {
342  // the lower line joins P[minmin] with P[maxmin]
343  if (cross(P[minmin], P[maxmin], P[i]) >= 0 && i < maxmin)
344  continue; // ignore P[i] above or on the lower line
345 
346  while (points.size() > 1) // there are at least 2 points on the stack
347  {
348  // test if P[i] is left of the line at the stack top
349  if (cross(points[points.size() - 2], points.back(), P[i]) > 0)
350  break; // P[i] is a new hull vertex
351  points.pop_back(); // pop top point off stack
352  }
353  // push P[i] onto stack
354  points.push_back(geometry_msgs::Point32());
355  P[i].toPointMsg(points.back());
356  }
357 
358  // Next, compute the upper hull on the stack H above the bottom hull
359  if (maxmax != maxmin) // if distinct xmax points
360  {
361  // push maxmax point onto stack
362  points.push_back(geometry_msgs::Point32());
363  P[maxmax].toPointMsg(points.back());
364  }
365  int bot = (int)points.size(); // the bottom point of the upper hull stack
366  i = maxmin;
367  while (--i >= minmax)
368  {
369  // the upper line joins P[maxmax] with P[minmax]
370  if (cross( P[maxmax], P[minmax], P[i]) >= 0 && i > minmax)
371  continue; // ignore P[i] below or on the upper line
372 
373  while ((int)points.size() > bot) // at least 2 points on the upper stack
374  {
375  // test if P[i] is left of the line at the stack top
376  if (cross(points[points.size() - 2], points.back(), P[i]) > 0)
377  break; // P[i] is a new hull vertex
378  points.pop_back(); // pop top point off stack
379  }
380  // push P[i] onto stack
381  points.push_back(geometry_msgs::Point32());
382  P[i].toPointMsg(points.back());
383  }
384  if (minmax != minmin)
385  {
386  // push joining endpoint onto stack
387  points.push_back(geometry_msgs::Point32());
388  P[minmin].toPointMsg(points.back());
389  }
390 
391  if (min_keypoint_separation_>0) // TODO maybe migrate to algorithm above to speed up computation
392  {
393  for (int i=0; i < (int) polygon.points.size() - 1; ++i)
394  {
395  if ( std::sqrt(std::pow((polygon.points[i].x - polygon.points[i+1].x),2) + std::pow((polygon.points[i].y - polygon.points[i+1].y),2)) < min_keypoint_separation_ )
396  polygon.points.erase(polygon.points.begin()+i+1);
397  }
398  }
399 }
400 
402 {
403  boost::mutex::scoped_lock lock(mutex_);
404  polygons_ = polygons;
405 }
406 
407 
409 {
410  boost::mutex::scoped_lock lock(mutex_);
412  return polygons;
413 }
414 
415 void CostmapToPolygonsDBSMCCH::reconfigureCB(CostmapToPolygonsDBSMCCHConfig& config, uint32_t level)
416 {
417  max_distance_ = config.cluster_max_distance;
418  min_pts_ = config.cluster_min_pts;
419  max_pts_ = config.cluster_max_pts;
420  min_keypoint_separation_ = config.cluster_min_pts;
421 }
422 
423 }//end namespace costmap_converter
424 
425 
virtual void updateCostmap2D()
Get updated data from the previously set Costmap2D.
void reconfigureCB(CostmapToPolygonsDBSMCCHConfig &config, uint32_t level)
Callback for the dynamic_reconfigure node.
std::vector< KeyPoint > occupied_cells_
List of occupied cells in the current map (updated by updateCostmap2D())
int max_pts_
Parameter for DB_Scan: maximum number of points that define a cluster (to avoid large L- and U-shapes...
static void convertPointToPolygon(const Point &point, geometry_msgs::Polygon &polygon)
Convert a generi point type to a geometry_msgs::Polygon.
void mapToWorld(unsigned int mx, unsigned int my, double &wx, double &wy) const
double max_distance_
Parameter for DB_Scan, maximum distance to neighbors [m].
f
double min_keypoint_separation_
Clear keypoints of the convex polygon that are close to each other [distance in meters] (0: keep all)...
virtual void compute()
This method performs the actual work (conversion of the costmap to polygons)
PLUGINLIB_EXPORT_CLASS(BAGReader, nodelet::Nodelet)
TFSIMD_FORCE_INLINE const tfScalar & y() const
virtual void setCostmap2D(costmap_2d::Costmap2D *costmap)
Pass a pointer to the costap to the plugin.
unsigned char getCost(unsigned int mx, unsigned int my) const
costmap_2d::Costmap2D * costmap_
Pointer to the costmap2d.
bool param(const std::string &param_name, T &param_val, const T &default_val) const
This abstract class defines the interface for plugins that convert the costmap into polygon types...
TFSIMD_FORCE_INLINE const tfScalar & x() const
dynamic_reconfigure::Server< CostmapToPolygonsDBSMCCHConfig > * dynamic_recfg_
Dynamic reconfigure server to allow config modifications at runtime.
unsigned int getSizeInCellsY() const
int min_pts_
Parameter for DB_Scan: minimum number of points that define a cluster.
This class converts the costmap_2d into a set of convex polygons (and points)
PolygonContainerPtr polygons_
Current shared container of polygons.
void dbScan(const std::vector< KeyPoint > &occupied_cells, std::vector< std::vector< KeyPoint > > &clusters)
DBSCAN algorithm for clustering.
void convexHull2(std::vector< KeyPoint > &cluster, geometry_msgs::Polygon &polygon)
Compute the convex hull for a single cluster.
unsigned int getSizeInCellsX() const
static const unsigned char LETHAL_OBSTACLE
boost::mutex mutex_
Mutex that keeps track about the ownership of the shared polygon instance.
void regionQuery(const std::vector< KeyPoint > &occupied_cells, int curr_index, std::vector< int > &neighbor_indices)
Helper function for dbScan to search for neighboring points.
bool isXCoordinateSmaller(const CostmapToPolygonsDBSMCCH::KeyPoint &p1, const CostmapToPolygonsDBSMCCH::KeyPoint &p2)
Defines a keypoint in metric coordinates of the map.
void updatePolygonContainer(PolygonContainerPtr polygons)
Thread-safe update of the internal polygon container (that is shared using getPolygons() from outside...
#define ROS_ERROR(...)
void convexHull(std::vector< KeyPoint > &cluster, geometry_msgs::Polygon &polygon)
Compute the convex hull for a single cluster (monotone chain algorithm)
PolygonContainerConstPtr getPolygons()
Get a shared instance of the current polygon container.
virtual void initialize(ros::NodeHandle nh)
Initialize the plugin.
long double cross(const P1 &O, const P2 &A, const P3 &B)
2D Cross product of two vectors defined by two points and a common origin


costmap_converter
Author(s): Christoph Rösmann , Franz Albers , Otniel Rinaldo
autogenerated on Fri Jun 7 2019 21:48:43