ℹ️ Select 'Choose Exercise', or randomize 'Next Random Exercise' in selected language.

Choose Exercise:
Timer 00:00
WPM --
Score --
Acc --
Correct chars --

PID Loop with Anti-Windup and Output Saturation

PLC Structured Text

Goal -- WPM

Ready
Exercise Algorithm Area
1PROGRAM PIDController
2
3VAR
4// Inputs
5Setpoint : REAL := 0.0;
6ProcessVariable : REAL := 0.0;
7EnableControl : BOOL := FALSE;
8
9// Outputs
10ControlOutput : REAL := 0.0;
11
12// PID Parameters
13Kp : REAL := 1.0;
14Ki : REAL := 0.5;
15Kd : REAL := 0.2;
16
17// Output Limits
18OutputMin : REAL := -100.0;
19OutputMax : REAL := 100.0;
20
21// Time Base
22CycleTime : TIME := T#100MS;
23dt : REAL;
24
25// Internal Variables
26error : REAL;
27lastError : REAL := 0.0;
28integral : REAL := 0.0;
29derivative : REAL;
30proportionalTerm : REAL;
31integralTerm : REAL;
32derivativeTerm : REAL;
33isOutputSaturated : BOOL := FALSE;
34lastControlOutput : REAL := 0.0;
35
36END_VAR
37
38// Convert CycleTime to seconds for calculations
39// Assuming CycleTime is a TIME literal like T#100MS
40dtc := TIME_TO_REAL(CycleTime) / 1000.0; // Convert ms to seconds
41
42// Main PID Calculation
43IF EnableControl THEN
44
45// Calculate Error
46error := Setpoint - ProcessVariable;
47
48// Proportional Term
49proportionalTerm := Kp * error;
50
51// Integral Term (with Anti-Windup)
52// Only integrate if the output is not saturated OR if the error is driving the output away from saturation
53IF NOT isOutputSaturated OR (isOutputSaturated AND (error * Kp) < 0) THEN
54integral := integral + (Ki * error * dt);
55// Clamp integral term to prevent excessive windup if Ki is very large
56// This is a basic form of anti-windup; more sophisticated methods exist.
57IF integral > (OutputMax / Ki) THEN
58integral := OutputMax / Ki;
59ELSIF integral < (OutputMin / Ki) THEN
60integral := OutputMin / Ki;
61END_IF;
62END_IF;
63integralTerm := integral;
64
65// Derivative Term
66// Avoid division by zero if dt is zero (though CycleTime should prevent this)
67IF dt > 0.0 THEN
68derivative := (error - lastError) / dt;
69derivativeTerm := Kd * derivative;
70ELSE
71derivativeTerm := 0.0;
72END_IF;
73
74// Calculate Total Control Output
75ControlOutput := proportionalTerm + integralTerm + derivativeTerm;
76
77// Output Saturation and Anti-Windup Check
78lastControlOutput := ControlOutput;
79IF ControlOutput > OutputMax THEN
80ControlOutput := OutputMax;
81isOutputSaturated := TRUE;
82ELSIF ControlOutput < OutputMin THEN
83ControlOutput := OutputMin;
84isOutputSaturated := TRUE;
85ELSE
86isOutputSaturated := FALSE;
87END_IF;
88
89// Update last error for the next cycle
90lastError := error;
91
92ELSE
93// If control is disabled, reset integral term and output
94integral := 0.0;
95ControlOutput := 0.0;
96lastError := 0.0;
97isOutputSaturated := FALSE;
98END_IF;
99
100END_PROGRAM
Algorithm description viewbox

PID Loop with Anti-Windup and Output Saturation

Algorithm description:

This program implements a Proportional-Integral-Derivative (PID) controller, a cornerstone of industrial automation for maintaining a process variable at a desired setpoint. It includes crucial features like output saturation, which limits the controller's output to physical constraints (e.g., maximum heater power), and anti-windup logic to prevent the integral term from accumulating excessively when the output is saturated, ensuring faster recovery. This is vital for systems like temperature control, flow regulation, or motor speed control.

Algorithm explanation:

The algorithm implements a discrete-time PID controller. It calculates the `error` between `Setpoint` and `ProcessVariable`. The `proportionalTerm` is `Kp * error`. The `integralTerm` accumulates `Ki * error` over time (`dt`). Crucially, the integral term is only updated if the output is not saturated or if the error is pushing the output away from saturation, preventing integral windup. The `derivativeTerm` is `Kd * (error - lastError) / dt`, helping to dampen oscillations. The `ControlOutput` is the sum of these three terms. Output saturation is handled by clamping `ControlOutput` to `OutputMin` and `OutputMax`, and setting `isOutputSaturated` flag. This flag is then used by the anti-windup logic. Time complexity is O(1) per scan cycle due to fixed calculations. Space complexity is O(1) as it uses a fixed number of variables.

Pseudocode:

PROGRAM PIDController
VAR
    // Inputs
    Setpoint, ProcessVariable, EnableControl : VARIOUS_TYPES
    // Outputs
    ControlOutput : REAL
    // PID Parameters
    Kp, Ki, Kd : REAL
    // Output Limits
    OutputMin, OutputMax : REAL
    // Time Base
    CycleTime : TIME
    dt : REAL
    // Internal Variables
    error, lastError, integral, derivative, proportionalTerm, integralTerm, derivativeTerm : REAL
    isOutputSaturated : BOOL
    lastControlOutput : REAL
END_VAR

// Convert CycleTime to dt (seconds)
dt := TIME_TO_REAL(CycleTime) / 1000.0

IF EnableControl THEN

    // Calculate Error
    error := Setpoint - ProcessVariable

    // Proportional Term
    proportionalTerm := Kp * error

    // Integral Term (with Anti-Windup)
    IF NOT isOutputSaturated OR (isOutputSaturated AND (error * Kp) < 0) THEN
        integral := integral + (Ki * error * dt)
        // Clamp integral term to prevent excessive windup
        IF integral > (OutputMax / Ki) THEN
            integral := OutputMax / Ki
        ELSIF integral < (OutputMin / Ki) THEN
            integral := OutputMin / Ki
        END_IF
    END_IF
    integralTerm := integral

    // Derivative Term
    IF dt > 0.0 THEN
        derivative := (error - lastError) / dt
        derivativeTerm := Kd * derivative
    ELSE
        derivativeTerm := 0.0
    END_IF

    // Calculate Total Control Output
    ControlOutput := proportionalTerm + integralTerm + derivativeTerm

    // Output Saturation and Anti-Windup Check
    lastControlOutput := ControlOutput
    IF ControlOutput > OutputMax THEN
        ControlOutput := OutputMax
        isOutputSaturated := TRUE
    ELSIF ControlOutput < OutputMin THEN
        ControlOutput := OutputMin
        isOutputSaturated := TRUE
    ELSE
        isOutputSaturated := FALSE
    END_IF

    // Update last error
    lastError := error

ELSE
    // Reset when control is disabled
    integral := 0.0
    ControlOutput := 0.0
    lastError := 0.0
    isOutputSaturated := FALSE
END_IF

END_PROGRAM