python的运筹学工业场景模拟第一百零二篇:遗传算法做厂区巡检路径规划,多巡检点位,求解巡检最短路线,替代手工规划巡检路线。

📅 2026/8/24 3:46:51
python的运筹学工业场景模拟第一百零二篇:遗传算法做厂区巡检路径规划,多巡检点位,求解巡检最短路线,替代手工规划巡检路线。
巡检“算着走”用遗传算法把厂区巡检路线压短 28%“某化工园区有 35 个关键巡检点位每天 3 班倒巡检工按经验绕路单趟巡检 8.7 公里耗时 126 分钟漏检率 4.2%年人工与误工成本 180 万。后来我用 Python 写了个遗传算法路径规划器0.6 秒算完全园最优巡检路线单趟缩到 6.3 公里耗时 86 分钟漏检率降到 0.3%年省 52 万。设备部长说‘原来不是人走得慢是路没算明白。’”—— 参考北京理工大学《运筹学》第 8 章“启发式算法”、第 6 章“图与网络优化”一、实际应用场景描述厂区巡检路径规划器是任何涉及“多点遍历、路径最短、顺序约束”场景的“导航大脑”。凡是“人要巡检、车要跑、路要最短”的地方都是它行业 典型场景 约束条件 痛点化工园区 动静设备、仪表、阀门巡检 防爆区、安全距离 路线长、漏检多电力场站 变压器、开关柜、线路巡检 带电间隔、操作顺序 安全风险高钢铁厂 高炉、轧机、天车巡检 高温区、粉尘区 环境恶劣、效率低制药厂 洁净区、设备、管道巡检 洁净度、压差要求 合规风险大物流中心 货架、分拣、装卸巡检 通道宽度、堆高限制 拥堵、效率低数据中心 机柜、空调、UPS巡检 冷热通道、冗余要求 宕机风险高核心矛盾- 运筹学教科书教“旅行商问题TSPN 个点、最短回路”- 巡检工拿到的是“巡检清单、点位位置”- 现场习惯“按经验走、就近绕路”- 结果要么路线超长要么漏检频发。┌──────────────────────────────────────────────────────────────┐│ 厂区巡检路径规划器 · 导航大脑 ││ ││ 【业务场景】 ││ ┌─────────────────────────────────────────────────────────┐││ │ 输入: 35个关键巡检点位 │││ │ • P01: 1#反应釜(坐标X100,Y200) │││ │ • P02: 2#泵房(坐标X150,Y180) │││ │ • P03: 3#冷却塔(坐标X200,Y150) │││ │ • ...共35个点位 │││ │ │││ │ 约束条件: │││ │ • 起点/终点: 中控室(坐标X0,Y0) │││ │ • 必须遍历所有点位一次 │││ │ • 相邻点位距离已知(欧氏距离) │││ │ • 单趟巡检时间≤120分钟 │││ │ │││ │ 遗传算法逻辑: │││ │ 1. 染色体编码: 巡检顺序(如[0,5,12,3,...]) │││ │ 2. 适应度函数: 总路径长度(越短越好) │││ │ 3. 选择: 轮盘赌选择优质路径 │││ │ 4. 交叉: 两点交换生成新路径 │││ │ 5. 变异: 随机交换两个点位位置 │││ │ 6. 进化: 迭代200代找到最优路径 │││ │ │││ │ 输出: │││ │ • 最优巡检路线(点位顺序) │││ │ • 总路径长度(公里) │││ │ • 巡检耗时(分钟) │││ │ • 路径可视化图 │││ └─────────────────────────────────────────────────────────┘││ ││ 【核心矛盾】 ││ • 设备部长: 想知道最短的巡检路线是什么 │││ • 教科书: 遗传算法输出染色体、适应度、进化 │││ • 现场: 35个点位、人工经验路线8.7公里 │││ • 本程序: 把启发式算法变成巡检工能看懂的路单 │││ │││ 【本程序处理流程】 │││ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐│││ │ 加载点位 │──►│ 构建遗传 │──►│ 进化求解 │──►│ 生成巡检 ││││ │ 坐标数据 │ │ 算法模型 │ │ 最优路径 │ │ 路线图 ││││ └──────────┘ └──────────┘ └──────────┘ └──────────┘││└──────────────────────────────────────────────────────────────┘二、引入痛点含量化对比2.1 现场真实困境某化工园区设备部长的原话“我们园区有 35 个关键巡检点位反应釜、泵房、冷却塔、储罐、阀门、仪表。每天 3 班倒每班 2 名巡检工单趟巡检要覆盖所有点位。以前我们巡检有个死规矩- ‘按区域走’先北区、再中区、后南区不管点位远近- ‘凭经验绕’老巡检带新巡检师傅怎么走徒弟怎么走- ‘手工画路单’每月更新一次巡检路线平时基本不改。结果就是- 单趟巡检路线长达 8.7 公里巡检工走完全程要 126 分钟- 每天 3 班 × 2 人 × 8.7 公里 52.2 公里/天全是无效行走- 漏检率 4.2%每年漏检约 500 个点次曾因漏检导致小事故 2 起- 年人工与误工成本 180 万巡检工工资 事故损失。厂长问我‘35 个点位直线距离总共才 3 公里怎么就走出 8.7 公里’我也很委屈点位分布不规则人工根本算不过来“最短的遍历顺序”。不是人走得慢是路没算明白。后来我研究北理工《运筹学》第 8 章‘启发式算法’才发现这是个标准的“旅行商问题TSP”。- 目标找到遍历所有点位的最短回路- 难点35 个点位全排列有 35! ≈ 10³⁰ 种可能暴力枚举不可能- 解法用遗传算法模拟生物进化在可接受时间内找到“足够好”的解。我写了个 Python 厂区巡检路径规划器——0.6 秒算完全园最优巡检路线- 单趟路线从 8.7 公里缩到 6.3 公里缩短 28%- 巡检耗时从 126 分钟降到 86 分钟效率提升 32%- 漏检率从 4.2% 降到 0.3%基本消除漏检- 年人工与误工成本从 180 万压到 128 万省 52 万。设备部长看完说‘原来不是人走得慢是路没算明白。这 0.6 秒的计算值 50 万。’”2.2 人工经验 vs 遗传算法优化量化对比指标 人工经验路线 遗传算法优化 改善效果单趟巡检距离 8.7 公里 6.3 公里 -28%单趟巡检耗时 126 分钟 86 分钟 -32%日总行走里程 52.2 公里/天 37.8 公里/天 -28%漏检率 4.2% 0.3% -93%年巡检工时 18,500 小时 13,300 小时 -28%年人工与误工成本 180 万/年 128 万/年 -29%路线规划耗时 3 天/月人工调整 0.6 秒/月 -99.99%关键发现巡检效率的瓶颈不在“人走得快不快”而在“路选得短不短”。遗传算法把“经验绕路”变成“数学最短”让每一步都走在最优路径上。三、核心逻辑讲解大白话版3.1 用大白话解释“厂区巡检路径规划问题”想象你要去 35 个朋友家送新年礼物- 朋友家分布在城市各个角落点位坐标不同- 你从自己家出发起点最后要回到自己家终点- 每个朋友家只能去一次不能重复送礼物- 你想走最短的路省油钱、省时间。问题是按什么顺序拜访朋友总路程最短遗传算法就是帮你算这个的“智能向导”1. 先想“怎么表示一条路线”染色体编码- 用一串数字表示拜访顺序[0, 5, 12, 3, 8, ...]- 0 是你家起点5 是朋友 A12 是朋友 B……- 这串数字就是“染色体”代表一条“候选路线”。2. 再想“哪条路线更好”适应度函数- 计算这条路线的总路程把所有相邻点的距离加起来- 路程越短这条路线“适应度越高”越优秀。3. 然后想“怎么进化出好路线”遗传操作- 选择从一堆候选路线中挑出几条短的优秀的父母- 交叉让两条优秀路线“生孩子”比如把[0,5,12,3] 和[0,8,2,10] 交叉成[0,5,2,10]新路线- 变异随机交换路线中的两个点比如把[0,5,12,3] 变成[0,12,5,3]小调整- 进化重复选择、交叉、变异一代比一代好。4. 最后想“什么时候停”终止条件- 进化 200 代迭代 200 次- 或者连续 50 代没有明显改善- 输出当前最好的路线。大白话逻辑- “拜访顺序” → 染色体编码- “总路程最短” → 适应度函数- “优秀路线生孩子” → 选择 交叉- “偶尔换个顺序试试” → 变异- “一代比一代好” → 进化- “智能向导” → 遗传算法。工业现场版- 朋友家 巡检点位- 你家 中控室起点/终点- 送礼物 巡检- 总路程 巡检路线长度- 智能向导 厂区巡检路径规划器。3.2 运筹学模型北理工《运筹学》映射参考北理工《运筹学》第 6 章“图与网络优化”、第 8 章“启发式算法”厂区巡检路径规划TSP模型集合定义- V \{0,1,2,\dots,n\} 点位集合 n35 0 为起点/终点中控室。参数- d_{ij} 点位 i 到点位 j 的距离欧氏距离- n 点位总数。决策变量隐式遗传算法不直接定义- 一个排列 \pi (\pi_0, \pi_1, \dots, \pi_n) 其中 \pi_0 0 起点 \pi_n 0 终点且 \{\pi_1, \dots, \pi_{n-1}\} 是 \{1,2,\dots,n-1\} 的一个排列。目标函数最小化总路径长度\min Z \sum_{k0}^{n-1} d_{\pi_k, \pi_{k1}}约束条件1. 每个点位访问一次排列约束2. 起点和终点固定为 0中控室。遗传算法求解框架北理工第 8 章1. 编码用排列表示路径染色体2. 适应度函数 f(\pi) 1 / Z(\pi) 路径越短适应度越高3. 选择策略轮盘赌选择适应度高的个体被选中的概率大4. 交叉算子顺序交叉OX、部分映射交叉PMX5. 变异算子交换变异、插入变异6. 终止条件最大迭代次数或收敛判据。北理工教材要点- 第 6 章 §6.3最短路问题两点间最短路径- 第 6 章 §6.5旅行商问题TSP多点遍历最短回路- 第 8 章 §8.2遗传算法的基本思想编码、适应度、遗传操作- 第 8 章 §8.3遗传算法的实现步骤初始化、选择、交叉、变异、进化- 本程序将TSP 模型与遗传算法结合实现厂区巡检路径的自动优化。3.3 如何映射到代码中业务逻辑 Python 代码遗传算法巡检点位InspectionPoint 数据类染色体编码list 表示点位访问顺序适应度函数calculate_total_distance() 计算总路径长度选择操作selection_roulette() 轮盘赌选择交叉操作crossover_ordered() 顺序交叉变异操作mutation_swap() 交换变异进化循环evolve() 迭代进化结果输出best_individual 最优路径四、OOP 代码实现精简可运行4.1 项目结构inspection_planner/├── inspection_planner.py # 核心代码单文件~400行├── README.md # 使用说明└── requirements.txt # 依赖库4.2 完整源代码可直接运行detailssummary/summary厂区巡检路径规划器 · 导航大脑参考: 北理工《运筹学》第6章图与网络优化、第8章启发式算法功能:1. 定义巡检点位、坐标、距离矩阵2. 构建遗传算法求解TSP问题3. 进化求解最优巡检路线4. 统计路径长度、巡检耗时、漏检风险运行:python inspection_planner.py(需要安装numpy, matplotlib)注意:本程序解决厂区巡检路径规划问题, 属于旅行商问题(TSP)的典型应用。对于超大规模问题(点位200), 建议使用LKH算法或蚁群算法。import numpy as npimport matplotlib.pyplot as pltfrom dataclasses import dataclass, fieldfrom typing import List, Dict, Tuple, Optional, Anyfrom enum import Enumimport randomimport mathimport timefrom collections import defaultdict# ─── 枚举与常量 ────────────────────────────────────────────────────────────class PointType(Enum):点位类型REACTOR 反应釜 # 高危设备PUMP 泵房 # 动设备COOLING 冷却塔 # 换热设备TANK 储罐 # 静设备VALVE 阀门 # 管道附件INSTRUMENT 仪表 # 测量设备CONTROL 中控室 # 起点/终点# ─── 数据模型 ────────────────────────────────────────────────────────────dataclassclass InspectionPoint:巡检点位point_id: strname: strx: float # X坐标(米)y: float # Y坐标(米)point_type: PointType PointType.VALVEinspection_time: float 3.0 # 单点巡检时间(分钟)priority: int 1 # 优先级(1-5, 5最高)def __str__(self):return f{self.name}({self.point_id}): ({self.x:.0f},{self.y:.0f}), {self.inspection_time}分钟dataclassclass InspectionRoute:巡检路线结果success: booltotal_distance: float # 总路径长度(米)total_time: float # 总巡检时间(分钟)route: List[str] # 点位访问顺序fitness: float # 适应度值generations: int # 进化代数convergence_history: List[float] # 收敛历史solve_time: floatalgorithm: str Genetic Algorithmpropertydef avg_speed(self) - float:平均行走速度(米/分钟)walking_time self.total_time - len(self.route) * 3.0 # 减去巡检时间return self.total_distance / walking_time if walking_time 0 else 0.0propertydef efficiency_gain(self) - float:效率提升(与人工路线对比)# 假设人工路线8.7公里, 126分钟manual_distance 8700 # 米manual_time 126 # 分钟distance_improvement (manual_distance - self.total_distance) / manual_distancetime_improvement (manual_time - self.total_time) / manual_timereturn (distance_improvement time_improvement) / 2# ─── 遗传算法巡检路径规划器 ───────────────────────────────────────────────────class GeneticInspectionPlanner:遗传算法巡检路径规划器def __init__(self,points: List[InspectionPoint],start_point_id: str CTRL,population_size: int 100,generations: int 200,mutation_rate: float 0.02,elite_ratio: float 0.1):Args:points: 巡检点位列表start_point_id: 起点/终点IDpopulation_size: 种群大小generations: 进化代数mutation_rate: 变异率elite_ratio: 精英保留比例self.points pointsself.start_point_id start_point_idself.population_size population_sizeself.generations generationsself.mutation_rate mutation_rateself.elite_ratio elite_ratio# 构建点位索引映射self.point_indices {point.point_id: i for i, point in enumerate(points)}self.start_index self.point_indices[start_point_id]# 计算距离矩阵self.distance_matrix self._build_distance_matrix()# 巡检时间矩阵(固定值, 实际可扩展为动态)self.inspection_times [point.inspection_time for point in points]# 种群与进化历史self.population []self.best_individual Noneself.best_fitness -np.infself.convergence_history []def _build_distance_matrix(self) - np.ndarray:构建欧氏距离矩阵n len(self.points)dist_matrix np.zeros((n, n))for i in range(n):for j in range(n):if i j:dist_matrix[i][j] 0else:dx self.points[i].x - self.points[j].xdy self.points[i].y - self.points[j].ydist_matrix[i][j] math.sqrt(dx*dx dy*dy)return dist_matrixdef _initialize_population(self) - List[List[int]]:初始化种群(随机排列)population []n len(self.points)# 固定起点, 随机排列其他点位other_indices [i for i in range(n) if i ! self.start_index]for _ in range(self.population_size):individual [self.start_index] random.sample(other_indices, len(other_indices)) [self.start_index]population.append(individual)return populationdef _calculate_fitness(self, individual: List[int]) - float:计算个体适应度(路径越短, 适应度越高)total_distance 0for i in range(len(individual) - 1):total_distance self.distance_matrix[individual[i]][individual[i1]]# 适应度 1 / (总距离 极小值), 避免除零return 1.0 / (total_distance 1e-6)def _calculate_total_distance(self, individual: List[int]) - float:计算个体总路径长度total_distance 0for i in range(len(individual) - 1):total_distance self.distance_matrix[individual[i]][individual[i1]]return total_distancedef _calculate_total_time(self, individual: List[int]) - float:计算个体总巡检时间total_distance self._calculate_total_distance(individual)walking_time total_distance / 80.0 # 假设行走速度80米/分钟# 加上各点位巡检时间(起点和终点不计入)inspection_time sum(self.inspection_times[i] for i in individual[1:-1])return walking_time inspection_timedef _selection_roulette(self, fitness_values: List[float]) - List[int]:轮盘赌选择total_fitness sum(fitness_values)if total_fitness 0:probabilities [1.0 / len(fitness_values)] * len(fitness_values)else:probabilities [f / total_fitness for f in fitness_values]# 轮盘赌选择两个父代selected_indices np.random.choice(len(fitness_values),size2,pprobabilities,replaceFalse)return selected_indicesdef _crossover_ordered(self, parent1: List[int], parent2: List[int]) - List[int]:顺序交叉(OX)算子n len(parent1)# 随机选择两个交叉点(避开起点和终点)start random.randint(1, n-3)end random.randint(start1, n-2)# 初始化子代child [-1] * n# 复制父代1的交叉段child[start:end] parent1[start:end]# 从父代2填充剩余位置pointer endfor i in range(end, n):if parent2[i] not in child:child[pointer] parent2[i]pointer 1if pointer n:pointer 1for i in range(1, start):if parent2[i] not in child:child[pointer] parent2[i]pointer 1if pointer n:pointer 1# 确保起点和终点正确child[0] self.start_indexchild[-1] self.start_indexreturn childdef _mutation_swap(self, individual: List[int]) - List[int]:交换变异算子if random.random() self.mutation_rate:# 随机选择两个非起点/终点的位置进行交换swap_indices random.sample(range(1, len(individual)-1), 2)individual[swap_indices[0]], individual[swap_indices[1]] \individual[swap_indices[1]], individual[swap_indices[0]]return individualdef _elite_preservation(self, population: List[List[int]], fitness_values: List[float]) - List[List[int]]:精英保留策略elite_count int(self.population_size * self.elite_ratio)elite_indices np.argsort(fitness_values)[-elite_count:]return [population[i] for i in elite_indices]def evolve(self) - InspectionRoute:进化求解最优巡检路线print( 启动遗传算法进化巡检路径...)print(f • 点位数量: {len(self.points)}个)print(f • 种群大小: {self.population_size})print(f • 进化代数: {self.generations})print(f • 变异率: {self.mutation_rate})print(f • 精英保留比例: {self.elite_ratio})start_time time.perf_counter()# 1. 初始化种群self.population self._initialize_population()print(f 种群初始化完成: {len(self.population)}个个体)# 2. 进化循环for gen in range(self.generations):# 计算适应度fitness_values [self._calculate_fitness(ind) for ind in self.population]# 更新最优个体current_best_idx np.argmax(fitness_values)current_best_fitness fitness_values[current_best_idx]if current_best_fitness self.best_fitness:self.best_fitness current_best_fitnessself.best_individual self.population[current_best_idx].copy()# 记录收敛历史self.convergence_history.append(1.0 / self.best_fitness)# 精英保留elites self._elite_preservation(self.population, fitness_values)# 生成新一代种群new_population elites.copy()while len(new_population) self.population_size:# 选择父代parent_indices self._selection_roulette(fitness_values)parent1 self.population[parent_indices[0]]parent2 self.population[parent_indices[1]]# 交叉child self._crossover_ordered(parent1, parent2)# 变异child self._mutation_swap(child)new_population.append(child)self.population new_population# 每50代打印一次进度if (gen 1) % 50 0:best_distance 1.0 / self.best_fitnessprint(f ▶ 第{gen1}代: 最优距离{best_distance:.1f}米, 适应度{self.best_fitness:.6f})end_time time.perf_counter()solve_time end_time - start_time# 3. 提取最优路线if self.best_individual is None:self.best_individual self.population[0]self.best_fitness self._calculate_fitness(self.best_individual)total_distance self._calculate_total_distance(self.best_individual)total_time self._calculate_total_time(self.best_individual)# 转换点位ID序列route_point_ids [self.points[i].point_id for i in self.best_individual]print(f ✅ 进化完成! 耗时: {solve_time:.3f}秒)print(f 最优路线: {total_distance:.1f}米, {total_time:.1f}分钟)print(f ️ 巡检顺序: { → .join(route_point_ids[:5])}... → {route_point_ids[-1]})return InspectionRoute(successTrue,total_distancetotal_distance,total_timetotal_time,routeroute_point_ids,fitnessself.best_fitness,generationsself.generations,convergence_historyself.convergence_history,solve_timesolve_time,algorithmGenetic Algorithm)def greedy_initialization(self) - InspectionRoute:贪心算法初始化(作为对比)print( 贪心算法生成初始路线...)start_time time.perf_counter()# 贪心算法: 从起点开始, 每次选择最近未访问的点位n len(self.points)visited [False] * nroute [self.start_index]visited[self.start_index] Truecurrent self.start_indexwhile len(route) n:# 找到最近的未访问点位nearest -1min_dist float(inf)for i in range(n):if not visited[i] and i ! self.start_index:dist self.distance_matrix[current][i]if dist min_dist:min_dist distnearest iif nearest -1:breakroute.append(nearest)visited[nearest] Truecurrent nearest# 返回起点route.append(self.start_index)end_time time.perf_counter()solve_time end_time - start_timetotal_distance self._calculate_total_distance(route)total_time self._calculate_total_time(route)fitness self._calculate_fitness(route)# 转换点位ID序列route_point_ids [self.points[i].point_id for i in route]print(f ✅ 贪心路线生成完成! 耗时: {solve_time:.3f}秒)print(f 贪心路线: {total_distance:.1f}米, {total_time:.1f}分钟)return InspectionRoute(successTrue,total_distancetotal_distance,total_timetotal_time,routeroute_point_ids,fitnessfit利用AI解决实际问题如果你觉得这个工具好用欢迎关注长安牧笛