Skip to content

Commit 2671688

Browse files
committed
release grid based_sweep_coverage_path_planner.py
1 parent 90ee562 commit 2671688

File tree

2 files changed

+174
-44
lines changed

2 files changed

+174
-44
lines changed

PathPlanning/GridBasedSweepCCP/grid_based_sweep_planner.py renamed to PathPlanning/GridBasedSweepCCP/grid_based_sweep_coverage_path_planner.py

Lines changed: 82 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import math
88
import os
99
import sys
10+
from enum import IntEnum
1011

1112
import matplotlib.pyplot as plt
1213
import numpy as np
@@ -21,14 +22,21 @@
2122

2223

2324
class SweepSearcher:
25+
class SweepDirection(IntEnum):
26+
UP = 1
27+
DOWN = -1
2428

25-
def __init__(self, mdirection, sdirection, xinds_miny, miny):
26-
self.moving_direction = mdirection # +1 or -1
27-
self.sweep_direction = sdirection # +1 or -1
29+
class MovingDirection(IntEnum):
30+
RIGHT = 1
31+
LEFT = -1
32+
33+
def __init__(self, mdirection, sdirection, xinds_goaly, goaly):
34+
self.moving_direction = mdirection
35+
self.sweep_direction = sdirection
2836
self.turing_window = []
2937
self.update_turning_window()
30-
self.xinds_miny = xinds_miny
31-
self.miny = miny
38+
self.xinds_goaly = xinds_goaly
39+
self.goaly = goaly
3240

3341
def move_target_grid(self, cxind, cyind, gmap):
3442

@@ -48,6 +56,7 @@ def move_target_grid(self, cxind, cyind, gmap):
4856
# moved backward, but the grid is occupied by obstacle
4957
return None, None
5058
else:
59+
# keep moving until end
5160
while not gmap.check_occupied_from_xy_index(ncxind + self.moving_direction, ncyind, occupied_val=0.5):
5261
ncxind += self.moving_direction
5362
self.swap_moving_direction()
@@ -67,8 +76,8 @@ def find_safe_turning_grid(self, cxind, cyind, gmap):
6776
return None, None
6877

6978
def is_search_done(self, gmap):
70-
for ix in self.xinds_miny:
71-
if not gmap.check_occupied_from_xy_index(ix, self.miny, occupied_val=0.5):
79+
for ix in self.xinds_goaly:
80+
if not gmap.check_occupied_from_xy_index(ix, self.goaly, occupied_val=0.5):
7281
return False
7382

7483
# all lower grid is occupied
@@ -86,19 +95,33 @@ def swap_moving_direction(self):
8695
self.moving_direction *= -1
8796
self.update_turning_window()
8897

98+
def search_start_grid(self, grid_map):
99+
xinds = [], y_ind = 0
100+
if self.sweep_direction == self.SweepDirection.DOWN:
101+
xinds, y_ind = search_free_grid_index_at_edge_y(grid_map, from_upper=True)
102+
elif self.sweep_direction == self.SweepDirection.UP:
103+
xinds, y_ind = search_free_grid_index_at_edge_y(grid_map, from_upper=False)
104+
105+
if self.moving_direction == self.MovingDirection.RIGHT:
106+
return min(xinds), y_ind
107+
elif self.moving_direction == self.MovingDirection.LEFT:
108+
return max(xinds), y_ind
109+
110+
raise ValueError("self.moving direction is invalid ")
111+
89112

90113
def find_sweep_direction_and_start_posi(ox, oy):
91114
# find sweep_direction
92-
maxd = 0.0
115+
max_dist = 0.0
93116
vec = [0.0, 0.0]
94117
sweep_start_pos = [0.0, 0.0]
95118
for i in range(len(ox) - 1):
96119
dx = ox[i + 1] - ox[i]
97120
dy = oy[i + 1] - oy[i]
98121
d = np.sqrt(dx ** 2 + dy ** 2)
99122

