模型算法推导
比例导引是一种制导算法,其经典程度相当于控制器中的PID,在本文中,只对其二维平面的情况做分析,考虑一个拦截弹拦截机动目标(固定目标相当于目标速度为0),其运动如下图所示:
M i M_i Mi 和 T T T 代表第 i i i 枚拦截弹和目标;连线 M T MT MT 称为拦截弹 M i M_i Mi 与目标 T T T 的视线; r i r_i ri 为弹目间的相对距离, r i r_i ri 关于时间的导数表示为 r ˙ i \dot r_i r˙i, q i q_i qi 为拦截弹 M i M_i Mi 视线角,其关于时间的导数为 q ˙ i \dot q_i q˙i , V M , V T V_M,V_T VM,VT分别表示拦截弹和目标的速度。 θ i \theta_i θi 为弹目间速度方向与水平线的夹角,称为速度方向角。拦截弹和目标的前置角分别为 ψ i = q i − θ i \psi_i = q_i - \theta_i ψi=qi−θi。
考虑平面内拦截单一机动目标的制导问题,拦截弹与目标的相对运动关系可以用如下微分方程表示
r
˙
i
=
V
T
cos
(
q
i
−
θ
T
)
−
V
M
i
cos
(
q
i
−
θ
M
i
)
r
i
q
˙
i
=
−
V
T
sin
(
q
i
−
θ
T
)
−
V
M
i
sin
(
q
i
−
θ
M
i
)
\begin{align} {\dot{r}}_{i} &= V_{T}\cos\left(q_{i}-\theta_{T}\right)-V_{Mi}\cos\left(q_{i}-\theta_{Mi}\right) \\ r_i {\dot{q}}_{i} &= - V_{T}\sin\left(q_{i}-\theta_{T}\right)-V_{Mi}\sin\left(q_{i}-\theta_{Mi}\right) \\ \end{align}
r˙iriq˙i=VTcos(qi−θT)−VMicos(qi−θMi)=−VTsin(qi−θT)−VMisin(qi−θMi)
拦截弹与目标的自身位置机动信息可以用如下微分方程表示
{
x
˙
i
=
V
M
i
cos
(
θ
M
i
)
y
˙
i
=
V
M
i
sin
(
θ
M
i
)
θ
˙
M
i
=
a
M
i
V
M
i
\begin{cases} \dot x_i = V_{Mi} \cos(\theta_{Mi}) \\ \dot y_i = V_{Mi} \sin(\theta_{Mi}) \\ \dot \theta_{Mi} = \frac{a_{Mi}}{V_{Mi}} \\ \end{cases}
⎩
⎨
⎧x˙i=VMicos(θMi)y˙i=VMisin(θMi)θ˙Mi=VMiaMi
{
x
˙
i
=
V
T
cos
(
θ
T
)
y
˙
i
=
V
T
sin
(
θ
T
)
θ
˙
T
=
a
T
V
T
\begin{cases} \dot x_i = V_{T} \cos(\theta_{T}) \\ \dot y_i = V_{T} \sin(\theta_{T}) \\ \dot \theta_{T} = \frac{a_{T}}{V_{T}} \\ \end{cases}
⎩
⎨
⎧x˙i=VTcos(θT)y˙i=VTsin(θT)θ˙T=VTaT
比例导引法即使拦截弹速度矢量的旋转角速度与目标线旋转角速度成正比的一种导引方法,这样就可以保证拦截弹拦截到目标,即
a
M
i
=
N
V
M
i
q
˙
i
a_{Mi} = N V_{Mi} \dot q_i
aMi=NVMiq˙i
其中N为比例导引系数,一般为2-6之间的一个值,选值应该在
1
<
N
<
∞
1<N<\infty
1<N<∞,比例导引法的弹道特性介于追踪法和平行接近法之间,比例系数越大,弹道越平直,需用法向过载越小。
python代码
我们这里考虑用python实现,并不是matlab,其一python的结构更加清晰一点,其二后期可能考虑用一些数据驱动的方法用在制导上,用python会更方便。程序的github地址为 https://github.com/professorli123/PNG.git
程序效果图,其中蓝线为拦截弹,黄线为目标
main程序是文件的主程序
model程序定义了拦截弹模型,以及中间的视线角距离的计算模型
settings程序定义了一些配置
如果只是使用本程序,只需要考虑修改main程序和settings程序
main.py
# -*- encoding: utf-8 -*-
"""
@File : main.py
@Contact : lyajpunov@hust.edu.cn
@Author : Li Yajun
@Time : 2023/9/21 9:11
@Version : 1.0
@Desciption :
"""
from model import missile, battle
import matplotlib.pyplot as plt
from settings import length
M0 = missile(0)
T0 = missile('T')
B = battle(M0, T0)
if __name__ == '__main__':
for i in range(length - 10):
a = 4 * M0.get_v() * B.get_dtheta_l()
M0.step(a)
T0.step(1)
B.step()
if B.get_r() < 1:
print(B.get_r())
break
plt.figure(1)
plt.plot(M0.x[:M0.pos], M0.y[:M0.pos])
plt.plot(T0.x[:T0.pos], T0.y[:T0.pos])
plt.show()
settings.py
# -*- encoding: utf-8 -*-
"""
@File : settings.py
@Contact : lyajpunov@hust.edu.cn
@Author : Li Yajun
@Time : 2023/9/21 9:15
@Version : 1.0
@Desciption :
"""
# 最长仿真时间
t = 100
# 仿真步长
dt = 1e-3
# 预留数组长度
length = int(t / dt)
data = {
0: {
'x': 0,
'y': 0,
'v': 300,
'theta': 0,
},
'T': {
'x': 10000,
'y': 10000,
'v': 50,
'theta': 0,
},
}
model.py
# -*- encoding: utf-8 -*-
"""
@File : model.py
@Contact : lyajpunov@hust.edu.cn
@Author : Li Yajun
@Time : 2023/9/21 9:11
@Version : 1.0
@Desciption :
"""
import numpy as np
from settings import dt, length, data
class missile:
def __init__(self, name):
self.pos = 0
self.name = name
self.length = length
# x轴位置
self.x = np.zeros(self.length, dtype='float64')
# y轴位置
self.y = np.zeros(self.length, dtype='float64')
# 速度
self.v = np.zeros(self.length, dtype='float64')
# 弹道倾角
self.theta = np.zeros(self.length, dtype='float64')
# 控制量:加速度
self.a = np.zeros(self.length, dtype='float64')
# 停止标志位
self.end = False
# 初始化,重置
self.reset()
def reset(self):
self.pos = 0
self.x[self.pos] = data[self.name]['x']
self.y[self.pos] = data[self.name]['y']
self.v[self.pos] = data[self.name]['v']
self.theta[self.pos] = np.deg2rad(data[self.name]['theta'])
def step(self, a):
# 如果已经结束直接返回
if self.end:
return
# 保存控制量
self.a[self.pos] = a
# 形成向量进行迭代
vector = np.array([self.x[self.pos],
self.y[self.pos],
self.v[self.pos],
self.theta[self.pos],
self.a[self.pos], ])
k1 = dt * self.iterateOnce(vector)
k2 = dt * self.iterateOnce(vector + 0.5 * k1)
k3 = dt * self.iterateOnce(vector + 0.5 * k2)
k4 = dt * self.iterateOnce(vector + k3)
vector = vector + (k1 + 2 * k2 + 2 * k3 + k4) / 6
# 保存迭代数据
self.pos += 1
self.x[self.pos] = vector[0]
self.y[self.pos] = vector[1]
self.v[self.pos] = vector[2]
self.theta[self.pos] = vector[3]
def iterateOnce(self, vector):
x, y, v, theta, a = vector[0], vector[1], vector[2], vector[3], vector[4]
_x = v * np.cos(theta)
_y = v * np.sin(theta)
_v = 0
_theta = a / v
_a = 0
return np.array([_x, _y, _v, _theta, _a])
'''以下为接口函数'''
def get_x(self):
return self.x[self.pos]
def get_y(self):
return self.y[self.pos]
def get_v(self):
return self.v[self.pos]
def get_a(self):
return self.a[self.pos]
def get_theta(self):
return self.theta[self.pos]
class battle:
def __init__(self, M, T):
self.pos = 0
self.M = M
self.T = T
self.length = length
# 距离及其导数
self.r = np.zeros(self.length, dtype='float64')
self.dr = np.zeros(self.length, dtype='float64')
# 视线倾角及其导数
self.theta_l = np.zeros(self.length, dtype='float64')
self.dtheta_l = np.zeros(self.length, dtype='float64')
# 初始化
self.reset()
def reset(self):
self.pos = 0
d_x = self.T.get_x() - self.M.get_x()
d_y = self.T.get_y() - self.M.get_y()
d_vx = self.T.get_v() * np.cos(self.T.get_theta()) - self.M.get_v() * np.cos(self.M.get_theta())
d_vy = self.T.get_v() * np.sin(self.T.get_theta()) - self.M.get_v() * np.sin(self.M.get_theta())
r = np.sqrt(d_x * d_x + d_y * d_y)
d_r = (d_x * d_vx + d_y * d_vy) / r
theta_l = np.arctan(d_y / d_x)
dtheta_l = d_x * d_x * (d_vy * d_x - d_vx * d_y) / (r * r) / (d_vx * d_vx)
self.r[self.pos] = r
self.dr[self.pos] = d_r
self.theta_l[self.pos] = theta_l
self.dtheta_l[self.pos] = dtheta_l
def step(self):
self.pos += 1
d_x = self.T.get_x() - self.M.get_x()
d_y = self.T.get_y() - self.M.get_y()
d_vx = self.T.get_v() * np.cos(self.T.get_theta()) - self.M.get_v() * np.cos(self.M.get_theta())
d_vy = self.T.get_v() * np.sin(self.T.get_theta()) - self.M.get_v() * np.sin(self.M.get_theta())
r = np.sqrt(d_x * d_x + d_y * d_y)
d_r = (d_x * d_vx + d_y * d_vy) / r
theta_l = np.arctan(d_y / d_x)
dtheta_l = d_x * d_x * (d_vy * d_x - d_vx * d_y) / (r * r) / (d_vx * d_vx)
self.r[self.pos] = r
self.dr[self.pos] = d_r
self.theta_l[self.pos] = theta_l
self.dtheta_l[self.pos] = dtheta_l
'''下面是接口函数'''
def get_r(self):
return self.r[self.pos]
def get_dr(self):
return self.dr[self.pos]
def get_theta_l(self):
return self.theta_l[self.pos]
def get_dtheta_l(self):
return self.dtheta_l[self.pos]