Some loops never tune. The gain that is right at low fire is twice too high at full fire, the dead time changes with the product, and the best operator on the night shift runs it by hand better than the PID ever did. That operator’s habits are a rule table, and a rule table is what a fuzzy logic plc controller executes. Here is where fuzzy earns its place against PID, how to hand-code it in Structured Text without a toolbox, and a dryer outlet-temperature controller from one line as the worked example.
Decide whether fuzzy is worth the trouble
| Situation | Better choice | Why |
|---|---|---|
| Linear process, one input, one output, gain steady across the range | PID | Two tunable numbers, autotune exists, everyone can maintain it |
| Gain changes a lot with load: kiln, dryer, burner from low to high fire | Fuzzy, or gain-scheduled PID | The rule table is a gain schedule you can read |
| Level control with a variable and unmeasured inflow | Fuzzy on error and error rate | Reacts to the trend, not just the level |
| Several inputs, one output, operator runs it by feel: HVAC comfort, ambient plus occupancy | Fuzzy | Rules combine inputs without a model |
| Loop that must pass a formal stability review | PID | Fuzzy stability is argued by simulation and trials, not by a margin |
Gain-scheduled PID solves half of the second row and is what I try first. Fuzzy wins when the operator rules involve more than one input, or when nobody can write a model but everyone can say what they would do. The PID side is set up in PLC PID control in Studio 5000.
Know what the vendors actually give you
Siemens sold FuzzyControl++ for the S7-300 and S7-400 generation and PCS 7, with a Windows tool that draws memberships and rules and downloads a block. It was the one mainstream packaged option, and I would not plan a new S7-1500 project around it without asking Siemens what is current for that platform. Rockwell has nothing native in Studio 5000. IEC 61131-7 defines a Fuzzy Control Language and the vocabulary worth learning, membership function, rule block, defuzzification method, but no mainstream PLC IDE loads it.
So you hand-code it: about sixty lines of Structured Text, and after the first one you keep the routine and change the tables.
Build the controller in four pieces
- Fuzzify. Each input gets three to five overlapping triangular sets, Negative, Zero, Positive for the small cases. A triangle is two IF clamps and a division; a lookup table is only needed for odd shapes.
- Rule table. One cell per input combination, holding the name of the output set. For two inputs with three sets each that is nine cells.
- Inference. AND is the minimum of the input memberships, so each rule gets a firing strength between 0 and 1.
- Defuzzify. True centroid needs the output sets integrated numerically every scan. On a PLC use singleton outputs and a weighted average: multiply each rule strength by its output value, sum, divide by the sum of strengths. This is the Takagi-Sugeno zero-order form, and it is what almost every PLC implementation runs.
Make the output an increment, a change in valve position per execution, not an absolute position. That gives the controller an integrating action for free, the same reason a velocity-form PI comes back from manual without a bump.
Worked example: dryer outlet temperature
The machine is a gas-fired belt dryer on a 1769-L33ER in Studio 5000 v32. Outlet air temperature is the controlled variable, the burner modulating valve is the output, setpoint on this product is 140 degC. The PID was stable at low fire and cycled at high fire, and the operators ran it in manual for two years. The fuzzy controller executes every 10 s in its own periodic task.
Inputs and sets, values on this dryer:
| Input | Negative fully at | Zero peak | Positive fully at | Meaning |
|---|---|---|---|---|
Err = SP minus PV, degC | minus 10 and below | 0 | plus 10 and above | Negative is too hot, Positive is too cold |
dErr, degC per minute | minus 2 and below | 0 | plus 2 and above | Negative means the error is falling, the dryer is heating up |
Rule table, output singletons in percent of valve travel per 10 s step:
Err down, dErr across | dErr Negative, heating up | dErr Zero | dErr Positive, cooling down |
|---|---|---|---|
| Negative, too hot | minus 3.0 | minus 1.0 | 0.0 |
| Zero | minus 1.0 | 0.0 | plus 1.0 |
| Positive, too cold | 0.0 | plus 1.0 | plus 3.0 |
Read it as the operator would. Too hot and still climbing, cut hard. Too hot but already cooling, leave it alone. On setpoint and drifting cold, nudge it up. The two 3.0 corners are the aggressive rules; the two 0.0 corners stop the overshoot a PID would produce.
(* Fuzzy_Dryer routine, 10 s periodic task, 1769-L33ER, Studio 5000 v32 *)
(* Rule_Out is REAL[3,3], row = Err set, column = dErr set, filled from the table at first scan *)
(* index 0 = Negative, 1 = Zero, 2 = Positive *)
Err := Dryer_SP - Dryer_PV_Filt; (* PV through a 30 s first-order filter *)
dErr := (Err - Err_Last) * 6.0; (* per 10 s step to degC per minute *)
Err_Last := Err;
(* fuzzify Err, triangles at -10, 0, +10 *)
IF Err <= -10.0 THEN
mE[0] := 1.0;
ELSIF Err >= 0.0 THEN
mE[0] := 0.0;
ELSE
mE[0] := -Err / 10.0;
END_IF;
IF Err >= 10.0 THEN
mE[2] := 1.0;
ELSIF Err <= 0.0 THEN
mE[2] := 0.0;
ELSE
mE[2] := Err / 10.0;
END_IF;
mE[1] := 1.0 - mE[0] - mE[2]; (* the three triangles sum to 1 across the range *)
(* fuzzify dErr, triangles at -2, 0, +2 *)
IF dErr <= -2.0 THEN
mD[0] := 1.0;
ELSIF dErr >= 0.0 THEN
mD[0] := 0.0;
ELSE
mD[0] := -dErr / 2.0;
END_IF;
IF dErr >= 2.0 THEN
mD[2] := 1.0;
ELSIF dErr <= 0.0 THEN
mD[2] := 0.0;
ELSE
mD[2] := dErr / 2.0;
END_IF;
mD[1] := 1.0 - mD[0] - mD[2];
(* inference, AND = min, and weighted-average defuzzification in one pass *)
Num := 0.0;
Den := 0.0;
FOR i := 0 TO 2 DO
FOR j := 0 TO 2 DO
IF mE[i] < mD[j] THEN
w := mE[i];
ELSE
w := mD[j];
END_IF;
Num := Num + w * Rule_Out[i, j];
Den := Den + w;
END_FOR;
END_FOR;
IF Den > 0.0 THEN
dCV := Num / Den;
ELSE
dCV := 0.0;
END_IF;
(* incremental output, clamped with IF, no LIMIT in Logix ST *)
IF Fuzzy_Active THEN
Burner_CV := Burner_CV + dCV;
IF Burner_CV > 100.0 THEN
Burner_CV := 100.0;
ELSIF Burner_CV < 20.0 THEN
Burner_CV := 20.0; (* low-fire limit on this burner *)
END_IF;
END_IF;
Two things to notice. Summing every rule’s strength in Den instead of taking the max per output set skips the aggregation step of a textbook Mamdani controller. With singleton outputs this is the standard weighted average, and the loop is nine iterations of REAL arithmetic. On the L33ER this routine adds well under a millisecond, confirmed on the Monitor tab of the task properties; see PLC scan time and cycle time. Second, the output is only integrated while Fuzzy_Active is true, so switching to manual or PID leaves Burner_CV where it was and switching back is bumpless.