100-
if d > maxd:
101-
maxd = d
123+
if d > max_dist:
124+
max_dist = d
102125
vec = [dx, dy]
103126
sweep_start_pos = [ox[i], oy[i]]
104127

@@ -156,9 +179,7 @@ def search_free_grid_index_at_edge_y(grid_map, from_upper=False):
156179
return xinds, yind
157180

158181

159-
def setup_grid_map(ox, oy, reso):
160-
offset_grid = 10
161-
182+
def setup_grid_map(ox, oy, reso, sweep_direction, offset_grid=10):
162183
width = math.ceil((max(ox) - min(ox)) / reso) + offset_grid
163184
height = math.ceil((max(oy) - min(oy)) / reso) + offset_grid
164185
center_x = np.mean(ox)
@@ -170,27 +191,28 @@ def setup_grid_map(ox, oy, reso):
170191

171192
grid_map.expand_grid()
172193

173-
xinds, miny = search_free_grid_index_at_edge_y(grid_map)
174-
175-
return grid_map, xinds, miny
176-
177-
178-
def search_start_grid(grid_map):
179-
xinds, y_ind = search_free_grid_index_at_edge_y(grid_map, from_upper=True)
180-
181-
return min(xinds), y_ind
194+
xinds_goaly = [], goaly = 0
195+
if sweep_direction == SweepSearcher.SweepDirection.UP:
196+
xinds_goaly, goaly = search_free_grid_index_at_edge_y(grid_map, from_upper=True)
197+
elif sweep_direction == SweepSearcher.SweepDirection.DOWN:
198+
xinds_goaly, goaly = search_free_grid_index_at_edge_y(grid_map, from_upper=False)
182199

200+
return grid_map, xinds_goaly, goaly
183201

184-
def sweep_path_search(sweep_searcher, gmap):
185-
px, py = [], []
186202

203+
def sweep_path_search(sweep_searcher, gmap, grid_search_animation=False):
187204
# search start grid
188-
cxind, cyind = search_start_grid(gmap)
205+
cxind, cyind = sweep_searcher.search_start_grid(gmap)
189206
if not gmap.set_value_from_xy_index(cxind, cyind, 0.5):
190207
print("Cannot find start grid")
191-
return px, py
208+
return [], []
209+
210+
x, y = gmap.calc_grid_central_xy_position_from_xy_index(cxind, cyind)
211+
px, py = [x], [y]
212+
213+
if grid_search_animation:
214+
fig, ax = plt.subplots()
192215

193-
# fig, ax = plt.subplots()
194216
while True:
195217
cxind, cyind = sweep_searcher.move_target_grid(cxind, cyind, gmap)
196218

@@ -206,44 +228,39 @@ def sweep_path_search(sweep_searcher, gmap):
206228

207229
gmap.set_value_from_xy_index(cxind, cyind, 0.5)
208230

209-
# gmap.plot_grid_map(ax=ax)
210-
# plt.pause(1.0)
231+
if grid_search_animation:
232+
gmap.plot_grid_map(ax=ax)
233+
plt.pause(1.0)
211234

212235
gmap.plot_grid_map()
213236

214237
return px, py
215238

216239

217-
def planning(ox, oy, reso):
240+
def planning(ox, oy, reso,
241+
moving_direction=SweepSearcher.MovingDirection.RIGHT,
242+
sweeping_direction=SweepSearcher.SweepDirection.UP,
243+
):
218244
sweep_vec, sweep_start_posi = find_sweep_direction_and_start_posi(ox, oy)
219245

220246
rox, roy = convert_grid_coordinate(ox, oy, sweep_vec, sweep_start_posi)
221247

222-
moving_direction = 1
223-
sweeping_direction = -1
248+
gmap, xinds_goaly, goaly = setup_grid_map(rox, roy, reso, sweeping_direction)
224249

225-
gmap, xinds_miny, miny = setup_grid_map(rox, roy, reso)
226-
227-
sweep_searcher = SweepSearcher(moving_direction, sweeping_direction, xinds_miny, miny)
250+
sweep_searcher = SweepSearcher(moving_direction, sweeping_direction, xinds_goaly, goaly)
228251

