Python GEKKO:Objective 函数未显示正确的结果

Python GEKKO: Objective function not showing correct results

我正在尝试优化推力推进系统的轨迹。控制变量是质量流率,最终的objective是最大化机器人的质量,最小化使用的推进量。轨迹类似于弹道,具有初始上升阶段和最终下降阶段。

我想我得到了一个很好的初始猜测,但是算法没有收敛。我检查了控制台中的输出,似乎 objective 函数没有正常工作,我认为这就是它没有收敛的原因。

这是我的代码

~# -*- coding: utf-8 -*-

from gekko import GEKKO
import numpy as np
import matplotlib.pyplot as plt

# create GEKKO model
m = GEKKO(remote=False)
# m = GEKKO()
print(m.path)               
g_0 = 9.81 

Isp = 46.28 

m_fb=1 
m_prop=0.1*m_fb 
m_dry=value=m_fb-m_prop

c = 0.8 
a = 0.4

g = 1.62 
R_moon = 1737.4e3 

J_y = 0.06666 


m_dot_up=2.5*m_fb*g/(Isp*g_0) 
m_dot_low=0*m_fb*g/(Isp*g_0) 


theta_0=70*np.pi/180 
v0= 3   
x_f=np.sin(2*theta_0)*v0*v0/g
v0_x= v0*np.cos(theta_0) 
y_max=v0*v0/(2*g)
m0=1
m1=(np.e**(-v0/(Isp*g_0)))    
time_burn= m_fb*(m0-m1)/m_dot_up
tf=2*v0*np.sin(theta_0)/g + 2*time_burn

t0=0
nr_intervals=30
step=tf/nr_intervals
t=np.linspace(t0, tf, nr_intervals) 
time_burn_node= min(range(len(t)), key=lambda i: abs(t[i]-time_burn))
m.time=t

m0=1
m1=(np.e**(-v0/(Isp*g_0)))    
time_burn= m_fb*(m0-m1)/m_dot_up 
time_burn_node= min(range(len(t)), key=lambda i: abs(t[i]-time_burn)) 

y_f=1
v_f_x = 1 
v_f_y=3 
gamma_f = -90*np.pi/180 
alpha_f= 180*np.pi/180
alpha_dot_f=5*np.pi/180 
parabola_profile= lambda x: -4*y_max/(x_f*x_f)*x*x+4*y_max*x/x_f  
velocity_profile_y=lambda t: (v0*np.sin(theta_0)-g*t) 
velocity_profile_x= lambda t: v0*np.cos(theta_0) 
velocity_profile=lambda t: np.sqrt(velocity_profile_x(t)*velocity_profile_x(t)+velocity_profile_y(t)*velocity_profile_y(t))

x2 = m.Var(value=[np.linspace(0,v0,time_burn_node+1)[int(i)] for i in np.linspace(0,time_burn_node,time_burn_node+1)]+[velocity_profile(i) for i in t[1:int(nr_intervals)-time_burn_node]],lb=0,ub=1e3) 

x3 = m.Var(value=[np.linspace(np.pi/2,theta_0,time_burn_node+1)[int(i)] for i in np.linspace(0,time_burn_node,time_burn_node+1)]+[np.arctan2(velocity_profile_y(i),velocity_profile_x(i)) for i in t[1:int(nr_intervals)-2*time_burn_node-1]]+[np.linspace(np.arctan2(velocity_profile_y(t[int(nr_intervals)-2*time_burn_node]),velocity_profile_x(t[int(nr_intervals)-2*time_burn_node])),-np.pi/2,time_burn_node+1)[int(i)] for i in np.linspace(0,time_burn_node,time_burn_node+1)],lb=-np.pi*2,ub=np.pi*2) 

x0 = m.Var(value=[np.trapz(np.multiply(x2.value[0:i],np.cos(x3.value[0:i])),x=t[0:i]) for i in range(0,int(nr_intervals))],lb=0,ub=x_f+1) 
x1 = m.Var(value=[np.trapz(np.multiply(x2.value[0:i],np.sin(x3.value[0:i])),x=t[0:i]) for i in range(0,int(nr_intervals))],lb=0,ub=x_f+15) 
x4 = m.Var(value=np.concatenate((np.zeros(int(nr_intervals/2)),np.pi*np.ones(int(nr_intervals)-int(nr_intervals/2)))),lb=-np.pi*2,ub=np.pi*2) 
cc= [x*180/3.1415 for x in x4.value]

