mst_path.cpp
Go to the documentation of this file.
1 #include "lib/mst_path.h"
2 
4 {
5 }
6 
7 bool mst_path::generate_path (geometry_msgs::Point start)
8 {
9  // reset counter of current waypoint
10  this->wp = 0;
11 
12  // starting vertex
13  geometry_msgs::Point wp;
14  wp.x = (start.x - origin.x) * cos(rotation) - (start.y - origin.y) * sin(rotation);
15  wp.y = (start.x - origin.x) * sin(rotation) + (start.y - origin.y) * cos(rotation);
16  path.push_back(wp);
17  int current = 2 * round(wp.y / map.info.resolution) * 2*map.info.width + 2 * round(wp.x / map.info.resolution);
18 
19  // visited vertices
20  unordered_set<int> removed;
21 
22  // previous vertex
23  int previous;
24 
25  // last movement
26  int offset;
27 
28  // possible movements
29  vector<int> movement;
30  movement.push_back(2*map.info.width);
31  movement.push_back(-1);
32  movement.push_back(-2*map.info.width);
33  movement.push_back(1);
34 
35  // valid movement
36  bool found = false;
37 
38  // look for valid first step
39  for (int idx=0; idx<movement.size(); idx++) {
40  if (nodes[current].count(current + movement[idx]) > 0) {
41  previous = current + movement[idx];
42  found = true;
43  break;
44  }
45  }
46 
47  // no valid first step found
48  if (!found) {
49  ROS_ERROR("No path found!");
50  return false;
51  }
52 
53  // generate path
54  do {
55  // remember visited vertices
56  removed.insert(current);
57 
58  // last movement
59  offset = distance(movement.begin(), find(movement.begin(), movement.end(), previous-current));
60 
61  // look for valid step
62  found = false;
63  previous = current;
64  for (int idx=0; idx<movement.size(); idx++){
65  if (nodes[previous].count(previous + movement[(idx+offset) % movement.size()]) > 0 &&
66  removed.count(previous + movement[(idx+offset) % movement.size()]) <= 0) {
67  current = previous + movement[(idx+offset) % movement.size()];
68  found = true;
69  break;
70  }
71  }
72  if (!found) {
73  break;
74  }
75 
76  // remove vertices from sets
77  nodes[current].erase(previous);
78  nodes[previous].erase(current);
79 
80  // convert index to relative waypoint position on map
81  wp.x = (current % (2*map.info.width)) / 2.0 * map.info.resolution;
82  wp.y = (current / (2*map.info.width)) / 2.0 * map.info.resolution;
83 
84  // shift waypoint to center path on map
85  double width_path = (round(width / map.info.resolution) * 2 - 1) / 2.0 * map.info.resolution;
86  double height_path = (round(height / map.info.resolution) * 2 - 1) / 2.0 * map.info.resolution;
87  wp.x += (width - width_path) / 2.0;
88  wp.y += (height - height_path) / 2.0;
89 
90  // add vertex to path
91  path.push_back(wp);
92  } while (found);
93 
94  // final waypoint
95  wp = path.front();
96  path.push_back(wp);
97 
98  return true;
99 }
100 
101 nav_msgs::Path mst_path::get_path ()
102 {
103  nav_msgs::Path nav_path;
104  vector<geometry_msgs::PoseStamped> poses;
105  geometry_msgs::PoseStamped pose;
106  for (auto p : path) {
107  // rotate
108  pose.pose.position.x = p.x * cos(-rotation) - p.y * sin(-rotation);
109  pose.pose.position.y = p.x * sin(-rotation) + p.y * cos(-rotation);
110 
111  // relative to original map
112  pose.pose.position.x += origin.x;
113  pose.pose.position.y += origin.y;
114 
115  poses.push_back(pose);
116  }
117  nav_path.poses = poses;
118  nav_path.header.stamp = Time::now();
119  nav_path.header.frame_id = "local_origin_ned";
120  return nav_path;
121 }
122 
123 geometry_msgs::Point mst_path::get_waypoint (geometry_msgs::Point position, double tolerance)
124 {
125  ROS_DEBUG("Check if distance between (%.2f,%.2f) and (%.2f,%.2f) < %.2f", position.x, position.y, get_wp().x, get_wp().y, tolerance);
126 
127  // close enough to or past current waypoint
128  if (dist(position, get_wp()) < tolerance || dist(position, get_wp(1)) < dist(position, get_wp())) {
129  // select next waypoint
130  ++wp;
131  ROS_DEBUG("Select waypoint %d", wp);
132  }
133 
134  ROS_DEBUG("Return waypoint %d at (%.2f,%.2f)", wp, get_wp().x, get_wp().y);
135 
136  return get_wp();
137 }
138 
139 void mst_path::initialize_graph (nav_msgs::OccupancyGrid gridmap, bool vertical, bool connect4)
140 {
141  ROS_DEBUG("Gridmap size %dx%d origin (%.2f,%.2f)", gridmap.info.width, gridmap.info.height, gridmap.info.origin.position.x, gridmap.info.origin.position.y);
142  // orientation of path edges
143  this->vertical = vertical;
144 
145  // initialize path
146  map = gridmap;
147  int rows = gridmap.info.height;
148  int cols = gridmap.info.width;
149  nodes.clear();
150  nodes.resize(2*rows*2*cols);
151  edges.clear();
152  path.clear();
153 
154  // double graph resolution
155  valarray<bool> inflated(2*rows*2*cols);
156  for (int i=0; i<2*rows; i++) {
157  for (int j=0; j<2*cols; j++) {
158  inflated[i*2*cols + j] = (gridmap.data[(i/2)*cols + j/2] == 0);
159  }
160  }
161 
162  // iterate all rows and columns
163  for (int i=0; i<2*rows; i++) {
164  for (int j=0; j<2*cols; j++) {
165  // found a vertex
166  if (inflated[2*i*cols+j]) {
167  // check von neumann neighborhood for connected vertices
168  if (i>0 && inflated[(i-1)*2*cols+j]) {
169  add_edge(i*2*cols+j, (i-1)*2*cols+j, 1);
170  }
171  if (i<2*rows-1 && inflated[(i+1)*2*cols+j]) {
172  add_edge(i*2*cols+j, (i+1)*2*cols+j, 1);
173  }
174  if (j>0 && inflated[i*2*cols+j-1]) {
175  add_edge(i*2*cols+j, i*2*cols+j-1, 1);
176  }
177  if (j<2*cols-1 && inflated[i*2*cols+j+1]) {
178  add_edge(i*2*cols+j, i*2*cols+j+1, 1);
179  }
180 
181  // check moore neighborhood for connected vertices
182  if (!connect4) {
183  if (i>0 && j>0 && inflated[(i-1)*2*cols+j-1]) {
184  add_edge(i*2*cols+j, (i-1)*2*cols+j-1, 1);
185  }
186  if (i<2*rows-1 && j<2*cols-1 && inflated[(i+1)*2*cols+j+1]) {
187  add_edge(i*2*cols+j, (i+1)*2*cols+j+1, 1);
188  }
189  if (i<2*rows-1 && j>0 && inflated[(i+1)*2*cols+j-1]) {
190  add_edge(i*2*cols+j, (i+1)*2*cols+j-1, 1);
191  }
192  if (i>0 && j<2*cols-1 && inflated[(i-1)*2*cols+j+1]) {
193  add_edge(i*2*cols+j, (i-1)*2*cols+j+1, 1);
194  }
195  }
196  }
197  }
198  }
199 }
200 
201 void mst_path::initialize_map (geometry_msgs::Point origin, double rotation, double width, double height)
202 {
203  // bottom left of area
204  this->origin = origin;
205 
206  // rotation of map
207  this->rotation = rotation;
208 
209  // dimensions of area
210  this->width = width;
211  this->height = height;
212 
213  ROS_DEBUG("Area size %.2fx%.2f origin (%.2f,%.2f)", width, height, origin.x, origin.y);
214 }
215 
216 void mst_path::initialize_tree (vector<edge> mst)
217 {
218  // remove edges not required by mst
219  int cols = map.info.width;
220  int alpha, vmax, vmin;
221  for (int i=0; i<mst.size(); i++) {
222  vmax = max(mst[i].from, mst[i].to);
223  vmin = min(mst[i].from, mst[i].to);
224 
225  if (vmax - vmin == 1) {
226  alpha = (4*vmin + 3) - 2 * (vmax % cols);
227  remove_edge(edge(alpha, alpha + 2*cols, 1));
228  remove_edge(edge(alpha + 2*cols, alpha, 1));
229  remove_edge(edge(alpha+1, alpha+1 + 2*cols, 1));
230  remove_edge(edge(alpha+1 + 2*cols, alpha+1, 1));
231 
232  }
233  else{
234  alpha = (4*vmin + 2*cols) - 2 * (vmax % cols);
235  remove_edge(edge(alpha, alpha+1, 1));
236  remove_edge(edge(alpha+1, alpha, 1));
237  remove_edge(edge(alpha + 2*cols, alpha+1 + 2*cols, 1));
238  remove_edge(edge(alpha+1 + 2*cols, alpha + 2*cols, 1));
239  }
240  }
241 }
242 
244 {
245  // iterate over path segments
246  if (path.size() > 2) {
247  for (auto it = path.begin()+1; it != path.end()-1; ) {
248  // direction of path segments
249  double dx1 = it->x - (it-1)->x;
250  double dy1 = it->y - (it-1)->y;
251  double dx2 = (it+1)->x - it->x;
252  double dy2 = (it+1)->y - it->y;
253 
254  // two consecutive path segments are parallel, remove intermediate point
255  if (atan2(dy1,dx1) == atan2(dy2,dx2))
256  it = path.erase(it);
257 
258  else
259  ++it;
260  }
261  }
262 }
263 
265 {
266  return 0 <= wp && wp < path.size();
267 }
268 
269 void mst_path::add_edge (int from, int to, int cost)
270 {
271  // add edge to priority queue
272  edge e(from,to,cost,vertical);
273  edges.insert(e);
274 
275  // add vertices to sets
276  nodes[from].insert(to);
277  nodes[to].insert(from);
278 }
279 
280 double mst_path::dist (geometry_msgs::Point p1, geometry_msgs::Point p2)
281 {
282  return hypot(p1.x - p2.x, p1.y - p2.y);
283 }
284 
285 geometry_msgs::Point mst_path::get_wp (int offset)
286 {
287  geometry_msgs::Point waypoint;
288 
289  if (0 <= wp+offset && wp+offset < path.size()) {
290  // rotate
291  waypoint.x = path[wp+offset].x * cos(-rotation) - path[wp+offset].y * sin(-rotation);
292  waypoint.y = path[wp+offset].x * sin(-rotation) + path[wp+offset].y * cos(-rotation);
293 
294  // relative to original map
295  waypoint.x += origin.x;
296  waypoint.y += origin.y;
297  }
298 
299  return waypoint;
300 }
301 
303 {
304  if (edges.count(e) > 0) {
305  edges.erase(e);
306  nodes[e.from].erase(e.to);
307  nodes[e.to].erase(e.from);
308  }
309 }
void initialize_map(geometry_msgs::Point origin, double rotation, double width, double height)
Initialize properties of the area to be covered.
Definition: mst_path.cpp:201
double rotation
The rotation of the map.
Definition: mst_path.h:148
void reduce()
Remove waypoints that are within straight line segments of the path. Only keep turning points of the ...
Definition: mst_path.cpp:243
int wp
The current way point.
Definition: mst_path.h:138
double height
Height of the area to cover.
Definition: mst_path.h:158
geometry_msgs::Point get_waypoint(geometry_msgs::Point position, double tolerance)
Get the current waypoint and possibly select next waypoint, if close enough.
Definition: mst_path.cpp:123
geometry_msgs::Point get_wp(int offset=0)
Get the current way point.
Definition: mst_path.cpp:285
int to
The ending vertex of the edge.
Definition: edge.h:36
A class for representing edges.
Definition: edge.h:9
int from
The starting vertex of the edge.
Definition: edge.h:31
mst_path()
Constructor.
Definition: mst_path.cpp:3
void add_edge(int from, int to, int cost)
Add an edge to the tree graph.
Definition: mst_path.cpp:269
double width
Width of the area to cover.
Definition: mst_path.h:153
vector< unordered_set< int > > nodes
The sets of connected vertices.
Definition: mst_path.h:128
nav_msgs::OccupancyGrid map
The grid map that needs to be covered by the MST path.
Definition: mst_path.h:133
deque< geometry_msgs::Point > path
The boustrophedon path.
Definition: mst_path.h:118
double dist(geometry_msgs::Point p1, geometry_msgs::Point p2)
Calculate the distance between two points.
Definition: mst_path.cpp:280
void remove_edge(edge e)
Remove an edge from the tree graph.
Definition: mst_path.cpp:302
void initialize_graph(nav_msgs::OccupancyGrid gridmap, bool vertical=false, bool connect4=true)
Initialize the internal graph structure that represents the area division.
Definition: mst_path.cpp:139
bool valid()
Check whether the current waypoint index is valid.
Definition: mst_path.cpp:264
void initialize_tree(vector< edge > mst)
Remove edges of the graph that overlap with the tree.
Definition: mst_path.cpp:216
geometry_msgs::Point origin
Coordinate of the bottom left bounding point of the area.
Definition: mst_path.h:143
#define ROS_ERROR(...)
bool vertical
Whether the sweeping pattern is vertical or horizontal.
Definition: mst_path.h:163
unordered_set< edge, hash_edge > edges
Priority queue of edge objects sorted by cost.
Definition: mst_path.h:123
#define ROS_DEBUG(...)
nav_msgs::Path get_path()
Get the complete path.
Definition: mst_path.cpp:101
bool generate_path(geometry_msgs::Point start)
Generate all way points for the path.
Definition: mst_path.cpp:7


coverage_path
Author(s): Micha Sende
autogenerated on Tue Jan 19 2021 03:30:03