From c9db9244b319ad69a78ac789e8c35d6817142b0f Mon Sep 17 00:00:00 2001 From: weixin_46229132 Date: Sat, 22 Mar 2025 17:16:58 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=81=8D=E5=8E=86-=E9=81=97?= =?UTF-8?q?=E4=BC=A0=E7=AE=97=E6=B3=95=E6=B1=82=E8=A7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- GA/ga.py | 2 +- GA/main.py | 101 ++++++++++++++ GA/main_parallel.py | 130 ++++++++++++++++++ GA/utils.py | 93 +++++++++++++ env.py | 9 +- mtkl_sovler.py | 13 +- ...lution_mtkl.json => mtkl_params2.yml.json} | 56 ++++---- visualization.py | 2 +- 8 files changed, 365 insertions(+), 41 deletions(-) create mode 100644 GA/main.py create mode 100644 GA/main_parallel.py create mode 100644 GA/utils.py rename solutions/{best_solution_mtkl.json => mtkl_params2.yml.json} (66%) diff --git a/GA/ga.py b/GA/ga.py index a9f8fdc..e000ade 100644 --- a/GA/ga.py +++ b/GA/ga.py @@ -165,7 +165,7 @@ class GA(object): continue else: flight_time += self.rectangles[point - 1]['flight_time'] # 注意,这里要减一!!! - bs_time += self.rectangles[point - 1]['comp_bs_time'] + bs_time += self.rectangles[point - 1]['bs_time'] system_time = max(flight_time + car_info['car_time'], bs_time) T_k_list.append(system_time) T_max = max(T_k_list) diff --git a/GA/main.py b/GA/main.py new file mode 100644 index 0000000..1c0ba7e --- /dev/null +++ b/GA/main.py @@ -0,0 +1,101 @@ +import random +import math +import yaml +import numpy as np +from utils import if_valid_partition, GA_solver +from itertools import product +import json +from tqdm import tqdm + +np.random.seed(42) +random.seed(42) +best_T = float('inf') +best_solution = None +best_row_boundaries = None +best_col_boundaries = None + +params_file = 'params2.yml' +with open(params_file, 'r', encoding='utf-8') as file: + params = yaml.safe_load(file) + +H = params['H'] +W = params['W'] +k = params['num_cars'] + +flight_time_factor = params['flight_time_factor'] +comp_time_factor = params['comp_time_factor'] +trans_time_factor = params['trans_time_factor'] +car_time_factor = params['car_time_factor'] +bs_time_factor = params['bs_time_factor'] + +flight_energy_factor = params['flight_energy_factor'] +comp_energy_factor = params['comp_energy_factor'] +trans_energy_factor = params['trans_energy_factor'] +battery_energy_capacity = params['battery_energy_capacity'] + +# 定义数字列表 +numbers = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9] + +# 生成所有的排列情况(取三次,每次都可以从10个数中选) +row_product = list(product(numbers, repeat=3)) +# 对每种情况从小到大排序,并剔除重复的情况 +row_cuts_set = set( + tuple(sorted(set(item for item in prod if item > 0))) for prod in row_product) +row_cuts_set = sorted(row_cuts_set) + +col_product = list(product(numbers, repeat=3)) +col_cuts_set = set( + tuple(sorted(set(item for item in prod if item > 0))) for prod in col_product) +col_cuts_set = sorted(col_cuts_set) + +total_iterations = len(row_cuts_set) * len(col_cuts_set) +with tqdm(total=total_iterations, desc="Processing") as pbar: + for row_cuts in row_cuts_set: + for col_cuts in col_cuts_set: + row_boundaries = [0.0] + list(row_cuts) + [1.0] + col_boundaries = [0.0] + list(col_cuts) + [1.0] + + # 这里面的距离不再是比例,而是真实距离! + rectrangles = if_valid_partition(row_boundaries, col_boundaries, params) + if not rectrangles: + pbar.update(1) + continue + else: + # 使用遗传算法求出每一种网格划分的可行解,然后选择其中的最优解 + current_solution, current_time, to_process_idx = GA_solver(rectrangles, k) + + if current_time < best_T: + best_T = current_time + best_solution = current_solution + best_row_boundaries = row_boundaries + best_col_boundaries = col_boundaries + + # 将best_solution分解成每个车队的路径 + found_start_points_indices = [] + for i in range(len(best_solution)): + if best_solution[i] in to_process_idx: + found_start_points_indices.append(i) + car_paths = [] + for j in range(len(found_start_points_indices) - 1): + from_index = found_start_points_indices[j] + end_index = found_start_points_indices[j + 1] + car_path = [] + for k in range(from_index, end_index + 1): + rectrangle_idx = best_solution[k] + car_path.append(rectrangles[rectrangle_idx]['center']) + car_paths.append(car_path) + pbar.update(1) + +# 输出最佳方案 +print("Best solution:", best_solution) +print("Time:", best_T) +print("Row boundaries:", best_row_boundaries) +print("Col boundaries:", best_col_boundaries) + +output_data = { + 'row_boundaries': row_boundaries, + 'col_boundaries': col_boundaries, + 'car_paths': car_paths +} +with open(f'./solutions/traverse_ga_{params_file}.json', 'w', encoding='utf-8') as file: + json.dump(output_data, file, ensure_ascii=False, indent=4) \ No newline at end of file diff --git a/GA/main_parallel.py b/GA/main_parallel.py new file mode 100644 index 0000000..2742ce4 --- /dev/null +++ b/GA/main_parallel.py @@ -0,0 +1,130 @@ +import random +import math +import yaml +import numpy as np +from utils import if_valid_partition, GA_solver +from itertools import product +import json +from tqdm import tqdm +from concurrent.futures import ProcessPoolExecutor, as_completed + +np.random.seed(42) +random.seed(42) + + +params_file = 'params2.yml' +with open(params_file, 'r', encoding='utf-8') as file: + params = yaml.safe_load(file) + +H = params['H'] +W = params['W'] +k = params['num_cars'] + +flight_time_factor = params['flight_time_factor'] +comp_time_factor = params['comp_time_factor'] +trans_time_factor = params['trans_time_factor'] +car_time_factor = params['car_time_factor'] +bs_time_factor = params['bs_time_factor'] + +flight_energy_factor = params['flight_energy_factor'] +comp_energy_factor = params['comp_energy_factor'] +trans_energy_factor = params['trans_energy_factor'] +battery_energy_capacity = params['battery_energy_capacity'] + +# 定义数字列表 +numbers = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9] + +# 生成所有的排列情况(取三次,每次都可以从10个数中选) +row_product = list(product(numbers, repeat=3)) +# 对每种情况从小到大排序,并剔除重复的情况 +row_cuts_set = set( + tuple(sorted(set(item for item in prod if item > 0))) for prod in row_product) +row_cuts_set = sorted(row_cuts_set) + +col_product = list(product(numbers, repeat=3)) +col_cuts_set = set( + tuple(sorted(set(item for item in prod if item > 0))) for prod in col_product) +col_cuts_set = sorted(col_cuts_set) + + +def process_partition(row_cuts, col_cuts): + row_boundaries = [0.0] + list(row_cuts) + [1.0] + col_boundaries = [0.0] + list(col_cuts) + [1.0] + rectrangles = if_valid_partition(row_boundaries, col_boundaries, params) + + if not rectrangles: + return None # 过滤无效划分 + + current_solution, current_time, to_process_idx = GA_solver(rectrangles, k) + + return (current_solution, current_time, row_boundaries, col_boundaries, to_process_idx, rectrangles) + + +if __name__ == "__main__": # 重要:在 Windows 上必须加这一行 + best_T = float('inf') + best_solution = None + best_row_boundaries = None + best_col_boundaries = None + batch_size = 60 # 控制一次最多并行多少个任务 + + all_tasks = [(row_cuts, col_cuts) for row_cuts in row_cuts_set for col_cuts in col_cuts_set] + total_iterations = len(all_tasks) + + with ProcessPoolExecutor(max_workers=batch_size) as executor: + futures = set() + results = [] + + with tqdm(total=total_iterations) as pbar: + for task in all_tasks: + if len(futures) >= batch_size: # 如果并行任务数达到 batch_size,等待已有任务完成 + for future in as_completed(futures): + results.append(future.result()) + pbar.update(1) # 更新进度条 + futures.clear() # 清空已完成的任务 + + futures.add(executor.submit(process_partition, *task)) # 提交新任务 + + # 处理剩余未完成的任务 + for future in as_completed(futures): + results.append(future.result()) + pbar.update(1) + + # 处理计算结果,找到最优解 + for result in results: + if result: + current_solution, current_time, row_boundaries, col_boundaries, to_process_idx, rectrangles = result + if current_time < best_T: + best_T = current_time + best_solution = current_solution + best_row_boundaries = row_boundaries + best_col_boundaries = col_boundaries + + # 解析最佳路径 + found_start_points_indices = [] + for i in range(len(best_solution)): + if best_solution[i] in to_process_idx: + found_start_points_indices.append(i) + car_paths = [] + for j in range(len(found_start_points_indices) - 1): + from_index = found_start_points_indices[j] + end_index = found_start_points_indices[j + 1] + car_path = [] + for k in range(from_index, end_index + 1): + rectrangle_idx = best_solution[k] + if rectrangle_idx not in to_process_idx: + car_path.append(rectrangles[rectrangle_idx]['center']) + car_paths.append(car_path) + + # 输出最佳方案 + print("Best solution:", best_solution) + print("Time:", best_T) + print("Row boundaries:", best_row_boundaries) + print("Col boundaries:", best_col_boundaries) + + output_data = { + 'row_boundaries': row_boundaries, + 'col_boundaries': col_boundaries, + 'car_paths': car_paths + } + with open(f'./solutions/travse_ga_{params_file}.json', 'w', encoding='utf-8') as file: + json.dump(output_data, file, ensure_ascii=False, indent=4) diff --git a/GA/utils.py b/GA/utils.py new file mode 100644 index 0000000..35065b8 --- /dev/null +++ b/GA/utils.py @@ -0,0 +1,93 @@ +import numpy as np +from ga import GA + + +def if_valid_partition(row_boundaries, col_boundaries, params): + H = params['H'] + W = params['W'] + k = params['num_cars'] + + flight_time_factor = params['flight_time_factor'] + comp_time_factor = params['comp_time_factor'] + trans_time_factor = params['trans_time_factor'] + car_time_factor = params['car_time_factor'] + bs_time_factor = params['bs_time_factor'] + + flight_energy_factor = params['flight_energy_factor'] + comp_energy_factor = params['comp_energy_factor'] + trans_energy_factor = params['trans_energy_factor'] + battery_energy_capacity = params['battery_energy_capacity'] + + # 根据分割边界生成所有矩形任务 + rectangles = [] + for i in range(len(row_boundaries) - 1): + for j in range(len(col_boundaries) - 1): + r1 = row_boundaries[i] + r2 = row_boundaries[i + 1] + c1 = col_boundaries[j] + c2 = col_boundaries[j + 1] + d = (r2 - r1) * H * (c2 - c1) * W # 任务的照片数量(矩形面积) + + # 求解rho + rho_time_limit = (flight_time_factor - trans_time_factor) / \ + (comp_time_factor - trans_time_factor) + rho_energy_limit = (battery_energy_capacity - flight_energy_factor * d - trans_energy_factor * d) / \ + (comp_energy_factor * d - trans_energy_factor * d) + if rho_energy_limit < 0: + return [] + + rho = min(rho_time_limit, rho_energy_limit) + flight_time = flight_time_factor * d + comp_time = comp_time_factor * rho * d + trans_time = trans_time_factor * (1 - rho) * d + bs_time = bs_time_factor * (1 - rho) * d + + # 计算任务矩形中心,用于后续车辆移动时间计算 + center_r = (r1 + r2) / 2.0 * H + center_c = (c1 + c2) / 2.0 * W + + rectangles.append({ + # 'r1': r1, 'r2': r2, 'c1': c1, 'c2': c2, + 'd': d, + 'rho': rho, + 'flight_time': flight_time, + 'comp_time': comp_time, + 'trans_time': trans_time, + 'bs_time': bs_time, + 'center': (center_r, center_c) + }) + return rectangles + + +def GA_solver(rectangles, k): + num_city = len(rectangles) + 1 # 划分好的区域中心点+整个区域的中心 + + # 初始化坐标 (第一个点是整个区域的中心) + center_data = [[1 / 2.0, 1 / 2.0]] + for rec in rectangles: + center_data.append(rec['center']) + center_data = np.array(center_data) + + # 关键:有k架无人机,则再增加N-1个`点` (坐标是起始点),这些点之间的距离是inf + for d in range(k - 1): + center_data = np.vstack([center_data, center_data[0]]) + num_city += 1 # 增加欺骗城市 + + to_process_idx = [0] + # print("start point:", location[0]) + for d in range(1, k): # 1, ... drone-1 + # print("added base point:", location[num_city - d]) + to_process_idx.append(num_city - d) + + model = GA(num_drones=k, num_city=center_data.shape[0], num_total=20, data=center_data.copy( + ), to_process_idx=to_process_idx, rectangles=rectangles) + Best_path, Best = model.run() + + # 根据最佳路径计算各系统任务分配 + if Best_path[0] not in to_process_idx: + Best_path.insert(0, 0) + + if Best_path[-1] not in to_process_idx: + Best_path.append(0) + + return Best_path, Best, to_process_idx diff --git a/env.py b/env.py index 6c9f102..3696dd8 100644 --- a/env.py +++ b/env.py @@ -244,25 +244,25 @@ class PartitionMazeEnv(gym.Env): new_row = current_row - 1 else: # 错误的移动给一些惩罚? new_row = current_row - # reward -= 10 + # reward -= 1 elif move_dir == 'down': if current_row < len(self.row_cuts) - 2: new_row = current_row + 1 else: new_row = current_row - # reward -= 10 + # reward -= 1 elif move_dir == 'left': if current_col > 0: new_col = current_col - 1 else: new_col = current_col - # reward -= 10 + # reward -= 1 elif move_dir == 'right': if current_col < len(self.col_cuts) - 2: new_col = current_col + 1 else: new_col = current_col - # reward -= 10 + # reward -= 1 # 如果移动不合法,或者动作为stay,则保持原位置 # 检查是否移动 @@ -320,7 +320,6 @@ class PartitionMazeEnv(gym.Env): # # TODO 让奖励在baseline附近变化更剧烈 # # reward = math.exp(-T / self.BASE_LINE) * 1000 reward += self.BASE_LINE / real_T * 5 - print(real_T, "="*20) # if reward > self.BASE_LINE: # reward -= 200 diff --git a/mtkl_sovler.py b/mtkl_sovler.py index cf67735..263826d 100644 --- a/mtkl_sovler.py +++ b/mtkl_sovler.py @@ -12,7 +12,8 @@ num_iterations = 10000 # --------------------------- # 参数设置 # --------------------------- -with open('params.yml', 'r', encoding='utf-8') as file: +params_file = 'params2.yml' +with open(params_file, 'r', encoding='utf-8') as file: params = yaml.safe_load(file) H = params['H'] @@ -38,8 +39,8 @@ best_solution = None for iteration in range(num_iterations): # 随机生成分区的行分段数与列分段数 - R = random.randint(0, 5) # 行分段数 - C = random.randint(0, 5) # 列分段数 + R = random.randint(0, 3) # 行分段数 + C = random.randint(0, 3) # 列分段数 # 生成随机的行、列分割边界 horiz = [np.clip(np.floor(random.random() * 10) /10, 0.0, 0.9) for _ in range(R)] @@ -174,7 +175,7 @@ if best_solution is not None: print("行分割边界:", best_solution['row_boundaries']) print("列分割边界:", best_solution['col_boundaries']) print("每辆车的运行轨迹情况:") - car_paths = {} + car_paths = [] for i in range(k): num_tasks = len(best_solution['system_tasks'][i]) print( @@ -193,7 +194,7 @@ if best_solution is not None: print( f" -> 任务{j}({current_pos[0]:.1f}, {current_pos[1]:.1f})", end="") print(" -> 区域中心") - car_paths[i] = car_path + car_paths.append(car_path) # 保存分区边界和车辆轨迹到JSON文件 output_data = { @@ -201,7 +202,7 @@ if best_solution is not None: 'col_boundaries': [boundary / W for boundary in best_solution['col_boundaries']], 'car_paths': car_paths } - with open('./solutions/best_solution_mtkl.json', 'w', encoding='utf-8') as f: + with open(f'./solutions/mtkl_{params_file}.json', 'w', encoding='utf-8') as f: json.dump(output_data, f, ensure_ascii=False, indent=4) else: print("在给定的模拟次数内未找到满足所有约束的方案。") diff --git a/solutions/best_solution_mtkl.json b/solutions/mtkl_params2.yml.json similarity index 66% rename from solutions/best_solution_mtkl.json rename to solutions/mtkl_params2.yml.json index 9e7403f..7e21c70 100644 --- a/solutions/best_solution_mtkl.json +++ b/solutions/mtkl_params2.yml.json @@ -1,9 +1,9 @@ { "row_boundaries": [ 0.0, - 0.2, - 0.4, - 0.7, + 0.3, + 0.6, + 0.8, 1.0 ], "col_boundaries": [ @@ -11,44 +11,44 @@ 0.5, 1.0 ], - "car_paths": { - "0": [ + "car_paths": [ + [ [ - 15.0, - 12.5 + 22.5, + 37.5 ], [ - 5.0, - 12.5 - ] - ], - "1": [ - [ - 42.5, - 12.5 - ], - [ - 42.5, + 7.5, 37.5 ] ], - "2": [ + [ [ - 27.5, + 7.5, 12.5 ], [ - 27.5, + 45.0, + 37.5 + ] + ], + [ + [ + 22.5, + 12.5 + ], + [ + 35.0, + 12.5 + ], + [ + 35.0, 37.5 ], [ - 15.0, - 37.5 - ], - [ - 5.0, - 37.5 + 45.0, + 12.5 ] ] - } + ] } \ No newline at end of file diff --git a/visualization.py b/visualization.py index 20e5ad0..75ad87a 100644 --- a/visualization.py +++ b/visualization.py @@ -29,7 +29,7 @@ def visualize_solution(row_boundaries, col_boundaries, car_paths, W, H): ax.axvline(x=col * W, color='black', linestyle='--') # 绘制每辆车的轨迹 - for system_id, path in car_paths.items(): + for system_id, path in enumerate(car_paths): path = [(region_center[0], region_center[1])] + path + [(region_center[0], region_center[1])] y, x = zip(*path) ax.plot(x, y, marker='o', color=colors[int(system_id) % len(colors)], label=f"系统 {system_id}")