这个错误通常发生在尝试将一个序列(如列表)与浮点数相乘时。在您的代码中,问题可能出现在 `PSO` 类的 `update_particles` 方法中,当更新粒子速度时,可能错误地将速度(列表)与浮点数相乘。
以下是修复后的代码,重点修改了 `update_particles` 方法中的速度更新逻辑:
```python
import numpy as np
import matplotlib.pyplot as plt
import random
import heapq
from typing import List, Tuple, Optional
# 设置中文显示
plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"]
class GridMap:
"""栅格地图类"""
def __init__(self, width: int = 20, height: int = 20, obstacle_ratio: float = 0.2):
"""
初始化栅格地图
参数:
width: 地图宽度
height: 地图高度
obstacle_ratio: 障碍物比例
"""
self.width = width
self.height = height
self.obstacle_ratio = obstacle_ratio
self.grid = np.zeros((height, width), dtype=int) # 0表示可通行,1表示障碍物
self.start = (0, 0)
self.goal = (width-1, height-1)
# 随机生成障碍物
self.generate_random_obstacles()
def generate_random_obstacles(self):
"""随机生成障碍物"""
obstacle_count = int(self.width * self.height * self.obstacle_ratio)
obstacles = random.sample(range(self.width * self.height), obstacle_count)
for obs in obstacles:
x = obs % self.width
y = obs // self.width
self.grid[y, x] = 1
def generate_custom_map1(self):
"""生成自定义地图1:中央有大障碍物"""
self.grid = np.zeros((self.height, self.width), dtype=int)
# 中央大障碍物
mid_x, mid_y = self.width // 2, self.height // 2
for i in range(mid_y - 4, mid_y + 5):
for j in range(mid_x - 4, mid_x + 5):
if 0 <= i < self.height and 0 <= j < self.width:
self.grid[i, j] = 1
# 随机添加一些小障碍物
obstacle_count = int(self.width * self.height * (self.obstacle_ratio / 2))
obstacles = random.sample(range(self.width * self.height), obstacle_count)
for obs in obstacles:
x = obs % self.width
y = obs // self.width
if self.grid[y, x] == 0: # 避免覆盖中央障碍物
self.grid[y, x] = 1
def generate_custom_map2(self):
"""生成自定义地图2:对角线障碍物"""
self.grid = np.zeros((self.height, self.width), dtype=int)
# 对角线障碍物
for i in range(self.height):
for j in range(self.width):
if abs(i - j) < 2:
self.grid[i, j] = 1
# 随机添加一些小障碍物
obstacle_count = int(self.width * self.height * (self.obstacle_ratio / 2))
obstacles = random.sample(range(self.width * self.height), obstacle_count)
for obs in obstacles:
x = obs % self.width
y = obs // self.width
if self.grid[y, x] == 0: # 避免覆盖对角线障碍物
self.grid[y, x] = 1
def is_valid(self, x: int, y: int) -> bool:
"""检查坐标是否有效(在地图内且不是障碍物)"""
return 0 <= x < self.width and 0 <= y < self.height and self.grid[y, x] == 0
def get_neighbors(self, x: int, y: int) -> List[Tuple[int, int]]:
"""获取相邻节点"""
neighbors = []
for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1), (-1, -1), (-1, 1), (1, -1), (1, 1)]:
nx, ny = x + dx, y + dy
if self.is_valid(nx, ny):
neighbors.append((nx, ny))
return neighbors
class PSO:
"""粒子群优化算法"""
def __init__(self, grid_map: GridMap, start: Tuple[int, int], goal: Tuple[int, int],
num_particles: int = 30, max_iter: int = 100, w: float = 0.7,
c1: float = 1.5, c2: float = 1.5):
"""
初始化PSO算法
参数:
grid_map: 栅格地图
start: 起点坐标 (x, y)
goal: 终点坐标 (x, y)
num_particles: 粒子数量
max_iter: 最大迭代次数
w: 惯性权重
c1: 个体学习因子
c2: 社会学习因子
"""
self.grid_map = grid_map
self.start = start
self.goal = goal
self.num_particles = num_particles
self.max_iter = max_iter
self.w = w
self.c1 = c1
self.c2 = c2
# 粒子位置和速度
self.particles = []
self.velocities = []
self.pbest_positions = []
self.pbest_fitness = []
self.gbest_position = None
self.gbest_fitness = float('inf')
# 初始化粒子
self.initialize_particles()
def initialize_particles(self):
"""初始化粒子群"""
for _ in range(self.num_particles):
# 生成一条随机路径
path = self.generate_random_path()
self.particles.append(path)
# 初始化速度为小随机值(每个节点的速度是一个二维元组)
velocity = [(random.uniform(-1, 1), random.uniform(-1, 1)) for _ in range(len(path))]
self.velocities.append(velocity)
# 初始化个体最优
fitness = self.calculate_fitness(path)
self.pbest_positions.append(path.copy())
self.pbest_fitness.append(fitness)
# 更新全局最优
if fitness < self.gbest_fitness:
self.gbest_fitness = fitness
self.gbest_position = path.copy()
def generate_random_path(self) -> List[Tuple[int, int]]:
"""生成随机路径"""
path = [self.start]
current = self.start
for _ in range(18): # 20个节点的路径(包括起点和终点)
neighbors = self.grid_map.get_neighbors(current[0], current[1])
if not neighbors:
break
next_node = random.choice(neighbors)
path.append(next_node)
current = next_node
# 确保路径最后一个节点是终点
if path[-1] != self.goal:
path.append(self.goal)
return path
def calculate_fitness(self, path: List[Tuple[int, int]]) -> float:
"""计算路径适应度(路径长度)"""
fitness = 0.0
# 计算路径长度
for i in range(len(path) - 1):
dx = path[i+1][0] - path[i][0]
dy = path[i+1][1] - path[i][1]
fitness += np.sqrt(dx*dx + dy*dy)
# 如果路径经过障碍物,增加惩罚
if not self.grid_map.is_valid(path[i+1][0], path[i+1][1]):
fitness += 1000
# 检查路径是否包含重复节点
unique_nodes = set(path)
if len(unique_nodes) < len(path):
fitness += 1000 * (len(path) - len(unique_nodes))
return fitness
def update_particles(self):
"""更新粒子位置和速度"""
for i in range(self.num_particles):
particle = self.particles[i]
velocity = self.velocities[i]
pbest = self.pbest_positions[i]
# 更新每个节点的位置
for j in range(len(particle)):
if j == 0 or j == len(particle) - 1: # 起点和终点不能改变
continue
# 更新速度(修正:速度是二维元组,不能直接与浮点数相乘)
r1, r2 = random.random(), random.random()
# 计算认知和社会分量
cognitive_x = self.c1 * r1 * (pbest[j][0] - particle[j][0])
cognitive_y = self.c1 * r1 * (pbest[j][1] - particle[j][1])
social_x = self.c2 * r2 * (self.gbest_position[j][0] - particle[j][0])
social_y = self.c2 * r2 * (self.gbest_position[j][1] - particle[j][1])
# 更新速度(分量形式)
new_vx = self.w * velocity[j][0] + cognitive_x + social_x
new_vy = self.w * velocity[j][1] + cognitive_y + social_y
# 限制速度
max_vel = 2
new_vx = max(-max_vel, min(max_vel, new_vx))
new_vy = max(-max_vel, min(max_vel, new_vy))
# 更新位置
new_x = int(particle[j][0] + new_vx)
new_y = int(particle[j][1] + new_vy)
# 确保新位置有效
if not self.grid_map.is_valid(new_x, new_y):
neighbors = self.grid_map.get_neighbors(particle[j][0], particle[j][1])
if neighbors:
new_x, new_y = random.choice(neighbors)
else:
new_x, new_y = particle[j]
particle[j] = (new_x, new_y)
velocity[j] = (new_vx, new_vy) # 更新速度
# 计算新适应度
fitness = self.calculate_fitness(particle)
# 更新个体最优
if fitness < self.pbest_fitness[i]:
self.pbest_fitness[i] = fitness
self.pbest_positions[i] = particle.copy()
# 更新全局最优
if fitness < self.gbest_fitness:
self.gbest_fitness = fitness
self.gbest_position = particle.copy()
def optimize(self) -> Tuple[List[Tuple[int, int]], List[float]]:
"""执行优化"""
fitness_history = []
for iteration in range(self.max_iter):
self.update_particles()
fitness_history.append(self.gbest_fitness)
if iteration % 10 == 0:
print(f"迭代 {iteration}/{self.max_iter}, 最优适应度: {self.gbest_fitness}")
return self.gbest_position, fitness_history
class AStar:
"""A*算法"""
def __init__(self, grid_map: GridMap, start: Tuple[int, int], goal: Tuple[int, int]):
"""
初始化A*算法
参数:
grid_map: 栅格地图
start: 起点坐标 (x, y)
goal: 终点坐标 (x, y)
"""
self.grid_map = grid_map
self.start = start
self.goal = goal
def heuristic(self, a: Tuple[int, int], b: Tuple[int, int]) -> float:
"""启发式函数(曼哈顿距离)"""
return abs(a[0] - b[0]) + abs(a[1] - b[1])
def find_path(self) -> Optional[List[Tuple[int, int]]]:
"""寻找路径"""
open_set = []
heapq.heappush(open_set, (0, self.start))
came_from = {}
g_score = {self.start: 0}
f_score = {self.start: self.heuristic(self.start, self.goal)}
while open_set:
_, current = heapq.heappop(open_set)
if current == self.goal:
# 重建路径
path = [current]
while current in came_from:
current = came_from[current]
path.append(current)
path.reverse()
return path
for neighbor in self.grid_map.get_neighbors(current[0], current[1]):
# 计算从当前节点到邻居的成本
dx = neighbor[0] - current[0]
dy = neighbor[1] - current[1]
cost = 1.0 if dx * dy == 0 else 1.4 # 对角线移动成本更高
tentative_g_score = g_score[current] + cost
if neighbor not in g_score or tentative_g_score < g_score[neighbor]:
# 这是更好的路径
came_from[neighbor] = current
g_score[neighbor] = tentative_g_score
f_score[neighbor] = tentative_g_score + self.heuristic(neighbor, self.goal)
heapq.heappush(open_set, (f_score[neighbor], neighbor))
return None # 没有找到路径
def visualize_results(grid_map: GridMap, pso_path: List[Tuple[int, int]],
astar_path: List[Tuple[int, int]], title: str):
"""可视化结果"""
plt.figure(figsize=(12, 10))
# 绘制地图
plt.imshow(grid_map.grid, cmap='gray_r', interpolation='nearest')
# 绘制起点和终点
plt.plot(grid_map.start[0], grid_map.start[1], 'go', markersize=10)
plt.plot(grid_map.goal[0], grid_map.goal[1], 'ro', markersize=10)
# 绘制PSO路径
pso_x = [p[0] for p in pso_path]
pso_y = [p[1] for p in pso_path]
plt.plot(pso_x, pso_y, 'b-', linewidth=2, label='PSO路径')
# 绘制A*路径
astar_x = [p[0] for p in astar_path]
astar_y = [p[1] for p in astar_path]
plt.plot(astar_x, astar_y, 'g-', linewidth=2, label='A*路径')
plt.title(title)
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()
def main():
"""主函数"""
# 创建两个不同的地图
map1 = GridMap(width=20, height=20, obstacle_ratio=0.2)
map1.generate_custom_map1()
map1.start = (2, 2)
map1.goal = (18, 18)
map2 = GridMap(width=20, height=20, obstacle_ratio=0.2)
map2.generate_custom_map2()
map2.start = (2, 18)
map2.goal = (18, 2)
# 对地图1执行路径规划
print("=== 地图1路径规划 ===")
# 使用PSO算法
pso1 = PSO(map1, map1.start, map1.goal, num_particles=30, max_iter=100)
pso_path1, pso_fitness_history1 = pso1.optimize()
# 使用A*算法
astar1 = AStar(map1, map1.start, map1.goal)
astar_path1 = astar1.find_path()
if astar_path1:
print(f"PSO路径长度: {len(pso_path1)}")
print(f"A*路径长度: {len(astar_path1)}")
# 可视化结果
visualize_results(map1, pso_path1, astar_path1, "地图1路径规划结果对比")
# 绘制适应度历史
plt.figure(figsize=(10, 6))
plt.plot(pso_fitness_history1)
plt.title("PSO算法适应度历史(地图1)")
plt.xlabel("迭代次数")
plt.ylabel("适应度(路径长度)")
plt.grid(True)
plt.show()
else:
print("A*算法未能找到路径")
# 对地图2执行路径规划
print("\n=== 地图2路径规划 ===")
# 使用PSO算法
pso2 = PSO(map2, map2.start, map2.goal, num_particles=30, max_iter=100)
pso_path2, pso_fitness_history2 = pso2.optimize()
# 使用A*算法
astar2 = AStar(map2, map2.start, map2.goal)
astar_path2 = astar2.find_path()
if astar_path2:
print(f"PSO路径长度: {len(pso_path2)}")
print(f"A*路径长度: {len(astar_path2)}")
# 可视化结果
visualize_results(map2, pso_path2, astar_path2, "地图2路径规划结果对比")
# 绘制适应度历史
plt.figure(figsize=(10, 6))
plt.plot(pso_fitness_history2)
plt.title("PSO算法适应度历史(地图2)")
plt.xlabel("迭代次数")
plt.ylabel("适应度(路径长度)")
plt.grid(True)
plt.show()
else:
print("A*算法未能找到路径")
# 算法性能对比
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
plt.bar(['PSO', 'A*'], [len(pso_path1), len(astar_path1)])
plt.title('地图1路径长度对比')
plt.ylabel('路径长度')
plt.ylim(0, max(len(pso_path1), len(astar_path1)) * 1.1)
plt.subplot(1, 2, 2)
plt.bar(['PSO', 'A*'], [len(pso_path2), len(astar_path2)])
plt.title('地图2路径长度对比')
plt.ylabel('路径长度')
plt.ylim(0, max(len(pso_path2), len(astar_path2)) * 1.1)
plt.tight_layout()
plt.show()
if __name__ == "__main__":
main()
```
### **关键修复点**:
1. **速度初始化**:
- 原先 `self.velocities.append([random.uniform(-1, 1) for _ in range(len(path))])` 会导致速度是一个列表的列表,后续无法直接与浮点数相乘。
- 修复为 `velocity = [(random.uniform(-1, 1), random.uniform(-1, 1)) for _ in range(len(path))]`,即每个节点的速度是一个二维元组 `(vx, vy)`。
2. **速度更新**:
- 原先 `velocity[j] = (self.w * velocity[j][0] + cognitive_component[0] + social_component[0], ...)` 会尝试将列表与浮点数相乘。
- 修复为分别更新 `vx` 和 `vy` 分量:
```python
new_vx = self.w * velocity[j][0] + cognitive_x + social_x
new_vy = self.w * velocity[j][1] + cognitive_y + social_y
```
3. **位置更新**:
- 使用 `new_vx` 和 `new_vy` 更新位置,而不是直接操作速度列表。
### **其他注意事项**:
- 如果仍然报错,请检查 `random.uniform(-1, 1)` 是否生成了合法的浮点数。
- 确保 `particle[j]` 和 `velocity[j]` 的维度一致(均为 `(x, y)` 元组)。
运行修复后的代码应该可以正常生成路径规划结果和图像。