python-motion-planning 1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. curve_generation/__init__.py +9 -0
  2. curve_generation/bezier_curve.py +131 -0
  3. curve_generation/bspline_curve.py +271 -0
  4. curve_generation/cubic_spline.py +128 -0
  5. curve_generation/curve.py +64 -0
  6. curve_generation/dubins_curve.py +348 -0
  7. curve_generation/fem_pos_smooth.py +114 -0
  8. curve_generation/polynomial_curve.py +226 -0
  9. curve_generation/reeds_shepp.py +736 -0
  10. global_planner/__init__.py +3 -0
  11. global_planner/evolutionary_search/__init__.py +4 -0
  12. global_planner/evolutionary_search/aco.py +186 -0
  13. global_planner/evolutionary_search/evolutionary_search.py +87 -0
  14. global_planner/evolutionary_search/pso.py +356 -0
  15. global_planner/graph_search/__init__.py +28 -0
  16. global_planner/graph_search/a_star.py +124 -0
  17. global_planner/graph_search/d_star.py +291 -0
  18. global_planner/graph_search/d_star_lite.py +188 -0
  19. global_planner/graph_search/dijkstra.py +77 -0
  20. global_planner/graph_search/gbfs.py +78 -0
  21. global_planner/graph_search/graph_search.py +87 -0
  22. global_planner/graph_search/jps.py +165 -0
  23. global_planner/graph_search/lazy_theta_star.py +114 -0
  24. global_planner/graph_search/lpa_star.py +230 -0
  25. global_planner/graph_search/s_theta_star.py +133 -0
  26. global_planner/graph_search/theta_star.py +171 -0
  27. global_planner/graph_search/voronoi.py +200 -0
  28. global_planner/sample_search/__init__.py +6 -0
  29. global_planner/sample_search/informed_rrt.py +152 -0
  30. global_planner/sample_search/rrt.py +151 -0
  31. global_planner/sample_search/rrt_connect.py +147 -0
  32. global_planner/sample_search/rrt_star.py +77 -0
  33. global_planner/sample_search/sample_search.py +135 -0
  34. local_planner/__init__.py +19 -0
  35. local_planner/apf.py +144 -0
  36. local_planner/ddpg.py +630 -0
  37. local_planner/dqn.py +687 -0
  38. local_planner/dwa.py +212 -0
  39. local_planner/local_planner.py +262 -0
  40. local_planner/lqr.py +146 -0
  41. local_planner/mpc.py +214 -0
  42. local_planner/pid.py +158 -0
  43. local_planner/rpp.py +147 -0
  44. python_motion_planning-1.0.dist-info/LICENSE +674 -0
  45. python_motion_planning-1.0.dist-info/METADATA +873 -0
  46. python_motion_planning-1.0.dist-info/RECORD +65 -0
  47. python_motion_planning-1.0.dist-info/WHEEL +5 -0
  48. python_motion_planning-1.0.dist-info/top_level.txt +4 -0
  49. utils/__init__.py +19 -0
  50. utils/agent/__init__.py +0 -0
  51. utils/agent/agent.py +135 -0
  52. utils/environment/__init__.py +0 -0
  53. utils/environment/env.py +134 -0
  54. utils/environment/node.py +85 -0
  55. utils/environment/point2d.py +96 -0
  56. utils/environment/pose2d.py +91 -0
  57. utils/helper/__init__.py +3 -0
  58. utils/helper/math_helper.py +65 -0
  59. utils/planner/__init__.py +0 -0
  60. utils/planner/control_factory.py +31 -0
  61. utils/planner/curve_factory.py +29 -0
  62. utils/planner/planner.py +40 -0
  63. utils/planner/search_factory.py +51 -0
  64. utils/plot/__init__.py +0 -0
  65. utils/plot/plot.py +274 -0
