MEAS of MV when fstatus=1

Viewed 296

everyone and Professor John

We are using gekko to do MPC on tclab simulation model. We try to emulate the situation that on site the actuator deviates from MV calculated by gekko because of the problems of actuator.

If the deviation is in the fixed pattern, for example a quite big constant deviation happens for a long time and may come back then work well for a long time. We can deal with it by extra logic to detect deviation and add the deviation value to the mv calculated by gekko.

one day, I noticed that there could be meas for MV when fstatus = 1. So I gave it a try. I hope gekko could deal with the deviation by itself. for example, if mv from gekko is 10 and the measurement is 5 and the pattern continues, gekko may spit out a higher MV value than 10, for example 15 and measurement is 10.

In the simulation, when I set MV's fstatus=1, the MV's curve becomes to : enter image description here

q1a is the q1 with manual deviation. In the above pic, q1a == q1. It looks like gekko takes one more step thinking about the MV's effect.

In the below pic, there are two times range, one with "q1a == q1+20" and the other with "q1a == q1 -20". q1a's value is fed to tclab and mv(q1)'s meas. enter image description here

I do not understand why the q1 calculated by gekko is going up or going down when meas deviates despite the t1 is going far away from sp.

Edit: Example Code

See the screen shot below from "normal" HMI. The sluggish MV disappeared, so it maybe caused by bug in my code. But the up-going or down-going could still be seen. enter image description here See my code below:

from random import random
from random import randrange

import tclab
from tclab import labtime
from tclab import TCLabModel

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

from tclab import TCLabModel

make_mp4 = True
if make_mp4:
    import imageio  # required to make animation
    import os
    try:
        os.mkdir('./figures')
    except:
        pass


class tclab_heaterpipe():
    def __init__(self,d1,d2,model):
        if(d1 >= 1 and d2 >=1):
            self.delay_q1_step = int(d1)
            self.delay_q2_step = int(d2)
            self.q1_buffer = [0] * self.delay_q1_step
            self.q2_buffer = [0] * self.delay_q2_step
            self.m = model
        else:
            self.delay_q1_step =0 
            self.delay_q2_step =0
        return

    def Q1_delay(self,q1):
        if(self.delay_q1_step == 0):
            self.m.Q1(q1)
        self.q1_buffer.insert(0,q1)
        self.m.Q1(self.q1_buffer.pop())

    def Q2_delay(self,q2):
        if(self.delay_q2_step == 0):
            self.m.Q1(q2)
        self.q2_buffer.insert(0,q2)
        self.m.Q2(self.q2_buffer.pop())

# Connect to Arduino
connected = False
theta1 = 1 
theta2 = 1 
T = tclab.setup(connected)
a = T()
tclab_delay = tclab_heaterpipe(theta1,theta2,a)
# Turn LED on
print('LED On')
a.LED(100)

# Simulate a time delay
# Run time in minutes
run_time = 80.0
# Number of cycles
loops = int(60.0*run_time)

# Temperature (K)

t1sp = 45.0
t2sp = 35.0

#########################################################
# Initialize Model
#########################################################
# use remote=True for MacOS
m = GEKKO(name='tclab-mpc',remote=False)

m.time = np.linspace(0,400,41)
step = 10

T1 = np.ones(int(loops/step)+1) * a.T1 # temperature (degC)
T2 = np.ones(int(loops/step)+1) * a.T2 # temperature (degC)
Tsp1 = np.ones(int(loops/step)+1) * t1sp # set point (degC)
Tsp2 = np.ones(int(loops/step)+1) * t2sp # set point (degC)
# heater values
Q1s = np.ones(int(loops/step)+1) * 0.0 
Q2s = np.ones(int(loops/step)+1) * 0.0 

