数学建模竞赛中的空中加油优化:模型、算法与Python实现

📅 2026/8/27 3:14:28
数学建模竞赛中的空中加油优化:模型、算法与Python实现
1. 问题重述与核心挑战解析2018年第七届数学建模国际赛小美赛的A题“空中加油飞行计划”是一个经典的运筹学与优化问题。题目通常会给出一系列参数比如一个或多个基地的位置、一支或多支需要执行任务的机群我们称之为“任务机”、为任务机提供燃油补给的加油机、任务机的燃油消耗率、巡航速度、最大载油量以及加油机的最大载油量、加油速率等。目标是在满足所有任务机完成既定飞行任务如从A点飞到B点执行侦察、打击等的前提下设计一套最优的空中加油方案使得整个机群完成任务的总耗时最短或者使用的加油机数量最少亦或是总油耗最低。具体目标函数需根据赛题原文确定。这个问题的核心挑战在于其高度的动态性和组合爆炸的可能性。它不像静态的车辆路径规划问题飞机在空中是连续移动的燃油是持续消耗的加油行为本身也需要时间并且会改变飞机的状态位置、油量。这导致了几个关键难点状态空间的连续性飞机的油量、位置都是连续变量这使得精确建模和求解变得复杂。我们通常需要将其离散化处理例如按时间步长或按关键事件如起飞、会合、加油、分离、到达来划分阶段。决策的耦合性一架加油机的决策如何安排其飞行路线和加油对象会直接影响多架任务机的油量状态进而影响整个机群的进度。这种强耦合性使得问题无法被简单地分解为独立的子问题。时空协同的复杂性加油机与任务机必须在特定的时间、特定的空间点空中会合点精确会合。这要求对飞行路径、速度进行精细的协调任何延误都可能导致加油失败甚至任务失败。资源的有限性加油机自身的油量也是有限的。它需要携带足够的油料为自己提供往返航程的消耗同时还要为任务机提供富余的油料。这就引出了“接力加油”或“多级加油”的概念即一架加油机在为任务机加油后自身油量不足返航可能需要另一架加油机为其加油形成复杂的支援链。解决这类问题本质上是在一个由时间、空间、油量构成的超维空间中寻找一条或多条满足所有约束条件油量非负、按时会合、任务完成的最优轨迹。接下来我将以一个典型的简化场景为例拆解建模与求解的全过程。2. 典型场景假设与数学模型构建为了清晰地展示解题思路我们构建一个具体的场景。假设任务如下任务机1架需从基地O飞往目标点T执行任务然后返回基地O。单程距离为D公里。任务机性能巡航速度V_task (km/h)燃油消耗率C_task (L/h)最大载油量F_task_max (L)。加油机若干架性能相同。从基地O起飞和降落。巡航速度V_tanker (km/h)燃油消耗率C_tanker (L/h)最大载油量F_tanker_max (L)空中加油速率为R (L/h)。目标设计加油方案使任务机完成往返任务的总时间最短。允许加油机进行“接力”加油即加油机A为任务机加油后由加油机B为加油机A加油使其能安全返航。2.1 关键决策变量与约束条件首先我们需要定义数学模型的核心——决策变量。会合点与分离点我们不知道需要多少次加油以及在哪里加油。因此我们将任务机的往返路径离散化为N个航段。每个航段的端点就是一个潜在的会合/分离点。设第i个关键点包括起点O、目标T、可能的会合点的坐标为 (x_i, y_i)任务机到达该点的时间为 t_i^task。加油事件定义一个二进制变量 δ_ijk。如果 δ_ijk 1表示在第i个航段即从点i到点i1之间由第k架加油机为任务机或另一架加油机进行了一次加油。油量状态方程这是模型的核心约束确保任何时刻任何飞机的油量不为负。对于任务机在点i1处的油量 在点i处的油量 - 从i到i1的飞行油耗 在此期间接收的加油量。公式化表达F_task(i1) F_task(i) - C_task * (t_{i1} - t_i) Σ(δ_ijk * R * Δt_加油)。其中 Δt_加油 是加油持续时间。同理对于每架加油机k也有其油量状态方程F_tanker_k(i1) F_tanker_k(i) - C_tanker * (飞行时间) - Σ(δ_ijk * R * Δt_加油)。这里减号表示它为别人供油。会合约束如果 δ_ijk 1那么加油机k和受油机必须在加油开始时间于空中某点会合。这意味着两者的位置坐标关于时间的函数在那一刻相等。这通常转化为对两者飞行路径和速度的约束。任务完成约束任务机必须在最终时间点返回基地O且其油量至少为某个安全值例如0。加油机返航约束每架加油机在完成所有供油任务后必须能安全返回基地O且剩余油量不低于安全值。2.2 目标函数与问题分类在我们的假设中目标是最小化总任务时间即任务机从O出发到返回O的耗时T_total t_end - t_start。根据不同的赛题要求目标函数可以变化最小化时间适用于对时效性要求极高的任务如紧急救援、快速反应。最小化加油机使用数量适用于加油机资源紧张的情况追求最高的资源利用率。最小化总油耗适用于对成本敏感或航程受限的长期任务。这个问题本质上是一个**混合整数非线性规划MINLP**问题。因为其中既包含整数变量如 δ_ijk 决定是否加油也包含连续变量如时间、位置、油量并且约束条件如会合约束是非线性的。直接求解全局最优解非常困难尤其当问题规模飞机数量、航段数增大时。3. 求解策略从精确算法到启发式方法面对这样一个复杂的MINLP问题我们通常采用分层或启发式的方法来寻找满意解而不是执着于全局最优。3.1 基于关键事件点的简化模型一个有效的简化思路是我们并不需要描述飞机每一刻的连续轨迹而只需关注“关键事件”起飞、会合开始加油、加油结束分离、到达目的地、返航会合等。在这些事件点上飞机的状态位置、油量、时间是确定的或需要被决策。我们可以将问题转化为一个网络流问题的变种。想象一个时空网络节点代表每个关键事件附带有时间、位置、飞机ID、油量状态等信息。弧代表飞机从一个事件状态转移到下一个事件状态例如“飞行一段距离”或“进行加油操作”。每条弧都有成本如时间消耗、油耗和资源转移油量变化。任务机的行程是从起点O点满油时间0到终点O点时间T油量0的一条路径。加油机的行程也是从起点O点满油时间0到终点O点时间T油量0的路径但其路径上包含“供油”节点这些节点会减少其油量并对应地增加任务机油量。这样问题就变成了在网络上为每架飞机寻找路径同时满足油量平衡约束流入油量加油量 消耗油量流出油量。这可以通过混合整数线性规划MILP来建模虽然依然复杂但比原始的MINLP更易处理可以使用CPLEX、Gurobi等专业求解器求解小规模问题。3.2 经典启发式算法“向前探查”与“向后倒推”对于无法直接求解MILP的较大规模问题或者作为快速求满意解的方法启发式算法非常有效。这里介绍两种直观的思路方法一向前探查任务机视角让任务机从起点满油出发沿着预定航线向前飞。我们实时计算它的剩余油量。设定一个“安全返航油量”阈值即任务机从当前位置直接返回基地所需的最低油量。当当前剩余油量 - 飞往下一关键点的预计油耗 安全返航油量时就意味着它无法在到达下一个点后安全返回必须在此之前进行加油。 此时算法需要决策派哪架可用的加油机在什么位置与会合加多少油一个简单的贪婪策略是派出一架从基地起飞、能最早与任务机会合的加油机为其加入恰好能使其飞到下一个决策点或目标点并留有安全余量的油。然后递归地处理加油机自身的返航问题它加油后可能自己也需要被加油。这种方法逻辑清晰易于编程实现但可能不是全局最优。方法二向后倒推油量需求视角从任务机的最终状态返回基地油量为安全值开始倒推出它在每个关键点所需的最低油量。例如要安全返回基地在离开目标点T时任务机至少需要有从T飞回O的油耗 安全余量的油量。那么在到达T点时它的油量至少要达到这个值加上从T点开始可能消耗的油量如盘旋等待。如果这个所需油量超过了它从上一个点飞来的最大可能油量考虑最大载油量和飞行消耗那么在上一个点就必须进行加油且加油量必须补足这个缺口。 通过这样从后往前推我们可以确定一系列“必须加油”的点和最低加油量。然后再正向安排加油机去满足这些“油量需求”。这种方法能保证找到可行解如果存在并且对最小化加油次数很有效。3.3 元启发式算法的应用遗传算法与模拟退火当问题规模进一步扩大多架任务机、多架加油机、复杂航线上述方法可能显得力不从心。这时遗传算法GA和模拟退火SA等元启发式算法就能大显身手。以遗传算法为例编码如何用一条“染色体”表示一个加油方案这是一个关键设计。可以这样设计染色体由一系列“加油事件基因”组成。每个基因包含加油时间或关联的航段、会合点位置或相对于航线的比例、参与加油的加油机ID、加油量。任务机的航线是固定的因此不编码。初始化种群随机生成一批染色体即随机生成一系列加油事件。必须通过一个“解码器”和“修复程序”来评估和修正这些随机方案使其满足基本约束如油量不为负、加油机会合可行。修复程序可能采用前述的“向后倒推”逻辑来补足油量缺口。适应度函数即目标函数。对于最小化时间问题适应度可以是总任务时间的倒数时间越短适应度越高。对于不可行方案如飞机油量中途耗尽赋予极低的适应度或进行惩罚。遗传操作选择根据适应度选择优秀的个体进入下一代。交叉随机选取两个父代染色体交换它们的一部分“加油事件基因”生成子代。这相当于融合了两个不同加油方案的片段。变异以一定概率随机改变某个基因的内容如调整加油时间、加油量甚至增加/删除一个加油事件。这有助于探索新的解空间区域。迭代重复选择、交叉、变异、评估的过程直到达到终止条件如最大迭代次数、适应度不再提升。遗传算法的优势在于能并行搜索大量解空间有较大可能找到全局较优解。其难点在于编码设计、修复程序的效率以及参数种群大小、交叉变异概率的调优。4. 编程实现核心模块与代码片段下面我将用Python勾勒出几个核心模块的代码框架重点展示“向前探查”启发式算法的逻辑。我们假设一个简化的一维场景基地O在坐标0目标T在坐标D处飞机沿直线飞行。import numpy as np class Aircraft: def __init__(self, name, speed, consumption_rate, max_fuel): self.name name self.speed speed # km/h self.consumption_rate consumption_rate # L/h self.max_fuel max_fuel # L self.fuel max_fuel # 当前油量 self.position 0.0 # 当前位置 self.time 0.0 # 当前时间 def fly_to(self, target_position): 飞行到目标位置更新油量、位置和时间 distance abs(target_position - self.position) flight_time distance / self.speed fuel_consumed flight_time * self.consumption_rate if fuel_consumed self.fuel: raise ValueError(f{self.name} 油量不足需要{fuel_consumed:.1f}L 仅有{self.fuel:.1f}L) self.fuel - fuel_consumed self.position target_position self.time flight_time return flight_time def can_return_to_base(self, base_position): 判断当前油量是否能返回基地 distance_to_base abs(self.position - base_position) fuel_needed (distance_to_base / self.speed) * self.consumption_rate return self.fuel fuel_needed def fuel_to_return(self, base_position): 计算返回基地所需油量 distance_to_base abs(self.position - base_position) return (distance_to_base / self.speed) * self.consumption_rate class Tanker(Aircraft): def __init__(self, name, speed, consumption_rate, max_fuel, transfer_rate): super().__init__(name, speed, consumption_rate, max_fuel) self.transfer_rate transfer_rate # L/h 加油速率 def forward_probe_mission(task_plane, target_point, base_point, tankers, safety_factor1.1): 向前探查启发式算法 :param task_plane: 任务机对象 :param target_point: 目标点坐标 :param base_point: 基地坐标 :param tankers: 可用的加油机列表 :param safety_factor: 安全系数用于放大返航油量需求 :return: 总任务时间 加油事件列表 events [] # 阶段1: 从基地飞向目标 waypoints [base_point, target_point] # 简化航路点 for i in range(len(waypoints)-1): start_wp waypoints[i] end_wp waypoints[i1] # 任务机飞到当前航段起点如果不在的话 if abs(task_plane.position - start_wp) 1e-6: task_plane.fly_to(start_wp) # 计算飞到下一个航路点所需的油量 distance_to_next abs(end_wp - start_wp) fuel_to_next (distance_to_next / task_plane.speed) * task_plane.consumption_rate # 计算从下一个航路点安全返回基地所需的油量 fuel_to_return_from_next task_plane.fuel_to_return(base_point) * safety_factor # 检查油量是否足够飞到下一个点并安全返航 fuel_required fuel_to_next fuel_to_return_from_next while task_plane.fuel fuel_required: # 需要加油选择一架可用的加油机 # 简化策略选择最早能抵达任务机当前位置的加油机 best_tanker None earliest_meet_time float(inf) meet_pos task_plane.position # 简化在当前点加油 for tanker in tankers: if tanker.fuel 0.1 * tanker.max_fuel: continue # 该加油机油量过低不考虑 # 计算加油机从当前位置飞到会合点所需时间 time_for_tanker_to_arrive abs(tanker.position - meet_pos) / tanker.speed meet_time max(task_plane.time, tanker.time time_for_tanker_to_arrive) if meet_time earliest_meet_time: earliest_meet_time meet_time best_tanker tanker if best_tanker is None: raise RuntimeError(没有可用的加油机) # 计算需要补充的油量 fuel_deficit fuel_required - task_plane.fuel # 加油量不能超过任务机最大容量也不能超过加油机可提供量考虑其自身返航 tanker_fuel_available best_tanker.fuel - best_tanker.fuel_to_return(base_point) * safety_factor transfer_amount min(fuel_deficit, task_plane.max_fuel - task_plane.fuel, max(0, tanker_fuel_available)) if transfer_amount 0: raise RuntimeError(f加油机{best_tanker.name}无法提供有效油量支援。) # 模拟时间推进和加油过程 # 1. 加油机飞到会合点 tanker_travel_time abs(best_tanker.position - meet_pos) / best_tanker.speed best_tanker.time tanker_travel_time best_tanker.position meet_pos best_tanker.fuel - tanker_travel_time * best_tanker.consumption_rate # 2. 任务机等待如果需要并会合 if task_plane.time best_tanker.time: wait_time best_tanker.time - task_plane.time # 任务机等待时会消耗燃油假设盘旋 task_plane.fuel - wait_time * task_plane.consumption_rate task_plane.time best_tanker.time # 3. 进行加油 transfer_time transfer_amount / best_tanker.transfer_rate task_plane.fuel transfer_amount best_tanker.fuel - transfer_amount # 加油期间两架飞机悬停或低速盘旋消耗燃油 task_plane.fuel - transfer_time * task_plane.consumption_rate best_tanker.fuel - transfer_time * best_tanker.consumption_rate task_plane.time transfer_time best_tanker.time transfer_time # 记录加油事件 events.append({ time: task_plane.time - transfer_time, # 加油开始时间 position: meet_pos, tanker: best_tanker.name, amount: transfer_amount, duration: transfer_time }) # 检查加油后任务机油量 if task_plane.fuel 0: raise RuntimeError(加油后任务机油量异常) # 油量满足要求飞向下一个航路点 task_plane.fly_to(end_wp) # 阶段2: 从目标点返回基地 (逻辑类似略) # ... 此处省略返航阶段的类似检查与加油逻辑 # 最终飞回基地 task_plane.fly_to(base_point) total_time task_plane.time return total_time, events # 参数示例 if __name__ __main__: task Aircraft(Task-1, speed800, consumption_rate3000, max_fuel20000) # 速度km/h, 耗率L/h, 油量L tanker1 Tanker(Tanker-A, speed700, consumption_rate2500, max_fuel50000, transfer_rate5000) tanker2 Tanker(Tanker-B, speed700, consumption_rate2500, max_fuel50000, transfer_rate5000) base 0 target 5000 # 5,000 km try: total_time, refuel_events forward_probe_mission(task, target, base, [tanker1, tanker2]) print(f任务总时间: {total_time:.2f} 小时) print(f加油次数: {len(refuel_events)}) for i, evt in enumerate(refuel_events): print(f 事件{i1}: 时间{evt[time]:.2f}h, 位置{evt[position]:.0f}km, f加油机{evt[tanker]}, 加油量{evt[amount]:.0f}L, 耗时{evt[duration]:.2f}h) except Exception as e: print(f任务规划失败: {e})注意以上代码是一个高度简化的框架用于阐述算法逻辑。真实问题中会合点需要精确计算不是简单地在任务机当前位置加油机的调度也需要考虑其起飞时间、多任务排序并且返航阶段也需要完整的油量检查和加油安排。此外错误处理、燃油消耗的精确建模可能与速度、高度有关等都需进一步完善。5. 模型验证、灵敏度分析与论文撰写要点得到一个求解方案后必须进行严谨的验证与分析。5.1 模型验证与仿真测试静态验证检查所有约束条件是否在数学上被严格满足。例如对于求得的每个加油事件手动代入油量状态方程检查是否在任何时间点油量都不为负。动态仿真编写一个离散事件仿真程序将规划好的方案起飞时间、会合点、加油量等作为输入模拟飞机按照给定速度飞行、耗油、会合、加油的过程。以较小的时间步长如1分钟推进实时监控每架飞机的油量和位置。这是检验方案可行性的黄金标准。如果仿真中出现油量告警或会合失败说明模型或求解过程有漏洞。边界测试改变输入参数观察方案的变化是否合理。极端距离将目标距离设置得非常远看方案是否会合理增加加油次数甚至出现“接力加油”加油机为加油机加油。极端性能大幅降低加油机的加油速率看方案是否会安排更长的加油接触时间从而影响总任务时间。资源限制减少可用加油机数量看算法是否能调整策略或者报告无解。5.2 灵敏度分析灵敏度分析是数学建模论文的亮点它回答“如果某个参数变了结果会怎样”的问题。对于空中加油问题可以分析任务机燃油效率的影响将任务机的燃油消耗率上下浮动10%观察总任务时间或所需加油机数量的变化。可以绘制折线图直观展示其敏感性。通常消耗率对总时间的影响是非线性的可能存在一个临界点超过后需要额外增加一次加油导致时间跃升。加油机部署位置的影响假设加油机不是全部从主基地起飞而是可以预先部署在前沿基地。分析不同前沿基地位置对减少总任务时间或节省加油机架次的效果。这可以通过参数化前沿基地的位置重新运行模型来实现。会合策略的影响对比“定点会合”在预定坐标点和“动态会合”在任务机航线上最优位置两种策略的效果。动态会合通常更优但模型更复杂。天气/风场的影响引入风场模型顺风增加地速、逆风减少地速从而影响飞行时间和油耗。分析在不同风场条件下原定方案的鲁棒性以及是否需要动态调整加油计划。5.3 论文撰写核心要点一篇优秀的数模论文除了有好的模型和结果清晰的表达至关重要。问题重述与假设用自己语言精炼概括问题并明确列出所有假设如飞机匀速直线飞行、忽略加减速、加油瞬间完成油量转移等。假设要合理且必要。符号说明在模型建立前用表格清晰列出所有使用的变量、参数及其含义和单位。模型建立这是核心。逐步推导从简单到复杂。可以先建立不考虑加油机返航油量的简单模型再引入加油机自身消耗和返航约束最后考虑多架加油机协同。流程图和示意图能极大帮助理解。模型求解详细说明你采用的算法。如果是启发式算法解释清楚编码方式、适应度函数、遗传操作等。如果是调用求解器如Gurobi说明模型类型MILP和求解设置。提供伪代码或关键代码片段。结果分析用表格和图表展示结果。例如一张表格列出不同场景下的最优总时间、加油次数、各加油机利用率一张甘特图展示飞机的时间线清晰显示起飞、飞行、加油、返航等事件一张航线图展示飞机和加油机的飞行轨迹及会合点。灵敏度分析用图表展示关键参数变化对结果的影响并给出合理解释。模型评价与推广客观评价模型的优点如考虑全面、求解高效和缺点如假设理想化、未考虑不确定性。提出模型的改进方向如加入随机延误、考虑三维地形和在其他领域的应用可能如无人机集群续航管理、长途货运车队的移动加油车调度。空中加油计划问题是一个融合了运筹学、动态规划和智能优化的经典题目。通过这道题的训练我们不仅学会了如何对一个复杂动态系统进行建模更掌握了面对“组合爆炸”问题时如何通过合理的简化、巧妙的转化和高效的启发式算法来寻找工程可用的满意解。这种从问题定义到模型构建再到算法实现与结果分析的完整闭环正是数学建模竞赛带给参赛者的核心能力提升。在实际编程调试中最深的体会是一个健壮的仿真验证模块比复杂的优化算法本身更重要它是一切结果可信的基石。