作者:欧新宇(Xinyu OU)
当前版本:Release v1.0
开发平台:Python3.11
运行环境:Intel Core i7-7700K CPU 4.2GHz, nVidia GeForce GTX 1080 Ti
本教案所涉及的数据集仅用于教学和交流使用,请勿用作商用。
最后更新:2024年3月17日
多目标规划、最优控制问题
在约 10000m 高空的某边长 160km 的正方形区域内,经常有若干架飞机做水平飞行。区域内每架飞机的位置和速度向量均由计算机记录其数据,以便进行飞行管理。当一架欲进入该区域的飞机达到边缘时,记录其数据后,要立即计算并判断是否会与区域内的其他飞机发生碰撞。如果会碰撞,则应计算如何调整各架(包括新进入的)飞机飞行的方向角,以避免碰撞。先假设条件如下:
(1)不碰撞的标准为任意两架飞机的距离大于8km。
(2)飞机飞行方向角调整的幅度不应超过 。
(3)所有飞机飞行速度均为 800km/h。
(4)进入该区域的飞机在到达区域边缘时,与区域内飞机的距离应在60km以上。
(5)最多需要考虑6架飞机。
(6)不必考虑飞机离开此区域后的状况。
试通过给定的假设条件,对这个避免碰撞的飞行管理问题建立数学模型,列出计算步骤,并对以下数据进行计算(方向角误差不超过 ),要求飞机飞行方向角调整的幅度尽量小。该区域4个顶点的坐标为 (0, 0), (160, 0), (160, 160), (0, 160)。记录数据如 表1 所列。
表1 飞行记录数据
飞机编号 | 横坐标 | 纵坐标 | 方向角() |
---|---|---|---|
1 | 150 | 140 | 243 |
2 | 85 | 85 | 236 |
3 | 150 | 155 | 220.5 |
4 | 145 | 50 | 159 |
5 | 130 | 150 | 230 |
新进入 | 0 | 0 | 52 |
注: 方向角指飞行方向与 轴正方向的夹角。
在拿到问题(特别是不熟悉的问题)时,首先应该认真扣题,也就是抓住题目中的关键词“管理”进行联想。在考虑“管理”问题时,可以考虑先抓住一些字面容易理解的词汇:“碰撞”、“调整”、“避免碰撞”、“判断”等词进行联想;然后,再根据关键词“管理”进行进一步的联想,比如“管理”可以引申为“规划”、“调度”、“控制”等,再者,根据“管理”引申出的关键词,可以进一步联想出“多目标”、“多变量”、“多约束”、“多方案”等。
在进行初步联想的时候,一定要学会天马行空,不加约束地联想,最后将一些关键词进行组合,构建10-20个问题或者假设,为后一步选取主线、假设、限定以及问题拓展做基础。
为了便于理解题目的目的,我们可以考虑根据题目中的已知数据构建一些框架图或示意图,在本题中最直接的是构建6架飞机的初始状态图。下面给出使用Python绘制初始状态的代码。
import matplotlib.pyplot as plt
import numpy as np
# 1. 定义数据
data = {
'编号': [1, 2, 3, 4, 5, 6],
'横坐标x': [150, 85, 150, 145, 130, 0],
'纵坐标y': [140, 85, 155, 50, 150, 0],
'方向角': [243, 236, 220.5, 159, 230, 52]
}
# 2. 转换为numpy数组
x = np.array(data['横坐标x'])
y = np.array(data['纵坐标y'])
angles = np.radians(np.array(data['方向角'])) # 将角度转换为弧度
# 2. 绘制基础坐标轴
plt.figure(figsize=(5, 5))
plt.gca().set_aspect('equal', adjustable='box') # 保持x和y轴比例相同
plt.xlim(0, 160) # 设置x轴范围
plt.ylim(0, 160) # 设置y轴范围
# 3. 绘制箭头
for i in range(len(x)):
# 计算箭头长度和尾部的位置
arrow_length = 30 # 可以根据需要调整箭头的长度
dx = arrow_length * np.cos(angles[i])
dy = arrow_length * np.sin(angles[i])
tail_x = x[i] - dx / 2
tail_y = y[i] - dy / 2
head_x = x[i] + dx / 2
head_y = y[i] + dy / 2
# 绘制箭头
plt.arrow(tail_x, tail_y, dx, dy, head_width=3, head_length=4, fc='b', ec='b')
# 4. 显示图形
plt.grid(True) # 添加网格
plt.show()
由上图,我们可以看出本项目的问题从形式上看,应该属于最优控制问题,若需要同时考虑6架飞机,该问题便有6个可控制对象,这是比较复杂的问题。因此,
对于本项目,我们可以将以上内容总结为如下示意图:
根据题意,要求飞机飞行方向角的调整要尽量小,我们可以得到以下两个重要假设:
如上图所示,若OB是飞机 的初始飞行方向,该方向有碰撞可能,OC为安全飞行路线。当飞机 位于O点时,它只需要调整 即可到达安全航线。若在O点时,飞机错过了调整,而到达D点再进行调整,此时要飞向C点,则其调整角 显然大于原来的调整角 。此外,由于飞机在D点时具有初始速度,因此若要飞向C点,其实际的调整角应该大于 ,而应该是 。考虑到飞机的持续飞行,甚至于要调整到DE的方向才能满足安全飞行,很显然,由于 ,因此位于点D的调整角 更加糟糕!
如上图所示,若飞机 依然位于初始点O,且OB依然是初始飞行方向,OC是安全航线。假设需要经过多次调整才能到达安全航线,则 都是第一次可能调整的方向。这之中 显然过大,而 方向反了,都不是理想的调整位置。设第一次调整的位置为 ,则当经过第二次或经过n次调整后,最终可以到达安全点C。 与上一种假设类似,从D点要调整到C点,其实际调整角应该为 ,其结果远大于 ,因此,多次调整也不如一次调整更好。
PS:不必强求一次写出所有符号的说明,可以考虑在完成模型的建立和求解后最后在进行整理。
根据1.3节重要假设的分析,我们可以得到一个结论是新进入监控区域的飞机会根据当前区域内的飞机立即进行方向角的调整并且一次调整到位。基于这个结论,我们就可以根据 表1 的数据来分析并确定飞机的初始状态和初始时飞机间的相互关系,从而求解避免碰撞的优化问题。
对于任何优化问题的求解,通常都需要确定两个重要数据:目标函数 和 约束条件。
在本题中,目标函数为飞机 到达安全航线的最小调整角,注意在调整过程中,由于新飞机 的加入,原本处于控制区域内的其他飞机也存在调整的可能。因此,我们需要将飞机 都纳入到优化的目标中。
设第 架飞机的初始方向角为,经过调整(一次调整到位)后的方向为 。根据题目要求的飞机飞行方向角调整幅度尽量小,可以得到目标函数:
注意:此处我们定义优化目标为所有飞机调整量的绝对值之和最小。但对于一个任务来说,可能会存在多种优化目标,这些优化目标有些是等价的,有些是不等价的,也有一些是不合理的。例如,针对本例的问题,还可以考虑使所有飞机的最大调整量最小、使所有飞机之间的相对距离最大、所有飞机以最少的距离飞离监控区域等。但应注意的是,如何正确地从中选取出最合适的目标函数。一般来说,目标函数的选择应紧扣题意,并且应注意目标函数应与约束条件相匹配,并且目标函数的选择应尽可能地使约束条件得到满足。
接下来,我们来讨论约束条件。
根据题目的要求,要确保所有飞机在飞离监控区域的过程中避免碰撞,则必须满足以下条件:
(1)任意两架飞机的距离大于 公里。
(2)飞机飞行方向角的调整幅度不应超过 。
(3)所有飞机飞行速度均为 800km/h。
(4)进入该区域的飞机在到达区域边缘时,与区域内飞机的距离应在60km以上。
以上约束条件均根据题意获得,但应注意的是对于有些问题,约束条件可能需要根据实际情况进行调整。例如,在某些情况下,可能需要对约束条件进行一定的放松,以使问题得到求解。
然而仔细思考,我们会发现位于监控区域中的飞机 和 在任意时刻的状态都是动态的,因此简单计算两者距离作为约束条件是不合理的。因此,约束条件(1)的实际情况应该是,对于任意时刻 ,都存在:
以上约束条件是一个关于时间 的函数,而时间 是一个未知量(或隐变量),并不容易求得,为此,我们可以考虑先描述每架飞机的飞行轨迹,再根据飞行轨迹来判断飞机之间的相对距离。该过程,我们稍后再描述。
约束条件(3)是一个常数,可以在求解过程中直接代入。
约束条件(4)为初始条件。根据已经给出的数据,可以轻松验证其满足性,因此本模型的设计中可以不予考虑。
接下来,我们将继续讨论约束条件(1)。根据相对运动的观点,在考查两架飞机 和 的相互关系时,不妨将飞机 视为静止状态,而飞机 以相对速度 相对于飞机 运动。
为了保证任意两架飞机的距离大于 公里,如 图1 所示, 我们可以假设飞机 附近存在一个半径为8km的禁飞区域。也就是说,除飞机 以外,不允许任意其他飞机进入该区域。
图1 飞机 和 的相对关系图
假设在距离飞机 一定距离的地方存在一架飞机 ,其初始飞行方向角为 ,速度 。要确保 和 不发生碰撞,则经过时间 后,飞机 始终不会进入到飞机 的禁飞区。若记 为调整前第 架飞机相对于第 架飞机的相对速度(矢量)与这架飞机连线(从 指向 的矢量)的夹角(以连线矢量为基准,逆时针方向为正,顺时针方向为负)。则 为飞机 的禁飞方向角。设 为飞机 的调整方向角,则调整后的方向角 应满足如下约束:
以上约束也可表示为:
为便于处理,我们将上述不碰撞条件整理为如下形式:
接下来,我们来探讨约束条件(1)中三个超参数 的建模。
如 图1 所示,两架飞机的初始相对距离 为:
根据三角函数关系可以得到:
在 图1 中, 表示初始方向角,也即调整前第 架飞机相对于第 架飞机的相对速度(矢量)与这架飞机连线(从 指向 的矢量)的夹角(以连线矢量为基准,逆时针方向为正,顺时针方向为负)。该值由飞机进入监控区域时的速度方向决定,为便于分别计算 方向和 方向的速度,我们将该值转换为复数形式。
相对速度 的幅角 - 从 指向 的连线矢量的幅角 。
注意,在上式中 表示虚数单位。
在 图1 的直角坐标系中,我们可以使用两个速度 和 的差来表示它们的相对速度,其形式如:
在上式中, 分别表示飞机 在调整后的方向。 表示方向调整后飞机 在 方向上的飞行速度,类似地 表示飞机 在 方向上的飞行速度,两者的差则为飞机 与飞机 在 方向上的相对速度。同理, 表示飞机 在 方向上的飞行速度, 表示飞机 在 方向上的飞行速度,两者的差则为飞机 与飞机 在 方向上的相对速度。
根据三角函数的计算规则,我们可以将公式(10)化简为:
此时,若设 ,则飞机 和 的相对飞行方向角为 。于是,只要当相对飞行方向角 满足:
时,两飞机不可能相撞。联合(6),(11)可得 。
根据以上分析,最终模型汇总如下:
利用Python程序,可求得 的值如表2所列, 的值如表3所列。
import numpy as np
import pandas as pd
from scipy.optimize import minimize
# 1. 定义初始变量及初始参数
x0 = np.array([150, 85, 150, 145, 130, 0]) # 设置初始横坐标
y0 = np.array([140, 85, 155, 50, 150, 0]) # 设置初始纵坐标
xy0 = np.vstack([x0, y0]).T # 组合飞机的初始坐标位置(x0, y0)
theta0 = np.array([243, 236, 220.5, 159, 230, 52]) # 设置初始方向角
d = np.zeros((6, 6)) # 定义飞机间的初始相对距离矩阵
a0 = np.zeros((6, 6)) # 定义飞机间的相对碰撞角
b0 = np.zeros((6, 6)) # 定义飞机间的相对调整角
# 2. 计算目标函数及约束条件的相关超参数
# 2.1 根据初始位置计算飞机的初始距离矩阵
for i in range(6):
for j in range(6):
d[i ,j] = np.linalg.norm(xy0[i] - xy0[j]) # 根据公式(7)计算初始相对距离
d[d==0] = np.inf # 自己到自己的距离设置成无穷远
# 2.2 根据初始距离计算初始碰撞角(公式8)
# 角度 = 弧度 × (180/π)
# 弧度 = 角度 × (π/180)
a0 = np.arcsin(8.0/d)*180/np.pi # 以角度表示
# 2.3 使用复数形式计算飞机的初始方向角
xy1 = x0 + 1j*y0 # 将初始坐标位置转换为复数
xy2 = np.exp(1j*theta0*np.pi/180) # 将初始方向转换为复数
for m in range(6):
for n in range(6):
if n!= m:
b0[m,n] = np.angle((xy2[n] - xy2[m])/(xy1[m] - xy1[n]), deg=True) # 计算初始方向角,angle():返回复数的幅角主值,当deg=True时,返回角度
# 2.4 将初始碰撞角和初始调整角写入文件保存
with pd.ExcelWriter('../../Data/Project06/res_projct06.xlsx') as f:
pd.DataFrame(a0).to_excel(f, 'a0')
pd.DataFrame(b0).to_excel(f, 'b0')
# 3. 模型定义
# 3.1 目标函数
obj = lambda x:sum(np.abs(x)) # x 为\Delta \theta 序列,即6架飞机的调整角
# 3.2 约束条件
bd = [(-30,30) for i in range(6)] # 约束条件2:飞机调整边界约束,即theta0的取值范围,此处使用优化的边界进行定义
theta0 = 30*np.random.rand(6) # 使用随机初始化对调整变量进行初始化
cons = [] # 初始化约束不等式,即约束条件1
for i in range(5):
for j in range(i+1,6):
cons.append({'type':'ineq','fun':lambda x: np.abs(b0[i,j] + (x[i] + x[j])/2) - a0[i,j]}) # 约束类型为不等式ineq
# 3.3 模型求解
res = minimize(obj, theta0, constraints=cons, bounds=bd)
# 4. 结果输出
print(res)
print('最优值:', np.round(res.fun, 4))
print('最优解:', np.round(res.x, 4))
message: Optimization terminated successfully
success: True
status: 0
fun: 0.7909290038642947
x: [ 1.922e-06 -1.225e-07 6.312e-06 -4.362e-06 4.603e-01
3.307e-01]
nit: 34
jac: [ 1.000e+00 1.000e+00 1.000e+00 -1.000e+00 1.000e+00
1.000e+00]
nfev: 275
njev: 34
最优值: 0.7909
最优解: [ 0. -0. 0. -0. 0.4603 0.3307]
如上所示,最终可以获得的最优解为 。需要注意的是,这里获得的每架飞机的调整量并不是定值,而是根据实际优化情况进行变化,但最优值,即总调整角 是不变的。