PyThermostat: Modify PIDAutotune for GUI usage #162

Open
atse wants to merge 3 commits from atse/thermostat:autotune-state into master

View File

@ -2,7 +2,7 @@ import math
import logging import logging
import time import time
from collections import deque, namedtuple from collections import deque, namedtuple
from enum import Enum from enum import Enum, auto
from pythermostat.client import Client from pythermostat.client import Client
@ -13,11 +13,12 @@ from pythermostat.client import Client
class PIDAutotuneState(Enum): class PIDAutotuneState(Enum):
STATE_OFF = 'off' OFF = auto()
STATE_RELAY_STEP_UP = 'relay step up' RELAY_STEP_UP = auto()
STATE_RELAY_STEP_DOWN = 'relay step down' RELAY_STEP_DOWN = auto()
STATE_SUCCEEDED = 'succeeded' SUCCEEDED = auto()
STATE_FAILED = 'failed' FAILED = auto()
READY = auto()
class PIDAutotune: class PIDAutotune:
@ -45,7 +46,7 @@ class PIDAutotune:
self._noiseband = noiseband self._noiseband = noiseband
self._out_min = -out_step self._out_min = -out_step
self._out_max = out_step self._out_max = out_step
self._state = PIDAutotuneState.STATE_OFF self._state = PIDAutotuneState.OFF
self._peak_timestamps = deque(maxlen=5) self._peak_timestamps = deque(maxlen=5)
self._peaks = deque(maxlen=5) self._peaks = deque(maxlen=5)
self._output = 0 self._output = 0
@ -57,6 +58,21 @@ class PIDAutotune:
self._Ku = 0 self._Ku = 0
self._Pu = 0 self._Pu = 0
def set_param(self, target, step, noiseband, sampletime, lookback):
self._setpoint = target
self._outputstep = step
self._out_max = step
self._out_min = -step
self._noiseband = noiseband
self._inputs = deque(maxlen=round(lookback / sampletime))
def set_ready(self):
self._state = PIDAutotuneState.READY
self._peak_count = 0
def set_off(self):
self._state = PIDAutotuneState.OFF
def state(self): def state(self):
"""Get the current state.""" """Get the current state."""
return self._state return self._state
@ -94,29 +110,30 @@ class PIDAutotune:
""" """
now = time_input * 1000 now = time_input * 1000
if (self._state == PIDAutotuneState.STATE_OFF if self._state not in {
or self._state == PIDAutotuneState.STATE_SUCCEEDED PIDAutotuneState.RELAY_STEP_DOWN,
or self._state == PIDAutotuneState.STATE_FAILED): PIDAutotuneState.RELAY_STEP_UP,
self._state = PIDAutotuneState.STATE_RELAY_STEP_UP }:
self._state = PIDAutotuneState.RELAY_STEP_UP
self._last_run_timestamp = now self._last_run_timestamp = now
# check input and change relay state if necessary # check input and change relay state if necessary
if (self._state == PIDAutotuneState.STATE_RELAY_STEP_UP if self._state == PIDAutotuneState.RELAY_STEP_UP
and input_val > self._setpoint + self._noiseband): and input_val > self._setpoint + self._noiseband:
self._state = PIDAutotuneState.STATE_RELAY_STEP_DOWN self._state = PIDAutotuneState.RELAY_STEP_DOWN
logging.debug('switched state: {0}'.format(self._state)) logging.debug('switched state: {0}'.format(self._state))
logging.debug('input: {0}'.format(input_val)) logging.debug('input: {0}'.format(input_val))
elif (self._state == PIDAutotuneState.STATE_RELAY_STEP_DOWN elif self._state == PIDAutotuneState.RELAY_STEP_DOWN
and input_val < self._setpoint - self._noiseband): and input_val < self._setpoint - self._noiseband:
self._state = PIDAutotuneState.STATE_RELAY_STEP_UP self._state = PIDAutotuneState.RELAY_STEP_UP
logging.debug('switched state: {0}'.format(self._state)) logging.debug('switched state: {0}'.format(self._state))
logging.debug('input: {0}'.format(input_val)) logging.debug('input: {0}'.format(input_val))
# set output # set output
if (self._state == PIDAutotuneState.STATE_RELAY_STEP_UP): if self._state == PIDAutotuneState.RELAY_STEP_UP:
self._output = self._initial_output - self._outputstep self._output = self._initial_output - self._outputstep
elif self._state == PIDAutotuneState.STATE_RELAY_STEP_DOWN: elif self._state == PIDAutotuneState.RELAY_STEP_DOWN:
self._output = self._initial_output + self._outputstep self._output = self._initial_output + self._outputstep
# respect output limits # respect output limits
@ -184,16 +201,16 @@ class PIDAutotune:
logging.debug('amplitude deviation: {0}'.format(amplitude_dev)) logging.debug('amplitude deviation: {0}'.format(amplitude_dev))
if amplitude_dev < PIDAutotune.PEAK_AMPLITUDE_TOLERANCE: if amplitude_dev < PIDAutotune.PEAK_AMPLITUDE_TOLERANCE:
self._state = PIDAutotuneState.STATE_SUCCEEDED self._state = PIDAutotuneState.SUCCEEDED
# if the autotune has not already converged # if the autotune has not already converged
# terminate after 10 cycles # terminate after 10 cycles
if self._peak_count >= 20: if self._peak_count >= 20:
self._output = 0 self._output = 0
self._state = PIDAutotuneState.STATE_FAILED self._state = PIDAutotuneState.FAILED
return True return True
if self._state == PIDAutotuneState.STATE_SUCCEEDED: if self._state == PIDAutotuneState.SUCCEEDED:
self._output = 0 self._output = 0
logging.debug('peak finding successful') logging.debug('peak finding successful')