# Parameters
Q1_ss = m.Param(value=0)
TC1_ss = m.Param(value=a.T1)
Q2_ss = m.Param(value=0)
TC2_ss = m.Param(value=a.T2)
Kp1 = m.Param(value= 0.7)
tau1 = m.Param(value=160.0)
Kp2 = m.Param(value=0.05)
tau2 = m.Param(value=160.0)
Kp3= m.Param(value=0.05)
tau3 = m.Param(value=160.0)
Kp4 = m.Param(value=0.4)
tau4 = m.Param(value=200.0)
sp1 = m.Param(value=a.T1)
sp2 = m.Param(value=a.T2)

# Manipulated variable
Q1 = m.MV(value=0, name='q1')
Q1.STATUS = 1  # use to control temperature
Q1.FSTATUS = 1 # no feedback measurement
Q1.LOWER = 0.0
Q1.UPPER = 100.0
Q1.DMAX = 10.0
Q1.DCOST = 5.0

Q2 = m.MV(value=0, name='q2')
Q2.STATUS = 1  # use to control temperature
Q2.FSTATUS = 1 # no feedback measurement
Q2.LOWER = 0.0
Q2.UPPER = 100.0
Q2.DMAX = 10.0
Q2.DCOST = 5.0

# Controlled variable
TC1 = m.CV(value=a.T1, name='tc1')
TC1.STATUS = 1     # minimize error with setpoint range
TC1.FSTATUS = 1    # receive measurement
TC1.TR_INIT = 2    # reference trajectory
# TC1.COST = 0.1
TC1.WSPHI = 20
TC1.WSPLO = 20
TC1.TAU = 50        # time constant for response
#TC1.TR_OPEN = 3

TC2 = m.CV(value=a.T2, name='tc2')
TC2.STATUS = 1     # minimize error with setpoint range
TC2.FSTATUS = 1    # receive measurement
TC2.TR_INIT = 2    # reference trajectory
# TC2.COST = 0.1
TC2.WSPHI = 20
TC2.WSPLO = 20
TC2.TAU = 30       # time constant for response
#kTC2.TR_OPEN = 3


# 添加延时
Q1d=m.Var()
m.delay(Q1, Q1d, theta1)
Q2d=m.Var()
m.delay(Q2, Q2d, theta2)
# Equation
#m.Equation(tau1 * TC1.dt() + (TC1 - TC1_ss) == Kp1 * (Q1d - Q1_ss))
# m.Equation(tau2 * TC2.dt() + (TC2 - TC2_ss)  == Kp2 * (Q1d - Q1_ss))
# m.Equation(tau3 * TC1.dt() + (TC1 - TC1_ss)  == Kp3 * (Q2d - Q2_ss))
# m.Equation(tau2 * TC2.dt() + (TC2 - TC2_ss) == Kp4 * (Q2d - Q2_ss))

m.Equation(0.5 * (tau1 * TC1.dt() + (TC1 - TC1_ss) + tau3 * TC1.dt() + (TC1 - TC1_ss)) == Kp1 * (Q1d - Q1_ss) + Kp3 * (Q2d -Q2_ss))
m.Equation(0.5 * (tau2 * TC2.dt() + (TC2 - TC2_ss) + tau4 * TC2.dt() + (TC2 - TC2_ss)) == Kp4 * (Q2d - Q2_ss) + Kp2 * (Q1d - Q1_ss))

# Steady-state initializations
m.options.IMODE = 1
m.options.SOLVER  = 1 # 1=APOPT, 3=IPOPT
m.solve()

sp1.VALUE = 45
sp2.VALUE = 35

# Global Options
m.options.IMODE   = 6 # MPC
m.options.CV_TYPE = 3 # Objective type
m.options.NODES   = 2 # Collocation nodes
m.options.MAX_TIME = 10
m.options.SOLVER  = 1 # 1=APOPT, 3=IPOPT
#m.options.CV_WGT_START = 2*theta 

#m.options.CV_WGT_SLOPE = theta
# m.options.MV_STEP_HOR = 5
##################################################################



