frappy_psi.picontrol: software control loop

example usage: use a temperature controller without changing
the calibration setting:
reading the raw sensor, calibrate by software and use 'manual'
heater output

Change-Id: I3dbcf37e7726b48a0516d7aa30758be52b80fe58
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/33910
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
This commit is contained in:
zolliker 2024-06-11 17:48:46 +02:00
parent 8b27e4012c
commit f5667a9267

View File

@ -16,12 +16,14 @@
# Module authors: # Module authors:
# Markus Zolliker <markus.zolliker@psi.ch> # Markus Zolliker <markus.zolliker@psi.ch>
# Jael Celia Lorenzana <jael-celia.lorenzana@psi.ch> # Jael Celia Lorenzana <jael-celia.lorenzana@psi.ch>
#
# ***************************************************************************** # *****************************************************************************
"""soft PI control""" """soft PI control"""
import time import time
from frappy.core import Writable, Attached, Parameter, FloatRange, Readable, BoolType, ERROR, IDLE import math
from frappy.core import Writable, Parameter, FloatRange, IDLE
from frappy.lib import clamp from frappy.lib import clamp
from frappy.datatypes import LimitsType, EnumType from frappy.datatypes import LimitsType, EnumType
from frappy.mixins import HasOutputModule from frappy.mixins import HasOutputModule
@ -30,8 +32,10 @@ from frappy.mixins import HasOutputModule
class PImixin(HasOutputModule, Writable): class PImixin(HasOutputModule, Writable):
p = Parameter('proportional term', FloatRange(0), readonly=False) p = Parameter('proportional term', FloatRange(0), readonly=False)
i = Parameter('integral term', FloatRange(0), readonly=False) i = Parameter('integral term', FloatRange(0), readonly=False)
output_range = Parameter('min output', LimitsType(FloatRange()), default=(0, 0), readonly=False) output_range = Parameter('min output',
output_func = Parameter('output function', EnumType(lin=0, square=1), readonly=False, default=0) LimitsType(FloatRange()), default=(0, 0), readonly=False)
output_func = Parameter('output function',
EnumType(lin=0, square=1), readonly=False, default=0)
value = Parameter(unit='K') value = Parameter(unit='K')
_lastdiff = None _lastdiff = None
_lasttime = 0 _lasttime = 0
@ -50,10 +54,8 @@ class PImixin(HasOutputModule, Writable):
self._clamp_limits = lambda v, o=out: clamp(v, 0, o.read_limit()) self._clamp_limits = lambda v, o=out: clamp(v, 0, o.read_limit())
else: else:
self._clamp_limits = lambda v: v self._clamp_limits = lambda v: v
self.log.info('OR %r', self.output_range)
if self.output_range == (0.0, 0.0): if self.output_range == (0.0, 0.0):
self.output_range = (0, self._clamp_limits(float('inf'))) self.output_range = (0, self._clamp_limits(float('inf')))
self.log.info('OR %r', self.output_range)
if not self.control_active: if not self.control_active:
return return
self.status = IDLE, 'controlling' self.status = IDLE, 'controlling'
@ -68,7 +70,7 @@ class PImixin(HasOutputModule, Writable):
out = self.output_module out = self.output_module
output = out.target output = out.target
if self.output_func == 'square': if self.output_func == 'square':
output = match.sqrt(max(0, output)) output = math.sqrt(max(0, output))
output += self.p * deltadiff + self.i * deltat * diff output += self.p * deltadiff + self.i * deltat * diff
if self.output_func == 'square': if self.output_func == 'square':
output = output ** 2 output = output ** 2
@ -79,21 +81,6 @@ class PImixin(HasOutputModule, Writable):
if not value: if not value:
self.output_module.write_target(0) self.output_module.write_target(0)
def write_target(self, target): def write_target(self, _):
if not self.control_active: if not self.control_active:
self.activate_control() self.activate_control()
class PI(PImixin, Writable):
input = Attached(Readable, 'the input module')
relais = Attached(Writable, 'the interlock relais', mandatory=False)
def read_value(self):
return self.input.value
def write_target(self, value):
super().write_target(value)
if self.relais:
self.relais.write_target(1)