import time
[docs]
class PID:
def __init__(self, Kp, Ki, Kd, setpoint=0, output_limits=(None, None)):
"""
Initialize the PID controller.
:param Kp: Proportional gain
:param Ki: Integral gain
:param Kd: Derivative gain
:param setpoint: The target value that the PID controller tries to achieve
:param output_limits: Tuple (min_output, max_output) for limiting output
"""
self.Kp = Kp
self.Ki = Ki
self.Kd = Kd
self.setpoint = setpoint
self.output_limits = output_limits
self._last_time = None
self._last_error = None
self._integral = 0
self._last_output = 0
[docs]
def update(self, feedback_value, dt=None):
"""
Update the PID loop with the current feedback value.
:param feedback_value: The current value from the process
:param dt: Optional time interval. If not provided, it's calculated internally.
:return: The control output
"""
current_time = time.time()
error = self.setpoint - feedback_value
if self._last_time is None:
# First call, just initialize and return 0
self._last_time = current_time
self._last_error = error
if self._last_output is not None:
return self._last_output
return 0
# Calculate time difference (dt) if not provided
if dt is None:
delta_time = current_time - self._last_time
else:
delta_time = dt
# Ensure we don't update too frequently
if delta_time <= 0.0:
return self._last_output
# Proportional term
proportional = self.Kp * error
# Integral term
self._integral += error * delta_time
integral = self.Ki * self._integral
# Derivative term
delta_error = error - self._last_error
derivative = self.Kd * (delta_error / delta_time)
# Compute the output
output = proportional + integral + derivative
# Limit the output to specified limits
min_output, max_output = self.output_limits
if min_output is not None:
output = max(min_output, output)
if max_output is not None:
output = min(max_output, output)
# Store values for the next loop iteration
self._last_output = output
self._last_time = current_time
self._last_error = error
return output
[docs]
def set_output_limits(self, min_output, max_output):
"""
Set the minimum and maximum output limits.
:param min_output: Minimum limit
:param max_output: Maximum limit
"""
self.output_limits = (min_output, max_output)
[docs]
def set_setpoint(self, setpoint):
"""
Set a new target value for the PID to reach.
:param setpoint: The target value
"""
self.setpoint = setpoint
[docs]
def set_last_output(self, output):
"""
Set the last output value.
:param output: The last output value
"""
self._last_output = output
[docs]
def set_last_error(self, error):
"""
Set the last error value.
:param error: The last error value
"""
self._last_error = error
[docs]
def set_integral_output(self, integral_output):
"""
Initialise the integral output for a desired output value.
:param integral: The integral integral_output value
"""
if self.Ki == 0:
self._integral = 0
else:
self._integral = integral_output / self.Ki
[docs]
def reset(self):
"""
Reset the internal state of the PID controller.
"""
self._last_time = None
self._last_error = None
self._integral = 0
self._last_output = 0