# Create plot
plt.figure()
plt.ion()
plt.show()


# Main Loop
a.Q1(0)
a.Q2(0)
Q2s[0:] = 0
start_time = time.time()

tm = np.linspace(1,loops,int(loops/step)+1)
j=0

try:
    time_start = time.time()
    labtime_start = labtime.time()
    if(not connected):
        labtime.set_rate(10)
    for i in tclab.clock(loops,adaptive=False):
        i = int(i)
        if(i == 0):
            continue
        print("-----------------------")
        t_real = time.time() - time_start
        t_lab  = labtime.time() - labtime_start
        print("real time = {0:4.1f}    lab time = {1:4.1f}    m.time = {1:4.1f}".format(t_real, t_lab,m.time))
        #print("real time = {0:4.1f}    m.time = {1:4.1f}".format(t_real, m.time))
       
        if(i%step != 0):
            continue

        j = i/step
        j = int(j)
        print(j)

        T1[j:] = a.T1
        T2[j:] = a.T2
        tm[j] = i
        ###############################
        ### MPC CONTROLLER          ###
        ###############################
        TC1.MEAS = T1[j] 
        TC2.MEAS = T2[j]
        print("T1 meas:{0:4.1f} ".format(a.T1))
        print("T2 meas:{0:4.1f} ".format(a.T2))
        
            
        # input setpoint with deadband +/- DT
        DT =0.5 
        TC1.SPHI = Tsp1[j] +DT 
        TC1.SPLO = Tsp1[j] -DT 
        TC2.SPHI = Tsp2[j] +DT 
        TC2.SPLO = Tsp2[j] -DT 

        try:
            # stop model time to solve MPC in cast the solver takes too much time
            if(not connected):
                labtime.stop()
            m.solve(disp=False)
            #start model time  
            if(not connected):
                labtime.start()
        except Exception as e:
            if(not connected):
                if(not labtime.running):
                    labtime.start()
            print("sovle's exception:")
            print(e)
            if(j != 0):
                Q1s[j] = Q1s[j-1]
                Q2s[j] = Q2s[j-1]
            continue
        # test for successful solution
        if (m.options.APPSTATUS==1):
            # retrieve the first Q value
            Q1s[j:] = np.ones(len(Q1s)-j) * Q1.NEWVAL
            Q2s[j:] = np.ones(len(Q2s)-j) * Q2.NEWVAL
            #a.Q1(Q1.NEWVAL)
            #a.Q2(Q2.NEWVAL)
            print("Q1 applied with delay: {0:4.1f}  ".format(Q1.NEWVAL))
            print("Q2 applied with delay: {0:4.1f}  ".format(Q2.NEWVAL))
            with open(m.path+'//results.json') as f:
                results = json.load(f)
        else:
            # not successful, set heater to zero
            print("APPSTATUS is not 1,set Q to 0")
            #Q1s[j] = 0
            #Q2s[j] = 0
        if i> 300 and i < 600:
          Q1s[j] = Q1s[j] - 20
          Q2s[j] = Q2s[j] - 20

        if i>= 600:
          Q1s[j] = Q1s[j] + 20
          Q2s[j] = Q2s[j] + 20

        Q1.meas= Q1s[j] 
        Q2.meas= Q2s[j]
        tclab_delay.Q1_delay(Q1s[j])
        tclab_delay.Q2_delay(Q2s[j])


        print("calc:"+str(Q1s[j]))
        print("calc:"+str(Q2s[j]))


        #apply disturbance on 50s, 200s,
        #if(i == 600):
        #    Q2s[j] = 100        
        #if(i == 1400):
        #    Q2s[j] = 0
            #Q2s[j] = 20 - randrange(20)  
        #Q2s[j:] = np.ones(len(Q2s)-j) * Q2s[j]

        #restore Q2 to 0
        #if(i == 300):
            #Q2s[j:] = 0

        #a.Q2(Q2s[j])
        #tclab_delay.Q2_delay(Q2s[j])


        #take Q2 to FV
        #Q2.MEAS = Q2s[j]


        if(not connected):
            labtime.stop()
        # Plot
        try:
            plt.clf()
            ax=plt.subplot(2,1,1)
            ax.grid()
            plt.plot(tm[0:j],T1[0:j],'ro',markersize=3,label=r'$T_1$')
            plt.plot(tm[0:j],Tsp1[0:j],'r-',markersize=3,label=r'$T_1 Setpoint$')
            plt.plot(tm[0:j],T2[0:j],'bo',markersize=3,label=r'$T_2$')
            plt.plot(tm[0:j],Tsp2[0:j],'b-',markersize=3,label=r'$T_2 Setpoint$')
        
            plt.plot(tm[j]+m.time,results['tc1.bcv'],'r-.',markersize=1,\
                     label=r'$T_1$ predicted',linewidth=1)

            plt.plot(tm[j]+m.time,results['tc2.bcv'],'b-.',markersize=1,\
                     label=r'$T_2$ predicted',linewidth=1)

            plt.plot(tm[j]+m.time,results['tc1.tr_hi'],'k--',\
                     label=r'$T_1$ trajectory')
            plt.plot(tm[j]+m.time,results['tc1.tr_lo'],'k--')

            
            plt.plot(tm[j]+m.time,results['tc2.tr_hi'],'k--',\
                     label=r'$T_2$ trajectory')
            plt.plot(tm[j]+m.time,results['tc2.tr_lo'],'k--')
            
            
            
            plt.ylabel('Temperature (degC)')
            plt.legend(loc='best')
            ax=plt.subplot(2,1,2)
            ax.grid()
            plt.plot(tm[0:j],Q1s[0:j],'r-',linewidth=3,label=r'$Q_1$')
            plt.plot(tm[0:j],Q2s[0:j],'b-',linewidth=3,label=r'$Q_2$')
            plt.plot(tm[j]+m.time,Q1.value,'r-.',\
                     label=r'$Q_1$ plan',linewidth=1)
            plt.plot(tm[j]+m.time,Q2.value,'b-.',\
                     label=r'$Q_2$ plan',linewidth=1)
            #plt.plot(tm[0:i],Q2s[0:i],'b:',LineWidth=3,label=r'$Q_2$')
            plt.ylabel('Heaters')
            plt.xlabel('Time (sec)')
            plt.legend(loc='best')
            plt.draw()
            plt.pause(0.05)
            if make_mp4:
                filename='./figures/plot_'+str(j+10000)+'.png'
                plt.savefig(filename)

        except Exception as e:
            print(e)
            pass

        if(not connected):
            labtime.start()

    # Turn off heaters
    a.Q1(0)
    a.Q2(0)
    print('Shutting down')
    input("Press Enter to continue...")
    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()
    if make_mp4:
        images = []
        iset = 0
        for i in range(1,int(loops/step)+1):
            filename='./figures/plot_'+str(i+10000)+'.png'
            if os.path.exists(filename):
                images.append(imageio.imread(filename))
                if ((i+1)%350)==0:
                    imageio.mimsave('results_'+str(iset)+'.mp4', images)
                    iset += 1
                    images = []
        if images!=[]:
            imageio.mimsave('results_'+str(iset)+'.mp4', images)