x5 = m.Var(value=[(x4.value[i+1]-x4.value[i])/step for i in range(0,len(x4.value)-1)]+[(x4[-1]-x4[-2])/step],lb=-step/J_y,ub=step/J_y) 
x6 = m.Var(value=np.concatenate((np.linspace(m0,m1,time_burn_node),m1*np.ones(len(t)-time_burn_node))),lb=0,ub=m0) 

x2 = m.Var(value=[np.linspace(0,v0,time_burn_node+1)[int(i)] for i in np.linspace(0,time_burn_node,time_burn_node+1)]+[velocity_profile(i) for i in t[1:int(nr_intervals)-time_burn_node]],lb=0,ub=1e3)
x3 = m.Var(value=[np.linspace(np.pi/2,theta_0,time_burn_node+1)[int(i)] for i in np.linspace(0,time_burn_node,time_burn_node+1)]+[np.arctan2(velocity_profile_y(i),velocity_profile_x(i)) for i in t[1:int(nr_intervals)-2*time_burn_node-1]]+[np.linspace(np.arctan2(velocity_profile_y(t[int(nr_intervals)-2*time_burn_node]),velocity_profile_x(t[int(nr_intervals)-2*time_burn_node])),-np.pi/2,time_burn_node+1)[int(i)] for i in np.linspace(0,time_burn_node,time_burn_node+1)],lb=-np.pi*2,ub=np.pi*2) 
x0 = m.Var(value=[np.trapz(np.multiply(x2.value[0:i],np.cos(x3.value[0:i])),x=t[0:i]) for i in range(0,int(nr_intervals))],lb=0,ub=x_f+1) 
x1 = m.Var(value=[np.trapz(np.multiply(x2.value[0:i],np.sin(x3.value[0:i])),x=t[0:i]) for i in range(0,int(nr_intervals))],lb=0,ub=x_f+15) 
x4 = m.Var(value=np.concatenate((np.zeros(int(nr_intervals/2)),np.pi*np.ones(int(nr_intervals)-int(nr_intervals/2)))),lb=-np.pi*2,ub=np.pi*2) 
x5 = m.Var(value=[(x4.value[i+1]-x4.value[i])/step for i in range(0,len(x4.value)-1)]+[(x4[-1]-x4[-2])/step],lb=-step/J_y,ub=step/J_y) 
x6 = m.Var(value=np.concatenate((np.linspace(m0,m1,time_burn_node),m1*np.ones(len(t)-time_burn_node))),lb=0,ub=m0) 

m_dot= m.MV(value=np.concatenate((m_dot_up*np.ones(time_burn_node),np.zeros(len(t)-time_burn_node))),lb=m_dot_low,ub=m_dot_up)
m_dot.value=m_dot.value[0:len(x1.value)]

p = np.zeros(len(x1.value)) 
p[-1]=1
final = m.Param(value=p,lb=0,ub=1)
m.Equation(x0.dt()==x2*m.cos(x3))
m.Equation(x1.dt()==x2*m.sin(x3))
m.Equation(x6*x2.dt()==(Isp*m_dot*g_0)*m.cos(x4)-x6*g*m.sin(x3))
m.Equation(x6*x2*x3.dt()==x2**2*m.cos(x3)*x6/R_moon+(Isp*m_dot*g_0)*m.sin(x4)-x6*g*m.cos(x3))
m.Equation(x6.dt()==-m_dot)


m.fix_final(x0,x0.value[-1])
m.Equation(x1*final<=1)
m.Minimize((-x6*final))

m.options.MAX_ITER = 1000     # adjust maximum iterations
m.options.SOLVER = 3
m.options.IMODE = 6
m.options.NODES = 3

m.solve()
print(final.value)
print(f"Final Mass:     {x6.value[-1]:.3f} s")