229252
px, py = sweep_path_search(sweep_searcher, gmap)
230253

231254
rx, ry = convert_global_coordinate(px, py, sweep_vec, sweep_start_posi)
232255

233-
return rx, ry
256+
print("Path length:", len(rx))
234257

258+
return rx, ry
235259

236-
def main():
237-
print("start!!")
238-
239-
ox = [0.0, 20.0, 50.0, 100.0, 130.0, 40.0, 0.0]
240-
oy = [0.0, -20.0, 0.0, 30.0, 60.0, 80.0, 0.0]
241-
reso = 5.0
242260

261+
def planning_animation(ox, oy, reso):
243262
px, py = planning(ox, oy, reso)
244263

245-
plt.subplots()
246-
247264
# animation
248265
if do_animation:
249266
for ipx, ipy in zip(px, py):
@@ -260,6 +277,27 @@ def main():
260277
plt.plot(px, py, "-r")
261278
plt.axis("equal")
262279
plt.grid(True)
280+
plt.pause(0.1)
281+
282+
283+
def main():
284+
print("start!!")
285+
286+
ox = [0.0, 20.0, 50.0, 100.0, 130.0, 40.0, 0.0]
287+
oy = [0.0, -20.0, 0.0, 30.0, 60.0, 80.0, 0.0]
288+
reso = 5.0
289+
planning_animation(ox, oy, reso)
290+
291+
ox = [0.0, 50.0, 50.0, 0.0, 0.0]
292+
oy = [0.0, 0.0, 30.0, 30.0, 0.0]
293+
reso = 1.3
294+
planning_animation(ox, oy, reso)
295+
296+
ox = [0.0, 20.0, 50.0, 200.0, 130.0, 40.0, 0.0]
297+
oy = [0.0, -80.0, 0.0, 30.0, 60.0, 80.0, 0.0]
298+
reso = 5.0
299+
planning_animation(ox, oy, reso)
300+
263301
plt.show()
264302