@@ -0,0 +1,3 @@
1
+ from .graph_search import *
2
+ from .sample_search import *
3
+ from .evolutionary_search import *
@@ -0,0 +1,4 @@
1
+ from .aco import ACO
2
+ from .pso import PSO
3
+
4
+ __all__ = ["ACO", "PSO"]
@@ -0,0 +1,186 @@
1
+ """
2
+ @file: aco.py
3
+ @breif: Ant Colony Optimization(ACO) motion planning
4
+ @author: Yang Haodong, Wu Maojia
5
+ @update: 2024.6.23
6
+ """
7
+ import random
8
+ from bisect import bisect_left
9
+
10
+ from .evolutionary_search import EvolutionarySearcher
11
+ from python_motion_planning.utils import Env, Node
12
+
13
+
14
+ class ACO(EvolutionarySearcher):
15
+ """
16
+ Class for Ant Colony Optimization(ACO) motion planning.
17
+
18
+ Parameters:
19
+ start (tuple): start point coordinate
20
+ goal (tuple): goal point coordinate
21
+ env (Env): environment
22
+ heuristic_type (str): heuristic function type, default is euclidean
23
+ n_ants (int): number of ants
24
+ alpha (float): pheromone and heuristic factor weight coefficient
25
+ beta (float): pheromone and heuristic factor weight coefficient
26
+ rho (float): evaporation coefficient
27
+ Q (float): pheromone gain
28
+ max_iter (int): maximum iterations
29
+
30
+ Examples:
31
+ >>> import python_motion_planning as pmp
32
+ >>> planner = pmp.ACO((5, 5), (45, 25), pmp.Grid(51, 31))
33
+ >>> cost, path, cost_list = planner.plan() # planning results only
34
+ >>> planner.plot.animation(path, str(planner), cost, cost_curve=cost_list) # animation
35
+ >>> planner.run() # run both planning and animation
36
+
37
+ References:
38
+ [1] Ant Colony Optimization: A New Meta-Heuristic
39
+ """
40
+ def __init__(self, start: tuple, goal: tuple, env: Env, heuristic_type: str = "euclidean",
41
+ n_ants: int = 50, alpha: float = 1.0, beta: float = 5.0, rho: float = 0.1, Q: float = 1.0,
42
+ max_iter: int = 100) -> None:
43
+ super().__init__(start, goal, env, heuristic_type)
44
+ self.n_ants = n_ants
45
+ self.alpha = alpha
46
+ self.beta = beta
47
+ self.rho = rho
48
+ self.Q = Q
49
+ self.max_iter = max_iter
50
+
51
+ def __str__(self) -> str:
52
+ return "Ant Colony Optimization(ACO)"
53
+
54
+ class Ant:
55
+ def __init__(self) -> None:
56
+ self.reset()
57
+
58
+ def reset(self) -> None:
59
+ self.found_goal = False
60
+ self.current_node = None
61
+ self.path = []
62
+ self.path_set = set()
63
+ self.steps = 0
64
+
65
+ def plan(self) -> tuple:
66
+ """
67
+ Ant Colony Optimization(ACO) motion plan function.
68
+
69
+ Returns:
70
+ cost (float): path cost
71
+ path (list): planning path
72
+ """
73
+ best_length_list, best_path = [], None
74
+
75
+ # pheromone initialization
76
+ pheromone_edges = {}
77
+ for i in range(self.env.x_range):
78
+ for j in range(self.env.y_range):
79
+ if (i, j) in self.obstacles:
80
+ continue
81
+ cur_node = Node((i, j), (i, j), 0, 0)
82
+ for node_n in self.getNeighbor(cur_node):
83
+ pheromone_edges[(cur_node, node_n)] = 1.0
84
+
85
+ # heuristically set max steps
86
+ max_steps = self.env.x_range * self.env.y_range / 2 + max(self.env.x_range, self.env.y_range)
87
+
88
+ # main loop
89
+ cost_list = []
90
+ for _ in range(self.max_iter):
91
+ ants_list = []
92
+ for _ in range(self.n_ants):
93
+ ant = self.Ant()
94
+ ant.current_node = self.start
95
+ while ant.current_node is not self.goal and ant.steps < max_steps:
96
+ ant.path.append(ant.current_node)
97
+ ant.path_set.add(ant.current_node.current)
98
+
99
+ # candidate
100
+ prob_sum = 0.0
101
+ next_positions, next_probabilities = [], []
102
+ for node_n in self.getNeighbor(ant.current_node):
103
+ # existed
104
+ if node_n.current in ant.path_set:
105
+ continue
106
+
107
+ node_n.parent = ant.current_node.current
108
+
109
+ # goal found
110
+ if node_n == self.goal:
111
+ ant.path.append(node_n)
112
+ ant.path_set.add(node_n.current)
113
+ ant.found_goal = True
114
+ break
115
+
116
+ next_positions.append(node_n)
117
+ prob_new = pheromone_edges[(ant.current_node, node_n)] ** self.alpha \
118
+ * (1.0 / self.h(node_n, self.goal)) ** self.beta
119
+ next_probabilities.append(prob_new)
120
+ prob_sum = prob_sum + prob_new
121
+
122
+ if prob_sum == 0 or ant.found_goal:
123
+ break
124
+
125
+ # roulette selection
126
+ next_probabilities = list(map(lambda prob: prob / prob_sum, next_probabilities))
127
+ p0, cp = 0, []
128
+ for prob in next_probabilities:
129
+ p0 = p0 + prob
130
+ cp.append(p0)
131
+ ant.current_node = next_positions[bisect_left(cp, random.random())]
132
+
133
+ ant.steps = ant.steps + 1
134
+
135
+ ants_list.append(ant)
136
+
137
+ # pheromone deterioration
138
+ for key, _ in pheromone_edges.items():
139
+ pheromone_edges[key] *= (1 - self.rho)
140
+
141
+ # pheromone update based on successful ants
142
+ bpl, bp = float("inf"), None
143
+ for ant in ants_list:
144
+ if ant.found_goal:
145
+ if len(ant.path) < bpl:
146
+ bpl, bp = len(ant.path), ant.path
147
+ c = self.Q / len(ant.path)
148
+ for i in range(len(ant.path) - 1):
149
+ pheromone_edges[(ant.path[i], ant.path[i + 1])] += c
150
+
151
+ if bpl < float("inf"):
152
+ best_length_list.append(bpl)
153
+
154
+ if len(best_length_list) > 0:
155
+ cost_list.append(min(best_length_list))
156
+ if bpl <= min(best_length_list):
157
+ best_path = bp
158
+
159
+ if best_path:
160
+ cost = 0
161
+ path = [self.start.current]
162
+ for i in range(len(best_path) - 1):
163
+ cost += self.dist(best_path[i], best_path[i + 1])
164
+ path.append(best_path[i + 1].current)
165
+ return cost, path, cost_list
166
+ return [], [], []
167
+
168
+ def getNeighbor(self, node: Node) -> list:
169
+ """
170
+ Find neighbors of node.
171
+
172
+ Parameters:
173
+ node (Node): current node
174
+
175
+ Returns:
176
+ neighbors (list): neighbors of current node
177
+ """
178
+ return [node + motion for motion in self.motions
179
+ if not self.isCollision(node, node + motion)]
180
+
181
+ def run(self) -> None:
182
+ """
183
+ Running both plannig and animation.
184
+ """
185
+ cost, path, cost_list = self.plan()
186
+ self.plot.animation(path, str(self), cost, cost_curve=cost_list)
@@ -0,0 +1,87 @@
1
+ """
2
+ @file: evolutionary_search.py
3
+ @breif: Base class for planner based on evolutionary searching
4
+ @author: Winter
5
+ @update: 2023.7.13
6
+ """
7
+ import math
8
+
9
+ from python_motion_planning.utils import Env, Node, Planner
10
+
11
+ class EvolutionarySearcher(Planner):
12
+ """
13
+ Base class for planner based on evolutionary searching.
14
+
15
+ Parameters:
16
+ start (tuple): start point coordinate
17
+ goal (tuple): goal point coordinate
18
+ env (Env): environment
19
+ heuristic_type (str): heuristic function type
20
+ """
21
+ def __init__(self, start: tuple, goal: tuple, env: Env, heuristic_type: str="euclidean") -> None:
22
+ super().__init__(start, goal, env)
23
+ # heuristic type
24
+ self.heuristic_type = heuristic_type
25
+ # allowed motions
26
+ self.motions = self.env.motions
27
+ # obstacles
28
+ self.obstacles = self.env.obstacles
29
+
30
+ def h(self, node: Node, goal: Node) -> float:
31
+ """
32
+ Calculate heuristic.
33
+
34
+ Parameters:
35
+ node (Node): current node
36
+ goal (Node): goal node
37
+
38
+ Returns:
39
+ h (float): heuristic function value of node
40
+ """
41
+ if self.heuristic_type == "manhattan":
42
+ return abs(goal.x - node.x) + abs(goal.y - node.y)
43
+ elif self.heuristic_type == "euclidean":
44
+ return math.hypot(goal.x - node.x, goal.y - node.y)
45
+
46
+ def cost(self, node1: Node, node2: Node) -> float:
47
+ """
48
+ Calculate cost for this motion.
49
+
50
+ Parameters:
51
+ node1 (Node): node 1
52
+ node2 (Node): node 2
53
+
54
+ Returns:
55
+ cost (float): cost of this motion
56
+ """
57
+ if self.isCollision(node1, node2):
58
+ return float("inf")
59
+ return self.dist(node1, node2)
60
+
61
+ def isCollision(self, node1: Node, node2: Node) -> bool:
62
+ """
63
+ Judge collision when moving from node1 to node2.
64
+
65
+ Parameters:
66
+ node1 (Node): node 1
67
+ node2 (Node): node 2
68
+
69
+ Returns:
70
+ collision (bool): True if collision exists else False
71
+ """
72
+ if node1.current in self.obstacles or node2.current in self.obstacles:
73
+ return True
74
+
75
+ x1, y1 = node1.current
76
+ x2, y2 = node2.current
77
+
78
+ if x1 != x2 and y1 != y2:
79
+ if x2 - x1 == y1 - y2:
80
+ s1 = (min(x1, x2), min(y1, y2))
81
+ s2 = (max(x1, x2), max(y1, y2))
82
+ else:
83
+ s1 = (min(x1, x2), max(y1, y2))
84
+ s2 = (max(x1, x2), min(y1, y2))
85
+ if s1 in self.obstacles or s2 in self.obstacles:
86
+ return True
87
+ return False
@@ -0,0 +1,356 @@
1
+ """
2
+ @file: pso.py
3
+ @breif: Particle Swarm Optimization (PSO) motion planning
4
+ @author: Yang Haodong, Wu Maojia
5
+ @update: 2024.6.24
6
+ """
7
+ import random, math
8
+ from copy import deepcopy
9
+
10
+ from .evolutionary_search import EvolutionarySearcher
11
+ from python_motion_planning.utils import Env, MathHelper
12
+ from python_motion_planning.curve_generation import BSpline
13
+
14
+ GEN_MODE_CIRCLE = 0
15
+ GEN_MODE_RANDOM = 1
16
+
17
+
18
+ class PSO(EvolutionarySearcher):
19
+ """
20
+ Class for Particle Swarm Optimization (PSO) motion planning.
21
+
22
+ Parameters:
23
+ start (tuple): start point coordinate
24
+ goal (tuple): goal point coordinate
25
+ env (Env): environment
26
+ heuristic_type (str): heuristic function type
27
+ n_particles (int): number of particles
28
+ w_inertial (float): inertial weight
29
+ w_cognitive (float): cognitive weight
30
+ w_social (float): social weight
31
+ point_num (int): number of position points contained in each particle
32
+ max_speed (int): The maximum velocity of particle motion
33
+ max_iter (int): maximum iterations
34
+ init_mode (int): Set the generation mode for the initial position points of the particle swarm
35
+
36
+ Examples:
37
+ >>> import python_motion_planning as pmp
38
+ >>> planner = pmp.PSO((5, 5), (45, 25), pmp.Grid(51, 31))
39
+ >>> cost, path, fitness_history = planner.plan(verbose=True) # planning results only
40
+ >>> cost_curve = [-f for f in fitness_history]
41
+ >>> planner.plot.animation(path, str(planner), cost, cost_curve=cost_curve) # animation
42
+ >>> planner.run() # run both planning and animation
43
+ """
44
+ def __init__(self, start: tuple, goal: tuple, env: Env, heuristic_type: str = "euclidean",
45
+ n_particles: int = 300, point_num: int = 5, w_inertial: float = 1.0,
46
+ w_cognitive: float = 1.0, w_social: float = 1.0, max_speed: int = 6,
47
+ max_iter: int = 200, init_mode: int = GEN_MODE_RANDOM) -> None:
48
+ super().__init__(start, goal, env, heuristic_type)
49
+ self.max_iter = max_iter
50
+ self.n_particles = n_particles
51
+ self.w_inertial = w_inertial
52
+ self.w_social = w_social
53
+ self.w_cognitive = w_cognitive
54
+ self.point_num = point_num
55
+ self.init_mode = init_mode
56
+ self.max_speed = max_speed
57
+
58
+ self.particles = []
59
+ self.inherited_particles = []
60
+ self.best_particle = self.Particle()
61
+ self.b_spline_gen = BSpline(step=0.01, k=4)
62
+
63
+ def __str__(self) -> str:
64
+ return "Particle Swarm Optimization (PSO)"
65
+
66
+ class Particle:
67
+ def __init__(self) -> None:
68
+ self.reset()
69
+
70
+ def reset(self):
71
+ self.position = []
72
+ self.velocity = []
73
+ self.fitness = -1
74
+ self.best_pos = []
75
+ self.best_fitness = -1
76
+
77
+ def plan(self, verbose: bool = False):
78
+ """
79
+ Particle Swarm Optimization (PSO) motion plan function.
80
+
81
+ Parameters:
82
+ verbose (bool): print the best fitness value of each iteration
83
+
84
+ Returns:
85
+ cost (float): path cost
86
+ path (list): planning path
87
+ """
88
+ # Generate initial position of particle swarm
89
+ init_positions = self.initializePositions()
90
+
91
+ # Particle initialization
92
+ for i in range(self.n_particles):
93
+ # Calculate fitness
94
+ init_fitness = self.calFitnessValue(init_positions[i])
95
+
96
+ if i == 0 or init_fitness > self.best_particle.fitness:
97
+ self.best_particle.fitness = init_fitness
98
+ self.best_particle.position = deepcopy(init_positions[i])
99
+
100
+ # Create and add particle objects to containers
101
+ p = self.Particle()
102
+ p.position = init_positions[i]
103
+ p.velocity = [(0, 0) for _ in range(self.point_num)]
104
+ p.best_pos = init_positions[i]
105
+ p.fitness = init_fitness
106
+ p.best_fitness = init_fitness
107
+ self.particles.append(p)
108
+
109
+ # Iterative optimization
110
+ fitness_history = []
111
+ for _ in range(self.max_iter):
112
+ for p in self.particles:
113
+ self.optimizeParticle(p)
114
+ fitness_history.append(self.best_particle.fitness)
115
+ if verbose:
116
+ print(f"iteration {_}: best fitness = {self.best_particle.fitness}")
117
+
118
+ # Generating Paths from Optimal Particles
119
+ points = [self.start.current] + self.best_particle.position + [self.goal.current]
120
+ points = sorted(set(points), key=points.index)
121
+ path = self.b_spline_gen.run(points, display=False)
122
+
123
+ return self.b_spline_gen.length(path), path, fitness_history
124
+
125
+ def initializePositions(self) -> list:
126
+ """
127
+ Generate n particles with pointNum_ positions each within the map range.
128
+
129
+ Returns:
130
+ init_positions (list): initial position sequence of particle swarm
131
+ """
132
+ init_positions = []
133
+
134
+ # Calculate sequence direction
135
+ x_order = self.goal.x > self.start.x
136
+ y_order = self.goal.y > self.start.y
137
+
138
+ # circle generation
139
+ center_x, center_y, radius = None, None, None
140
+ if self.init_mode == GEN_MODE_CIRCLE:
141
+ # Calculate the center and the radius of the circle (midpoint between start and goal)
142
+ center_x = (self.start.x + self.goal.x) / 2
143
+ center_y = (self.start.y + self.goal.y) / 2
144
+ radius = 5 if self.dist(self.start, self.goal) / 2.0 < 5 else self.dist(self.start, self.goal) / 2.0
145
+
146
+ # initialize n_particles positions
147
+ for _ in range(self.n_particles):
148
+ point_id, visited = 0, []
149
+ pts_x, pts_y = [], []
150
+ # Generate point_num_ unique coordinates
151
+ while point_id < self.point_num:
152
+ if self.init_mode == GEN_MODE_RANDOM:
153
+ pt_x = random.randint(self.start.x, self.goal.x)
154
+ pt_y = random.randint(self.start.y, self.goal.y)
155
+ pos_id = pt_x + self.env.x_range * pt_y
156
+ else:
157
+ # Generate random angle in radians
158
+ angle = random.random() * 2 * math.pi
159
+ # Generate random distance from the center within the circle
160
+ r = math.sqrt(random.random()) * radius
161
+ # Convert polar coordinates to Cartesian coordinates
162
+ pt_x = int(center_x + r * math.cos(angle))
163
+ pt_y = int(center_y + r * math.sin(angle))
164
+ # Check if the coordinates are within the map range
165
+ if 0 <= pt_x < self.env.x_range and 0 <= pt_y < self.env.y_range:
166
+ pos_id = pt_x + self.env.x_range * pt_y
167
+ else:
168
+ continue
169
+
170
+ # Check if the coordinates have already been used
171
+ if not pos_id in visited:
172
+ point_id = point_id + 1
173
+ visited.append(pos_id)
174
+ pts_x.append(pt_x)
175
+ pts_y.append(pt_y)
176
+
177
+ # sort
178
+ pts_x = sorted(pts_x, reverse=False) if x_order else sorted(pts_x, reverse=True)
179
+ pts_y = sorted(pts_y, reverse=False) if y_order else sorted(pts_y, reverse=True)
180
+
181
+ # Store elements from x and y in particle_positions
182
+ init_positions.append([(ix, iy) for (ix, iy) in zip(pts_x, pts_y)])
183
+
184
+ return init_positions
185
+
186
+ def calFitnessValue(self, position: list) -> float:
187
+ """
188
+ Calculate the value of fitness function.
189
+
190
+ Parameters:
191
+ position (list): control points calculated by PSO
192
+
193
+ Returns:
194
+ fitness (float): the value of fitness function
195
+ """
196
+ points = [self.start.current] + position + [self.goal.current]
197
+ points = sorted(set(points), key=points.index)
198
+ try:
199
+ path = self.b_spline_gen.run(points, display=False)
200
+ except:
201
+ return float("inf")
202
+
203
+ # collision detection
204
+ obs_cost = 0
205
+ for i in range(len(path) - 1):
206
+ p1 = (round(path[i][0]), round(path[i][1]))
207
+ p2 = (round(path[i+1][0]), round(path[i+1][1]))
208
+ if self.isCollision(p1, p2):
209
+ obs_cost = obs_cost + 1
210
+
211
+ # Calculate particle fitness
212
+ return 100000.0 / (self.b_spline_gen.length(path) + 50000 * obs_cost)
213
+
214
+ def updateParticleVelocity(self, particle):
215
+ """
216
+ A function to update the particle velocity
217
+
218
+ Parameters:
219
+ particle (Particle): the particle
220
+ """
221
+ # update Velocity
222
+ for i in range(self.point_num):
223
+ rand1, rand2 = random.random(), random.random()
224
+ vx, vy = particle.velocity[i]
225
+ px, py = particle.position[i]
226
+ vx_new = self.w_inertial * vx + self.w_cognitive * rand1 * (particle.best_pos[i][0] - px) \
227
+ + self.w_social * rand2 * (self.best_particle.position[i][0] - px)
228
+
229
+ vy_new = self.w_inertial * vy + self.w_cognitive * rand1 * (particle.best_pos[i][1] - py) \
230
+ + self.w_social * rand2 * (self.best_particle.position[i][1] - py)
231
+
232
+ # Velocity Scaling
233
+ if self.env.x_range > self.env.y_range:
234
+ vx_new *= self.env.x_range / self.env.y_range
235
+ else:
236
+ vy_new *= self.env.y_range / self.env.x_range
237
+
238
+ # Velocity limit
239
+ vx_new = MathHelper.clamp(vx_new, -self.max_speed, self.max_speed)
240
+ vy_new = MathHelper.clamp(vy_new, -self.max_speed, self.max_speed)
241
+ particle.velocity[i] = (vx_new, vy_new)
242
+
243
+ def updateParticlePosition(self, particle):
244
+ """
245
+ A function to update the particle position
246
+
247
+ Parameters:
248
+ particle (Particle): the particle
249
+ """
250
+ # update Position
251
+ for i in range(self.point_num):
252
+ px = particle.position[i][0] + int(particle.velocity[i][0])
253
+ py = particle.position[i][1] + int(particle.velocity[i][1])
254
+
255
+ # Position limit
256
+ px = MathHelper.clamp(px, 0, self.env.x_range - 1)
257
+ py = MathHelper.clamp(py, 0, self.env.y_range - 1)
258
+
259
+ particle.position[i] = (px, py)
260
+ particle.position.sort(key=lambda p: p[0])
261
+
262
+ def optimizeParticle(self, particle):
263
+ """
264
+ Particle update optimization iteration
265
+
266
+ Parameters:
267
+ particle (Particle): the particle
268
+ """
269
+ # update speed
270
+ self.updateParticleVelocity(particle)
271
+ # update position
272
+ self.updateParticlePosition(particle)
273
+
274
+ # Calculate fitness
275
+ particle.fitness = self.calFitnessValue(particle.position)
276
+
277
+ # Update individual optima
278
+ if particle.fitness > particle.best_fitness:
279
+ particle.best_fitness = particle.fitness
280
+ particle.best_pos = particle.position
281
+
282
+ # Update global optimal particles
283
+ if particle.best_fitness > self.best_particle.fitness:
284
+ self.best_particle.fitness = particle.best_fitness
285
+ self.best_particle.position = deepcopy(particle.position)
286
+
287
+ def run(self):
288
+ """
289
+ Running both plannig and animation.
290
+ """
291
+ cost, path, fitness_history = self.plan(verbose=True)
292
+ cost_curve = [-f for f in fitness_history]
293
+ self.plot.animation(path, str(self), cost, cost_curve=cost_curve)
294
+
295
+ def isCollision(self, p1: tuple, p2: tuple) -> bool:
296
+ """
297
+ Judge collision when moving from node1 to node2 using Bresenham.
298
+
299
+ Parameters:
300
+ p1 (tuple): start point
301
+ p2 (tuple): end point
302
+
303
+ Returns:
304
+ collision (bool): True if collision exists, False otherwise.
305
+ """
306
+ if p1 in self.obstacles or p2 in self.obstacles:
307
+ return True
308
+
309
+ x1, y1 = p1
310
+ x2, y2 = p2
311
+
312
+ if x1 < 0 or x1 >= self.env.x_range or y1 < 0 or y1 >= self.env.y_range:
313
+ return True
314
+ if x2 < 0 or x2 >= self.env.x_range or y2 < 0 or y2 >= self.env.y_range:
315
+ return True
316
+
317
+ d_x = abs(x2 - x1)
318
+ d_y = abs(y2 - y1)
319
+ s_x = 0 if (x2 - x1) == 0 else (x2 - x1) / d_x
320
+ s_y = 0 if (y2 - y1) == 0 else (y2 - y1) / d_y
321
+ x, y, e = x1, y1, 0
322
+
323
+ # check if any obstacle exists between node1 and node2
324
+ if d_x > d_y:
325
+ tau = (d_y - d_x) / 2
326
+ while not x == x2:
327
+ if e > tau:
328
+ x = x + s_x
329
+ e = e - d_y
330
+ elif e < tau:
331
+ y = y + s_y
332
+ e = e + d_x
333
+ else:
334
+ x = x + s_x
335
+ y = y + s_y
336
+ e = e + d_x - d_y
337
+ if (x, y) in self.obstacles:
338
+ return True
339
+ # swap x and y
340
+ else:
341
+ tau = (d_x - d_y) / 2
342
+ while not y == y2:
343
+ if e > tau:
344
+ y = y + s_y
345
+ e = e - d_x
346
+ elif e < tau:
347
+ x = x + s_x
348
+ e = e + d_y
349
+ else:
350
+ x = x + s_x
351
+ y = y + s_y
352
+ e = e + d_y - d_x
353
+ if (x, y) in self.obstacles:
354
+ return True
355
+
356
+ return False
@@ -0,0 +1,28 @@
1
+ from .a_star import AStar
2
+ from .dijkstra import Dijkstra
3
+ from .gbfs import GBFS
4
+ from .jps import JPS
5
+ from .d_star import DStar
6
+ from .lpa_star import LPAStar
7
+ from .d_star_lite import DStarLite
8
+ from .voronoi import VoronoiPlanner
9
+ from .theta_star import ThetaStar
10
+ from .lazy_theta_star import LazyThetaStar
11
+ from .s_theta_star import SThetaStar
12
+ # from .anya import Anya
13
+ # from .hybrid_a_star import HybridAStar
14
+
15
+ __all__ = ["AStar",
16
+ "Dijkstra",
17
+ "GBFS",
18
+ "JPS",
19
+ "DStar",
20
+ "LPAStar",
21
+ "DStarLite",
22
+ "VoronoiPlanner",
23
+ "ThetaStar",
24
+ "LazyThetaStar",
25
+ "SThetaStar",
26
+ # "Anya",
27
+ # "HybridAStar"
28
+ ]