I am trying to solve the following Optimal Control Problem in GEKKO:
I know a priori that the path for c is:
where the parameter values are: r = 0.33, i = 0.5, K(0) = 10 and T = 10.
I wrote the following code in Python:
import numpy as np
import matplotlib.pyplot as plt
from gekko import GEKKO
m = GEKKO(remote=True)
nt = 101; m.time = np.linspace(0,10,nt)
r = 0.33
i = 0.5
# Variables
c = m.Var()
k = m.Var(value=10)
objective = m.Var()
rate = [-r*t/10 for t in range(0, 101)]
factor = m.exp(rate)
p = np.zeros(nt)
p[-1] = 1.0
final = m.Param(value=p)
disc = m.Param(value=factor)
# Equations
m.Equation(k.dt() == i*k - c)
m.Equation(objective.dt() == disc*m.log(c))
# Objective Function
m.Maximize(final*objective)
m.options.IMODE = 6
m.solve()
plt.figure(1)
plt.plot(m.time,c.value,'k:',LineWidth=2,label=r'$C$')
plt.plot(m.time,k.value,'b-',LineWidth=2,label=r'$K$')
plt.legend(loc='best')
plt.xlabel('Time')
plt.ylabel('Value')
plt.show()
The solved path for c and k is as shown below:
This clearly is not right because c should be increasing with time from just eye-balling the solution given before hand.
I'm not sure where I am wrong.
The optimal control problem as it is currently written is unbounded. The value of c will go to infinity to maximize the function. I set an upper bound of 100 on c and the solver went to that bound. I reformulated the model to reflect the current problem statement. Here are a few suggestions:
Use the m.integral() function to make the model more readable.
Initialize c at a value other than 0 (default). You may also want to set a lower bound with c>0.01 so that m.log(c) is not undefined if the solver tries a value <0.
Only use Gekko functions inside Gekko equations such as with factor = m.exp(rate). Use factor = np.exp(rate) instead unless it is in a Gekko equation where it can be evaluated.
Include a plot of the exact solution so that you can compare the exact and numerical solution.
Use m.options.NODES=3 with c=m.MV() and c.STATUS=1 to increase the solution accuracy. The default is m.options.NODES=2 that isn't as accurate.
You can free the initial condition with m.free_initial(c) to calculate the initial value of c.
import numpy as np
import matplotlib.pyplot as plt
from gekko import GEKKO
m = GEKKO(remote=True)
nt = 101; m.time = np.linspace(0,10,nt)
r = 0.33
i = 0.5
# Variables
c = m.MV(4,lb=0.01,ub=100); c.STATUS=1
#m.free_initial(c)
k = m.Var(value=10)
objective = m.Var(0)
t = m.Param(m.time)
m.Equation(objective==m.exp(-r*t)*m.log(c))
# just to include on the plot
iobj = m.Intermediate(m.integral(objective))
p = np.zeros(nt)
p[-1] = 1.0
final = m.Param(value=p)
# Equations
m.Equation(k.dt() == i*k - c)
# Objective Function
m.Maximize(final*m.integral(objective))
m.options.IMODE = 6
m.solve()
plt.figure(1)
plt.subplot(3,1,1)
plt.plot(m.time,c.value,'k:',linewidth=2,label=r'$C_{gekko}$')
C_sol = r*10*np.exp((i-r)*m.time)/(1-np.exp(-r*10))
plt.plot(m.time,C_sol,'r--',linewidth=2,label=r'$C_{exact}$')
plt.ylabel('Value'); plt.legend(loc='best')
plt.subplot(3,1,2)
plt.plot(m.time,k.value,'b-',linewidth=2,label=r'$K$')
plt.legend(loc='best')
plt.subplot(3,1,3)
plt.plot(m.time,objective.value,'g:',linewidth=2,label=r'$obj$')
plt.plot(m.time,iobj.value,'k',linewidth=2,label=r'$\int obj$')
plt.legend(loc='best')
plt.xlabel('Time')
plt.show()
Is there additional information that this problem is missing?
Edit: Added additional constraint k>0.
Added additional constraint as suggested in the comment. There is a small difference at the end from the exact solution because the last c value does not appear to influence the solution.
import numpy as np
import matplotlib.pyplot as plt
from gekko import GEKKO
m = GEKKO(remote=True)
nt = 101; m.time = np.linspace(0,10,nt)
r = 0.33
i = 0.5
# Variables
c = m.MV(4,lb=0.001,ub=100); c.STATUS=1; c.DCOST=1e-6
m.free_initial(c)
k = m.Var(value=10,lb=0)
objective = m.Var(0)
t = m.Param(m.time)
m.Equation(objective==m.exp(-r*t)*m.log(c))
# just to include on the plot
iobj = m.Intermediate(m.integral(objective))
p = np.zeros(nt)
p[-1] = 1.0
final = m.Param(value=p)
# Equations
m.Equation(k.dt() == i*k - c)
# Objective Function
m.Maximize(final*m.integral(objective))
m.options.IMODE = 6
m.options.NODES = 3
m.solve()
plt.figure(1)
plt.subplot(3,1,1)
plt.plot(m.time,c.value,'k:',linewidth=2,label=r'$C_{gekko}$')
C_sol = r*10*np.exp((i-r)*m.time)/(1-np.exp(-r*10))
plt.plot(m.time,C_sol,'r--',linewidth=2,label=r'$C_{exact}$')
plt.ylabel('Value'); plt.legend(loc='best')
plt.subplot(3,1,2)
plt.plot(m.time,k.value,'b-',linewidth=2,label=r'$K$')
plt.legend(loc='best')
plt.subplot(3,1,3)
plt.plot(m.time,objective.value,'g:',linewidth=2,label=r'$obj$')
plt.plot(m.time,iobj.value,'k',linewidth=2,label=r'$\int obj$')
plt.legend(loc='best')
plt.xlabel('Time')
plt.show()
Related
from gekko import GEKKO
import numpy as np
import matplotlib.pyplot as plt
m = GEKKO() # initialize gekko
nt=200
m.time = np.linspace(0,1,nt)
# Variables
x1 = m.Var(value=20,lb=0,)
x2 = m.Var(value=0,lb=0,)
x3 = m.Var(value=0,lb=0,)
p = np.zeros(nt) # mark final time point
p[-1] = 1.0
# optimize final time
tf = m.FV(value=0.7,lb=0.001,ub=100.0)
tf.STATUS = 1
# Equations
m.Equation(x1.dt() == -5*x1*tf)
m.Equation(x2.dt() == (5* x1 - 0.5*x2)*tf)
m.Equation(x3.dt() == 0.5*x2*tf)
#Minifunc
#m.Minimize(tf)
m.Maximize(x2)
#options
m.options.IMODE = 6
m.options.SOLVER = 3 #IPOPT optimizer
m.options.RTOL = 1E-8
m.options.OTOL = 1E-8
m.options.NODES = 5
m.solve(disp=True,debug=1)
print('Final Time: ' + str(tf.value[0]))
#Plotting stuff
tm = np.linspace(0,tf.value[0],nt)
plt.figure(1) # plot results
plt.plot(tm, x1.value, "k-", label=r"$x_1$")
plt.plot(tm, x2.value, "b-", label=r"$x_2$")
plt.plot(tm, x3.value, "r--", label=r"$x_3$")
plt.legend(loc="best")
plt.xlabel("Time")
plt.ylabel("Value")
plt.show()
I am trying to get used to Gekko as an optimization tool for some kinetic data. Therefore, I tried to implement a simple ode system based on an example of the Gekko website. Nevertheless, it never found the global maximum unless I force the derivative to be dx2/dt>0 to get the right maximum. This is somewhat unsatisfying, as I would expect a solver to find the maximum of component x2 just by asking to find the Maximum via m.Maximize(x2) for such a simple system. Is there any setting I did wrong or is my solution badly proposed? Thanks for helping.
The result: shows clearly that the maximum is not found...
The unconstrained objective (without the constraint dx2dt>=0) is 10416.8 while the objective with the constraint is 9236.4 (worse). Additional constraints never improve the optimization result but can make the objective worse for convex optimization problems such as this one.
m.Equation(x2.dt() >= 0)
Here is the code that shows the results of adding the constraint:
import numpy as np
from gekko import GEKKO
import matplotlib.pyplot as plt
m = GEKKO() # initialize gekko
nt=200
m.time = np.linspace(0,1,nt)
# Variables
x1 = m.Var(value=20,lb=0,)
x2 = m.Var(value=0,lb=0,)
x3 = m.Var(value=0,lb=0,)
p = np.zeros(nt) # mark final time point
p[-1] = 1.0
# optimize final time
tf = m.FV(value=0.7,lb=0.001,ub=100.0)
tf.STATUS = 1
# Equations
m.Equation(x1.dt() == -5*x1*tf)
m.Equation(x2.dt() == (5* x1 - 0.5*x2)*tf)
m.Equation(x3.dt() == 0.5*x2*tf)
#Minifunc
#m.Minimize(tf)
m.Maximize(x2)
#options
m.options.IMODE = 6
m.options.SOLVER = 1 #APOPT optimizer
m.options.RTOL = 1E-8
m.options.OTOL = 1E-8
m.options.NODES = 5
m.solve(disp=True,debug=1)
plt.figure(1)
tm = np.linspace(0,tf.value[0],nt)
plt.plot(tm, x2.value, "b:", label=r"$x_2$ Optimal")
# add constraint
m.Equation(x2.dt() >= 0)
m.options.TIME_SHIFT=0
m.solve(disp=True,debug=1)
tm = np.linspace(0,tf.value[0],nt)
plt.plot(tm, x2.value, "r--", label=r"$x_2$ Constrained")
plt.legend(loc="best"); plt.xlabel("Time"); plt.ylabel("Value")
print('Final Time: ' + str(tf.value[0]))
plt.figure(2) # plot results
plt.plot(tm, x1.value, "k-", label=r"$x_1$")
plt.plot(tm, x2.value, "b-", label=r"$x_2$")
plt.plot(tm, x3.value, "r--", label=r"$x_3$")
plt.legend(loc="best"); plt.xlabel("Time"); plt.ylabel("Value")
plt.show()
The current objective statement is to maximize the value of x2 everywhere.
m.Maximize(x2)
Even though there is a maximum part-way through the simulation, the final objective is the summation of all the x2 values. Make the following changes to maximize only the final x2 point.
p = np.zeros(nt) # mark final time point
p[-1] = 1.0
final = m.Param(p)
m.Maximize(x2*final)
The optimal solution is now just the final point for x2 that is 15.485.
I'm trying to reach a final condition subject to many Differential and Algebraic Equations. There is the option to change parameters in the model or adjust the initial condition to meet the final target. It is easy to do this with an adjustable parameter, but how can the final condition be met with an adjustable initial condition in Python Gekko?
import numpy as np
from gekko import GEKKO
import matplotlib.pyplot as plt
# simulate
m = GEKKO() # create GEKKO model
k = m.Param(0.1) # constant
y = m.Var(5.0) # create GEKKO variable
m.Equation(y.dt()==-k*y) # create GEKKO equation
m.time = np.linspace(0,20) # time points
m.options.IMODE = 4
m.solve(disp=False)
plt.plot(m.time,y,'r-',linewidth=2,label='k=0.1')
# adjust k to meet target
m = GEKKO() # create GEKKO model
k = m.FV(0.1); k.STATUS=1 # adjustable parameter
y = m.Var(5.0)
m.Equation(y.dt()==-k*y)
m.time = np.linspace(0,20,50)
m.options.IMODE = 6
p = np.zeros(50); p[-1]=1
final = m.Param(p)
m.Minimize(final*(y-1)**2)
m.solve(disp=False)
kv = k.value[0]
plt.plot(m.time,y,'k:',linewidth=2,label='k='+str(kv))
plt.plot([20],[1],'bo',markersize=5,label='target')
plt.xlabel('time')
plt.ylabel('y(t)')
plt.legend()
plt.show()
Set fixed_initial=False to calculate the initial condition as shown in the Model Building Gekko documentation.
y = m.Var(5.0,fixed_initial=False)
Additional details are in the Gekko Documentation about m.free_initial(y) as a function if the variable needs to be fixed or calculated after it is defined.
import numpy as np
from gekko import GEKKO
import matplotlib.pyplot as plt
# simulate
m = GEKKO() # create GEKKO model
k = m.Param(0.1) # constant
y = m.Var(5.0) # create GEKKO variable
m.Equation(y.dt()==-k*y) # create GEKKO equation
m.time = np.linspace(0,20) # time points
m.options.IMODE = 4
m.solve(disp=False)
plt.plot(m.time,y,'r-',linewidth=2,label='IC=5')
# adjust initial condition to meet target
m = GEKKO() # create GEKKO model
y = m.Var(5.0,fixed_initial=False)
k = 0.1
m.Equation(y.dt()==-k*y)
m.time = np.linspace(0,20,50)
m.options.IMODE = 6
p = np.zeros(50); p[-1]=1
final = m.Param(p)
m.Minimize(final*(y-1)**2)
m.solve(disp=False)
plt.plot(m.time,y,'k:',linewidth=2,label='IC='+str(y.value[0]))
plt.plot([20],[1],'bo',markersize=5,label='target')
plt.xlabel('time')
plt.ylabel('y(t)')
plt.legend()
plt.show()
I'm trying to implement a receding horizon control (RHC) scheme using GEKKO in Python, and I'd like to check my formulation. The goal is to solve the OCP over some horizon from t=tk to t=tk+H-1, apply the control solution at tk, and discard the remaining values (u_k+1 to u_k+H-1). The following code appears to give the correct solution, but I want to verify I've used the correct functions in GEKKO, namely when "resetting" the states for the next horizon. I had a few issues trying to use the .VALUE function to reset x1 and x2, e.g. TypeError: 'float' object is not subscriptable.
import numpy as np
import matplotlib.pylab as plt
from gekko import GEKKO
if __name__ == '__main__':
# Instantiate GEKKO
m = GEKKO()
# Constants
nRHC = 21
tRHC = 2
m.time = np.linspace(0, tRHC, nRHC)
# Control
u = m.MV(value=0.0,fixed_initial=False)
u.STATUS = 1
u.DCOST = 0
# Vars
t = m.SV(value=0)
x1 = m.SV(value=1)
x2 = m.SV(value=0)
# Equations
m.Equation(t.dt() == 1)
m.Equation(x1.dt() == x2)
m.Equation(x2.dt() == (1 - x2*x2)*x1 - x2 + u)
# Objective Function
m.Minimize(10*x1**2 + 10*x2**2 + u**2)
# Solve RHC
m.options.IMODE = 6
m.options.NODES = 11
m.options.MV_TYPE = 2
m.options.SOLVER = 3
nTotal = 101
tTotal = np.linspace(0, 10, nTotal)
uStore = np.zeros((1,nTotal))
xStore = np.zeros((2,nTotal))
xStore[:,0] = [1, 0]
for i in range(nTotal):
print('Solving Step: ', i+1, ' of ', nTotal-1)
if i == nTotal-1:
break
# Solve MPC over horizon
m.solve(disp=False)
# Update States
t.VALUE = t[1]
x1.MEAS = x1[1]
x2.MEAS = x2[1]
# Store
uStore[:,i] = u.NEWVAL
xStore[:,i+1] = np.array([x1[1], x2[1]])
# Plot States
f1, axs = plt.subplots(2)
axs[0].plot(tTotal, xStore[0,:])
axs[0].set_ylabel('x')
axs[0].grid()
axs[1].plot(tTotal, xStore[1,:])
axs[1].set_ylabel('x_dot')
axs[1].set_xlabel('time')
axs[1].grid()
# Show Plots
plt.show()
Thank you!
There is no need to update the states because Gekko does this automatically.
# Update States
t.VALUE = t[1]
x1.MEAS = x1[1]
x2.MEAS = x2[1]
The state values are stored in run directory files (see m.path or open with m.open_folder()). The file is ctl.t0. At the next command m.solve(), that file is imported and time shifted to make the values at the next time step the initial conditions. The time shift is adjusted with m.options.TIME_SHIFT=1 (1 is the default). If you do want to override the initial condition, use x1.MEAS=x1.value[1] or x1.value=x1.value[1].
I am starting to learn Gekko and I am testing optimal control problems. I am trying to solve the following optimal control problem with Gekko
The solution of this problem is (x_1(t) = (t-2)^2 - 2)
How to build the constraint x(0) + x(2) = 0?
My code gives me a wrong solution.
m = GEKKO(remote=False) # initialize gekko
nt = 101
m.time = np.linspace(0,2,nt)
#end_loc = nt-1
# Variables
x1 = m.CV(fixed_initial=False)
x2 = m.CV(fixed_initial=False)
x3 = m.Var(value=0)
#u = m.Var(value=0,lb=-2,ub=2)
u = m.MV(fixed_initial=False,lb=-2,ub=2)
u.STATUS = 1
p = np.zeros(nt) # mark final time point
p[-1] = 1.0
final = m.Param(value=p)
p1 = np.zeros(nt)
p1[0] = 1.0
p1[-1] = 1.0
infin = m.Param(value=p1)
# Equations
m.Equation(x1.dt()==x2)
m.Equation(x2.dt()==u)
m.Equation(x3.dt()==x1)
# Constraints
m.Equation(infin*x1==0)
m.Equation(final*x2==0)
m.Obj(x3*final) # Objective function
#m.fix(x2,pos=end_loc,val=0.0)
m.options.IMODE = 6 # optimal control mode
#m.solve(disp=True) # solve
m.solve(disp=False) # solve
plt.figure(1) # plot results
plt.plot(m.time,x1.value,'k-',label=r'$x_1$')
plt.plot(m.time,x2.value,'b-',label=r'$x_2$')
plt.plot(m.time,x3.value,'g-',label=r'$x_3$')
plt.plot(m.time,u.value,'r--',label=r'$u$')
plt.legend(loc='best')
plt.xlabel('Time')
plt.ylabel('Value')
plt.show()
plt.figure(1) # plot results
plt.plot(m.time,x1.value,'k-',label=r'$x_1$')
plt.plot(m.time,(m.time-2)**2-2,'g--',label=r'$\hat x_1$')
plt.legend(loc='best')
plt.xlabel('Time')
plt.ylabel('Value')
plt.show()
Use m.integral or m.vsum() to create a time weighted summation or vertical summation along the time direction. Here is a solution that replicates the exact solution.
from gekko import GEKKO
import numpy as np
import matplotlib.pyplot as plt
m = GEKKO(remote=True) # initialize gekko
nt = 501
m.time = np.linspace(0,2,nt)
# insert a small time step at the beginning and end
# for final and initial condition equation x(1e-8)+x(2-1e-8)=0
# this doesn't change the numerical solution significantly
m.time = np.insert(m.time,1,1e-8)
m.time = np.insert(m.time,len(m.time)-1,2.0-1e-8)
nt += 2
# Variables
x1 = m.Var(fixed_initial=False,lb=-100,ub=100)
x2 = m.Var(fixed_initial=False)
u = m.MV(fixed_initial=False,lb=-2,ub=2)
u.STATUS = 1
p = np.zeros(nt) # mark final time point
p[-2] = 1.0
final = m.Param(value=p)
q = p.copy()
q[1] = 1.0
final_initial = m.Param(value=q)
xfi = m.Var()
m.Equation(xfi==final_initial*x1)
# Equations
m.Equation(x1.dt()==x2)
m.Equation(x2.dt()==u)
m.Equation(final*m.vsum(xfi)==0)
m.Equation(final*x2==0)
m.Minimize(m.integral(x1)*final) # Objective function
m.options.IMODE = 6 # optimal control mode
m.options.NODES = 2
m.solve(disp=True) # solve
plt.figure(1) # plot results
plt.subplot(2,1,1)
plt.plot(m.time,x1.value,'k-',label=r'$x_1$')
plt.plot(m.time,x2.value,'b-',label=r'$x_2$')
plt.plot(m.time,u.value,'--',color='orange',label=r'$u$')
plt.legend(loc='best')
plt.ylabel('Value')
plt.subplot(2,1,2)
plt.plot(m.time,x1.value,'k-',label=r'$x_1$')
plt.plot(m.time,(m.time-2)**2-2,'r--',label=r'$\hat x_1$')
plt.legend(loc='best')
plt.xlabel('Time')
plt.ylabel('Value')
plt.show()
One issue is that x1(1-e8)+x1(2-1e-8)=0 is used as a constraint instead of x1(0)+x1(2)=0. The numerical solutions should be nearly equivalent and the 1e-8 can be further reduced.
I am trying to find a trajectory that minimizes the squared integral of the force to move a block from one point to another. Here are the system dynamics:
dx/dt = v (derivative of position is velocity)
dv/dt = u (derivative of velocity is acceleration, which is what I am trying to minimize)
min integral of u**2
The initial conditions and final conditions are:
x(0) = 0, v(0) = 0
x(1) = 1, v(1) = 1
I have implemented this in python using the Gekko library, but I cannot get the final conditions working properly. Using m.fix() to fix the end position makes the problem unsolvable.
Reading online, I used m.Minimize() to make a soft constraint, but the solution was very far off from the end conditions. I added an extra equation to make the velocity less than zero at the end, and that made the solution look like the correct solution, albeit the end position was wrong (if the solution was scaled by a factor, it would be correct).
I should I properly solve this problem?
from gekko import GEKKO
import numpy as np
import matplotlib.pyplot as plt
##model
m = GEKKO() # initialize gekko
nt = 101
m.time = np.linspace(0,1,nt)
# Variables
x = m.Var(value=0)
v = m.Var(value=0)
u = m.Var( fixed_initial=False)
p = np.zeros(nt) # mark final time point
p[-1] = 1.0
final = m.Param(value=p)
# Equations
m.Equation(x.dt()==v)
m.Equation(v.dt()==u)
#m.Equation(x*final >= 1) ##error: Solution Not Found
m.Equation(v*final <= 0)
m.Minimize(final*(x-1)**2)
m.Minimize(final*(v-0)**2)
m.Obj(m.integral(u**2)*final) # Objective function
m.options.IMODE = 6 # optimal control mode
##solve
m.solve() # solve
##plot
plt.figure(1) # plot results
plt.plot(m.time,x.value,'k-',label=r'$x$')
plt.plot(m.time,v.value,'b-',label=r'$v$')
plt.plot(m.time,u.value,'r--',label=r'$u$')
plt.legend(loc='best')
plt.xlabel('Time')
plt.ylabel('Value')
##show plot
plt.show()
Plot of my results
You can fix the problem by putting a higher weight on the final conditions:
m.Minimize(final*1e5*(x-1)**2)
m.Minimize(final*1e5*(v-0)**2)
There is still some tradeoff with the u minimization but it is minimal.
The constraint m.Equation(x*final >= 1) is infeasible when final=0 because this results in the inequality 0 >= 1. If you'd like to use the final position constraint, you'll need to use m.Equation((x-1)*final >= 0) so that the constraint is enforced only at the end but is feasible (0 >= 0) elsewhere. You don't necessarily need the hard constraints with the soft (objective function) constraints for the final condition. Here is a related problem with an inverted pendulum.
from gekko import GEKKO
import numpy as np
import matplotlib.pyplot as plt
m = GEKKO() # initialize gekko
nt = 101; m.time = np.linspace(0,1,nt)
# Variables
x = m.Var(value=0)
v = m.Var(value=0)
u = m.Var(fixed_initial=False)
p = np.zeros(nt) # mark final time point
p[-1] = 1.0
final = m.Param(value=p)
# Equations
m.Equation(x.dt()==v)
m.Equation(v.dt()==u)
m.Equation((x-1)*final >= 0)
m.Equation(v*final <= 0)
m.Minimize(final*1e5*(x-1)**2)
m.Minimize(final*1e5*(v-0)**2)
m.Obj(m.integral(u**2)*final) # Objective function
m.options.IMODE = 6 # optimal control mode
m.solve() # solve
plt.figure(1) # plot results
plt.grid()
plt.plot(m.time,x.value,'k-',label=r'$x$')
plt.plot(m.time,v.value,'b-',label=r'$v$')
plt.plot(m.time,u.value,'r--',label=r'$u$')
plt.legend(loc='best')
plt.xlabel('Time')
plt.ylabel('Value')
plt.show()