这是控制台的输出,显示 objective 函数从负到正,这没有意义,因为最终质量始终为正

iter    objective    inf_pr   inf_du lg(mu)  ||d||  lg(rg) alpha_du alpha_pr  ls
  90r-8.8685190e-001 2.32e+000 9.25e+003   0.9 3.41e+001    -  1.18e-001 2.17e-002f  1
  91 -8.8659478e-001 3.03e+000 1.79e+005   0.3 5.68e+001    -  2.78e-001 1.27e-001f  2
  92 -8.8283316e-001 6.47e+000 2.86e+007   1.6 1.16e+002    -  9.91e-001 4.76e-002f  1
  93 1.8507003e+000 1.87e+001 1.55e+007   1.6 5.71e+003    -  2.41e-001 3.44e-001f  1
  94 1.8506996e+000 1.86e+001 1.16e+011   1.6 1.25e+001  10.6 6.17e-003 2.28e-003h  1
  95 1.8506996e+000 1.86e+001 1.16e+011   1.6 1.25e+001  10.1 5.75e-002 2.28e-005h  1

我还输出了最终矢量和最终质量的最终值,它们显示了正确的结果:

[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0]
Final Mass:     0.993 s

非常感谢您的任何建议

使用 m.open_folder() 检查模型文件并在文本编辑器中查看 gk0_model.apm 文件。

Model
Parameters
    p1<= 0.008920571233734825, >= 0.0
    p2<= 1, >= 0
End Parameters
Variables
    v1<= 1000.0, >= 0
    v2<= 6.283185307179586, >= -6.283185307179586
    v3<= 4.57104227603633, >= 0
    v4<= 18.57104227603633, >= 0
    v5<= 6.283185307179586, >= -6.283185307179586
    v6<= 2.478718169538892, >= -2.478718169538892
    v7<= 1, >= 0
    v8<= 1000.0, >= 0
    v9<= 6.283185307179586, >= -6.283185307179586
    v10<= 4.57104227603633, >= 0
    v11<= 18.57104227603633, >= 0
    v12<= 6.283185307179586, >= -6.283185307179586
    v13<= 2.478718169538892, >= -2.478718169538892
    v14<= 1, >= 0
End Variables
Equations
    $v10=((v8)*(cos(v9)))
    $v11=((v8)*(sin(v9)))
    ((v14)*($v8))=(((((((46.28)*(p1)))*(9.81)))*(cos(v12)))-((((v14)*(1.62)))*(sin(v9))))
    ((((v14)*(v8)))*($v9))=((((((((((v8)^(2)))*(cos(v9))))*(v14)))/(1737400.0))+((((((46.28)*(p1)))*(9.81)))*(sin(v12))))-((((v14)*(1.62)))*(cos(v9))))
    $v14=(-p1)
    ((v11)*(p2))<=1
    minimize (((-v14))*(p2))
End Equations
Connections
    p(end).n(end).v10=4.25390890270255
    p(end).n(end).v10=fixed
End Connections

End Model

objective 是 minimize (((-v14))*(p2)) 并且求解器不受限于采用可行的搜索路径达到最优。 v14 的值可能在正值和负值之间波动。

初始化和软化约束有助于找到解决方案。尝试用软约束替换硬终端约束,例如:

#m.fix_final(x0,x0.value[-1])
#m.Equation(x1*final<=1)
m.Minimize(final*(x0-x0.value[-1])**2)
m.Minimize(final*(x1-1)**2)

另一种策略是首先求解一个可行的解决方案,其中决策变量固定在合理的值。设置 m.options.COLDSTART=1 关闭 MV 以找到可行的解决方案。

# -*- coding: utf-8 -*-

from gekko import GEKKO
import numpy as np
import matplotlib.pyplot as plt

# create GEKKO model
m = GEKKO(remote=False)
# m = GEKKO()
print(m.path)               
g_0 = 9.81 

Isp = 46.28 

m_fb=1 
m_prop=0.1*m_fb 
m_dry=value=m_fb-m_prop

c = 0.8 
a = 0.4

g = 1.62 
R_moon = 1737.4e3 

