Source code for cleo.ioproc.controllers

"""Contains a basic PI controller"""
from __future__ import annotations
from cleo.ioproc import ProcessingBlock
from typing import Any


[docs] class PIController(ProcessingBlock): """Simple PI controller. :meth:`compute_output` requires a ``sample_time_ms`` keyword argument. Only tested on controlling scalar values, but could be easily adapted to controlling a multi-dimensional state. """ ref_signal: callable[[float], Any] """Callable returning the target as a function of time in ms""" def __init__( self, ref_signal: callable, Kp: float, Ki: float = 0, sample_period_ms: float = 0, **kwargs: Any, ): """ Parameters ---------- ref_signal : callable Must return the target as a function of time in ms Kp : float Gain on the proportional error Ki : float, optional Gain on the integral error, by default 0 sample_period_ms : float, optional Rate at which processor takes samples, by default 0. Only used to compute integrated error on first sample """ super().__init__(**kwargs) self.ref_signal = ref_signal self.Kp = Kp self.Ki = Ki self.sample_period_ms = sample_period_ms self.integrated_error = 0 self.prev_time_ms = None
[docs] def compute_output(self, input: float, **kwargs) -> float: """Compute control input to the system using previously specified gains. Parameters ---------- input : Any Current system state Returns ------- float Control signal """ time_ms = kwargs["sample_time_ms"] if self.prev_time_ms is None: self.prev_time_ms = time_ms - self.sample_period_ms intersample_period_s = (time_ms - self.prev_time_ms) / 1000 error = self.ref_signal(time_ms) - input self.integrated_error += error * intersample_period_s self.prev_time_ms = time_ms return self.Kp * error + self.Ki * self.integrated_error