ControlForge PIDE Autotune Guide
James M. Belcher Founder, JMB Technical Services LLC May 2026 | ControlForge v1.0.885
1. Why this exists
Rockwell's Logix PIDE has an autotune. It's good. It's also a paid Studio 5000 add-on, and it doesn't run anywhere else.
ControlForge ships a relay-feedback (Åström-Hägglund) autotune built directly into the PIDE function block. It is free, runs on any platform ControlForge runs on, and uses the same loop variables you already have.
Pulse one bit. Wait. Read out tuned KP, KI, KD.
2. How it works (in 60 seconds)
When you start a tune, the FB stops running the PID law and instead drives the loop with a relay: a square wave around your control variable's current value. The process responds with a self-sustained oscillation. From the period and amplitude of that oscillation, the FB derives:
- Ku — the ultimate gain, the proportional gain at which the loop would oscillate marginally
- Pu — the ultimate period of that oscillation
These are the two numbers Ziegler-Nichols, Tyreus-Luyben, and most other tuning rules want. Once they're known, picking gains is a one-liner.
PV
│ ┌──────┐ ┌──────┐ ┌──────┐
SP+hys ─┼─────│ │─────────│ │─────────│ │──── ← relay flips here
│ / \ / \ / \
SP ──┼───/ \─────/ \─────/ \────
│ / \ / \ / \
SP-hys ─┼─/ \─/ \─/ \── ← relay flips here
│
└────────────────────────────────────────────────────► t
◄────────── Pu ──────────►
The relay magnitude is the parameter ATSTEP. The hysteresis band is ATHYS. Wider hysteresis is more robust against measurement noise; tighter hysteresis converges faster.
3. The pins
Inputs
| Pin | Type | Default | Purpose |
|---|---|---|---|
AT | BOOL | — | Rising edge starts a tune. Falling edge clears status. |
ATSTEP | REAL | 5% of MAXO−MINO | Relay amplitude in CV units (one-sided). |
ATHYS | REAL | 0.5 | Relay hysteresis in PV units. Must exceed PV noise. |
ATTUNE | DINT | 0 | Tuning rule. See below. |
ATCYCLES | DINT | 5 | Full oscillation cycles to collect before computing. |
ATTIMEOUT | REAL | 600.0 | Hard cap in seconds before the FB declares failure. |
Outputs
| Pin | Type | Meaning |
|---|---|---|
ATA | BOOL | Autotune active. Relay is driving CV. |
ATD | BOOL | Done — gains valid. Stays high until AT drops. |
ATF | BOOL | Failed — timeout or no usable oscillation. |
ATKU | REAL | Measured ultimate gain. |
ATPU | REAL | Measured ultimate period (s). |
KP_AT | REAL | Recommended Kp. Commit into the live KP tag yourself once you've reviewed it. |
KI_AT | REAL | Recommended Ki. |
KD_AT | REAL | Recommended Kd. |
The autotune does NOT silently overwrite your
KP/KI/KDinputs. The result goes toKP_AT/KI_AT/KD_AT. Your ST decides whether — and when — to copy them into the active gain tags. This mirrors Rockwell PIDE's faceplate "Set Gains" gesture and avoids surprising the operator with a tune they didn't accept. See §4 for the pattern.
Tuning rules
ATTUNE | Name | Character |
|---|---|---|
| 0 | Ziegler-Nichols (closed-loop) | Aggressive. Fast response, ~25% overshoot. Good default for "make it move." |
| 1 | Tyreus-Luyben | Conservative. Better disturbance rejection, less overshoot. Best for processes that can't tolerate ringing. |
| 2 | Conservative ZN | Half the proportional gain of ZN, longer integral. Use when stability matters more than speed. |
If you don't know which to pick: start with rule 1 (Tyreus-Luyben). It is the rule used by most commercial autotuners.
4. Quick start
PROGRAM TankLevel
VAR
Level_PV : REAL; (* m, from sensor *)
Level_SP : REAL := 1.5; (* m, from operator *)
StartTune : BOOL; (* HMI momentary "Autotune" button *)
AcceptTune : BOOL := TRUE; (* HMI checkbox / latched commit gesture *)
(* Operator-managed gain tags. Pass these to the FB; never wire KP/KI/KD
to literal constants if you also want to apply the autotune result. *)
Loop_Kp : REAL := 2.0;
Loop_Ki : REAL := 0.1;
Loop_Kd : REAL := 0.5;
Loop : PIDE;
PrevATD : BOOL;
END_VAR
Loop(
PV := Level_PV,
SP := Level_SP,
KP := Loop_Kp, (* live gains, NOT literals *)
KI := Loop_Ki,
KD := Loop_Kd,
CYCLE := 0.1, (* 100 ms scan *)
MAXO := 100.0, (* 0..100 % valve *)
MINO := 0.0,
MAXS := 5.0, (* SP clamp *)
MINS := 0.0,
AT := StartTune,
ATSTEP := 10.0, (* ±10% valve swing *)
ATHYS := 0.05, (* 5 cm noise band *)
ATCYCLES := 5,
ATTUNE := 1 (* Tyreus-Luyben *)
);
(* Commit the autotune result on the rising edge of ATD, gated on the
operator's accept gesture. Without this block the autotune just sits
in KP_AT/KI_AT/KD_AT until you explicitly take it. *)
IF Loop.ATD AND NOT PrevATD AND AcceptTune AND Loop.KP_AT > 0.0 THEN
Loop_Kp := Loop.KP_AT;
Loop_Ki := Loop.KI_AT;
Loop_Kd := Loop.KD_AT;
END_IF;
PrevATD := Loop.ATD;
That's it. Toggle StartTune to TRUE once. Wait until Loop.ATD is TRUE. The ST block above copies KP_AT / KI_AT / KD_AT into Loop_Kp / Ki / Kd; the next FB scan applies them and the loop is back in closed-loop control with the tuned gains.
If you'd rather review the proposed gains on an HMI screen and accept them with a separate button, keep AcceptTune := FALSE initially and let the operator latch it.
5. Picking the parameters
ATSTEP — relay amplitude
Big enough to move the process clearly above the noise. Small enough that you don't slam alarms or violate process limits.
| Process feel | Suggested ATSTEP |
|---|---|
| Fast, well-behaved (flow, pressure on a small vessel) | 2–5% of CV range |
| Typical (level, temperature on a small reactor) | 5–10% |
| Sluggish or large dead time (long pipe runs, big tanks) | 10–20% |
If the process won't oscillate, increase ATSTEP. If oscillation is too violent, decrease.
ATHYS — relay hysteresis
Set this above your PV noise band but well below your acceptable swing.
A common rule: ATHYS ≈ 2 × σ(PV at steady state). If your PV jitters by ±0.1 units at rest, set ATHYS = 0.5. This stops the relay from chattering on noise.
If you set hysteresis too high, the loop won't switch and ATF will fire on timeout.
ATCYCLES — how many cycles to collect
The first half-cycle is always discarded as transient, so you actually need 2 × ATCYCLES + 1 half-period crossings. More cycles = more averaging = a more stable estimate, at the cost of longer tune time.
- 3 cycles: fast, noisier estimate
- 5 cycles: default, good balance
- 8–10 cycles: slow processes where Pu is large and one bad sample matters
ATTIMEOUT — bail-out clock
Total seconds of process time the tune is allowed to take. If the process hasn't completed ATCYCLES full cycles in this window, ATF fires.
Rule of thumb: ATTIMEOUT ≥ 4 × ATCYCLES × estimated Pu. For a 60-second loop, 5 cycles, allow ≥ 1200 seconds.
6. Reading the results
Success
IF Loop.ATD THEN
LogString := CONCAT('Autotune complete. Ku=',
REAL_TO_STRING(Loop.ATKU),
' Pu=', REAL_TO_STRING(Loop.ATPU),
' Kp_AT=', REAL_TO_STRING(Loop.KP_AT),
' Ki_AT=', REAL_TO_STRING(Loop.KI_AT),
' Kd_AT=', REAL_TO_STRING(Loop.KD_AT));
END_IF;
The recommended gains live in Loop.KP_AT / KI_AT / KD_AT whether or not you've committed them. The active values are whatever your ST is currently passing into the FB's KP / KI / KD inputs — usually a separate set of operator-managed tags (see §4).
Failure
IF Loop.ATF THEN
Alarm_AutotuneFailed := TRUE;
StartTune := FALSE; (* clear the trigger *)
END_IF;
Common causes are listed in §8.
7. Operating notes
When the FB will refuse to start a tune
ENis FALSE — loop is disabledMOis TRUE — loop is in manual mode
In both cases the AT pulse is silently ignored (ATD and ATF stay FALSE). Bring the loop online in auto first, then trigger.
What happens to the loop while tuning
- The PID law is bypassed. Integral, derivative state, and
firstRunare reset. - CV is driven by the relay only, clamped to
MAXO/MINO. E,PV,SPoutputs are still updated normally.- When the tune finishes, the FB returns to closed-loop with the new gains and a clean integrator — bumpless transfer.
Holding KP/KI/KD constant in the call
If your CALL pins KP, KI, or KD to literal constants:
Loop(KP := 1.0, KI := 0.5, ...); (* WRONG for autotune use *)
…the tuned values are overwritten by your constant on the next scan and the autotune accomplishes nothing.
Use VARs and let the FB write to them:
VAR
Loop_Kp : REAL := 1.0;
Loop_Ki : REAL := 0.5;
Loop_Kd : REAL := 0.0;
END_VAR
Loop(KP := Loop_Kp, KI := Loop_Ki, KD := Loop_Kd, ...);
(* After ATD, also do this if you want to persist after a restart: *)
IF Loop.ATD AND NOT Tuned THEN
Loop_Kp := Loop.KP_AT;
Loop_Ki := Loop.KI_AT;
Loop_Kd := Loop.KD_AT;
Tuned := TRUE;
END_IF;
8. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
ATF fires fast (< 1× expected Pu) | ATTIMEOUT too short | Raise it. Aim for ≥ 4× expected Pu × ATCYCLES. |
ATF fires at timeout, no oscillation visible | ATSTEP too small to move the process | Double ATSTEP. |
ATF fires at timeout, PV oscillating but never crosses hysteresis | ATHYS too wide | Lower it. Should be roughly twice your noise σ. |
| Relay chatters constantly, period too short | ATHYS too narrow vs. PV noise | Raise it. |
Tune completes but tuned KP is huge / KD is negative | Process is integrating (level, position) and ZN is over-aggressive | Switch ATTUNE to 1 (Tyreus-Luyben) or 2 (conservative). |
| Tune completes but the closed loop oscillates with new gains | Describing-function bias on a process with large dead time | Switch to ATTUNE = 1. If still bad, take ATKU and ATPU and tune by hand. |
ATA never goes TRUE despite rising edge | Loop is in MO (manual) or EN is FALSE | Switch loop to auto, ensure EN = TRUE. |
| Tuned gains overwritten back to your constants every scan | KP/KI/KD pinned to literals in the CALL | Use VARs. See §7. |
9. Theory (for the curious)
Under a relay of amplitude d and a process with frequency response G(jω), the loop oscillates at the frequency ω_u where the describing function locus −1/N(a) intersects G(jω). For an ideal relay (no hysteresis):
4 d
N(a) = ───
π a
so the ultimate gain is
4 d
Ku = ─────
π a
where a is the half-amplitude of the PV oscillation. The ultimate period Pu = 2π/ω_u is read directly from the time between same-direction switches.
This estimate is biased — typically Ku is overestimated by 10–30% on a first-order-plus-deadtime process. That bias is why Tyreus-Luyben (Kp = Ku/2.2) is often safer than the textbook ZN (Kp = 0.6·Ku) on real plants.
For the canonical reference, see:
Åström, K. J. & Hägglund, T. (1984). "Automatic tuning of simple regulators with specifications on phase and amplitude margins." Automatica 20(5):645–651.
10. Cheat sheet
(* Minimum-friction autotune — copy, edit two values, go. *)
VAR
Loop : PIDE;
PV, SP : REAL;
Kp : REAL := 1.0;
Ki : REAL := 0.0;
Kd : REAL := 0.0;
Tune : BOOL;
END_VAR
Loop(
PV := PV, SP := SP,
CYCLE := 0.1, (* edit: your scan in seconds *)
KP := Kp, KI := Ki, KD := Kd,
MAXO := 100.0, MINO := 0.0,
AT := Tune,
ATSTEP := 10.0, (* edit: ±% of CV range *)
ATHYS := 0.5,
ATCYCLES := 5,
ATTUNE := 1 (* Tyreus-Luyben *)
);
IF Loop.ATD THEN
Kp := Loop.KP_AT;
Ki := Loop.KI_AT;
Kd := Loop.KD_AT;
Tune := FALSE;
END_IF;