Software PID

A block diagram of a PID controller in a feedback
loop.

A proportional-integral-derivative controller (PID controller) is a feedback-based control loop mechanism that is widely employed in industrial control systems and a variety of other applications requiring constantly modulated control. Hence the name, a PID controller constantly calculates an error value e as the difference between a desired set-point (SP) and a measured process variable (PV) and then makes a correction based on proportional, integral, and derivative terms (denoted P, I, and D, respectively).

The PID control scheme is named for its three correcting terms, the sum of which represents the variable being controlled. The proportional, integral, and derivative terms are added together to determine the PID controller’s output. Using U_out as the output of the controller, the final version of the PID algorithm is

formula

where:

  • ( K_p ) is the proportional gain, a tuning parameter.
  • ( K_i ) is the integral gain, a tuning parameter.
  • ( K_d ) is the derivative gain, a tuning parameter.
  • ( e(t) ) is the error, defined as the difference between the set-point (SP) and the process variable (PV(t)).
  • ( t ) is the time or instantaneous time (the present).
  • ( T ) is the variable of integration, taking on values from time ( 0 ) to the present time ( t ).

Proportional term

The output produced by the proportional term is proportionate to the current error value. Adjusting the proportional response is accomplished by multiplying the error by the proportional gain constant, $K_p$.

A high proportional gain causes a substantial change in output for a given error change. If the proportionate gain is excessively large, the system may become unstable. A modest gain, on the other hand, results in a limited output reaction to a big input error and a controller that is less responsive or sensitive. In response to system disturbances, the control action may be too modest if the proportional gain is too low.

Integral term

The contribution made by the integral term is proportional to both the amount and duration of the mistake. In a PID controller, the integral is the total of the instantaneous error across time and provides the cumulative offset that should have been rectified before. Error accumulation is then multiplied by integral gain $K_i$ and added to the controller output.

The integral term speeds the process’s approach to the set-point and removes the residual steady-state error that happens with a proportional controller. Nevertheless, as the integral term responds to mistakes accumulated in the past, it might lead the current result to exceed the set-point value.

Derivative term

Calculating the process error’s derivative involves finding the error’s slope over time and multiplying this rate of change by the derivative gain $K_d$. The size of the derivative term’s contribution to the total control action is known as the derivative gain, or $K_d$.

Derivative action enhances the system’s settling time and stability by predicting its behavior. Implementations of PID controllers require extra low-pass filtering for the derivative term to reduce high-frequency gain and noise because an ideal derivative is not causative.

Code

import time
from PyQt5.QtCore import QObject
import pyexcel as p

class BasisTempStabilizer(QObject):
    def __init__(self, data, pid_mutex, the_mutex, pid, thermostat, settings):
        DATA = data
        SETTINGS = settings
        PID = pid
        THE = thermostat
        PIDMUX = pid_mutex
        THEMUX = the_mutex

        first_iteration = True
        previousTime = 0
        sleepTime = 1

        pidP = SETTINGS.pidP
        pidI = SETTINGS.pidI
        pidD = SETTINGS.pidD

        erri = 0
        errd = 0
        errPrev = 0

        while True:
            with PIDMUX:
                if DATA.sigmaMode:
                    th0 = 0.5*(PID.getSensorTemp(1) + PID.getSensorTemp(2))
                else:
                    th0 = PID.getSensorTemp(2)

            currentTime = time.time()
            dt = currentTime - previousTime
            previousTime = currentTime

            # initial good guess for the thermostat target temperature
            th1 = self.ThermostatFluidTargetTemperature(DATA.stabilizerTargetTemp)
            err = DATA.stabilizerTargetTemp - th0

            # start controlling the thermostat actively only when we are close to the target temperature
            # PID control
            if abs(err) < 1 or not DATA.sigmaMode:
                # proportional term
                th1 += pidP*err
                # integral term                
                if not first_iteration:
                    erri += err*dt
                    th1 += pidI*erri
                # derrivative term
                if not first_iteration:
                    errd = (err - errPrev)/dt
                    errPrev = err
                    th1 += pidD*errd
                first_iteration = False
            else:
                first_iteration = True
                erri = 0
                errPrev = 0

            errPrev = err

            with THEMUX:
                DATA.thermostatTargetTemp = th1
                THE.setTargetTemperature(th1)

            # stop thread check
            if DATA.stabilizerStopThread:
                break

            # pause thread check
            while DATA.stabilizerPauseThread:
                time.sleep(0)
        
            time.sleep(sleepTime)

    def ThermostatFluidTargetTemperature(self, coldBasisTargetTemperature):
        a = - 0.000108772
        b = 1.06805
        c = - 1.82042
        x = coldBasisTargetTemperature
        return a*x*x + b*x + c