# 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

Regards Tibalt

1 Answers

Is the FSTATUS also ON for the CVs such as t1.FSTATUS=1? If you update the measurement such as:

t1.MEAS = lab.T1
t2.MEAS = lab.T2

then this updates the BIAS for t1 and t2 (BIAS documentation). This should take care of any process / model mismatch that you are introducing by arbitrarily increasing or decreasing the heater by 20%. If t1.FSTATUS is OFF (0) then it is not able to compensate for the mismatch.

Another thing to try is to adjust the reference trajectory. The controller can appear sluggish if TAU is too high. Here is an example application with MPC and a linear model.

One additional way to compensate for the mismatch is to use Moving Horizon Estimation as shown here.

It looks like you have created a nice interface!

Response to Edit

Thanks for adding the code. The problem is that Q1.DMAX=10 and Q2.DMAX=10. When the Q1 and Q2 values are shifted up by 20 each cycle, the most that the controller can shift down is 20-10=10 so the controller appears that it is ramping in the wrong direction. Changing to DMAX=100 fixes the problem. There is still offset from the setpoint because the recommended Q1 and Q2 are shifted each cycle. The true recommended values are never implemented. Another thing to try is to impose an offset on the measured values such as TC1.MEAS = T1[j] + 20. The model bias will remove the offset in this case.

