From ec1ee13db15c0f72115ca2fad2e9f13d14cacd68 Mon Sep 17 00:00:00 2001 From: wzh <2965067137@qq.com> Date: Fri, 26 Sep 2025 12:46:01 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=9C=80=E4=BC=98=E8=B7=AF?= =?UTF-8?q?=E5=BE=84=E7=AE=97=E6=B3=95=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ese_msjs_algorithm.cpython-311.pyc | Bin 0 -> 20812 bytes .../ese_msjs_algorithm.cpython-312.pyc | Bin 0 -> 19854 bytes .../simplified_uav_planning.cpython-311.pyc | Bin 0 -> 22736 bytes .../simplified_uav_planning.cpython-312.pyc | Bin 0 -> 21199 bytes src/ese_msjs_algorithm.py | 383 +++++++++++++++ src/main_simplified.py | 461 ++++++++++++++++++ src/simplified_uav_planning.py | 400 +++++++++++++++ 7 files changed, 1244 insertions(+) create mode 100644 src/__pycache__/ese_msjs_algorithm.cpython-311.pyc create mode 100644 src/__pycache__/ese_msjs_algorithm.cpython-312.pyc create mode 100644 src/__pycache__/simplified_uav_planning.cpython-311.pyc create mode 100644 src/__pycache__/simplified_uav_planning.cpython-312.pyc create mode 100644 src/ese_msjs_algorithm.py create mode 100644 src/main_simplified.py create mode 100644 src/simplified_uav_planning.py diff --git a/src/__pycache__/ese_msjs_algorithm.cpython-311.pyc b/src/__pycache__/ese_msjs_algorithm.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..03d9dcaa878211e6d81a260448a4ee6f86b4c0a1 GIT binary patch literal 20812 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 O9+_y}-zbD+!v7nW1z@NE literal 0 HcmV?d00001 diff --git a/src/__pycache__/simplified_uav_planning.cpython-311.pyc b/src/__pycache__/simplified_uav_planning.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..687201e8a8b330894224bca06472943698c0d0df GIT binary patch literal 22736 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 float: + """计算约束满足度 μ(Pi)""" + if not constraints: + return 1.0 + + min_satisfaction = 1.0 + for constraint_name, violation in constraints.items(): + if violation <= 0: # 满足约束 + satisfaction = 1.0 + else: # 违反约束 + # 使用分段线性函数 + a_m = 1.0 # 可以根据具体问题调整 + satisfaction = max(0.0, 1.0 - violation / a_m) + + min_satisfaction = min(min_satisfaction, satisfaction) + + return min_satisfaction + + def dynamic_alpha(self, t: int, t_max: int) -> float: + """动态α值计算""" + if t < t_max / 2: + return 1.0 / (1.0 + np.exp(4.0 - self.epsilon * t / t_max)) + else: + return 1.0 + + def alpha_level_comparison(self, sol1: Tuple[float, float], sol2: Tuple[float, float], + alpha: float) -> bool: + """α-level比较策略 + 返回True如果sol1优于sol2 + sol1, sol2格式: (fitness, satisfaction_level) + """ + f1, mu1 = sol1 + f2, mu2 = sol2 + + if mu1 >= alpha and mu2 >= alpha: + return f1 <= f2 # 都可行,比较目标函数 + elif mu1 == mu2: + return f1 <= f2 # 满足度相同,比较目标函数 + else: + return mu1 >= mu2 # 否则比较满足度 + + +class ESE_MSJS: + """进化状态估计的多策略水母搜索算法""" + + def __init__(self, + pop_size: int = 30, + max_iter: int = 50, + dim: int = 10, + lb: float = 0.0, + ub: float = 300.0, + epsilon: float = 4.0, + p_neighborhood: int = 2, + transition_prob: float = 0.5): + """ + 初始化ESE-MSJS算法 + + Args: + pop_size: 种群大小 + max_iter: 最大迭代次数 + dim: 问题维度 + lb: 下界 + ub: 上界 + epsilon: 混沌映射参数 + p_neighborhood: 邻域半径 + transition_prob: 转换概率 + """ + self.pop_size = pop_size + self.max_iter = max_iter + self.dim = dim + self.lb = lb if isinstance(lb, (list, np.ndarray)) else [lb] * dim + self.ub = ub if isinstance(ub, (list, np.ndarray)) else [ub] * dim + self.epsilon = epsilon + self.p_neighborhood = p_neighborhood + self.transition_prob = transition_prob + + # 种群和相关属性 + self.population = None + self.fitness = None + self.constraints = None + self.satisfaction_levels = None + self.best_solution = None + self.best_fitness = float('inf') + self.best_constraints = None + + # 约束处理器 + self.constraint_handler = ConstraintHandler() + + # 历史记录 + self.fitness_history = [] + self.best_fitness_history = [] + + def logistic_chaotic_map(self, x: float) -> float: + """逻辑混沌映射""" + return self.epsilon * x * (1 - x) + + def initialize_population(self): + """使用混沌映射初始化种群""" + self.population = np.zeros((self.pop_size, self.dim)) + + # 生成混沌序列 + x0 = random.uniform(0, 1) # 避免不动点 + while x0 in [0, 0.25, 0.5, 0.75, 1.0]: + x0 = random.uniform(0, 1) + + for i in range(self.pop_size): + for j in range(self.dim): + x0 = self.logistic_chaotic_map(x0) + self.population[i, j] = self.lb[j] + x0 * (self.ub[j] - self.lb[j]) + + def calculate_evolutionary_factor(self, jellyfish_idx: int, all_distances: list[np.floating],d_max: np.floating,d_min: np.floating) -> float: + """计算进化因子""" + if d_max == d_min: + return 0.1 + eF = (all_distances[jellyfish_idx] - d_min) / (d_max - d_min) + return max(0.0, min(1.0, eF)) + + def determine_evolutionary_state(self, eF: float) -> str: + """确定进化状态""" + if 2 / 3 < eF <= 1: + return "exploration" + elif 1 / 3 < eF <= 2 / 3: + return "transition" + else: + return "exploitation" + + def get_neighborhood_indices(self, idx: int) -> List[int]: + """获取环形拓扑邻域索引""" + neighborhood = [] + for i in range(-self.p_neighborhood, self.p_neighborhood + 1): + neighbor_idx = (idx + i) % self.pop_size + neighborhood.append(neighbor_idx) + return neighborhood + + def neighborhood_elite_learning(self, idx: int, rank_mapping: np.ndarray) -> np.ndarray: + """基于邻域拓扑的精英示例学习策略""" + neighborhood_indices = self.get_neighborhood_indices(idx) + + # 使用全局排序信息直接找到邻域最优个体(性能优化) + # TODO:完善约束与目标函数后进一步检查这部分 + best_neighbor_idx = min(neighborhood_indices, key=lambda x: rank_mapping[x]) + + # 计算邻域均值 + neighborhood_mean = np.mean([self.population[i] for i in neighborhood_indices], axis=0) + + # 加权均值计算 + eta = round(random.random()) # 0或1 + chosen_idx = random.choice([i for i in neighborhood_indices if i != idx]) + weighted_mean = (neighborhood_mean + + (eta * self.population[idx] + (1 - eta) * self.population[chosen_idx]) ) / 2 + + # 更新位置 - 进一步增大探索步长 + r = random.uniform(0.3, 2.5) # 进一步增大步长范围 + tau = 0.1 # 进一步减小分布系数,大幅增加移动幅度 + new_position = (self.population[idx] + + r * (self.population[best_neighbor_idx] - tau * r * weighted_mean)) + + return new_position + + def adaptive_scaling_factor_strategy(self, idx: int, fitness_ranks: np.ndarray, + rank_mapping: np.ndarray, best_idx: int) -> np.ndarray: + """最佳信息引导的自适应缩放因子策略""" + # 找到引导个体 + guide_idx = (idx + random.randint(1, self.pop_size - 1)) % self.pop_size + + # 使用预计算的排名信息 + # TODO:完善约束与目标函数后进一步检查这部分 + rank_b = rank_mapping[guide_idx] # O(1) + rank_l = rank_mapping[idx] # O(1) + + # 计算缩放因子 - 进一步增大移动幅度 + if rank_b < rank_l: # guide_idx排名更好 + kappa = 1.2 * (1 + (self.pop_size - rank_b) / self.pop_size) # 进一步增大系数 + else: + kappa = -np.random.normal(1.2, 0.5) # 进一步增大随机移动幅度 + + # 更新位置 - 进一步增加探索步长 + step_size = random.uniform(0.5, 2.5) # 进一步增加步长变化范围 + new_position = (self.population[best_idx] + + step_size * kappa * (self.population[guide_idx] - self.population[idx])) + + return new_position + + def gaussian_barebone_mechanism(self, idx: int, fitness_ranks: np.ndarray) -> np.ndarray: + """高斯裸骨机制""" + # 使用预计算的排名信息 + # TODO:完善约束与目标函数后进一步检查这部分 + rank_l = np.where(fitness_ranks == idx)[0][0] # 这里仍需要查找,但没有重新排序 + + # 计算概率系数 + PC_l = np.exp(-(rank_l)) + + if random.random() < PC_l: + # 高斯函数更新 - 增大标准差 + best_idx = fitness_ranks[0] + mean = (self.population[best_idx] + self.population[idx]) / 2 + std = np.abs(self.population[best_idx] - self.population[idx]) + std = np.maximum(std, 0.2 * (np.array(self.ub) - np.array(self.lb))) # 进一步增大最小标准差 + new_position = np.random.normal(mean, std) + else: + # DE变异策略 - 进一步增大变异步长 + indices = set() + while len(indices) < 3: + rand_idx = random.randint(0, self.pop_size - 1) + if rand_idx != idx: + indices.add(rand_idx) + i1, i2, i3 = list(indices) + mutation_factor = random.uniform(0.8, 3.0) # 进一步增大变异因子 + new_position = (self.population[i1] + + mutation_factor * (self.population[i2] - self.population[i3])) + + return new_position + + def jellyfish_dist(self)->list[np.floating]: + + distances = [] + all_distances = [] + for i in range(self.pop_size): + for j in range(self.pop_size): + if i!=j: + dist = np.sqrt(np.sum((self.population[i] - self.population[j]) ** 2)) + distances.append(dist) + dist_i=np.mean(distances) + all_distances.append(dist_i) + return all_distances + + def update_jellyfish(self, idx: int, iteration: int, fitness_ranks: np.ndarray, + rank_mapping: np.ndarray, best_idx: int) -> np.ndarray: + """根据进化状态更新水母位置""" + all_distances = self.jellyfish_dist() + d_min = min(all_distances) + d_max = max(all_distances) + eF = self.calculate_evolutionary_factor(idx,all_distances,d_max,d_min) + state = self.determine_evolutionary_state(eF) + + if state == "exploration": + new_position = self.neighborhood_elite_learning(idx, rank_mapping) + elif state == "exploitation": + new_position = self.adaptive_scaling_factor_strategy(idx, fitness_ranks, rank_mapping, best_idx) + else: # transition + new_position = self.gaussian_barebone_mechanism(idx, fitness_ranks) + + # 改进的边界处理:反弹策略而非直接裁剪 + new_position = self.boundary_handling(new_position) + + return new_position + + def boundary_handling(self, position: np.ndarray) -> np.ndarray: + """改进的边界处理策略:反弹+随机扰动""" + new_position = position.copy() + + for i in range(len(position)): + lb_i = self.lb[i] + ub_i = self.ub[i] + + if position[i] < lb_i: + # 反弹策略:超出下界时反弹到合理范围内 + overshoot = abs(position[i] - lb_i) + range_size = ub_i - lb_i + new_position[i] = lb_i + overshoot % range_size + elif position[i] > ub_i: + # 反弹策略:超出上界时反弹到合理范围内 + overshoot = abs(position[i] - ub_i) + range_size = ub_i - lb_i + new_position[i] = ub_i - overshoot % range_size + + # 添加更大的随机扰动以配合增大的移动步长 + if np.random.random() < 0.25: # 增加扰动概率到25% + for i in range(len(new_position)): + range_size = self.ub[i] - self.lb[i] + perturbation = np.random.normal(0, 0.05) * range_size # 增大扰动幅度 + new_position[i] += perturbation + + # 确保扰动后仍在边界内 + new_position[i] = max(self.lb[i], min(self.ub[i], new_position[i])) + + return new_position + + def evaluate_population(self, objective_func: Callable, constraint_func: Callable = None): + """评估种群""" + self.fitness = np.zeros(self.pop_size) + self.constraints = [] + self.satisfaction_levels = np.zeros(self.pop_size) + + for i in range(self.pop_size): + # 计算目标函数值 + + self.fitness[i] = objective_func(self.population[i]) + + # 计算约束 + if constraint_func: + constraints = constraint_func(self.population[i]) + self.constraints.append(constraints) + self.satisfaction_levels[i] = self.constraint_handler.calculate_satisfaction_level(constraints) + else: + self.constraints.append({}) + self.satisfaction_levels[i] = 1.0 + + def update_best_solution(self, iteration: int): + """使用α-level比较更新最优解""" + alpha = self.constraint_handler.dynamic_alpha(iteration, self.max_iter) + + for i in range(self.pop_size): + current_sol = (self.fitness[i], self.satisfaction_levels[i]) + best_sol = (self.best_fitness, 1.0 if self.best_constraints is None + else self.constraint_handler.calculate_satisfaction_level(self.best_constraints)) + + if self.constraint_handler.alpha_level_comparison(current_sol, best_sol, alpha): + self.best_solution = self.population[i].copy() + self.best_fitness = self.fitness[i] + self.best_constraints = self.constraints[i].copy() if self.constraints[i] else None + + def optimize(self, objective_func: Callable, constraint_func: Callable = None) -> Tuple[np.ndarray, float]: + """主优化循环""" + # 初始化 + self.initialize_population() + self.evaluate_population(objective_func, constraint_func) + + # 初始化最优解 + best_idx = np.argmin(self.fitness) + self.best_solution = self.population[best_idx].copy() + self.best_fitness = self.fitness[best_idx] + self.best_constraints = self.constraints[best_idx].copy() if self.constraints else None + + # 主循环 + for iteration in range(self.max_iter): + new_population = np.zeros_like(self.population) + + # 预计算排序信息(性能优化) + fitness_ranks = np.argsort(self.fitness) + rank_mapping = np.empty(self.pop_size, dtype=int) + rank_mapping[fitness_ranks] = np.arange(self.pop_size) + best_idx = fitness_ranks[0] + + # 更新每个水母 + for i in range(self.pop_size): + new_position = self.update_jellyfish(i, iteration, fitness_ranks, rank_mapping, best_idx) + + # 评估新位置 + new_fitness = objective_func(new_position) + new_constraints = constraint_func(new_position) if constraint_func else {} + new_satisfaction = self.constraint_handler.calculate_satisfaction_level(new_constraints) + + # α-level比较决定是否接受新位置 + alpha = self.constraint_handler.dynamic_alpha(iteration, self.max_iter) + current_sol = (self.fitness[i], self.satisfaction_levels[i]) + new_sol = (new_fitness, new_satisfaction) + + if self.constraint_handler.alpha_level_comparison(new_sol, current_sol, alpha): + new_population[i] = new_position + self.fitness[i] = new_fitness + self.constraints[i] = new_constraints + self.satisfaction_levels[i] = new_satisfaction + else: + new_population[i] = self.population[i] + + self.population = new_population + + # 更新最优解 + self.update_best_solution(iteration) + + # 记录历史 + self.fitness_history.append(self.fitness.copy()) + self.best_fitness_history.append(self.best_fitness) + + # 打印进度(可选) + if iteration % 50 == 0: + print(f"Iteration {iteration}: Best fitness = {self.best_fitness:.6f}") + + return self.best_solution, self.best_fitness \ No newline at end of file diff --git a/src/main_simplified.py b/src/main_simplified.py new file mode 100644 index 0000000..da566db --- /dev/null +++ b/src/main_simplified.py @@ -0,0 +1,461 @@ + + + +import numpy as np +import matplotlib.pyplot as plt +from mpl_toolkits.mplot3d import Axes3D +import time +import os + +# 导入简化模块 +from ese_msjs_algorithm import ESE_MSJS +from simplified_uav_planning import ( + SimplifiedUAVConfig, SimplifiedUAVEnvironment, SimplifiedUAVPlanner, + TerrainData, NoFlyZone3D +) + + +def create_test_scenario(): + """创建测试场景""" + # 创建配置 + config = SimplifiedUAVConfig( + num_uavs=2, + num_waypoints=8, + x_min=0.0, x_max=1000.0, + y_min=0.0, y_max=800.0, + z_min=50.0, z_max=400.0, + max_flight_distance=2500.0, # 增加最大飞行距离限制 + ground_clearance=30.0, # 地面安全间隙 + uav_speed=25.0, # 飞行速度 + collision_safe_distance=60.0 # 无人机间安全距离 + ) + + # 创建环境 + environment = SimplifiedUAVEnvironment(config) + + # 设置地形数据 + terrain = TerrainData( + x_range=(config.x_min, config.x_max), + y_range=(config.y_min, config.y_max), + resolution=20.0 + ) + environment.set_terrain_data(terrain) + + # 添加三维禁飞区 - 调整位置使其更容易规避 + nfz_zones = [ + NoFlyZone3D( + center=(300, 300, 150), + size=(80, 80, 100), # 减小长方体禁飞区 + shape="box" + ), + NoFlyZone3D( + center=(600, 400, 200), + size=(60, 60, 60), # 减小球形禁飞区 + shape="sphere" + ) + ] + + for nfz in nfz_zones: + environment.add_no_fly_zone(nfz) + + # 设置起始点和目标点 + start_points = [ + (50, 100, 100), # UAV1起始点 + (50, 700, 100), # UAV2起始点 + ] + + target_points = [ + (950, 150, 100), # UAV1目标点 + (950, 650, 100), # UAV2目标点 + ] + + environment.set_start_target_points(start_points, target_points) + + return config, environment + + +def evaluate_solution_quality(paths, constraints, planner): + """评估解的质量""" + print(f"\n=== 解质量评估 ===") + + # 约束满足情况 + print(f"约束满足情况:") + feasible_constraints = 0 + total_constraints = len(constraints) + + for constraint_name, violation in constraints.items(): + status = "满足" if violation <= 1e-6 else f"违反 (程度: {violation:.3f})" + if violation <= 1e-6: + feasible_constraints += 1 + print(f" {constraint_name}: {status}") + + feasibility_rate = feasible_constraints / total_constraints if total_constraints > 0 else 1.0 + print(f"约束满足率: {feasible_constraints}/{total_constraints} ({feasibility_rate*100:.1f}%)") + + # 路径统计 + stats = planner.get_path_statistics(paths) + print(f"\n路径统计:") + print(f" 总飞行距离: {stats['total_distance']:.1f}m") + print(f" 平均飞行距离: {stats['avg_distance']:.1f}m") + print(f" 最大飞行距离: {stats['max_distance']:.1f}m") + print(f" 最小飞行距离: {stats['min_distance']:.1f}m") + print(f" 最大飞行时间: {stats['max_flight_time']:.1f}s") + + # 详细路径信息 + print(f"\n各UAV路径详情:") + for i, path in enumerate(paths): + distance = planner.calculate_path_distance(path) + flight_time = planner.calculate_flight_time(path) + direct_distance = np.linalg.norm(path[-1] - path[0]) + efficiency = direct_distance / distance if distance > 0 else 0 + + print(f" UAV{i+1}: 距离={distance:.1f}m, 时间={flight_time:.1f}s, " + f"直线距离={direct_distance:.1f}m, 效率={efficiency:.3f}") + + # 输出完整航线数组 + print(f"\n完整航线数组:") + for i, path in enumerate(paths): + print(f" UAV{i+1} 航线 (起点->航路点->终点):") + print(f" 起点: [{path[0, 0]:.1f}, {path[0, 1]:.1f}, {path[0, 2]:.1f}]") + + print(f" 航路点:") + for j in range(1, len(path) - 1): # 排除起点和终点 + print(f" 点{j}: [{path[j, 0]:.1f}, {path[j, 1]:.1f}, {path[j, 2]:.1f}]") + + print(f" 终点: [{path[-1, 0]:.1f}, {path[-1, 1]:.1f}, {path[-1, 2]:.1f}]") + + # 完整数组形式 + waypoints_array = [] + for j in range(len(path)): + waypoints_array.append([round(path[j, 0], 1), round(path[j, 1], 1), round(path[j, 2], 1)]) + print(f" 完整数组: {waypoints_array}") + print() + + return feasibility_rate, stats + + +def test_simplified_planning(): + """测试简化的路径规划""" + print("=== 简化版无人机路径规划测试 ===") + + try: + # 创建测试场景 + config, environment = create_test_scenario() + + # 创建规划器 + planner = SimplifiedUAVPlanner(config, environment) + + print(f"问题维度: {planner.total_dim}") + print(f"禁飞区数量: {len(environment.no_fly_zones)}") + print(f"最大飞行距离限制: {config.max_flight_distance}m") + print(f"碰撞安全距离: {config.collision_safe_distance}m") + + # 创建ESE-MSJS算法实例 + algorithm = ESE_MSJS( + pop_size=30, + max_iter=2000, + dim=planner.total_dim, + lb=planner.lb, + ub=planner.ub + ) + + # 运行路径规划 + print(f"\n开始路径规划...") + start_time = time.time() + + best_solution, best_fitness = algorithm.optimize( + planner.objective_function, + planner.constraint_function + ) + + end_time = time.time() + + # 解析结果 + best_paths = planner.decode_solution(best_solution) + final_constraints = planner.constraint_function(best_solution) + + print(f"\n路径规划完成!") + print(f"最优目标函数值(总飞行距离): {best_fitness:.2f}m") + print(f"运行时间: {end_time - start_time:.2f}秒") + + # 评估解质量 + feasibility_rate, stats = evaluate_solution_quality(best_paths, final_constraints, planner) + + return best_paths, algorithm.best_fitness_history, environment, feasibility_rate >= 1.0 + + except Exception as e: + print(f"路径规划过程中出现错误: {e}") + import traceback + traceback.print_exc() + return None, None, None, False + + +def visualize_3d_results(paths, environment, fitness_history=None, save_fig=True): + """三维可视化结果""" + if paths is None: + print("没有可视化的路径数据") + return + + try: + fig = plt.figure(figsize=(20, 6)) + + # 3D路径图 + ax1 = fig.add_subplot(131, projection='3d') + colors = ['red', 'blue', 'green', 'orange', 'purple'] + + for i, path in enumerate(paths): + color = colors[i % len(colors)] + ax1.plot(path[:, 0], path[:, 1], path[:, 2], + color=color, linewidth=3, marker='o', markersize=5, + label=f'UAV{i + 1}') + + # 起始点和目标点 + ax1.scatter(path[0, 0], path[0, 1], path[0, 2], + color=color, s=200, marker='^', + edgecolors='black', linewidth=2) + ax1.scatter(path[-1, 0], path[-1, 1], path[-1, 2], + color=color, s=200, marker='s', + edgecolors='black', linewidth=2) + + # 绘制地形数据 + if environment.terrain_data is not None: + terrain = environment.terrain_data + # 创建地形网格 + x_terrain = np.linspace(terrain.x_min, terrain.x_max, terrain.height_map.shape[1]) + y_terrain = np.linspace(terrain.y_min, terrain.y_max, terrain.height_map.shape[0]) + X_terrain, Y_terrain = np.meshgrid(x_terrain, y_terrain) + + # 绘制地形表面(使用半透明的棕色) + ax1.plot_surface(X_terrain, Y_terrain, terrain.height_map, + alpha=0.3, color='brown', linewidth=0, antialiased=True) + + # 绘制禁飞区 + for nfz in environment.no_fly_zones: + cx, cy, cz = nfz.center + if nfz.shape == "box": + w, l, h = nfz.size + # 绘制立方体框架 + from mpl_toolkits.mplot3d.art3d import Poly3DCollection + + # 定义立方体的8个顶点 + vertices = [ + [cx-w/2, cy-l/2, cz-h/2], [cx+w/2, cy-l/2, cz-h/2], + [cx+w/2, cy+l/2, cz-h/2], [cx-w/2, cy+l/2, cz-h/2], + [cx-w/2, cy-l/2, cz+h/2], [cx+w/2, cy-l/2, cz+h/2], + [cx+w/2, cy+l/2, cz+h/2], [cx-w/2, cy+l/2, cz+h/2] + ] + + # 定义立方体的6个面 + faces = [ + [vertices[0], vertices[1], vertices[2], vertices[3]], # 底面 + [vertices[4], vertices[5], vertices[6], vertices[7]], # 顶面 + [vertices[0], vertices[1], vertices[5], vertices[4]], # 前面 + [vertices[2], vertices[3], vertices[7], vertices[6]], # 后面 + [vertices[1], vertices[2], vertices[6], vertices[5]], # 右面 + [vertices[4], vertices[7], vertices[3], vertices[0]] # 左面 + ] + + ax1.add_collection3d(Poly3DCollection(faces, alpha=0.3, + facecolor='red', edgecolor='darkred')) + + elif nfz.shape == "sphere": + # 绘制球体 + u, v = np.mgrid[0:2*np.pi:20j, 0:np.pi:20j] + radius = nfz.size[0] + x = cx + radius * np.cos(u) * np.sin(v) + y = cy + radius * np.sin(u) * np.sin(v) + z = cz + radius * np.cos(v) + ax1.plot_surface(x, y, z, alpha=0.3, color='red') + + ax1.set_xlabel('X (m)') + ax1.set_ylabel('Y (m)') + ax1.set_zlabel('Z (m)') + ax1.set_title('3D Flight Paths with Terrain & No-Fly Zones') + ax1.legend() + + # 俯视图 + ax2 = fig.add_subplot(132) + + # 绘制地形等高线(俯视图) + if environment.terrain_data is not None: + terrain = environment.terrain_data + x_terrain = np.linspace(terrain.x_min, terrain.x_max, terrain.height_map.shape[1]) + y_terrain = np.linspace(terrain.y_min, terrain.y_max, terrain.height_map.shape[0]) + X_terrain, Y_terrain = np.meshgrid(x_terrain, y_terrain) + + # 绘制等高线 + contours = ax2.contour(X_terrain, Y_terrain, terrain.height_map, + levels=8, colors='gray', alpha=0.4, linewidths=0.8) + ax2.clabel(contours, inline=True, fontsize=8, fmt='%dm') + + for i, path in enumerate(paths): + color = colors[i % len(colors)] + ax2.plot(path[:, 0], path[:, 1], color=color, linewidth=2, + marker='o', markersize=4, label=f'UAV{i + 1}') + ax2.scatter(path[0, 0], path[0, 1], color=color, s=100, marker='^') + ax2.scatter(path[-1, 0], path[-1, 1], color=color, s=100, marker='s') + + # 绘制禁飞区投影 + for nfz in environment.no_fly_zones: + cx, cy, cz = nfz.center + if nfz.shape == "box": + w, l, h = nfz.size + rect = plt.Rectangle((cx-w/2, cy-l/2), w, l, + fill=False, color='red', linestyle='--', linewidth=2) + ax2.add_patch(rect) + elif nfz.shape == "sphere": + radius = nfz.size[0] + circle = plt.Circle((cx, cy), radius, + fill=False, color='red', linestyle='--', linewidth=2) + ax2.add_patch(circle) + + ax2.set_xlabel('X (m)') + ax2.set_ylabel('Y (m)') + ax2.set_title('Top View (with Terrain Contours)') + ax2.set_aspect('equal') + ax2.grid(True, alpha=0.3) + ax2.legend() + + # 收敛曲线 + if fitness_history: + ax3 = fig.add_subplot(133) + ax3.plot(fitness_history, 'b-', linewidth=2) + ax3.set_xlabel('Iteration') + ax3.set_ylabel('Best Fitness (Total Distance)') + ax3.set_title('Convergence Curve') + ax3.grid(True, alpha=0.3) + + plt.tight_layout() + + if save_fig: + os.makedirs('results', exist_ok=True) + filename = f'results/simplified_uav_planning_{time.strftime("%Y%m%d_%H%M%S")}.png' + plt.savefig(filename, dpi=300, bbox_inches='tight') + print(f"图表已保存为: {filename}") + + plt.show() + + except Exception as e: + print(f"可视化过程中出现错误: {e}") + + +def run_performance_test(num_runs=5): + """运行性能测试""" + print(f"=== 运行 {num_runs} 次性能测试 ===") + + results = [] + for run in range(num_runs): + print(f"\n--- 第 {run + 1} 次运行 ---") + np.random.seed(42 + run) + + paths, fitness_history, environment, success = test_simplified_planning() + + if success and paths is not None: + # 创建简化规划器来计算统计信息 + config, _ = create_test_scenario() + planner = SimplifiedUAVPlanner(config, environment) + stats = planner.get_path_statistics(paths) + + results.append({ + 'total_distance': stats['total_distance'], + 'max_flight_time': stats['max_flight_time'], + 'success': True + }) + else: + results.append({ + 'total_distance': float('inf'), + 'max_flight_time': float('inf'), + 'success': False + }) + + # 统计结果 + successful_runs = [r for r in results if r['success']] + success_rate = len(successful_runs) / num_runs + + print(f"\n=== 性能测试结果统计 ===") + print(f"成功率: {len(successful_runs)}/{num_runs} ({success_rate*100:.1f}%)") + + if successful_runs: + distances = [r['total_distance'] for r in successful_runs] + times = [r['max_flight_time'] for r in successful_runs] + + print(f"总飞行距离统计:") + print(f" 最优: {min(distances):.2f}m") + print(f" 平均: {np.mean(distances):.2f}m") + print(f" 标准差: {np.std(distances):.2f}m") + + print(f"最大飞行时间统计:") + print(f" 最短: {min(times):.2f}s") + print(f" 平均: {np.mean(times):.2f}s") + print(f" 标准差: {np.std(times):.2f}s") + + # 可视化最好的结果 + best_idx = np.argmin(distances) + print(f"\n可视化第 {best_idx + 1} 次运行的最优结果") + + # 重新运行最优设置 + np.random.seed(42 + best_idx) + paths, fitness_history, environment, _ = test_simplified_planning() + if paths is not None: + visualize_3d_results(paths, environment, fitness_history) + + +def main(): + """主函数""" + print("简化版ESE-MSJS多无人机路径规划") + print("=" * 50) + + try: + # 随机种子选择 + print("选择随机种子模式:") + print("1. 固定种子 (seed=42, 结果可重复)") + print("2. 随机种子 (每次不同结果)") + + try: + seed_choice = input("请输入选择 (1 或 2,默认为 1): ").strip() + except EOFError: + seed_choice = "1" # 默认选择 + + if seed_choice == "2": + import time + seed = int(time.time() * 1000) % 100000 + np.random.seed(seed) + print(f"使用随机种子: {seed}") + else: + np.random.seed(42) + print(f"使用固定种子: 42") + + print("\n选择运行模式:") + print("1. 单次测试并可视化") + print("2. 性能测试(多次运行)") + + try: + choice = input("请输入选择 (1 或 2,默认为 1): ").strip() + except EOFError: + choice = "1" # 默认选择 + + if choice == "2": + run_performance_test(5) + else: + # 单次测试 + paths, fitness_history, environment, success = test_simplified_planning() + + if success and paths is not None: + print(f"\n开始可视化结果...") + visualize_3d_results(paths, environment, fitness_history) + print(f"\n测试完成!") + else: + print("测试失败,请检查错误信息") + print(f"\n开始可视化结果...") + visualize_3d_results(paths, environment, fitness_history) + + except KeyboardInterrupt: + print("\n用户中断程序") + except Exception as e: + print(f"程序运行出错: {e}") + import traceback + traceback.print_exc() + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/src/simplified_uav_planning.py b/src/simplified_uav_planning.py new file mode 100644 index 0000000..7f7ef1a --- /dev/null +++ b/src/simplified_uav_planning.py @@ -0,0 +1,400 @@ +import numpy as np +from typing import Dict, List, Tuple, Optional +from dataclasses import dataclass +import math + + +@dataclass +class SimplifiedUAVConfig: + """简化的无人机路径配置参数""" + num_uavs: int = 3 # 无人机数量 + num_waypoints: int = 10 # 航路点数量 + + # 环境边界 + x_min: float = 0.0 + x_max: float = 1000.0 + y_min: float = 0.0 + y_max: float = 1000.0 + z_min: float = 50.0 + z_max: float = 500.0 + + # 约束参数 + max_flight_distance: float = 1200.0 # 最大飞行距离(米) + ground_clearance: float = 30.0 # 地面安全间隙(米) + uav_speed: float = 30.0 # 无人机速度(m/s) + collision_safe_distance: float = 50.0 # 无人机间安全距离(米) + + +@dataclass +class TerrainData: + """地形数据""" + def __init__(self, x_range: Tuple[float, float], y_range: Tuple[float, float], + resolution: float = 10.0): + self.x_min, self.x_max = x_range + self.y_min, self.y_max = y_range + self.resolution = resolution + + # 创建简单的地形高度图(可以从实际DEM数据加载) + x_size = int((self.x_max - self.x_min) / resolution) + 1 + y_size = int((self.y_max - self.y_min) / resolution) + 1 + + # 简单的随机地形生成(实际应用中应从DEM文件读取) + np.random.seed(42) # 确保可重复 + self.height_map = np.random.uniform(0, 50, (y_size, x_size)) + + def get_height(self, x: float, y: float) -> float: + """获取指定坐标的地面高度""" + # 将坐标转换为网格索引 + i = int((y - self.y_min) / self.resolution) + j = int((x - self.x_min) / self.resolution) + + # 边界检查 + i = max(0, min(i, self.height_map.shape[0] - 1)) + j = max(0, min(j, self.height_map.shape[1] - 1)) + + return self.height_map[i, j] + + +@dataclass +class NoFlyZone3D: + """三维禁飞区定义""" + center: Tuple[float, float, float] # (x, y, z) 中心点 + size: Tuple[float, float, float] # (width, length, height) 尺寸 + shape: str = "box" # 形状:'box' 或 'sphere' + + def is_inside(self, point: np.ndarray) -> bool: + """判断点是否在禁飞区内""" + x, y, z = point + cx, cy, cz = self.center + + if self.shape == "box": + w, l, h = self.size + return (abs(x - cx) <= w/2 and + abs(y - cy) <= l/2 and + abs(z - cz) <= h/2) + elif self.shape == "sphere": + radius = self.size[0] # 使用第一个尺寸作为半径 + distance = np.sqrt((x-cx)**2 + (y-cy)**2 + (z-cz)**2) + return distance <= radius + + return False + + def distance_to_boundary(self, point: np.ndarray) -> float: + """计算点到禁飞区边界的距离(内部为负值)""" + x, y, z = point + cx, cy, cz = self.center + + if self.shape == "box": + w, l, h = self.size + # 计算到各个面的距离 + dx = max(0, abs(x - cx) - w/2) + dy = max(0, abs(y - cy) - l/2) + dz = max(0, abs(z - cz) - h/2) + + if self.is_inside(point): + # 内部点:返回到最近边界的负距离 + inner_dx = w/2 - abs(x - cx) + inner_dy = l/2 - abs(y - cy) + inner_dz = h/2 - abs(z - cz) + return -min(inner_dx, inner_dy, inner_dz) + else: + # 外部点:返回正距离 + return np.sqrt(dx**2 + dy**2 + dz**2) + + elif self.shape == "sphere": + radius = self.size[0] + distance = np.sqrt((x-cx)**2 + (y-cy)**2 + (z-cz)**2) + return distance - radius + + return 0.0 + + +class SimplifiedUAVEnvironment: + """简化的无人机环境""" + + def __init__(self, config: SimplifiedUAVConfig): + self.config = config + self.terrain_data: Optional[TerrainData] = None + self.no_fly_zones: List[NoFlyZone3D] = [] + + # 起始点和目标点 + self.start_points: List[Tuple[float, float, float]] = [] + self.target_points: List[Tuple[float, float, float]] = [] + + def set_terrain_data(self, terrain_data: TerrainData): + """设置地形数据""" + self.terrain_data = terrain_data + + def add_no_fly_zone(self, nfz: NoFlyZone3D): + """添加禁飞区""" + self.no_fly_zones.append(nfz) + + def set_start_target_points(self, starts: List[Tuple[float, float, float]], + targets: List[Tuple[float, float, float]]): + """设置起始点和目标点""" + self.start_points = starts + self.target_points = targets + + +class SimplifiedUAVPlanner: + """简化的无人机路径规划器""" + + def __init__(self, config: SimplifiedUAVConfig, environment: SimplifiedUAVEnvironment): + self.config = config + self.environment = environment + + # 计算编码维度 + self.dim_per_uav = config.num_waypoints * 3 + self.total_dim = self.dim_per_uav * config.num_uavs + + # 边界约束 + self.lb = [] + self.ub = [] + for _ in range(config.num_uavs): + for _ in range(config.num_waypoints): + self.lb.extend([config.x_min, config.y_min, config.z_min]) + self.ub.extend([config.x_max, config.y_max, config.z_max]) + + def decode_solution(self, solution: np.ndarray) -> List[np.ndarray]: + """解码解决方案为UAV路径""" + paths = [] + for i in range(self.config.num_uavs): + start_idx = i * self.dim_per_uav + end_idx = (i + 1) * self.dim_per_uav + uav_genes = solution[start_idx:end_idx] + + # 重构为(num_waypoints, 3)的路径 + path = uav_genes.reshape(self.config.num_waypoints, 3) + + # 添加起始点和目标点 + start_point = np.array(self.environment.start_points[i]) + target_point = np.array(self.environment.target_points[i]) + + full_path = np.vstack([start_point, path, target_point]) + paths.append(full_path) + + return paths + + def objective_function(self, solution: np.ndarray) -> float: + """目标函数:最小化总飞行距离""" + paths = self.decode_solution(solution) + total_distance = 0.0 + + for path in paths: + path_distance = self.calculate_path_distance(path) + total_distance += path_distance + + return total_distance + + def calculate_path_distance(self, path: np.ndarray) -> float: + """计算路径总距离""" + total_distance = 0.0 + for i in range(len(path) - 1): + segment_distance = np.linalg.norm(path[i + 1] - path[i]) + total_distance += segment_distance + return total_distance + + def constraint_function(self, solution: np.ndarray) -> Dict[str, float]: + """约束函数:返回四个约束的违反值""" + paths = self.decode_solution(solution) + constraints = {} + + # 1. 地形约束 + terrain_violations = [] + for path in paths: + violation = self.check_terrain_constraint(path) + terrain_violations.append(violation) + constraints['terrain'] = max(terrain_violations) + + # 2. 禁飞区约束 + nfz_violations = [] + for path in paths: + violation = self.check_no_fly_zone_constraint(path) + nfz_violations.append(violation) + constraints['no_fly_zone'] = max(nfz_violations) + + # 3. 路径长度约束 + distance_violations = [] + for path in paths: + violation = self.check_distance_constraint(path) + distance_violations.append(violation) + constraints['max_distance'] = max(distance_violations) + + # 4. 碰撞约束(新增) + constraints['collision'] = self.check_collision_constraint(paths) + + return constraints + + def check_terrain_constraint(self, path: np.ndarray) -> float: + """检查地形约束 + 返回值:0表示满足约束,>0表示约束违反程度 + """ + max_violation = 0.0 + + if self.environment.terrain_data is None: + # 如果没有地形数据,只检查最低高度 + for point in path: + violation = max(0, self.config.z_min - point[2]) + max_violation = max(max_violation, violation) + return max_violation + + # 检查每个路径点的地形约束 + for point in path: + x, y, z = point + ground_height = self.environment.terrain_data.get_height(x, y) + required_height = ground_height + self.config.ground_clearance + + # 违反约束:实际高度低于要求高度 + violation = max(0, required_height - z) + max_violation = max(max_violation, violation) + + # 还需要检查路径段是否与地形相交 + for i in range(len(path) - 1): + segment_violation = self.check_segment_terrain_collision(path[i], path[i + 1]) + max_violation = max(max_violation, segment_violation) + + return max_violation + + def check_segment_terrain_collision(self, start_point: np.ndarray, + end_point: np.ndarray, num_samples: int = 10) -> float: + """检查路径段是否与地形相撞""" + if self.environment.terrain_data is None: + return 0.0 + + max_violation = 0.0 + + # 在路径段上采样检查 + for i in range(num_samples): + t = i / (num_samples - 1) if num_samples > 1 else 0 + sample_point = start_point + t * (end_point - start_point) + + x, y, z = sample_point + ground_height = self.environment.terrain_data.get_height(x, y) + required_height = ground_height + self.config.ground_clearance + + violation = max(0, required_height - z) + max_violation = max(max_violation, violation) + + return max_violation + + def check_no_fly_zone_constraint(self, path: np.ndarray) -> float: + """检查禁飞区约束""" + max_violation = 0.0 + + # 检查每个路径点 + for point in path: + for nfz in self.environment.no_fly_zones: + if nfz.is_inside(point): + # 点在禁飞区内,违反约束 + distance_to_boundary = abs(nfz.distance_to_boundary(point)) + max_violation = max(max_violation, distance_to_boundary) + + # 检查路径段是否穿过禁飞区 + for i in range(len(path) - 1): + segment_violation = self.check_segment_nfz_collision(path[i], path[i + 1]) + max_violation = max(max_violation, segment_violation) + + return max_violation + + def check_segment_nfz_collision(self, start_point: np.ndarray, + end_point: np.ndarray, num_samples: int = 20) -> float: + """检查路径段是否穿过禁飞区""" + max_violation = 0.0 + + # 在路径段上采样检查 + for i in range(num_samples): + t = i / (num_samples - 1) if num_samples > 1 else 0 + sample_point = start_point + t * (end_point - start_point) + + for nfz in self.environment.no_fly_zones: + if nfz.is_inside(sample_point): + distance_to_boundary = abs(nfz.distance_to_boundary(sample_point)) + max_violation = max(max_violation, distance_to_boundary) + + return max_violation + + def check_distance_constraint(self, path: np.ndarray) -> float: + """检查路径长度约束""" + path_distance = self.calculate_path_distance(path) + + # 违反约束:实际距离超过最大允许距离 + violation = max(0, path_distance - self.config.max_flight_distance) + return violation + + def check_collision_constraint(self, paths: List[np.ndarray]) -> float: + """检查无人机间碰撞约束""" + max_violation = 0.0 + + if len(paths) < 2: + return 0.0 # 只有一架UAV,无碰撞风险 + + # 检查每对无人机 + for i in range(len(paths)): + for j in range(i + 1, len(paths)): + violation = self.check_pairwise_collision(paths[i], paths[j]) + max_violation = max(max_violation, violation) + + return max_violation + + def check_pairwise_collision(self, path1: np.ndarray, path2: np.ndarray) -> float: + """检查两架无人机之间的碰撞约束""" + max_violation = 0.0 + + # 假设两架UAV同步飞行(相同的路径点数) + min_points = min(len(path1), len(path2)) + + # 检查每个对应的路径点 + for i in range(min_points): + distance = np.linalg.norm(path1[i] - path2[i]) + violation = max(0, self.config.collision_safe_distance - distance) + max_violation = max(max_violation, violation) + + # 检查路径段之间的最小距离 + for i in range(min_points - 1): + # 检查线段之间的最小距离 + seg_violation = self.check_segment_collision( + path1[i], path1[i+1], path2[i], path2[i+1] + ) + max_violation = max(max_violation, seg_violation) + + return max_violation + + def check_segment_collision(self, p1_start: np.ndarray, p1_end: np.ndarray, + p2_start: np.ndarray, p2_end: np.ndarray, + num_samples: int = 10) -> float: + """检查两个路径段之间的最小距离""" + max_violation = 0.0 + + # 在两个路径段上采样点 + for i in range(num_samples): + t = i / (num_samples - 1) if num_samples > 1 else 0 + + # 在第一条路径段上的采样点 + sample1 = p1_start + t * (p1_end - p1_start) + + # 在第二条路径段上的采样点 + sample2 = p2_start + t * (p2_end - p2_start) + + # 计算距离 + distance = np.linalg.norm(sample1 - sample2) + violation = max(0, self.config.collision_safe_distance - distance) + max_violation = max(max_violation, violation) + + return max_violation + + def calculate_flight_time(self, path: np.ndarray) -> float: + """计算飞行时间""" + path_distance = self.calculate_path_distance(path) + return path_distance / self.config.uav_speed + + def get_path_statistics(self, paths: List[np.ndarray]) -> Dict: + """获取路径统计信息""" + stats = { + 'total_distance': sum(self.calculate_path_distance(path) for path in paths), + 'max_distance': max(self.calculate_path_distance(path) for path in paths), + 'min_distance': min(self.calculate_path_distance(path) for path in paths), + 'avg_distance': sum(self.calculate_path_distance(path) for path in paths) / len(paths), + 'flight_times': [self.calculate_flight_time(path) for path in paths], + 'max_flight_time': max(self.calculate_flight_time(path) for path in paths), + } + return stats \ No newline at end of file