Skip to content

Commit 15087e6

Browse files
committed
formatting p1
1 parent 5cf40fc commit 15087e6

File tree

3 files changed

+95
-32
lines changed

3 files changed

+95
-32
lines changed

PathPlanning/TimeBasedPathPlanning/moving_obstacles.py renamed to PathPlanning/TimeBasedPathPlanning/GridWithDynamicObstacles.py

Lines changed: 81 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import numpy as np
22
import matplotlib.pyplot as plt
33
from enum import Enum
4+
5+
46
class Position:
57
x: int
68
y: int
@@ -15,12 +17,16 @@ def as_ndarray(self) -> np.ndarray[int, int]:
1517
def __add__(self, other):
1618
if isinstance(other, Position):
1719
return Position(self.x + other.x, self.y + other.y)
18-
raise NotImplementedError(f"Addition not supported for Position and {type(other)}")
20+
raise NotImplementedError(
21+
f"Addition not supported for Position and {type(other)}"
22+
)
1923

2024
def __sub__(self, other):
2125
if isinstance(other, Position):
2226
return Position(self.x - other.x, self.y - other.y)
23-
raise NotImplementedError(f"Subtraction not supported for Position and {type(other)}")
27+
raise NotImplementedError(
28+
f"Subtraction not supported for Position and {type(other)}"
29+
)
2430

2531
def __eq__(self, other):
2632
if isinstance(other, Position):
@@ -30,12 +36,14 @@ def __eq__(self, other):
3036
def __repr__(self):
3137
return f"Position({self.x}, {self.y})"
3238

39+
3340
class ObstacleArrangement(Enum):
3441
# Random obstacle positions and movements
3542
RANDOM = 0
3643
# Obstacles start in a line in y at center of grid and move side-to-side in x
3744
ARRANGEMENT1 = 1
3845

46+
3947
class Grid:
4048
# Set in constructor
4149
grid_size = None
@@ -50,7 +58,14 @@ class Grid:
5058
# Logging control
5159
verbose = False
5260