Animation results

from random import random
from random import randrange

import tclab
from tclab import labtime
from tclab import TCLabModel

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

from tclab import TCLabModel

make_gif = True
make_mp4 = True
if make_gif or make_mp4:
    # pip install imageio-ffmpeg with imageio to make MP4
    import imageio  # required to make animation
    import os
    try:
        os.mkdir('./figures')
    except:
        pass

class tclab_heaterpipe():
    def __init__(self,d1,d2,model):
        if(d1 >= 1 and d2 >=1):
            self.delay_q1_step = int(d1)
            self.delay_q2_step = int(d2)
            self.q1_buffer = [0] * self.delay_q1_step
            self.q2_buffer = [0] * self.delay_q2_step
            self.m = model
        else:
            self.delay_q1_step =0 
            self.delay_q2_step =0
        return

    def Q1_delay(self,q1):
        if(self.delay_q1_step == 0):
            self.m.Q1(q1)
        self.q1_buffer.insert(0,q1)
        self.m.Q1(self.q1_buffer.pop())

    def Q2_delay(self,q2):
        if(self.delay_q2_step == 0):
            self.m.Q1(q2)
        self.q2_buffer.insert(0,q2)
        self.m.Q2(self.q2_buffer.pop())

# Connect to Arduino
connected = False  # switch to connected=True with physical hardware
theta1 = 1 
theta2 = 1 
T = tclab.setup(connected)
a = T()
tclab_delay = tclab_heaterpipe(theta1,theta2,a)
# Turn LED on
print('LED On')
a.LED(100)

# Simulate a time delay
# Run time in minutes
run_time = 20.0
# Number of cycles
loops = int(60.0*run_time)

# Temperature (K)
t1sp = 45.0
t2sp = 35.0

#########################################################
# Initialize Model
#########################################################
# use remote=True for MacOS
m = GEKKO(name='tclab-mpc',remote=False)

m.time = np.linspace(0,400,41)
step = 10

T1 = np.ones(int(loops/step)+1) * a.T1 # temperature (degC)
T2 = np.ones(int(loops/step)+1) * a.T2 # temperature (degC)
Tsp1 = np.ones(int(loops/step)+1) * t1sp # set point (degC)
Tsp2 = np.ones(int(loops/step)+1) * t2sp # set point (degC)
# heater values
Q1s = np.ones(int(loops/step)+1) * 0.0 
Q2s = np.ones(int(loops/step)+1) * 0.0 

# Parameters
Q1_ss = m.Param(value=0)
TC1_ss = m.Param(value=a.T1)
Q2_ss = m.Param(value=0)
TC2_ss = m.Param(value=a.T2)
Kp1 = m.Param(value= 0.7)
tau1 = m.Param(value=160.0)
Kp2 = m.Param(value=0.05)
tau2 = m.Param(value=160.0)
Kp3= m.Param(value=0.05)
tau3 = m.Param(value=160.0)
Kp4 = m.Param(value=0.4)
tau4 = m.Param(value=200.0)
sp1 = m.Param(value=a.T1)
sp2 = m.Param(value=a.T2)

