Universal fuzzy controller pv/sp -> y

FuzzyLogic.bog (11.2 KB)

// fuzzyLoop
// ===== Program:program =====
//
// ==============================================================================
// UNIVERSAL FUZZY CONTROLLER PV/SP → Y (via output increment)
// For: pressure / temperature / flow / speed / RPM, etc.
//
// Based on:
// - Timer: one-shot Clock.schedule(…) + re-create at the end of onExecute():contentReference[oaicite:0]{index=0}
// - Manual mode: full bypass of the logic (like “Service Mode”):contentReference[oaicite:1]{index=1}
// - Read BStatusNumeric only if status is OK or Overridden:contentReference[oaicite:2]{index=2}
//
// ------------------------------------------------------------------------------
// 1) WHAT THIS BLOCK DOES
// - Takes PV (measurement) and SP (setpoint)
// - Computes error: E = SP - PVf (PVf = filtered PV)
// - Computes error rate: dE/dt
// - From (E, dE/dt) selects a “level” L approximately from -2 to +2
// - Converts L to an output increment: dY = L * inStepPerSec * dt
// - Limits output slew rate inRateLimit and clamps to inOutMin..inOutMax
//
// Important: this is NOT a PID. There is no integral here.
// The block changes Y in “steps” based on error and error rate.
// That’s why it’s usually stable and easy to commission, but it needs tuning of Emax/DEmax/Step.
//
// ------------------------------------------------------------------------------
// 2) DEFINITIONS
// PV - measurement (Process Value), PV units (bar, °C, m3/h, rpm, …)
// SP - setpoint (Setpoint), PV units
// PVf - filtered PV, PV units
// E - error = SP - PVf, PV units
// dE/dt - error rate, PV/s
// Y - output (command), Y units (usually %)
// Direct: Y↑ → PV↑
// Reverse: Y↑ → PV↓
// ------------------------------------------------------------------------------
// 5) WIRING (CONNECTIONS)
// - inPV ← temperature/pressure/… sensor (StatusNumeric)
// - inSP ← setpoint (StatusNumeric)
// - outCmd → command to actuator/valve/VFD (usually 0..100%)
// - inEnable turn on from schedule/logic
// - inSrvMode + inManualOut for manual mode
//
// ------------------------------------------------------------------------------
// 6) FIRST COMMISSIONING (SIMPLEST)
// 1) Set output limits:
// inOutMin=0, inOutMax=100 (if %)
// 2) inDisableMode=Hold, inBadMode=Hold
// 3) Set inSrvMode=true and move inManualOut: make sure outCmd changes
// 4) inSrvMode=false, inEnable=true
// 5) Check direction:
// - if SP > PV then output must increase → inReverseAct=false (Direct)
// - if SP > PV then output must decrease → inReverseAct=true (Reverse)
// 6) Start with small “boldness” (inStepPerSec) and increase until the loop feels responsive.
//
// ------------------------------------------------------------------------------
// 7) STARTING POINT FOR TEMPERATURE (TYPICAL)
// (PV/SP in °C, Y in %)
// inPeriod = 500 ms
// inFilterTime = 3 s
// inDeadband = 0.2 °C
// inEmax = 3.0 °C
// inDEmax = 0.2 °C/s (for very slow air you can use 0.05)
// inStepPerSec = 4 %/s
// inRateLimit = 8 %/s
//
// ------------------------------------------------------------------------------
// 8) TUNING BY SYMPTOMS
// - Oscillates around the setpoint:
// increase inDeadband (0.3..0.5),
// or decrease inStepPerSec,
// or increase inFilterTime (5..8s).
// - Too slow:
// increase inStepPerSec,
// or decrease inEmax.
// - Overshoots:
// increase inEmax,
// or decrease inRateLimit.
//
// ------------------------------------------------------------------------------
// 9) WHAT outState MEANS
// Disabled - automation is off (inEnable=false)
// Manual - manual mode (inSrvMode=true)
// BadInput - PV/SP are bad (not OK and not Override):contentReference[oaicite:3]{index=3}
// Auto - normal operation
//
// ------------------------------------------------------------------------------
// 10) ABOUT TIME-BASED EXECUTION
// There are no while loops and no sleep here.
// Everything works via one-shot schedule and re-creating the ticket:contentReference[oaicite:4]{index=4}.
// Same style as in your updateTimer() examples:contentReference[oaicite:5]{index=5}.


