GEKKO
is optimization software for mixed-integer and differential algebraic equations. It is coupled with large-scale solvers
for linear, quadratic, nonlinear
, and mixed integer programming (LP, QP, NLP, MILP, MINLP
).
I use gekko
to control my TCLab Arduino
, but when I give a disturbance, no matter how I adjust the parameters, there will be a overshoot temperature. How can I solve this problem?
Here is my code:
import tclab
import numpy as np
import time
import matplotlib.pyplot as plt
from gekko import GEKKO
# Connect to Arduino
a = tclab.TCLab()
# Get Version
print(a.version)
# Turn LED on
print('LED On')
a.LED(100)
# Run time in minutes
run_time = 60.0
# Number of cycles
loops = int(60.0*run_time)
tm = np.zeros(loops)
# Temperature (K)
T1 = np.ones(loops) * a.T1 # temperature (degC)
Tsp1 = np.ones(loops) * 35.0 # set point (degC)
# heater values
Q1s = np.ones(loops) * 0.0
#########################################################
# Initialize Model
#########################################################
# use remote=True for MacOS
m = GEKKO(name='tclab-mpc',remote=False)
# 100 second time horizon
m.time = np.linspace(0,100,101)
# Parameters
Q1_ss = m.Param(value=0)
TC1_ss = m.Param(value=a.T1)
Kp = m.Param(value=0.8)
tau = m.Param(value=160.0)
# Manipulated variable
Q1 = m.MV(value=0)
Q1.STATUS = 1 # use to control temperature
Q1.FSTATUS = 0 # no feedback measurement
Q1.LOWER = 0.0
Q1.UPPER = 100.0
Q1.DMAX = 50.0
# Q1.COST = 0.0
Q1.DCOST = 0.2
# Controlled variable
TC1 = m.CV(value=TC1_ss.value)
TC1.STATUS = 1 # minimize error with setpoint range
TC1.FSTATUS = 1 # receive measurement
TC1.TR_INIT = 2 # reference trajectory
TC1.TR_OPEN = 2 # reference trajectory
TC1.TAU = 35 # time constant for response
m.Equation(tau * TC1.dt() + (TC1-TC1_ss) == Kp * (Q1-Q1_ss))
# Global Options
m.options.IMODE = 6 # MPC
m.options.CV_TYPE = 1 # Objective type
m.options.NODES = 2 # Collocation nodes
m.options.SOLVER = 1 # 1=APOPT, 3=IPOPT
##################################################################
# Create plot
plt.figure()
plt.ion()
plt.show()
filter_tc1 = []
def movefilter(predata, new, n):
if len(predata) < n:
predata.append(new)
else:
predata.pop(0)
predata.append(new)
return np.average(predata)
# Main Loop
start_time = time.time()
prev_time = start_time
try:
for i in range(1,loops):
# Sleep time
sleep_max = 1.0
sleep = sleep_max - (time.time() - prev_time)
if sleep>=0.01:
time.sleep(sleep)
else:
time.sleep(0.01)
# Record time and change in time
t = time.time()
dt = t - prev_time
prev_time = t
tm[i] = t - start_time
# Read temperatures in Kelvin
curr_T1 = a.T1
last_T1 = curr_T1
avg_T1 = movefilter(filter_tc1, last_T1, 3)
T1[i] = curr_T1
###############################
### MPC CONTROLLER ###
###############################
TC1.MEAS = avg_T1
# input setpoint with deadband +/- DT
DT = 0.1
TC1.SPHI = Tsp1[i] + DT
TC1.SPLO = Tsp1[i] - DT
# solve MPC
m.solve(disp=False)
# test for successful solution
if (m.options.APPSTATUS==1):
# retrieve the first Q value
Q1s[i] = Q1.NEWVAL
else:
# not successful, set heater to zero
Q1s[i] = 0
# Write output (0-100)
a.Q1(Q1s[i])
# Plot
plt.clf()
ax=plt.subplot(2,1,1)
ax.grid()
plt.plot(tm[0:i],T1[0:i],'ro',MarkerSize=3,label=r'$T_1$')
plt.plot(tm[0:i],Tsp1[0:i],'b-',MarkerSize=3,label=r'$T_1 Setpoint$')
plt.ylabel('Temperature (degC)')
plt.legend(loc='best')
ax=plt.subplot(2,1,2)
ax.grid()
plt.plot(tm[0:i],Q1s[0:i],'r-',LineWidth=3,label=r'$Q_1$')
plt.ylabel('Heaters')
plt.xlabel('Time (sec)')
plt.legend(loc='best')
plt.draw()
plt.pause(0.05)
# Turn off heaters
a.Q1(0)
a.Q2(0)
print('Shutting down')
a.close()
# Allow user to end loop with Ctrl-C
except KeyboardInterrupt:
# Disconnect from Arduino
a.Q1(0)
a.Q2(0)
print('Shutting down')
a.close()
# Make sure serial connection still closes when there's an error
except:
# Disconnect from Arduino
a.Q1(0)
a.Q2(0)
print('Error: Shutting down')
a.close()
raise
When you add the disturbance (such as turn on the other heater), the apparent system gain increases because the temperature rises higher than anticipated by the controller. That means you start to go left on the mismatch plot (leads to worst control performance).
This is Figure 14 in Hedengren, J. D., Eaton, A. N., Overview of Estimation Methods for Industrial Dynamic Systems, Optimization and Engineering, Springer, Vol 18 (1), 2017, pp. 155-178, DOI: 10.1007/s11081-015-9295-9.
One of the reasons for the overshoot is because of model mismatch. Here are a few ways to deal with this:
K
(maybe to 1) or decrease your modeltau
(maybe to 120) so that the controller becomes less aggressive. You may also want to re-identify your model so that it better reflects your TCLab system dynamics. Here is a tutorial on getting a first order or second order model. A higher order ARX model also works well for the TCLab.TC.TAU=50
and include the reference trajectory on the plot so that you can observe what the controller is planning. I also like to include the unbiased model on the plot to show how the model is performing.