diff --git a/Control/brewpi/Rezept-001.json b/Control/brewpi/Rezept-001.json index 67c754d..fd5b9d4 100644 --- a/Control/brewpi/Rezept-001.json +++ b/Control/brewpi/Rezept-001.json @@ -11,7 +11,7 @@ { "time" : 15, "temp" : 50.0, - "heatRate" : 0.25, + "heatRate" : 1.20, "waitForUser" : true }, { diff --git a/Control/brewpi/aplant.py b/Control/brewpi/aplant.py index b0cdcf6..6f6e7a7 100644 --- a/Control/brewpi/aplant.py +++ b/Control/brewpi/aplant.py @@ -1,4 +1,4 @@ -import numpy as np +import time import abc @@ -6,6 +6,9 @@ class APlant(abc.ABC): def __init__(self, params): pass + def log(self, s): + print("{:.2f}: {}".format(time.time(), s)) + @abc.abstractmethod def process(self): pass @@ -14,6 +17,10 @@ class APlant(abc.ABC): def setPower(self, power_W): pass + @abc.abstractmethod + def getPower(self): + return None + @abc.abstractmethod def getTemperature(self): return None diff --git a/Control/brewpi/astirrer.py b/Control/brewpi/astirrer.py index 2f722e6..9ab0a72 100644 --- a/Control/brewpi/astirrer.py +++ b/Control/brewpi/astirrer.py @@ -1,4 +1,4 @@ -import numpy as np +import time import abc @@ -6,6 +6,9 @@ class AStirrer(abc.ABC): def __init__(self): pass + def log(self, s): + print("{:.2f}: {}".format(time.time(), s)) + @abc.abstractmethod def process(self): pass diff --git a/Control/brewpi/controller.py b/Control/brewpi/controller.py index 1fadb09..d51300b 100644 --- a/Control/brewpi/controller.py +++ b/Control/brewpi/controller.py @@ -4,6 +4,7 @@ from enum import Enum from pid import Pid from matplotlib.pyplot import figure, clf, plot, xlabel, ylabel, xlim, ylim, title, grid, axes, show, subplot from utils import Smoother, Stable +import time ovenStates = Enum('ovenStates', 'NOP HEAT HOLD') controllerStates = Enum('controllerStates', 'ACQU TRACK') @@ -20,12 +21,31 @@ class Controller(): self.dt = params['dt'] self.params = params self.pid = Pid() + self.theta_ist = 0 + self.heatrate_ist = 0 self.theta_v = np.empty(0) self.heatrate_v = np.empty(0) self.time_v = np.empty(0) self.power_v = np.empty(0) self.error_v = np.empty(0) self.time = 0 + self.sim_warp_factor = 10 + self.log_report_interval = 1.0 + self.log_last_time = 0 + + def log(self, s): + now = time.time() + print("{:.2f}: {}".format(now, s)) + + def report(self): + now = time.time() + if (now - self.log_last_time) >= self.log_report_interval: + + self.log("Temp IST = {:0.2f} °C".format(self.theta_ist)) + self.log("Heatrate IST = {:0.2f} °C/min.".format(self.heatrate_ist)) + self.log("Power = {:0.2f} W".format(self.plant.getPower())) + # self.log("theta_err = {:0.2f} °C".format(theta_err)) + self.log_last_time = now def start(self, recipe): print(json.dumps({recipe['Name'] : recipe}, indent=4, sort_keys=True)) @@ -73,10 +93,10 @@ class Controller(): self.stirrer.setCycleTime(self.stirrCycleTime) # Temperature - theta_last = self.plant.getTemperature() + self.theta_ist = self.plant.getTemperature() theta_soll = rast['temp'] heatrate_soll = rast['heatRate'] - print("Target temperature {} °C".format(theta_soll)) + self.log("Target temperature {} °C".format(theta_soll)) # Timer timer_ist = 0 @@ -89,6 +109,8 @@ class Controller(): theta_err_sm = Smoother(0.5) heatrate_err_sm = Smoother(0.5) while(doLoop): + time_start = time.time() + controllerStateNext = self.controllerState ovenStateNext = self.ovenState @@ -97,9 +119,7 @@ class Controller(): self.plant.process() theta_ist = self.plant.getTemperature() - heatrate_ist = 60/self.dt * (theta_ist - theta_last) - - # print ("{}: Temp IST = {:0.2f} °C".format(timer_ist, temp_ist)) + heatrate_ist = 60/self.dt * (theta_ist - self.theta_ist) theta_err = theta_soll - theta_ist heatrate_err = heatrate_soll - heatrate_ist @@ -150,9 +170,6 @@ class Controller(): power = max(self.params['P_min'], min(self.params['P_max'], self.params['P_max']*y)) self.plant.setPower(power) - self.time += self.dt - theta_last = theta_ist - self.theta_v = np.append(self.theta_v, theta_ist) self.heatrate_v = np.append(self.heatrate_v, heatrate_ist) self.time_v = np.append(self.time_v, self.time/60) @@ -160,12 +177,10 @@ class Controller(): self.error_v = np.append(self.error_v, pid_err) #self.error_v = np.append(self.error_v, self.stirrer.getRpm()) - # print ("theta_err = {:0.2f} °C".format(theta_err)) - # print ("Power = {:0.2f} W".format(power)) # ----------------------------------------- if ovenStateNext != self.ovenState: - print("{} -> {}".format(self.ovenState, ovenStateNext)) + self.log("{} -> {}".format(self.ovenState, ovenStateNext)) if ovenStateNext == ovenStates.HEAT: self.stirrer.setRpm(self.stirrRpmHeat) self.stirrer.setDutyCycle(1.0) @@ -175,10 +190,23 @@ class Controller(): self.stirrer.setDutyCycle(self.stirrDutyRast) if controllerStateNext != self.controllerState: - print("{} -> {}".format(self.controllerState, controllerStateNext)) + self.log("{} -> {}".format(self.controllerState, controllerStateNext)) self.ovenState = ovenStateNext self.controllerState = controllerStateNext # ------------------------------ + time_stop = time.time() + time_to_sleep = self.dt / self.sim_warp_factor - (time_stop - time_start) + if time_to_sleep < 0: + self.log("Warning: dt is too small!") + time_to_sleep = 0 + + self.time += self.dt + self.theta_ist = theta_ist + self.heatrate_ist = heatrate_ist + + self.report() + + time.sleep(time_to_sleep) self.stirrer.stop() diff --git a/Control/brewpi/mass.py b/Control/brewpi/mass.py index b83d6c1..7059ce4 100644 --- a/Control/brewpi/mass.py +++ b/Control/brewpi/mass.py @@ -33,5 +33,8 @@ class Mass(APlant): def setPower(self, power_w): self.P = power_w + def getPower(self): + return self.P + def getTemperature(self): return self.theta + self.theta_amb diff --git a/Control/brewpi/stirrer.py b/Control/brewpi/stirrer.py index 36e7c7f..b3f155f 100644 --- a/Control/brewpi/stirrer.py +++ b/Control/brewpi/stirrer.py @@ -19,23 +19,23 @@ class Stirrer(AStirrer): if self.cycleCounter <= (self.cycleTime * self.dutyCycle): if not self.isOn: - print ("Stirrer: On") + self.log ("Stirrer: On") self.isOn = 1 else: if self.isOn: - print ("Stirrer: Off") + self.log ("Stirrer: Off") self.isOn = 0 def setRpm(self, rpm): - print ("Stirrer: Set RPM to {} rpm".format(rpm)) + self.log ("Stirrer: Set RPM to {} rpm".format(rpm)) self.rpm = rpm def setCycleTime(self, time): - print ("Stirrer: Set cycle time to {} s".format(time)) + self.log ("Stirrer: Set cycle time to {} s".format(time)) self.cycleTime = time def setDutyCycle(self, dutyCycle): - print ("Stirrer: Set duty cycle to {} %".format(100*dutyCycle)) + self.log ("Stirrer: Set duty cycle to {} %".format(100*dutyCycle)) self.dutyCycle = dutyCycle def getRpm(self): @@ -45,8 +45,8 @@ class Stirrer(AStirrer): def start(self): self.isMasterOn = 1 - print("Stirrer: switched On") + self.log("Stirrer: switched On") def stop(self): self.isMasterOn = 0 - print("Stirrer: switched Off") + self.log("Stirrer: switched Off")