The enable rung lives in ladder where operators expect it. Any fault on the burner management side drops Fuzzy_Active and the valve holds its last position until the burner sequence takes over.
Compare the response, with example numbers
The chart is from a 20 degC setpoint step on this dryer, drawn as bands rather than curves. Settled means the outlet has been inside plus or minus 2 degC of setpoint for three minutes without leaving.

| Measure, same step, same dryer | PID as tuned at low fire | Fuzzy, table above |
|---|---|---|
| First reaches setpoint | 11 min after the step | 14 min after the step |
| Overshoot above the 2 degC band | 6 degC, from minute 13 to minute 19 | none |
| Settled inside 2 degC | 33 min after the step, minute 34 | 17 min after the step, minute 18 |
These are example values from one product on one dryer, not a claim about fuzzy in general; a well gain-scheduled PID would have closed most of the gap. What the table shows is the shape: fuzzy arrives later and does not overshoot, because the two zero corners of the rule table stop pushing as soon as the trend is right.
Tune it from what the operator says
- Sit with the operator for a shift and write down every move as a sentence. “When it is running hot and still climbing I pull the gas right back” is the top-left cell.
- Set the input ranges from the trend, not the spec sheet. On this dryer the error stayed inside 10 degC in normal running and the rate inside 2 degC per minute, so those became the full-membership points.
- Start with singletons of 1.0 and 3.0 percent per step and run a setpoint step. Too slow, raise the corners. Oscillation, lower the middle ring first, then the corners.
- Narrow the Err triangles if small errors close too slowly; a wide Zero set barely moves the valve for a 3 degC error.
- Log
mE,mD,dCVandBurner_CVon the trend for the first week. Seeing which rule fires when is the only way to argue about the table with the operator.
Field notes
Rate input chasing thermocouple noise. The first version computed dErr from the raw PV every 10 s, so a 1 degC flicker on the thermocouple became a 6 degC per minute swing and flipped the rule from Zero to Positive every other step. A 30 s first-order filter on the PV before the error calculation cured it. Filter before you differentiate.
Valve stuck at low fire, controller still pushing. The incremental output kept adding negative steps while the valve sat at its 20 percent low-fire limit, so when the dryer finally needed heat the controller had to climb from a number far below 20. The clamp in the routine above stops the accumulation at the limits. It is integral windup by another name.
Slow to recover after a product change. On a sister dryer running the same routine, the outlet sat 3 degC low for most of an hour after every product change. With the Err triangles at plus or minus 10 degC, a 3 degC error with a flat trend fires the Zero rule at 0.7 and the Positive rule at 0.3, so the weighted average is 0.3 percent a step. Changing the 10.0 in that dryer’s Err fuzzifier to 5.0 made it 0.6 percent a step and cut the recovery time roughly in half.
Frequently asked questions
Is fuzzy control better than PID?
Not in general. On a linear loop PID is simpler, faster to tune and easier to hand over. Fuzzy is for loops where the gain changes across the range, or where more than one input decides the move.
Can I write this in ladder instead of ST?
Yes, with CPT instructions for each membership and each rule, but nine rules and six memberships become forty rungs that nobody wants to maintain. Put the inference in an ST routine or an Add-On Instruction and keep only the enable and the mode selection in ladder, as the loops in Allen Bradley PLC function block programming do.
How many rules do I need?
Nine covers most two-input loops. Five sets per input gives 25 rules and is only worth it when the operator genuinely has more than three words for how hot the process is.
Next step
Before you write a rule table, confirm the loop is really non-linear and not just badly tuned: run the bump test in PLC PID control automation at low and high load and compare the process gains. If they differ by less than a factor of two, tune the PID and go home. If the operator is beating it by hand, build the table.