# Manipulated variable
Q1 = m.MV(value=0, name='q1')
Q1.STATUS = 1  # use to control temperature
Q1.FSTATUS = 1 # no feedback measurement
Q1.LOWER = 0.0
Q1.UPPER = 100.0
Q1.DMAX = 100.0
Q1.DCOST = 1e-3

Q2 = m.MV(value=0, name='q2')
Q2.STATUS = 1  # use to control temperature
Q2.FSTATUS = 1 # no feedback measurement
Q2.LOWER = 0.0
Q2.UPPER = 100.0
Q2.DMAX = 100.0
Q2.DCOST = 1e-3

# Controlled variable
TC1 = m.CV(value=a.T1, name='tc1')
TC1.STATUS = 1     # minimize error with setpoint range
TC1.FSTATUS = 1    # receive measurement
TC1.TR_INIT = 2    # reference trajectory
# TC1.COST = 0.1
TC1.WSPHI = 20
TC1.WSPLO = 20
TC1.TAU = 50        # time constant for response
#TC1.TR_OPEN = 3

TC2 = m.CV(value=a.T2, name='tc2')
TC2.STATUS = 1     # minimize error with setpoint range
TC2.FSTATUS = 1    # receive measurement
TC2.TR_INIT = 2    # reference trajectory
# TC2.COST = 0.1
TC2.WSPHI = 20
TC2.WSPLO = 20
TC2.TAU = 30       # time constant for response
#kTC2.TR_OPEN = 3

# 添加延时
Q1d=m.Var()
m.delay(Q1, Q1d, theta1)
Q2d=m.Var()
m.delay(Q2, Q2d, theta2)
# Equation
#m.Equation(tau1 * TC1.dt() + (TC1 - TC1_ss) == Kp1 * (Q1d - Q1_ss))
# m.Equation(tau2 * TC2.dt() + (TC2 - TC2_ss)  == Kp2 * (Q1d - Q1_ss))
# m.Equation(tau3 * TC1.dt() + (TC1 - TC1_ss)  == Kp3 * (Q2d - Q2_ss))
# m.Equation(tau2 * TC2.dt() + (TC2 - TC2_ss) == Kp4 * (Q2d - Q2_ss))

m.Equation(0.5 * (tau1 * TC1.dt() + (TC1 - TC1_ss) + tau3 * TC1.dt() + (TC1 - TC1_ss)) == Kp1 * (Q1d - Q1_ss) + Kp3 * (Q2d -Q2_ss))
m.Equation(0.5 * (tau2 * TC2.dt() + (TC2 - TC2_ss) + tau4 * TC2.dt() + (TC2 - TC2_ss)) == Kp4 * (Q2d - Q2_ss) + Kp2 * (Q1d - Q1_ss))

# Steady-state initializations
m.options.IMODE = 1
m.options.SOLVER  = 1 # 1=APOPT, 3=IPOPT
m.solve()

sp1.VALUE = 45
sp2.VALUE = 35

# Global Options
m.options.IMODE   = 6 # MPC
m.options.CV_TYPE = 3 # Objective type
m.options.NODES   = 2 # Collocation nodes
m.options.MAX_TIME = 10
m.options.SOLVER  = 1 # 1=APOPT, 3=IPOPT
#m.options.CV_WGT_START = 2*theta 

#m.options.CV_WGT_SLOPE = theta
# m.options.MV_STEP_HOR = 5
##################################################################
# Create plot
plt.figure(figsize=(12,8))
plt.ion()
plt.show()

# Main Loop
a.Q1(0)
a.Q2(0)
Q2s[0:] = 0
start_time = time.time()

tm = np.linspace(1,loops,int(loops/step)+1)
j=0

