4

I'm trying to implement a thermostat control using MPC and GEKKO.

The state variable (temperature) should be within a lower and upper pre-specified temp values , temp_low and temp_upper in the code below.

Both bound vary along the day: one value per hour.

The objective function is the cost-to-go of using the heating. The price also changes along the day, TOU below. T_external is the room's exterior temperature that plays a role in the differential equation.

How can implement this so that it optimizes?

This is my attempt:

from gekko import GEKKO
import numpy as np

m = GEKKO(remote=False)
m.time = np.linspace(0,23,24)

#initialize variables
T_external = [50.,50.,50.,50.,45.,45.,45.,60.,60.,63.,64.,45.,45.,50.,52.,53.,53.,54.,54.,53.,52.,51.,50.,45.]
temp_low = [55.,55.,55.,55.,55.,55.,55.,68.,68.,68.,68.,55.,55.,68.,68.,68.,68.,55.,55.,55.,55.,55.,55.,55.]
temp_upper = [75.,75.,75.,75.,75.,75.,75.,70.,70.,70.,70.,75.,75.,70.,70.,70.,70.,75.,75.,75.,75.,75.,75.,75.]
TOU = [0.05,0.05,0.05,0.05,0.05,0.05,0.05,200.,200.,200.,200.,200.,200.,200.,200.,200.,200.,200.,200.,200.,200.,0.05,0.05,0.05]

b = m.Param(value=1.)
k = m.Param(value=0.05)
T_e = m.Param(value=T_external)

u = m.MV(value=[0]*24, lb=[0.0]*24, ub=[1.]*24)
u.STATUS = 1  # allow optimizer to change

# Controlled Variable
T = m.SV(value=[60]*24, lb=temp_low, ub=temp_upper)

m.Equation(T.dt() == k*(T_e-T) + b*u)

m.Obj(np.dot(TOU,u))

m.options.IMODE = 6
m.solve(debug=True)

When I run this I get:

@error: Model Expression
 *** Error in syntax of function string: Missing operator

Position: 4                   
 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
    ?
Arraval
  • 1,110
  • 9
  • 20

1 Answers1

1

Gekko needs the constaints as inequality expressions where the variable T is compared to the upper TH or lower TL values. If you have b=1., it leads to an infeasible solution because the heater isn't powerful enough to maintain the temperature within the upper and lower limits. I changed the value to b=10 to get a feasible solution.

from gekko import GEKKO
import numpy as np

m = GEKKO(remote=False)
m.time = np.linspace(0,23,24)

#initialize variables
T_external = [50.,50.,50.,50.,45.,45.,45.,60.,60.,63.,\
              64.,45.,45.,50.,52.,53.,53.,54.,54.,\
              53.,52.,51.,50.,45.]
temp_low = [55.,55.,55.,55.,55.,55.,55.,68.,68.,68.,68.,\
            55.,55.,68.,68.,68.,68.,55.,55.,55.,55.,55.,55.,55.]
temp_upper = [75.,75.,75.,75.,75.,75.,75.,70.,70.,70.,70.,75.,\
              75.,70.,70.,70.,70.,75.,75.,75.,75.,75.,75.,75.]
TOU_v = [0.05,0.05,0.05,0.05,0.05,0.05,0.05,200.,200.,200.,200.,\
         200.,200.,200.,200.,200.,200.,200.,200.,200.,200.,0.05,\
         0.05,0.05]

b = m.Param(value=10.)
k = m.Param(value=0.05)
T_e = m.Param(value=T_external)
TL = m.Param(value=temp_low)
TH = m.Param(value=temp_upper)
TOU = m.Param(value=TOU_v)

u = m.MV(lb=0, ub=1)
u.STATUS = 1  # allow optimizer to change

# Controlled Variable
T = m.SV(value=60)

m.Equations([T>=TL,T<=TH])
m.Equation(T.dt() == k*(T_e-T) + b*u)

m.Minimize(TOU*u)

