def output_limits(self, limits): if limits is None: self._min_output, self._max_output = None, None return min_output, max_output = limits if None not in limits and max_output < min_output: raise ValueError('lower limit must be less than upper limit') self._min_output = min_output self._max_output = max_output self._integral = Helpers.clamp(self._integral, self.output_limits) self._last_output = Helpers.clamp(self._last_output, self.output_limits)
def __call__(self, _input, dt=None): if not self.auto_mode: # if we are in manual mode, dont calculate. return self._last_output now = Helpers.get_current_time() # getting current time. if dt is None: dt = now - self._last_time if now - self._last_time else 1e-16 elif dt <= 0: raise ValueError( 'dt has a non-positive value {}. Value Must be non-negative.'. format(dt)) if self.update_t is not None and dt < self.update_t and self._last_output is not None: # only update every update_t seconds return self._last_output # COMPUTING ERROR TERM, reducing from the setpoint our current input (how far are we from the setpoint). error = self.set_point - _input d_input = _input - (self._last_input if self._last_input is not None else _input) # COMPUTING PROPORTIONAL TERM. self._proportional = self.kp * error # COMPUTING INTEGRAL TERM. self._integral += self.ki * error * dt self._integral = Helpers.clamp(self._integral, self.output_limits) # COMPUTING DERIVATIVE TERM. self._derivative = -self.kd * d_input / dt # final output, summing all values together. output = self._proportional + self._integral + self._derivative output = Helpers.clamp(output, self.output_limits) # keep track of state self._last_output = output self._last_input = _input self._last_time = now return output
def set_auto_mode(self, enabled, last_output=None): """ Enable or disable the PID controller, optionally setting the last output value. This is useful if some system has been manually controlled and if the PID should take over. In that case, pass the last output variable (the control variable) and it will be set as the starting I-term when the PID is set to auto mode. :param enabled: Whether auto mode should be enabled, True or False :param last_output: The last output, or the control variable, that the PID should start from when going from manual mode to auto mode """ if enabled and not self.auto_mode: # switching from manual mode to auto, reset self.reset() self._integral = last_output if last_output is not None else 0 self._integral = Helpers.clamp(self._integral, self.output_limits) self.auto_mode = enabled