try:
    time_start = time.time()
    labtime_start = labtime.time()
    if(not connected):
        labtime.set_rate(10)
    for i in tclab.clock(loops,adaptive=False):
        i = int(i)
        if(i == 0):
            continue
        print("-----------------------")
        t_real = time.time() - time_start
        t_lab  = labtime.time() - labtime_start
        print("real time = {0:4.1f}    lab time = {1:4.1f}    m.time = {1:4.1f}".format(t_real, t_lab,m.time))
        #print("real time = {0:4.1f}    m.time = {1:4.1f}".format(t_real, m.time))

        if(i%step != 0):
            continue

        j = i/step
        j = int(j)
        print(j)

        T1[j:] = a.T1
        T2[j:] = a.T2
        tm[j] = i
        ###############################
        ### MPC CONTROLLER          ###
        ###############################
        TC1.MEAS = T1[j] 
        TC2.MEAS = T2[j]
        print("T1 meas:{0:4.1f} ".format(a.T1))
        print("T2 meas:{0:4.1f} ".format(a.T2))
        
        # input setpoint with deadband +/- DT
        DT =0.5 
        TC1.SPHI = Tsp1[j] +DT 
        TC1.SPLO = Tsp1[j] -DT 
        TC2.SPHI = Tsp2[j] +DT 
        TC2.SPLO = Tsp2[j] -DT 

        try:
            # stop model time to solve MPC in cast the solver takes too much time
            if(not connected):
                labtime.stop()
            m.solve(disp=False)
            #start model time  
            if(not connected):
                labtime.start()
        except Exception as e:
            if(not connected):
                if(not labtime.running):
                    labtime.start()
            print("sovle's exception:")
            print(e)
            if(j != 0):
                Q1s[j] = Q1s[j-1]
                Q2s[j] = Q2s[j-1]
            continue
        # test for successful solution
        if (m.options.APPSTATUS==1):
            # retrieve the first Q value
            Q1s[j:] = np.ones(len(Q1s)-j) * Q1.NEWVAL
            Q2s[j:] = np.ones(len(Q2s)-j) * Q2.NEWVAL
            #a.Q1(Q1.NEWVAL)
            #a.Q2(Q2.NEWVAL)
            print("Q1 applied with delay: {0:4.1f}  ".format(Q1.NEWVAL))
            print("Q2 applied with delay: {0:4.1f}  ".format(Q2.NEWVAL))
            with open(m.path+'//results.json') as f:
                results = json.load(f)
        else:
            # not successful, set heater to zero
            print("APPSTATUS is not 1,set Q to 0")
            #Q1s[j] = 0
            #Q2s[j] = 0
        if i> 300 and i < 600:
          Q1s[j] = max(0,Q1s[j] - 20)
          Q2s[j] = max(0,Q2s[j] - 20)

        if i>= 600:
          Q1s[j] = min(100,Q1s[j] + 20)
          Q2s[j] = min(100,Q2s[j] + 20)

        Q1.meas= Q1s[j] 
        Q2.meas= Q2s[j]
        tclab_delay.Q1_delay(Q1s[j])
        tclab_delay.Q2_delay(Q2s[j])


        print("calc:"+str(Q1s[j]))
        print("calc:"+str(Q2s[j]))

        if(not connected):
            labtime.stop()
        # Plot
        try:
            plt.clf()
            ax=plt.subplot(2,1,1)
            ax.grid()
            plt.plot(tm[0:j],T1[0:j],'ro',markersize=3,label=r'$T_1$')
            plt.plot(tm[0:j],Tsp1[0:j],'r-',markersize=3,label=r'$T_1 Setpoint$')
            plt.plot(tm[0:j],T2[0:j],'bo',markersize=3,label=r'$T_2$')
            plt.plot(tm[0:j],Tsp2[0:j],'b-',markersize=3,label=r'$T_2 Setpoint$')
        
            plt.plot(tm[j]+m.time,results['tc1.bcv'],'r-.',markersize=1,\
                     label=r'$T_1$ predicted',linewidth=1)

            plt.plot(tm[j]+m.time,results['tc2.bcv'],'b-.',markersize=1,\
                     label=r'$T_2$ predicted',linewidth=1)

            plt.plot(tm[j]+m.time,results['tc1.tr_hi'],'k--',\
                     label=r'$T_1$ trajectory')
            plt.plot(tm[j]+m.time,results['tc1.tr_lo'],'k--')

            
            plt.plot(tm[j]+m.time,results['tc2.tr_hi'],'k--',\
                     label=r'$T_2$ trajectory')
            plt.plot(tm[j]+m.time,results['tc2.tr_lo'],'k--')
            
            plt.ylabel('Temperature (degC)')
            plt.legend(loc=1)
            ax=plt.subplot(2,1,2)
            ax.grid()
            plt.plot(tm[0:j],Q1s[0:j],'r-',linewidth=3,label=r'$Q_1$')
            plt.plot(tm[0:j],Q2s[0:j],'b-',linewidth=3,label=r'$Q_2$')
            plt.plot(tm[j]+m.time,Q1.value,'r-.',\
                     label=r'$Q_1$ plan',linewidth=1)
            plt.plot(tm[j]+m.time,Q2.value,'b-.',\
                     label=r'$Q_2$ plan',linewidth=1)
            #plt.plot(tm[0:i],Q2s[0:i],'b:',LineWidth=3,label=r'$Q_2$')
            plt.ylabel('Heaters')
            plt.xlabel('Time (sec)')
            plt.legend(loc=1)
            plt.draw()
            plt.pause(0.05)
            if make_mp4:
                filename='./figures/plot_'+str(j+10000)+'.png'
                plt.savefig(filename)

        except Exception as e:
            print(e)
            pass

        if(not connected):
            labtime.start()

    # Turn off heaters
    a.Q1(0)
    a.Q2(0)
    print('Shutting down')
    input("Press Enter to continue...")
    a.close()

    # make gif
    if make_gif:
        images = []
        iset = 0
        for i in range(1,int(loops/step)+1):
            filename='./figures/plot_'+str(i+10000)+'.png'
            if os.path.exists(filename):
                images.append(imageio.imread(filename))
                if ((i+1)%350)==0:
                    imageio.mimsave('results_'+str(iset)+'.gif', images)
                    iset += 1
                    images = []
        if images!=[]:
            imageio.mimsave('results_'+str(iset)+'.gif', images)

    if make_mp4:
        images = []
        iset = 0
        for i in range(1,int(loops/step)+1):
            filename='./figures/plot_'+str(i+10000)+'.png'
            if os.path.exists(filename):
                images.append(imageio.imread(filename))
                if ((i+1)%350)==0:
                    imageio.mimsave('results_'+str(iset)+'.gif', images)
                    iset += 1
                    images = []
        if images!=[]:
            imageio.mimsave('results_'+str(iset)+'.gif', images)

# Allow user to end loop with Ctrl-C
except KeyboardInterrupt:
    # Disconnect from Arduino
    a.Q1(0)
    a.Q2(0)
    print('Shutting down')
    a.close()
    if make_gif:
        images = []
        iset = 0
        for i in range(1,int(loops/step)+1):
            filename='./figures/plot_'+str(i+10000)+'.png'
            if os.path.exists(filename):
                images.append(imageio.imread(filename))
                if ((i+1)%350)==0:
                    imageio.mimsave('results_'+str(iset)+'.gif', images)
                    iset += 1
                    images = []
        if images!=[]:
            imageio.mimsave('results_'+str(iset)+'.gif', images)
    if make_mp4:
        images = []
        iset = 0
        for i in range(1,int(loops/step)+1):
            filename='./figures/plot_'+str(i+10000)+'.png'
            if os.path.exists(filename):
                images.append(imageio.imread(filename))
                if ((i+1)%350)==0:
                    imageio.mimsave('results_'+str(iset)+'.mp4', images)
                    iset += 1
                    images = []
        if images!=[]:
            imageio.mimsave('results_'+str(iset)+'.mp4', images)

# 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
Related