m.options.IMODE = 6
m.solve(disp=True,debug=True)

A potentially better solution is to set up soft constraints by redefining the limits as an error. You can minimize the error to stay within the limits. Even if it can't stay within the limits, the optimizer will do the best it can to minimize the infeasibility. This also allows you to trade-off multiple objectives simultaneously such as between comfort and cost.

Stay within temperature bounds

from gekko import GEKKO
import numpy as np

m = GEKKO(remote=False)
m.time = np.linspace(0,23,24)

#initialize variables
T_external = [50.,50.,50.,50.,45.,45.,45.,60.,60.,63.,\
              64.,45.,45.,50.,52.,53.,53.,54.,54.,\
              53.,52.,51.,50.,45.]
temp_low = [55.,55.,55.,55.,55.,55.,55.,68.,68.,68.,68.,\
            55.,55.,68.,68.,68.,68.,55.,55.,55.,55.,55.,55.,55.]
temp_upper = [75.,75.,75.,75.,75.,75.,75.,70.,70.,70.,70.,75.,\
              75.,70.,70.,70.,70.,75.,75.,75.,75.,75.,75.,75.]
TOU_v = [0.05,0.05,0.05,0.05,0.05,0.05,0.05,200.,200.,200.,200.,\
         200.,200.,200.,200.,200.,200.,200.,200.,200.,200.,0.05,\
         0.05,0.05]

b = m.Param(value=10.)
k = m.Param(value=0.05)
T_e = m.Param(value=T_external)
TL = m.Param(value=temp_low)
TH = m.Param(value=temp_upper)
TOU = m.Param(value=TOU_v)

u = m.MV(lb=0, ub=1)
u.STATUS = 1  # allow optimizer to change

# Controlled Variable
T = m.SV(value=60)

# Soft constraints
eH = m.CV(value=0)
eL = m.CV(value=0)

eH.SPHI=0; eH.WSPHI=100; eH.WSPLO=0  ; eH.STATUS = 1
eL.SPLO=0; eL.WSPHI=0  ; eL.WSPLO=100; eL.STATUS = 1

m.Equations([eH==T-TH,eL==T-TL])

m.Equation(T.dt() == k*(T_e-T) + b*u)

m.Minimize(TOU*u)

m.options.IMODE = 6
m.solve(disp=True,debug=True)

import matplotlib.pyplot as plt
plt.subplot(2,1,1)
plt.plot(m.time,temp_low,'k--')
plt.plot(m.time,temp_upper,'k--')
plt.plot(m.time,T.value,'r-')
plt.ylabel('Temperature')
plt.subplot(2,1,2)
plt.step(m.time,u.value,'b:')
plt.ylabel('Heater')
plt.xlabel('Time (hr)')
plt.show()
John Hedengren
  • 12,068
  • 1
  • 21
  • 25
  • 1
    Thanks a lot for the help this is truly impressive. There is something that intrigues me. Yesterday I kept trying to make it work and I finally did but with `IMODE=3` and `b=1.0`. What's different so that in one mode it works and in the other it leads to an infeasible solution. I posted the question here: https://stackoverflow.com/questions/60772432/gekko-rto-vs-mpc-modes. Kind regards. – Arraval Mar 20 '20 at 10:41
  • 1
    I'll be glad to add a more detailed reply to that post. `IMODE=3` sets all the derivative values to zero but your solution with that other mode is also allowing the initial condition to change to a higher value so the heater doesn't need to be as powerful to stay feasible. – John Hedengren Mar 20 '20 at 12:29
  • How can I write such soft constraints in the objective function I want to document my algorithm. ? Do I simply add e_{H}*weight and e_{L}*weight in the Objective function? @John Hedengren . – tt40kiwi Oct 13 '20 at 10:04
  • Here is more information on the CV objective function with an l1-norm objective: https://apmonitor.com/do/index.php/Main/ControllerObjective – John Hedengren Oct 13 '20 at 16:41