Solving Receding Horizon Control in GEKKO

Viewed 29

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!

1 Answers

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].

Related