An "error" is introduced in the system at t1, and the controller takes of course corrective actions to make the error go away. It finally succeeds to do that at time t2, see "Error" in figure A.

But: The "Integration" part in the controller has been adding all errors so far, see curve "Integrated Error" in figure A. At the moment the actual error becomes zero, the "Integrated Error" is at its maximum (it can not go down unless the sign of the error itself changes).

This is a problem, because this "Integration residue" makes the controller believe there is still an error while this is not true: The result: the error now increases in the other direction, see the red "Error" line in figure A. This "overshoot" behaviour is of course totally unwanted.

Proportional only:

Proportional + Integrational, uncorrected:

As one can see here the error itself is zero much more sooner than with proporional correction alone. Unfortunately the above described mechanism causes an overshoot and a damped oscillation.

Proportional + Integrational, corrected:

As one can see here the overshoot is almost gone, and the damped oscillation is absent.

Now some "differentiation" correction is added, resulting in the red error behaviour as shown in figure A.

The good part of the behaviour: the amplitude of the error has become smaller, this behaviour is desired.

The bad part of the behaviour: it takes longer to finally get back to the correct value (error = 0). This behaviour is of course undesired.

Ths reason for this is that the differential behaviour is symmetrical: it tries to prevent "changes" in the error, also if the error is getting less.

Proportional only:

Proportional + Differential, uncorrected:

As can be seen here the amplitude of the error is much lower, but the time to go to zero error has almost doubled.

Proportional + Differential, corrected:

As can be seen here the zero error is achieved without extra delay.

type TPIDController = record PID_Kp, PID_Ki, PID_Kd: real; PID_Integrated : real; PID_Prev_Input: real; PID_MinOutput, PID_MaxOutput: real; PID_First_Time, PID_Int_Improvement, PID_Diff_Improvement: boolean; PID_Prev_AbsError: real; end; procedure Reset_PID(var Controller: TPIDController); begin with Controller do begin PID_Integrated := 0.0; PID_Prev_Input := 0.0; PID_Prev_AbsError := 0.0; PID_First_Time := true; end; end; procedure Set_PID_Int_Improvement(var Controller: TPIDController; On_: boolean); begin Controller.PID_Int_Improvement := On_; end; procedure Set_PID_Diff_Improvement(var Controller: TPIDController; On_: boolean); begin Controller.PID_Diff_Improvement := On_; end; procedure Init_PID(var Controller: TPIDController; Kp, Ki, Kd, MinOutput, MaxOutput: real); begin With Controller do begin PID_Kp := Kp; PID_Ki := Ki; PID_Kd := Kd; PID_MinOutput := MinOutput; PID_MaxOutput := MaxOutput; PID_Integrated := 0.0; PID_Prev_Input := 0.0; PID_Prev_AbsError := 0.0; PID_Int_Improvement := false; PID_Diff_Improvement := false; PID_First_Time := true; end; end; function Sign(Value: real): byte; begin Result := 0; // negative if Value >= 0 then Result := 1; // positive end; function AbsReal(val1: real): real; var Tmp: real; begin Result := Val1; if Result < 0 then Result := -Result; end; procedure Limit(var Value: real; MinOutput, MaxOutput: real); begin if Value < MinOutput then Value := MinOutput else if Value > MaxOutput then Value := MaxOutput; end; function PID_Calculate(var Controller: TPIDController; Setpoint, InputValue: real): real; var Err, ErrValue, DiffValue, ErrAbs: real; begin With Controller do begin Err := SetPoint - InputValue; if PID_Diff_Improvement then ErrAbs := AbsReal(Err); // --- calculate proportional value --- ErrValue := Err * PID_Kp; // --- PID Int Improvement --- if PID_Int_Improvement then if Sign(Err) <> Sign(PID_Integrated) then PID_Integrated := 0; // --- Calculate integrated value --- PID_Integrated := PID_Integrated + (Err * PID_Ki); // limit it to output minimum and maximum Limit(PID_Integrated, PID_MinOutput, PID_MaxOutput); // --- calculate derivative value --- if PID_First_Time then begin // to avoid a huge DiffValue the first time (PID_Prev_Input = 0) PID_First_Time := false; PID_Prev_Input := InputValue; PID_Prev_AbsError := 0; end; DiffValue := (InputValue - PID_Prev_Input) * PID_Kd; PID_Prev_Input := InputValue; // --- PID Diff Improvement --- if PID_Diff_Improvement then begin if ErrAbs < PID_Prev_AbsError then DiffValue := 0; // error becomes smaller, stop differentiation action PID_Prev_AbsError := ErrAbs; end; // --- calculate total --- Result := ErrValue + PID_Integrated - DiffValue; // mind the minus sign!!! // limit it to output minimum and maximum Limit(Result, PID_MinOutput, PID_MaxOutput); end; end;