-
-
Notifications
You must be signed in to change notification settings - Fork 6.9k
added pso algorithm #1279
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
added pso algorithm #1279
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,7 @@ _build/ | |
# Distribution / packaging | ||
.Python | ||
env/ | ||
venv/ | ||
build/ | ||
develop-eggs/ | ||
dist/ | ||
|
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,312 @@ | ||||||||
""" | ||||||||
Particle Swarm Optimization (PSO) Path Planning | ||||||||
|
||||||||
author: Anish (@anishk85) | ||||||||
|
||||||||
See Wikipedia article (https://en.wikipedia.org/wiki/Particle_swarm_optimization) | ||||||||
|
||||||||
References: | ||||||||
- Kennedy, J.; Eberhart, R. (1995). "Particle Swarm Optimization" | ||||||||
- Shi, Y.; Eberhart, R. (1998). "A Modified Particle Swarm Optimizer" | ||||||||
- https://machinelearningmastery.com/a-gentle-introduction-to-particle-swarm-optimization/ | ||||||||
|
||||||||
This implementation uses PSO to find collision-free paths by treating | ||||||||
path planning as an optimization problem where particles explore the | ||||||||
search space to minimize distance to target while avoiding obstacles. | ||||||||
""" | ||||||||
import numpy as np | ||||||||
import matplotlib | ||||||||
import matplotlib.pyplot as plt | ||||||||
import matplotlib.animation as animation | ||||||||
import matplotlib.patches as patches | ||||||||
import signal | ||||||||
import sys | ||||||||
|
||||||||
# Add show_animation flag for consistency with other planners | ||||||||
show_animation = True | ||||||||
|
||||||||
def signal_handler(sig, frame): | ||||||||
print('\nExiting...') | ||||||||
plt.close('all') | ||||||||
sys.exit(0) | ||||||||
|
||||||||
signal.signal(signal.SIGINT, signal_handler) | ||||||||
|
||||||||
class Particle: | ||||||||
def __init__(self, search_bounds, spawn_bounds): | ||||||||
self.search_bounds = search_bounds | ||||||||
self.max_velocity = np.array([(b[1] - b[0]) * 0.05 for b in search_bounds]) | ||||||||
self.position = np.array([np.random.uniform(b[0], b[1]) for b in spawn_bounds]) | ||||||||
self.velocity = np.random.randn(2) * 0.1 | ||||||||
self.pbest_position = self.position.copy() | ||||||||
self.pbest_value = np.inf | ||||||||
self.path = [self.position.copy()] | ||||||||
|
||||||||
def update_velocity(self, gbest_pos, w, c1, c2): | ||||||||
"""Update particle velocity using PSO equation: | ||||||||
v = w*v + c1*r1*(pbest - x) + c2*r2*(gbest - x) | ||||||||
""" | ||||||||
r1 = np.random.rand(2) | ||||||||
r2 = np.random.rand(2) | ||||||||
|
||||||||
cognitive = c1 * r1 * (self.pbest_position - self.position) | ||||||||
social = c2 * r2 * (gbest_pos - self.position) | ||||||||
|
||||||||
self.velocity = w * self.velocity + cognitive + social | ||||||||
self.velocity = np.clip(self.velocity, -self.max_velocity, self.max_velocity) | ||||||||
|
||||||||
def update_position(self): | ||||||||
self.position = self.position + self.velocity | ||||||||
|
||||||||
# Keep in bounds | ||||||||
for i in range(2): | ||||||||
self.position[i] = np.clip(self.position[i], | ||||||||
self.search_bounds[i][0], | ||||||||
self.search_bounds[i][1]) | ||||||||
|
||||||||
self.path.append(self.position.copy()) | ||||||||
|
||||||||
|
||||||||
class PSOSwarm: | ||||||||
def __init__(self, n_particles, max_iter, target, search_bounds, | ||||||||
spawn_bounds, obstacles): | ||||||||
self.n_particles = n_particles | ||||||||
self.max_iter = max_iter | ||||||||
self.target = np.array(target) | ||||||||
self.obstacles = obstacles | ||||||||
self.search_bounds = search_bounds | ||||||||
|
||||||||
# PSO parameters | ||||||||
self.w_start = 0.9 # Initial inertia weight | ||||||||
self.w_end = 0.4 # Final inertia weight | ||||||||
self.c1 = 1.5 # Cognitive coefficient | ||||||||
self.c2 = 1.5 # Social coefficient | ||||||||
|
||||||||
# Initialize particles | ||||||||
self.particles = [Particle(search_bounds, spawn_bounds) | ||||||||
for _ in range(n_particles)] | ||||||||
|
||||||||
self.gbest_position = None | ||||||||
self.gbest_value = np.inf | ||||||||
self.gbest_path = [] | ||||||||
self.iteration = 0 | ||||||||
|
||||||||
def fitness(self, pos): | ||||||||
"""Calculate fitness - distance to target + obstacle penalty""" | ||||||||
dist = np.linalg.norm(pos - self.target) | ||||||||
|
||||||||
# Obstacle penalty | ||||||||
penalty = 0 | ||||||||
for ox, oy, r in self.obstacles: | ||||||||
obs_dist = np.linalg.norm(pos - np.array([ox, oy])) | ||||||||
if obs_dist < r: | ||||||||
penalty += 1000 # Inside obstacle | ||||||||
elif obs_dist < r + 5: | ||||||||
penalty += 50 / (obs_dist - r + 0.1) # Too close | ||||||||
|
||||||||
return dist + penalty | ||||||||
|
||||||||
def check_collision(self, start, end, obstacle): | ||||||||
"""Check if path from start to end hits obstacle using line-circle intersection""" | ||||||||
ox, oy, r = obstacle | ||||||||
center = np.array([ox, oy]) | ||||||||
|
||||||||
# Vector math for line-circle intersection | ||||||||
d = end - start | ||||||||
f = start - center | ||||||||
|
||||||||
a = np.dot(d, d) | ||||||||
b = 2 * np.dot(f, d) | ||||||||
c = np.dot(f, f) - r * r | ||||||||
|
||||||||
discriminant = b * b - 4 * a * c | ||||||||
|
||||||||
if discriminant < 0: | ||||||||
return False | ||||||||
|
||||||||
# Check if intersection on segment | ||||||||
t1 = (-b - np.sqrt(discriminant)) / (2 * a) | ||||||||
t2 = (-b + np.sqrt(discriminant)) / (2 * a) | ||||||||
anishk85 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||
|
||||||||
return (0 <= t1 <= 1) or (0 <= t2 <= 1) | ||||||||
|
||||||||
def step(self): | ||||||||
"""Run one PSO iteration""" | ||||||||
if self.iteration >= self.max_iter: | ||||||||
return False | ||||||||
|
||||||||
# Update inertia weight (linear decay) | ||||||||
w = self.w_start - (self.w_start - self.w_end) * (self.iteration / self.max_iter) | ||||||||
|
||||||||
# Evaluate all particles | ||||||||
for particle in self.particles: | ||||||||
value = self.fitness(particle.position) | ||||||||
|
||||||||
# Update personal best | ||||||||
if value < particle.pbest_value: | ||||||||
particle.pbest_value = value | ||||||||
particle.pbest_position = particle.position.copy() | ||||||||
|
||||||||
# Update global best | ||||||||
if value < self.gbest_value: | ||||||||
self.gbest_value = value | ||||||||
self.gbest_position = particle.position.copy() | ||||||||
|
||||||||
if self.gbest_position is not None: | ||||||||
self.gbest_path.append(self.gbest_position.copy()) | ||||||||
|
||||||||
# Update particles | ||||||||
for particle in self.particles: | ||||||||
particle.update_velocity(self.gbest_position, w, self.c1, self.c2) | ||||||||
|
||||||||
# Predict next position | ||||||||
next_pos = particle.position + particle.velocity | ||||||||
|
||||||||
# Check collision | ||||||||
collision = False | ||||||||
for obs in self.obstacles: | ||||||||
if self.check_collision(particle.position, next_pos, obs): | ||||||||
collision = True | ||||||||
break | ||||||||
|
||||||||
if collision: | ||||||||
# Reduce velocity if collision detected | ||||||||
particle.velocity *= 0.2 | ||||||||
|
||||||||
particle.update_position() | ||||||||
|
||||||||
self.iteration += 1 | ||||||||
if show_animation and self.iteration % 20 == 0: | ||||||||
print(f"Iteration {self.iteration}/{self.max_iter}, Best: {self.gbest_value:.2f}") | ||||||||
return True | ||||||||
|
||||||||
|
||||||||
def main(): | ||||||||
"""Test PSO path planning algorithm""" | ||||||||
anishk85 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||
print(__file__ + " start!!") | ||||||||
|
||||||||
# Set matplotlib backend for headless environments | ||||||||
if not show_animation: | ||||||||
matplotlib.use('Agg') # Use non-GUI backend for testing | ||||||||
|
||||||||
# Setup | ||||||||
N_PARTICLES = 15 | ||||||||
MAX_ITER = 150 | ||||||||
SEARCH_BOUNDS = [(-50, 50), (-50, 50)] | ||||||||
TARGET = [40, 35] | ||||||||
SPAWN_AREA = [(-45, -35), (-45, -35)] | ||||||||
OBSTACLES = [ | ||||||||
(10, 15, 8), | ||||||||
(-20, 0, 12), | ||||||||
(20, -25, 10), | ||||||||
(-5, -30, 7) | ||||||||
] | ||||||||
|
||||||||
swarm = PSOSwarm( | ||||||||
n_particles=N_PARTICLES, | ||||||||
max_iter=MAX_ITER, | ||||||||
target=TARGET, | ||||||||
search_bounds=SEARCH_BOUNDS, | ||||||||
spawn_bounds=SPAWN_AREA, | ||||||||
obstacles=OBSTACLES | ||||||||
) | ||||||||
|
||||||||
if show_animation: # pragma: no cover | ||||||||
|
if show_animation: # pragma: no cover | |
# pragma: no cover | |
if show_animation: |
Copilot uses AI. Check for mistakes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed in next commit
Uh oh!
There was an error while loading. Please reload this page.