265303
print("done!!")
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
from unittest import TestCase
2+
3+
import grid_based_sweep_coverage_path_planner
4+
5+
6+
class TestPlanning(TestCase):
7+
8+
def test_planning1(self):
9+
ox = [0.0, 20.0, 50.0, 100.0, 130.0, 40.0, 0.0]
10+
oy = [0.0, -20.0, 0.0, 30.0, 60.0, 80.0, 0.0]
11+
reso = 5.0
12+
13+
px, py = grid_based_sweep_coverage_path_planner.planning(ox, oy, reso,
14+
moving_direction=grid_based_sweep_coverage_path_planner.SweepSearcher.MovingDirection.RIGHT,
15+
sweeping_direction=grid_based_sweep_coverage_path_planner.SweepSearcher.SweepDirection.DOWN,
16+
)
17+
self.assertTrue(len(px) >= 5)
18+
19+
px, py = grid_based_sweep_coverage_path_planner.planning(ox, oy, reso,
20+
moving_direction=grid_based_sweep_coverage_path_planner.SweepSearcher.MovingDirection.LEFT,
21+
sweeping_direction=grid_based_sweep_coverage_path_planner.SweepSearcher.SweepDirection.DOWN,
22+
)
23+
self.assertTrue(len(px) >= 5)
24+
25+
px, py = grid_based_sweep_coverage_path_planner.planning(ox, oy, reso,
26+
moving_direction=grid_based_sweep_coverage_path_planner.SweepSearcher.MovingDirection.RIGHT,
27+
sweeping_direction=grid_based_sweep_coverage_path_planner.SweepSearcher.SweepDirection.UP,
28+
)
29+
self.assertTrue(len(px) >= 5)
30+
31+
px, py = grid_based_sweep_coverage_path_planner.planning(ox, oy, reso,
32+
moving_direction=grid_based_sweep_coverage_path_planner.SweepSearcher.MovingDirection.RIGHT,
33+
sweeping_direction=grid_based_sweep_coverage_path_planner.SweepSearcher.SweepDirection.DOWN,
34+
)
35+
self.assertTrue(len(px) >= 5)
36+
37+
def test_planning2(self):
38+
ox = [0.0, 50.0, 50.0, 0.0, 0.0]
39+
oy = [0.0, 0.0, 30.0, 30.0, 0.0]
40+
reso = 1.3
41+
42+
px, py = grid_based_sweep_coverage_path_planner.planning(ox, oy, reso,
43+
moving_direction=grid_based_sweep_coverage_path_planner.SweepSearcher.MovingDirection.RIGHT,
44+
sweeping_direction=grid_based_sweep_coverage_path_planner.SweepSearcher.SweepDirection.DOWN,
45+
)
46+
self.assertTrue(len(px) >= 5)
47+
48+
px, py = grid_based_sweep_coverage_path_planner.planning(ox, oy, reso,
49+
moving_direction=grid_based_sweep_coverage_path_planner.SweepSearcher.MovingDirection.LEFT,
50+
sweeping_direction=grid_based_sweep_coverage_path_planner.SweepSearcher.SweepDirection.DOWN,
51+
)
52+
self.assertTrue(len(px) >= 5)
53+
54+
px, py = grid_based_sweep_coverage_path_planner.planning(ox, oy, reso,
55+
moving_direction=grid_based_sweep_coverage_path_planner.SweepSearcher.MovingDirection.RIGHT,
56+
sweeping_direction=grid_based_sweep_coverage_path_planner.SweepSearcher.SweepDirection.UP,
57+
)
58+
self.assertTrue(len(px) >= 5)
59+
60+
px, py = grid_based_sweep_coverage_path_planner.planning(ox, oy, reso,
61+
moving_direction=grid_based_sweep_coverage_path_planner.SweepSearcher.MovingDirection.RIGHT,
62+
sweeping_direction=grid_based_sweep_coverage_path_planner.SweepSearcher.SweepDirection.DOWN,
63+
)
64+
self.assertTrue(len(px) >= 5)
65+
66+
def test_planning3(self):
67+
ox = [0.0, 20.0, 50.0, 200.0, 130.0, 40.0, 0.0]
68+
oy = [0.0, -80.0, 0.0, 30.0, 60.0, 80.0, 0.0]
69+
reso = 5.1
70+
px, py = grid_based_sweep_coverage_path_planner.planning(ox, oy, reso,
71+
moving_direction=grid_based_sweep_coverage_path_planner.SweepSearcher.MovingDirection.RIGHT,
72+
sweeping_direction=grid_based_sweep_coverage_path_planner.SweepSearcher.SweepDirection.DOWN,
73+
)
74+
self.assertTrue(len(px) >= 5)
75+
76+
px, py = grid_based_sweep_coverage_path_planner.planning(ox, oy, reso,
77+
moving_direction=grid_based_sweep_coverage_path_planner.SweepSearcher.MovingDirection.LEFT,
78+
sweeping_direction=grid_based_sweep_coverage_path_planner.SweepSearcher.SweepDirection.DOWN,
79+
)
80+
self.assertTrue(len(px) >= 5)
81+
82+
px, py = grid_based_sweep_coverage_path_planner.planning(ox, oy, reso,
83+
moving_direction=grid_based_sweep_coverage_path_planner.SweepSearcher.MovingDirection.RIGHT,
84+
sweeping_direction=grid_based_sweep_coverage_path_planner.SweepSearcher.SweepDirection.UP,
85+
)
86+
self.assertTrue(len(px) >= 5)
87+
88+
px, py = grid_based_sweep_coverage_path_planner.planning(ox, oy, reso,
89+
moving_direction=grid_based_sweep_coverage_path_planner.SweepSearcher.MovingDirection.RIGHT,
90+
sweeping_direction=grid_based_sweep_coverage_path_planner.SweepSearcher.SweepDirection.DOWN,
91+
)
92+
self.assertTrue(len(px) >= 5)

0 commit comments

Comments
 (0)