Our standard form for PID control has been

\begin{align} MV & = \overline{MV} + K_P (SP - PV) + K_I\int_0^t (SP - PV)\ dt' + K_D\frac{d(SP - PV)}{dt} \end{align}

Our initial implementation of PID control uncovered an issue with this formula when there are sudden changes to the setpoint $SP$. The issue is most obvious in the derivative term where

\begin{align} K_D\frac{d(SP - PV)}{dt} & \longrightarrow \infty \end{align}

for a step change in setpoint. The issue is also evident in the proportional term $K_P (SP - PV)$ which undergoes a step change in value. In process control applications, a step change in the manipulated variable may require sudden and abrupt changes in valve position, process flows, or pressures, all of which can cause significant strain on very large devices.

This phenomenon is called **setpoint kick** (or sometimes **derivative kick**) which is generally to be avoided.

The most common remedy is to introduce setpoint weighting. To this end, the PID control formula is written

\begin{align} MV & = \overline{MV} + K_P e_P + K_I\int_0^t e_I\ dt' + K_D\frac{de_D}{dt} \end{align}

where the error terms are given by

\begin{align*} e_P(t) & = \beta\ SP - PV \\ e_I(t) & = SP - PV \\ e_D(t) & = \gamma\ SP - PV \end{align*}

The effect is to introduce a different error term for each term in the control equation. The integral term remains the same as before, which is necessary in order to force the process variable $PV$ to be equal to the setpoint $SP$ at steady-state. The error terms for the proportional and derivative terms, however, include constants $\beta$ and $\gamma$ which are used to modify the response to setpoint changes.

Common practice is to set $\gamma = 0$ which entirely eliminates derivative action based on change in the setpoint. When incorporated in PID control, this feature is sometimes called **derivative on output**. This almost always a good idea in process control because it eliminates the 'derivative kick' associated with a quick change in setpoint.

In practice, the term $\beta$ is generally tuned to meet the specific application requirements. In this case, where setpoint tracking is not a high priority, setting $\beta = 0$ is a reasonable starting point.

The implementation of setpoint weighting is straightforward. The object definition is modified to include additional parameters `beta`

and `gamma`

with typical default values. Error terms for the proportional and derivative terms are computed explicitly, and in the case of the derivative also saved for the next time step.

In [1]:

```
def PID(Kp, Ki, Kd, MV_bar=0, beta=1, gamma=0):
# initialize stored data
eD_prev = 0
t_prev = -100
I = 0
# initial control
MV = MV_bar
while True:
# yield MV, wait for new t, SP, PV
t, PV, SP = yield MV
# PID calculations
P = Kp*(beta*SP - PV)
I = I + Ki*(SP - PV)*(t - t_prev)
eD = gamma*SP - PV
D = Kd*(eD - eD_prev)/(t - t_prev)
MV = MV_bar + P + I + D
# update stored data for next iteration
eD_prev = eD
t_prev = t
```

Let's see how well this PID implementation works. We'll perform the simulation with a setpoint that starts at room temperature, then rises to 50°C at $t = 50$ seconds. The data historian will record values of the setpoint, process variable, computed manipulated variable, and the actual value of the manipulated variable.

In [2]:

```
%matplotlib inline
from tclab import clock, setup, Historian, Plotter
TCLab = setup(connected=False, speedup=10)
controller = PID(2, 0.1, 2, beta=0) # create pid control
controller.send(None) # initialize
tfinal = 800
with TCLab() as lab:
h = Historian([('SP', lambda: SP), ('T1', lambda: lab.T1), ('MV', lambda: MV), ('Q1', lab.Q1)])
p = Plotter(h, tfinal)
T1 = lab.T1
for t in clock(tfinal, 2):
SP = T1 if t < 50 else 50 # get setpoint
PV = lab.T1 # get measurement
MV = controller.send([t, PV, SP]) # compute manipulated variable
lab.U1 = MV # apply
p.update(t) # update information display
```