53-
def __init__(self, grid_size: np.ndarray[int, int], num_obstacles: int = 40, obstacle_avoid_points: list[Position] = [], obstacle_arrangement: ObstacleArrangement = ObstacleArrangement.RANDOM, time_limit: int = 100):
61+
def __init__(
62+
self,
63+
grid_size: np.ndarray[int, int],
64+
num_obstacles: int = 40,
65+
obstacle_avoid_points: list[Position] = [],
66+
obstacle_arrangement: ObstacleArrangement = ObstacleArrangement.RANDOM,
67+
time_limit: int = 100,
68+
):
5469
self.obstacle_avoid_points = obstacle_avoid_points
5570
self.time_limit = time_limit
5671
self.grid_size = grid_size
@@ -64,17 +79,18 @@ def __init__(self, grid_size: np.ndarray[int, int], num_obstacles: int = 40, obs
6479
elif obstacle_arrangement == ObstacleArrangement.ARRANGEMENT1:
6580
self.obstacle_paths = self.obstacle_arrangement_1(num_obstacles)
6681

67-
for (i, path) in enumerate(self.obstacle_paths):
68-
obs_idx = i + 1 # avoid using 0 - that indicates free space in the grid
69-
for (t, position) in enumerate(path):
82+
for i, path in enumerate(self.obstacle_paths):
83+
obs_idx = i + 1 # avoid using 0 - that indicates free space in the grid
84+
for t, position in enumerate(path):
7085
# Reserve old & new position at this time step
7186
if t > 0:
72-
self.grid[path[t-1].x, path[t-1].y, t] = obs_idx
87+
self.grid[path[t - 1].x, path[t - 1].y, t] = obs_idx
7388
self.grid[position.x, position.y, t] = obs_idx
7489

7590
"""
7691
Generate dynamic obstacles that move around the grid. Initial positions and movements are random
7792
"""
93+
7894
def generate_dynamic_obstacles(self, obs_count: int) -> list[list[Position]]:
7995
obstacle_paths = []
8096
for _obs_idx in (0, obs_count):
@@ -90,10 +106,18 @@ def generate_dynamic_obstacles(self, obs_count: int) -> list[list[Position]]:
90106
# Encourage obstacles to mostly stay in place - too much movement leads to chaotic planning scenarios
91107
# that are not fun to watch
92108
weights = [0.05, 0.05, 0.05, 0.05, 0.8]
93-
diffs = [Position(0, 1), Position(0, -1), Position(1, 0), Position(-1, 0), Position(0, 0)]
94-
95-
for t in range(1, self.time_limit-1):
96-
sampled_indices = np.random.choice(len(diffs), size=5, replace=False, p=weights)
109+
diffs = [
110+
Position(0, 1),
111+
Position(0, -1),
112+
Position(1, 0),
113+
Position(-1, 0),
114+
Position(0, 0),
115+
]
116+
117+
for t in range(1, self.time_limit - 1):
118+
sampled_indices = np.random.choice(
119+
len(diffs), size=5, replace=False, p=weights
120+
)
97121
rand_diffs = [diffs[i] for i in sampled_indices]
98122

99123
valid_position = None
@@ -122,6 +146,7 @@ def generate_dynamic_obstacles(self, obs_count: int) -> list[list[Position]]:
122146
Bottom half start moving right, top half start moving left. If `obs_count` is less than the length of
123147
the grid, only the first `obs_count` obstacles will be generated.
124148
"""
149+
125150
def obstacle_arrangement_1(self, obs_count: int) -> list[list[Position]]:
126151
obstacle_paths = []
127152
half_grid_x = self.grid_size[0] // 2
@@ -132,17 +157,21 @@ def obstacle_arrangement_1(self, obs_count: int) -> list[list[Position]]:
132157
position = Position(half_grid_x, y_idx)
133158
path = [position]
134159

135-
for t in range(1, self.time_limit-1):
160+
for t in range(1, self.time_limit - 1):
136161
# sit in place every other time step
137162
if t % 2 == 0:
138163
path.append(position)
139164
continue
140165

141166
# first check if we should switch direction (at edge of grid)
142-
if (moving_right and position.x == self.grid_size[0] - 1) or (not moving_right and position.x == 0):
167+
if (moving_right and position.x == self.grid_size[0] - 1) or (
168+
not moving_right and position.x == 0
169+
):
143170
moving_right = not moving_right
144171
# step in direction
145-
position = Position(position.x + (1 if moving_right else -1), position.y)
172+
position = Position(
173+
position.x + (1 if moving_right else -1), position.y
174+
)
146175
path.append(position)
147176

148177
obstacle_paths.append(path)
@@ -159,8 +188,8 @@ def obstacle_arrangement_1(self, obs_count: int) -> list[list[Position]]:
159188
output:
160189
bool: True if position/time combination is valid, False otherwise
161190
"""
162-
def valid_position(self, position: Position, t: int) -> bool:
163191

192+
def valid_position(self, position: Position, t: int) -> bool:
164193
# Check if new position is in grid
165194
if not self.inside_grid_bounds(position):
166195
return False
@@ -171,62 +200,87 @@ def valid_position(self, position: Position, t: int) -> bool:
171200
"""
172201
Returns True if the given position is valid at time t and is not in the set of obstacle_avoid_points
173202
"""
203+
174204
def valid_obstacle_position(self, position: Position, t: int) -> bool:
175-
return self.valid_position(position, t) and position not in self.obstacle_avoid_points
205+
return (
206+
self.valid_position(position, t)
207+
and position not in self.obstacle_avoid_points
208+
)
176209

177210
"""
178211
Returns True if the given position is within the grid's boundaries
179212
"""
213+
180214
def inside_grid_bounds(self, position: Position) -> bool:
181-
return position.x >= 0 and position.x < self.grid_size[0] and position.y >= 0 and position.y < self.grid_size[1]
215+
return (
216+
position.x >= 0
217+
and position.x < self.grid_size[0]
218+
and position.y >= 0
219+
and position.y < self.grid_size[1]
220+
)
182221

183222
"""
184223
Sample a random position that is within the grid's boundaries
185224
186225
output:
187226
np.ndarray[int, int]: (x, y) position
188227
"""
228+
189229
def sample_random_position(self) -> Position:
190-
return Position(np.random.randint(0, self.grid_size[0]), np.random.randint(0, self.grid_size[1]))
230+
return Position(
231+
np.random.randint(0, self.grid_size[0]),
232+
np.random.randint(0, self.grid_size[1]),
233+
)
191234

192235
"""
193236
Returns a tuple of (x_positions, y_positions) of the obstacles at time t
194237
"""
195-
def get_obstacle_positions_at_time(self, t: int) -> tuple[list[int], list[int]]:
196238

239+
def get_obstacle_positions_at_time(self, t: int) -> tuple[list[int], list[int]]:
197240
x_positions = []
198241
y_positions = []
199242
for obs_path in self.obstacle_paths:
200243
x_positions.append(obs_path[t].x)
201244
y_positions.append(obs_path[t].y)
202245
return (x_positions, y_positions)
203246

247+
204248
show_animation = True
205249

250+
206251
def main():
207-
grid = Grid(np.array([11, 11]), num_obstacles=10, obstacle_arrangement=ObstacleArrangement.ARRANGEMENT1)
252+
grid = Grid(
253+
np.array([11, 11]),
254+
num_obstacles=10,
255+
obstacle_arrangement=ObstacleArrangement.ARRANGEMENT1,
256+
)
208257

209258
if not show_animation:
210259
return
211260

212261
fig = plt.figure(figsize=(8, 7))
213-
ax = fig.add_subplot(autoscale_on=False, xlim=(0, grid.grid_size[0]-1), ylim=(0, grid.grid_size[1]-1))
214-
ax.set_aspect('equal')
262+
ax = fig.add_subplot(
263+
autoscale_on=False,
264+
xlim=(0, grid.grid_size[0] - 1),
265+
ylim=(0, grid.grid_size[1] - 1),
266+
)
267+
ax.set_aspect("equal")
215268
ax.grid()
216269
ax.set_xticks(np.arange(0, 11, 1))
217270
ax.set_yticks(np.arange(0, 11, 1))
218-
obs_points, = ax.plot([], [], 'ro', ms=15)
271+
(obs_points,) = ax.plot([], [], "ro", ms=15)
219272

220273
# for stopping simulation with the esc key.
221-
plt.gcf().canvas.mpl_connect('key_release_event',
222-
lambda event: [exit(
223-
0) if event.key == 'escape' else None])
274+
plt.gcf().canvas.mpl_connect(
275+
"key_release_event", lambda event: [exit(0) if event.key == "escape" else None]
276+
)
224277

225278
for i in range(0, grid.time_limit - 1):
226279
obs_positions = grid.get_obstacle_positions_at_time(i)
227280
obs_points.set_data(obs_positions[0], obs_positions[1])
228281
plt.pause(0.2)
229282
plt.show()
230283

231-
if __name__ == '__main__':
284+
285+
if __name__ == "__main__":
232286
main()

PathPlanning/TimeBasedPathPlanning/SpaceTimeAStar.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from __future__ import annotations # For typehints of a class within itself
99
import numpy as np
1010
import matplotlib.pyplot as plt
11-
from PathPlanning.TimeBasedPathPlanning.moving_obstacles import Grid, ObstacleArrangement, Position
11+
from PathPlanning.TimeBasedPathPlanning.GridWithDynamicObstacles import Grid, ObstacleArrangement, Position
1212
import heapq
1313
from collections.abc import Generator
1414
import random

tests/test_space_time_astar.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
1-
from PathPlanning.TimeBasedPathPlanning.moving_obstacles import Grid, ObstacleArrangement, Position
1+
from PathPlanning.TimeBasedPathPlanning.GridWithDynamicObstacles import (
2+
Grid,
3+
ObstacleArrangement,
4+
Position,
5+
)
26
from PathPlanning.TimeBasedPathPlanning import SpaceTimeAStar as m
37
import numpy as np
48
import conftest
59

10+
611
def test_1():
712
start = Position(1, 11)
813
goal = Position(19, 19)
914
grid_side_length = 21
10-
grid = Grid(np.array([grid_side_length, grid_side_length]), obstacle_arrangement=ObstacleArrangement.ARRANGEMENT1)
15+
grid = Grid(
16+
np.array([grid_side_length, grid_side_length]),
17+
obstacle_arrangement=ObstacleArrangement.ARRANGEMENT1,
18+
)
1119

1220
m.show_animation = False
1321
planner = m.TimeBasedAStar(grid, start, goal)
@@ -20,5 +28,6 @@ def test_1():
2028
# path should end at the goal
2129
assert path.path[-1].position == goal
2230

23-
if __name__ == '__main__':
24-
conftest.run_this_test(__file__)
31+
32+
if __name__ == "__main__":
33+
conftest.run_this_test(__file__)

0 commit comments

Comments
 (0)