PIDController.bog (11.8 KB)

INSTRUCTION: PID Controller

  1. Purpose
  • This module computes the control output in the range outMin…outMax (typically 0…100).
  • Works for a valve, damper, VFD, etc.
  1. Signals
  • setpoint (SP) : setpoint
  • input (PV) : measurement (controlled variable)
  • feedbackValue : actual/previous device output (important for incremental form)
  • output : controller output (command to the device)

Why feedbackValue:

  • The module computes u(t)=u(t-h)+du(t).
  • If feedbackValue is wired to the real device output, the module handles manual overrides,
    external limits, and saturation more stably (less integrator “windup”).
  1. Action direction
  • reverseAction=false (Direct): e = SP - PV
  • reverseAction=true (Reverse): e = PV - SP
  1. PID algorithm
  • Incremental form (velocity form)
  • D term based on PV (to avoid spikes on SP steps)
  • Dead zone deadZone: if |e| < deadZone, the module holds feedbackValue (or lastU/outMin)
  1. enable=false behavior via disableMode
  • Min : outputs outMin
  • Max : outputs outMax
  • Zero : outputs 0
  • Hold : holds a safe value (feedbackValue → lastU → outMin → 0)
  1. Status protection
  • If any critical input has a non-ok status, the module does NOT write null.
    It holds a safe value (Hold) and sets output with fault status.
  1. Autotune (autotuneEnable)
    7.1) tuningMode = Relay
  • output toggles between outMin and outMax:
    • when PV crosses SP, or
    • by timer relayPeriod
  • autotuneState shows Relay: High / Relay: Low
  • This mode does NOT compute kp/ki/kd. It is used to “excite” the plant and observe the response.

relayPeriod recommendations (seconds):

  • fast loops (pressure/flow/speed/RPM): 5…10
  • slow loops (temperature): 15…30

7.2) tuningMode = Ziegler
Important: in this version Ku is taken from the current kp.
That means:

  • you set kp yourself so that PV oscillates around SP (with SP crossings).
  • the module computes Tu from SP crossings (error sign change = half-period).
  • after 10 half-periods (5 periods) and after zieglerMinTime it writes new kp/ki/kd by Ziegler–Nichols:
    Kp = 0.6Ku
    Ki = 1.2
    Ku/Tu
    Kd = 0.075KuTu
  • then autotuneEnable turns off automatically, autotuneState becomes “Done: Tu=…”

zieglerMinTime recommendations (seconds):

  • fast loops: 60…120
  • temperature: 300…600

How to tell Ziegler is running:

  • autotuneState: “ZN: half=0/10” → “ZN: half=10/10”
  • the half counter increases only when PV crosses SP back and forth
  1. Wiring in Wire Sheet (required)
  • Feed input (PV) from the sensor
  • Feed setpoint (SP) from the setpoint source
  • Feed feedbackValue from the actual device output (if unavailable — leave empty, the module uses lastU/outMin)
  • Connect output to the device command
  1. Startup flags (Execute On Change)
    To make onExecute run automatically:
  • set “x” on: enable, disableMode, setpoint, input, feedbackValue,
    kp,ki,kd, dt, deadZone, outMin,outMax, reverseAction,
    autotuneEnable, tuningMode, relayPeriod, zieglerMinTime
  • do NOT set “x” on: output, autotuneState
  1. Common “doesn’t work” causes
  • output doesn’t change: onExecute is not called (missing x on inputs)
  • Relay doesn’t toggle: outMin/outMax not ok or equal
  • ZN half doesn’t increase: PV doesn’t cross SP (kp too small / wrong reverseAction / plant not responding)
1 Like