frontier_search.cpp
Go to the documentation of this file.
2 
3 #include <mutex>
4 
7 #include <geometry_msgs/Point.h>
8 
10 
11 namespace frontier_exploration
12 {
16 
18  double potential_scale, double gain_scale,
19  double min_frontier_size)
20  : costmap_(costmap)
21  , potential_scale_(potential_scale)
22  , gain_scale_(gain_scale)
23  , min_frontier_size_(min_frontier_size)
24 {
25 }
26 
27 std::vector<Frontier> FrontierSearch::searchFrom(geometry_msgs::Point position)
28 {
29  std::vector<Frontier> frontier_list;
30 
31  // Sanity check that robot is inside costmap bounds before searching
32  unsigned int mx, my;
33  if (!costmap_->worldToMap(position.x, position.y, mx, my)) {
34  ROS_ERROR("Robot out of costmap bounds, cannot search for frontiers");
35  return frontier_list;
36  }
37 
38  // make sure map is consistent and locked for duration of search
39  std::lock_guard<costmap_2d::Costmap2D::mutex_t> lock(*(costmap_->getMutex()));
40 
44 
45  // initialize flag arrays to keep track of visited and frontier cells
46  std::vector<bool> frontier_flag(size_x_ * size_y_, false);
47  std::vector<bool> visited_flag(size_x_ * size_y_, false);
48 
49  // initialize breadth first search
50  std::queue<unsigned int> bfs;
51 
52  // find closest clear cell to start search
53  unsigned int clear, pos = costmap_->getIndex(mx, my);
54  if (nearestCell(clear, pos, FREE_SPACE, *costmap_)) {
55  bfs.push(clear);
56  } else {
57  bfs.push(pos);
58  ROS_WARN("Could not find nearby clear cell to start search");
59  }
60  visited_flag[bfs.front()] = true;
61 
62  while (!bfs.empty()) {
63  unsigned int idx = bfs.front();
64  bfs.pop();
65 
66  // iterate over 4-connected neighbourhood
67  for (unsigned nbr : nhood4(idx, *costmap_)) {
68  // add to queue all free, unvisited cells, use descending search in case
69  // initialized on non-free cell
70  if (map_[nbr] <= map_[idx] && !visited_flag[nbr]) {
71  visited_flag[nbr] = true;
72  bfs.push(nbr);
73  // check if cell is new frontier cell (unvisited, NO_INFORMATION, free
74  // neighbour)
75  } else if (isNewFrontierCell(nbr, frontier_flag)) {
76  frontier_flag[nbr] = true;
77  Frontier new_frontier = buildNewFrontier(nbr, pos, frontier_flag);
78  if (new_frontier.size * costmap_->getResolution() >=
80  frontier_list.push_back(new_frontier);
81  }
82  }
83  }
84  }
85 
86  // set costs of frontiers
87  for (auto& frontier : frontier_list) {
88  frontier.cost = frontierCost(frontier);
89  }
90  std::sort(
91  frontier_list.begin(), frontier_list.end(),
92  [](const Frontier& f1, const Frontier& f2) { return f1.cost < f2.cost; });
93 
94  return frontier_list;
95 }
96 
97 Frontier FrontierSearch::buildNewFrontier(unsigned int initial_cell,
98  unsigned int reference,
99  std::vector<bool>& frontier_flag)
100 {
101  // initialize frontier structure
102  Frontier output;
103  output.centroid.x = 0;
104  output.centroid.y = 0;
105  output.size = 1;
106  output.min_distance = std::numeric_limits<double>::infinity();
107 
108  // record initial contact point for frontier
109  unsigned int ix, iy;
110  costmap_->indexToCells(initial_cell, ix, iy);
111  costmap_->mapToWorld(ix, iy, output.initial.x, output.initial.y);
112 
113  // push initial gridcell onto queue
114  std::queue<unsigned int> bfs;
115  bfs.push(initial_cell);
116 
117  // cache reference position in world coords
118  unsigned int rx, ry;
119  double reference_x, reference_y;
120  costmap_->indexToCells(reference, rx, ry);
121  costmap_->mapToWorld(rx, ry, reference_x, reference_y);
122 
123  while (!bfs.empty()) {
124  unsigned int idx = bfs.front();
125  bfs.pop();
126 
127  // try adding cells in 8-connected neighborhood to frontier
128  for (unsigned int nbr : nhood8(idx, *costmap_)) {
129  // check if neighbour is a potential frontier cell
130  if (isNewFrontierCell(nbr, frontier_flag)) {
131  // mark cell as frontier
132  frontier_flag[nbr] = true;
133  unsigned int mx, my;
134  double wx, wy;
135  costmap_->indexToCells(nbr, mx, my);
136  costmap_->mapToWorld(mx, my, wx, wy);
137 
138  geometry_msgs::Point point;
139  point.x = wx;
140  point.y = wy;
141  output.points.push_back(point);
142 
143  // update frontier size
144  output.size++;
145 
146  // update centroid of frontier
147  output.centroid.x += wx;
148  output.centroid.y += wy;
149 
150  // determine frontier's distance from robot, going by closest gridcell
151  // to robot
152  double distance = sqrt(pow((double(reference_x) - double(wx)), 2.0) +
153  pow((double(reference_y) - double(wy)), 2.0));
154  if (distance < output.min_distance) {
155  output.min_distance = distance;
156  output.middle.x = wx;
157  output.middle.y = wy;
158  }
159 
160  // add to queue for breadth first search
161  bfs.push(nbr);
162  }
163  }
164  }
165 
166  // average out frontier centroid
167  output.centroid.x /= output.size;
168  output.centroid.y /= output.size;
169  return output;
170 }
171 
172 bool FrontierSearch::isNewFrontierCell(unsigned int idx,
173  const std::vector<bool>& frontier_flag)
174 {
175  // check that cell is unknown and not already marked as frontier
176  if (map_[idx] != NO_INFORMATION || frontier_flag[idx]) {
177  return false;
178  }
179 
180  // frontier cells should have at least one cell in 4-connected neighbourhood
181  // that is free
182  for (unsigned int nbr : nhood4(idx, *costmap_)) {
183  if (map_[nbr] == FREE_SPACE) {
184  return true;
185  }
186  }
187 
188  return false;
189 }
190 
191 double FrontierSearch::frontierCost(const Frontier& frontier)
192 {
193  return (potential_scale_ * frontier.min_distance *
194  costmap_->getResolution()) -
195  (gain_scale_ * frontier.size * costmap_->getResolution());
196 }
197 }
geometry_msgs::Point centroid
geometry_msgs::Point initial
void mapToWorld(unsigned int mx, unsigned int my, double &wx, double &wy) const
std::vector< unsigned int > nhood4(unsigned int idx, const costmap_2d::Costmap2D &costmap)
Determine 4-connected neighbourhood of an input cell, checking for map edges.
Definition: costmap_tools.h:18
bool isNewFrontierCell(unsigned int idx, const std::vector< bool > &frontier_flag)
isNewFrontierCell Evaluate if candidate cell is a valid candidate for a new frontier.
static const unsigned char FREE_SPACE
#define ROS_WARN(...)
bool nearestCell(unsigned int &result, unsigned int start, unsigned char val, const costmap_2d::Costmap2D &costmap)
Find nearest cell of a specified value.
Definition: costmap_tools.h:91
unsigned char * getCharMap() const
Represents a frontier.
double frontierCost(const Frontier &frontier)
computes frontier cost
Frontier buildNewFrontier(unsigned int initial_cell, unsigned int reference, std::vector< bool > &frontier_flag)
Starting from an initial cell, build a frontier from valid adjacent cells.
double distance(double x0, double y0, double x1, double y1)
std::vector< unsigned int > nhood8(unsigned int idx, const costmap_2d::Costmap2D &costmap)
Determine 8-connected neighbourhood of an input cell, checking for map edges.
Definition: costmap_tools.h:54
unsigned int getSizeInCellsY() const
std::vector< geometry_msgs::Point > points
unsigned int getSizeInCellsX() const
static const unsigned char LETHAL_OBSTACLE
costmap_2d::Costmap2D * costmap_
static const unsigned char NO_INFORMATION
void indexToCells(unsigned int index, unsigned int &mx, unsigned int &my) const
std::vector< Frontier > searchFrom(geometry_msgs::Point position)
Runs search implementation, outward from the start position.
double getResolution() const
geometry_msgs::Point middle
#define ROS_ERROR(...)
bool worldToMap(double wx, double wy, unsigned int &mx, unsigned int &my) const
unsigned int getIndex(unsigned int mx, unsigned int my) const


explore
Author(s): Jiri Horner
autogenerated on Mon Jun 10 2019 13:56:49