J_y = 0.06666 


m_dot_up=2.5*m_fb*g/(Isp*g_0) 
m_dot_low=0*m_fb*g/(Isp*g_0) 


theta_0=70*np.pi/180 
v0= 3   
x_f=np.sin(2*theta_0)*v0*v0/g
v0_x= v0*np.cos(theta_0) 
y_max=v0*v0/(2*g)
m0=1
m1=(np.e**(-v0/(Isp*g_0)))    
time_burn= m_fb*(m0-m1)/m_dot_up
tf=2*v0*np.sin(theta_0)/g + 2*time_burn

t0=0
nr_intervals=30
step=tf/nr_intervals
t=np.linspace(t0, tf, nr_intervals) 
time_burn_node= min(range(len(t)), key=lambda i: abs(t[i]-time_burn))
m.time=t

m0=1
m1=(np.e**(-v0/(Isp*g_0)))    
time_burn= m_fb*(m0-m1)/m_dot_up 
time_burn_node= min(range(len(t)), key=lambda i: abs(t[i]-time_burn)) 

y_f=1
v_f_x = 1 
v_f_y=3 
gamma_f = -90*np.pi/180 
alpha_f= 180*np.pi/180
alpha_dot_f=5*np.pi/180 
parabola_profile= lambda x: -4*y_max/(x_f*x_f)*x*x+4*y_max*x/x_f  
velocity_profile_y=lambda t: (v0*np.sin(theta_0)-g*t) 
velocity_profile_x= lambda t: v0*np.cos(theta_0) 
velocity_profile=lambda t: np.sqrt(velocity_profile_x(t)*velocity_profile_x(t)+velocity_profile_y(t)*velocity_profile_y(t))

x2 = m.Var(value=[np.linspace(0,v0,time_burn_node+1)[int(i)] for i in np.linspace(0,time_burn_node,time_burn_node+1)]+[velocity_profile(i) for i in t[1:int(nr_intervals)-time_burn_node]],lb=0,ub=1e3) 

x3 = m.Var(value=[np.linspace(np.pi/2,theta_0,time_burn_node+1)[int(i)] for i in np.linspace(0,time_burn_node,time_burn_node+1)]+[np.arctan2(velocity_profile_y(i),velocity_profile_x(i)) for i in t[1:int(nr_intervals)-2*time_burn_node-1]]+[np.linspace(np.arctan2(velocity_profile_y(t[int(nr_intervals)-2*time_burn_node]),velocity_profile_x(t[int(nr_intervals)-2*time_burn_node])),-np.pi/2,time_burn_node+1)[int(i)] for i in np.linspace(0,time_burn_node,time_burn_node+1)],lb=-np.pi*2,ub=np.pi*2) 

x0 = m.Var(value=[np.trapz(np.multiply(x2.value[0:i],np.cos(x3.value[0:i])),x=t[0:i]) for i in range(0,int(nr_intervals))],lb=0,ub=x_f+1) 
x1 = m.Var(value=[np.trapz(np.multiply(x2.value[0:i],np.sin(x3.value[0:i])),x=t[0:i]) for i in range(0,int(nr_intervals))],lb=0,ub=x_f+15) 
x4 = m.Var(value=np.concatenate((np.zeros(int(nr_intervals/2)),np.pi*np.ones(int(nr_intervals)-int(nr_intervals/2)))),lb=-np.pi*2,ub=np.pi*2) 
cc= [x*180/3.1415 for x in x4.value]

x5 = m.Var(value=[(x4.value[i+1]-x4.value[i])/step for i in range(0,len(x4.value)-1)]+[(x4[-1]-x4[-2])/step],lb=-step/J_y,ub=step/J_y) 
x6 = m.Var(value=np.concatenate((np.linspace(m0,m1,time_burn_node),m1*np.ones(len(t)-time_burn_node))),lb=0,ub=m0) 

