pytec: __name__ check, examples does not run when pytec module is imported, change autotune state management to use enum

This commit is contained in:
topquark12 2021-01-04 10:54:04 +08:00
parent d1f8a4761b
commit ab305d5fa5
3 changed files with 175 additions and 164 deletions

View File

@ -3,12 +3,15 @@ import logging
from time import time from time import time
from collections import deque, namedtuple from collections import deque, namedtuple
from pytec.client import Client from pytec.client import Client
from enum import Enum
# Based on hirshmann pid-autotune libiary # Based on hirshmann pid-autotune libiary
# See https://github.com/hirschmann/pid-autotune # See https://github.com/hirschmann/pid-autotune
# Which is in turn based on a fork of Arduino PID AutoTune Library # Which is in turn based on a fork of Arduino PID AutoTune Library
# See https://github.com/t0mpr1c3/Arduino-PID-AutoTune-Library # See https://github.com/t0mpr1c3/Arduino-PID-AutoTune-Library
if __name__ == "__main__":
# Auto tune parameters # Auto tune parameters
# Thermostat channel # Thermostat channel
channel = 0 channel = 0
@ -21,17 +24,20 @@ lookback = 3
# Determines by how much the input value must overshoot/undershoot the setpoint, celcius # Determines by how much the input value must overshoot/undershoot the setpoint, celcius
noiseband = 1.5 noiseband = 1.5
class PIDAutotune(object): class PIDAutotuneState(Enum):
PIDParams = namedtuple('PIDParams', ['Kp', 'Ki', 'Kd'])
PEAK_AMPLITUDE_TOLERANCE = 0.05
STATE_OFF = 'off' STATE_OFF = 'off'
STATE_RELAY_STEP_UP = 'relay step up' STATE_RELAY_STEP_UP = 'relay step up'
STATE_RELAY_STEP_DOWN = 'relay step down' STATE_RELAY_STEP_DOWN = 'relay step down'
STATE_SUCCEEDED = 'succeeded' STATE_SUCCEEDED = 'succeeded'
STATE_FAILED = 'failed' STATE_FAILED = 'failed'
class PIDAutotune():
PIDParams = namedtuple('PIDParams', ['Kp', 'Ki', 'Kd'])
PEAK_AMPLITUDE_TOLERANCE = 0.05
_tuning_rules = { _tuning_rules = {
"ziegler-nichols": [0.6, 1.2, 0.075], "ziegler-nichols": [0.6, 1.2, 0.075],
"tyreus-luyben": [0.4545, 0.2066, 0.07214], "tyreus-luyben": [0.4545, 0.2066, 0.07214],
@ -62,7 +68,7 @@ class PIDAutotune(object):
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 = PIDAutotune.STATE_OFF self._state = PIDAutotuneState.STATE_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
@ -115,30 +121,30 @@ class PIDAutotune(object):
""" """
now = time_input * 1000 now = time_input * 1000
if (self._state == PIDAutotune.STATE_OFF if (self._state == PIDAutotuneState.STATE_OFF
or self._state == PIDAutotune.STATE_SUCCEEDED or self._state == PIDAutotuneState.STATE_SUCCEEDED
or self._state == PIDAutotune.STATE_FAILED): or self._state == PIDAutotuneState.STATE_FAILED):
self._initTuner(input_val, now) self._initTuner(input_val, now)
self._last_run_timestamp = now self._last_run_timestamp = now
# print("temp : ", input_val) # print("temp : ", input_val)
# check input and change relay state if necessary # check input and change relay state if necessary
if (self._state == PIDAutotune.STATE_RELAY_STEP_UP if (self._state == PIDAutotuneState.STATE_RELAY_STEP_UP
and input_val > self._setpoint + self._noiseband): and input_val > self._setpoint + self._noiseband):
self._state = PIDAutotune.STATE_RELAY_STEP_DOWN self._state = PIDAutotuneState.STATE_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 == PIDAutotune.STATE_RELAY_STEP_DOWN elif (self._state == PIDAutotuneState.STATE_RELAY_STEP_DOWN
and input_val < self._setpoint - self._noiseband): and input_val < self._setpoint - self._noiseband):
self._state = PIDAutotune.STATE_RELAY_STEP_UP self._state = PIDAutotuneState.STATE_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 == PIDAutotune.STATE_RELAY_STEP_UP): if (self._state == PIDAutotuneState.STATE_RELAY_STEP_UP):
self._output = self._initial_output + self._outputstep self._output = self._initial_output + self._outputstep
elif self._state == PIDAutotune.STATE_RELAY_STEP_DOWN: elif self._state == PIDAutotuneState.STATE_RELAY_STEP_DOWN:
self._output = self._initial_output - self._outputstep self._output = self._initial_output - self._outputstep
# respect output limits # respect output limits
@ -204,17 +210,17 @@ class PIDAutotune(object):
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 = PIDAutotune.STATE_SUCCEEDED self._state = PIDAutotuneState.STATE_SUCCEEDED
# logging.debug('peak finding succeeded') # logging.debug('peak finding 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 = PIDAutotune.STATE_FAILED self._state = PIDAutotuneState.STATE_FAILED
return True return True
if self._state == PIDAutotune.STATE_SUCCEEDED: if self._state == PIDAutotuneState.STATE_SUCCEEDED:
self._output = 0 self._output = 0
logging.debug('peak finding successful') logging.debug('peak finding successful')
@ -250,7 +256,9 @@ class PIDAutotune(object):
self._peaks.clear() self._peaks.clear()
self._peak_timestamps.clear() self._peak_timestamps.clear()
self._peak_timestamps.append(timestamp) self._peak_timestamps.append(timestamp)
self._state = PIDAutotune.STATE_RELAY_STEP_UP self._state = PIDAutotuneState.STATE_RELAY_STEP_UP
if __name__ == "__main__":
# logging.basicConfig(level=logging.DEBUG) # logging.basicConfig(level=logging.DEBUG)
@ -269,7 +277,6 @@ for data in tec.report_mode():
temperature = ch['temperature'] temperature = ch['temperature']
if (tuner.run(temperature, ch['time'])): if (tuner.run(temperature, ch['time'])):
# logging.debug('true')
break break
tunerOut = tuner.output() tunerOut = tuner.output()

View File

@ -1,3 +1,5 @@
if __name__ == "__main__":
from pytec.client import Client from pytec.client import Client
tec = Client() #(host="localhost", port=6667) tec = Client() #(host="localhost", port=6667)

View File

@ -4,6 +4,8 @@ import matplotlib.animation as animation
from threading import Thread, Lock from threading import Thread, Lock
from pytec.client import Client from pytec.client import Client
if __name__ == "__main__":
TIME_WINDOW = 300.0 TIME_WINDOW = 300.0
tec = Client() tec = Client()
@ -30,7 +32,7 @@ class Series:
series = { series = {
'adc': Series(), 'adc': Series(),
'sens': Series(lambda x: x * 0.0001), 'sens': Series(lambda x: x * 0.0001),
'temperature': Series(lambda t: t - target_temperature), 'temperature': Series(),
'i_set': Series(), 'i_set': Series(),
'pid_output': Series(), 'pid_output': Series(),
'vref': Series(), 'vref': Series(),