x2 = m.Var(value=[np.linspace(0,v0,time_burn_node+1)[int(i)] for i in np.linspace(0,time_burn_node,time_burn_node+1)]+[velocity_profile(i) for i in t[1:int(nr_intervals)-time_burn_node]],lb=0,ub=1e3)
x3 = m.Var(value=[np.linspace(np.pi/2,theta_0,time_burn_node+1)[int(i)] for i in np.linspace(0,time_burn_node,time_burn_node+1)]+[np.arctan2(velocity_profile_y(i),velocity_profile_x(i)) for i in t[1:int(nr_intervals)-2*time_burn_node-1]]+[np.linspace(np.arctan2(velocity_profile_y(t[int(nr_intervals)-2*time_burn_node]),velocity_profile_x(t[int(nr_intervals)-2*time_burn_node])),-np.pi/2,time_burn_node+1)[int(i)] for i in np.linspace(0,time_burn_node,time_burn_node+1)],lb=-np.pi*2,ub=np.pi*2) 
x0 = m.Var(value=[np.trapz(np.multiply(x2.value[0:i],np.cos(x3.value[0:i])),x=t[0:i]) for i in range(0,int(nr_intervals))],lb=0,ub=x_f+1) 
x1 = m.Var(value=[np.trapz(np.multiply(x2.value[0:i],np.sin(x3.value[0:i])),x=t[0:i]) for i in range(0,int(nr_intervals))],lb=0,ub=x_f+15) 
x4 = m.Var(value=np.concatenate((np.zeros(int(nr_intervals/2)),np.pi*np.ones(int(nr_intervals)-int(nr_intervals/2)))),lb=-np.pi*2,ub=np.pi*2) 
x5 = m.Var(value=[(x4.value[i+1]-x4.value[i])/step for i in range(0,len(x4.value)-1)]+[(x4[-1]-x4[-2])/step],lb=-step/J_y,ub=step/J_y) 
x6 = m.Var(value=np.concatenate((np.linspace(m0,m1,time_burn_node),m1*np.ones(len(t)-time_burn_node))),lb=0,ub=m0) 

m_dot= m.MV(value=np.concatenate((m_dot_up*np.ones(time_burn_node),np.zeros(len(t)-time_burn_node))),lb=m_dot_low,ub=m_dot_up)
m_dot.value=m_dot.value[0:len(x1.value)]

p = np.zeros(len(x1.value)) 
p[-1]=1
final = m.Param(value=p,lb=0,ub=1)
m.Equation(x0.dt()==x2*m.cos(x3))
m.Equation(x1.dt()==x2*m.sin(x3))
m.Equation(x6*x2.dt()==(Isp*m_dot*g_0)*m.cos(x4)-x6*g*m.sin(x3))
m.Equation(x6*x2*x3.dt()==x2**2*m.cos(x3)*x6/R_moon+(Isp*m_dot*g_0)*m.sin(x4)-x6*g*m.cos(x3))
m.Equation(x6.dt()==-m_dot)

#m.fix_final(x0,x0.value[-1])
#m.Equation(x1*final<=1)
m.Minimize(final*(x0-x0.value[-1])**2)
m.Minimize(final*(x1-1)**2)
m.Minimize((-x6*final))

m.options.MAX_ITER = 2000     # adjust maximum iterations
m.options.SOLVER = 2
m.options.IMODE = 6
m.options.NODES = 3

m.open_folder()

m.options.COLDSTART = 1
m.solve()
print(final.value)
print(f"Final Mass:     {x6.value[-1]:.3f} s")

这给出了一个错误:

 Number of state variables:           1247
 Number of total equations: -          725
 Number of slack variables: -            0
 ---------------------------------------
 Degrees of freedom       :            522
 
 @error: Degrees of Freedom
 * Error: DOF must be zero for this mode
 STOPPING...
Traceback (most recent call last):
  File "C:\Users\johnh\Desktop\test.py", line 111, in <module>
    m.solve()
  File "C:\Users\johnh\Python39\lib\site-packages\gekko\gekko.py", line 2185, in solve
    raise Exception(response)
Exception:  @error: Degrees of Freedom
 * Error: DOF must be zero for this mode
 STOPPING...

尝试删除任何约束并确定每个变量与约束的配对。如果自由度为零(相同数量的方程和变量),则求解器有时可以找到可以随后优化的初始解。