Skip to content

L07: PID Tuning Methods

Prerequisites: L06 | Effort: 60 min | Seborg: Chapter 12


Learning Objectives

By the end of this lesson you will:

  1. โœ… Apply Ziegler-Nichols tuning rules
  2. โœ… Use IMC/Direct Synthesis tuning method
  3. โœ… Compare tuning methods for FOPTD processes
  4. โœ… Understand tuning tradeoffs (speed vs robustness)
  5. โœ… Generate training data for different tuning strategies

Theory Recap: PID Tuning (Seborg Ch. 12)

The Tuning Problem: Given a process model (K, ฯ„, ฮธ), calculate PID parameters (Kp, Ki, Kd) for desired performance.

Method 1: Ziegler-Nichols (1942)

For FOPTD model: G(s) = Kยทe^(-ฮธs) / (ฯ„s + 1)

Controller Kp Ti (integral time) Td (derivative time)
P ฯ„/(Kยทฮธ) โˆž 0
PI 0.9ฯ„/(Kยทฮธ) ฮธ/0.3 0
PID 1.2ฯ„/(Kยทฮธ) 2ฮธ 0.5ฮธ

Convert to Ki, Kd: - Ki = Kp / Ti - Kd = Kp ยท Td

Pros: Simple, widely used, aggressive (fast response) Cons: Often too aggressive (20-30% overshoot), poor for noisy loops

Method 2: IMC/Direct Synthesis

Idea: Design controller to make closed-loop behave like first-order system with time constant ฮป.

For FOPTD:

Kp = ฯ„ / (Kยท(ฮป + ฮธ))
Ti = ฯ„
Td = 0  (PI controller)

Tuning knob: ฮป (closed-loop time constant) - ฮป small โ†’ fast, aggressive (like ZN) - ฮป large โ†’ slow, conservative (more robust) - Rule of thumb: ฮป = ฮธ to 3ฮธ

Pros: More conservative than ZN, good robustness Cons: Slower response, PI-only (no derivative)


Odibi Hands-On

Example 1: FOPTD Process - Ziegler-Nichols Tuning

Process: Temperature control (K=1.5, ฯ„=10 min, ฮธ=2 min)

ZN Calculations:

Kp = 1.2ฯ„/(Kยทฮธ) = 1.2(10)/(1.5ยท2) = 4.0
Ti = 2ฮธ = 2(2) = 4.0 min
Td = 0.5ฮธ = 0.5(2) = 1.0 min

Ki = Kp/Ti = 4.0/4.0 = 1.0
Kd = KpยทTd = 4.0ยท1.0 = 4.0

# temp_control_zn.yaml
nodes:
  - name: temp_control_zn
    read:
      format: simulation
      options:
        simulation:
          scope:
            start_time: "2024-01-01T00:00:00Z"
            timestep: "30sec"
            row_count: 1200  # 10 hours
            seed: 42

          entities:
            count: 1
            id_prefix: "TC-"

          columns:
            - name: entity_id
              data_type: string
              generator:
                type: constant
                value: "{entity_id}"

            - name: timestamp
              data_type: timestamp
              generator:
                type: timestamp

            - name: seconds_elapsed
              data_type: float
              generator:
                type: sequential
                start: 0
                step: 30

            # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
            # PROCESS PARAMETERS (FOPTD)
            # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
            - name: K_process
              data_type: float
              generator:
                type: constant
                value: 1.5

            - name: tau_process_min
              data_type: float
              generator:
                type: constant
                value: 10.0

            - name: theta_process_min
              data_type: float
              generator:
                type: constant
                value: 2.0

            # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
            # SETPOINT (step change at t=600 sec)
            # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
            - name: temp_sp_degc
              data_type: float
              generator:
                type: derived
                expression: "25.0 if seconds_elapsed < 600 else 30.0"

            # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
            # CONTROLLER OUTPUT (Ziegler-Nichols tuning)
            # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
            - name: temp_pv_degc
              data_type: float
              generator:
                type: derived
                expression: "prev('temp_pv_degc', 25.0)"

            - name: heater_output_pct
              data_type: float
              generator:
                type: derived
                expression: >
                  pid(
                    pv=temp_pv_degc,
                    sp=temp_sp_degc,
                    Kp=4.0,
                    Ki=1.0,
                    Kd=4.0,
                    dt=30,
                    output_min=0,
                    output_max=100,
                    anti_windup=True
                  )

            # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
            # FOPTD PROCESS DYNAMICS
            # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
            # Dead time buffer (simple delay using modulo)
            - name: delay_steps
              data_type: int
              generator:
                type: constant
                value: 4  # 2 min / 0.5 min = 4 steps

            # Delayed input (simplified - real delay needs buffer)
            - name: heater_delayed_pct
              data_type: float
              generator:
                type: derived
                expression: "prev('heater_output_pct', 0) if seconds_elapsed >= 120 else 0"

            # First-order response
            - name: temp_pv_degc_next
              data_type: float
              generator:
                type: derived
                expression: >
                  prev('temp_pv_degc', 25.0) +
                  (30.0 / 60.0) *
                  (K_process * heater_delayed_pct - prev('temp_pv_degc', 25.0)) / tau_process_min

            # Update state
            - name: temp_pv_degc
              data_type: float
              generator:
                type: derived
                expression: "temp_pv_degc_next"

            # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
            # METRICS
            # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
            - name: error_degc
              data_type: float
              generator:
                type: derived
                expression: "temp_sp_degc - temp_pv_degc"

            - name: iae
              data_type: float
              generator:
                type: derived
                expression: "prev('iae', 0) + abs(error_degc) * 0.5"

    write:
      connection: local
      format: parquet
      path: data/output/temp_control_zn.parquet
      mode: overwrite

Working example: /examples/cheme_course/L07_tuning/temp_control_zn.yaml


Example 2: Same Process - IMC Tuning

IMC Calculations (ฮป = ฮธ):

ฮป = 2.0 min
Kp = ฯ„/(Kยท(ฮป + ฮธ)) = 10/(1.5ยท(2+2)) = 1.67
Ti = ฯ„ = 10 min
Td = 0

Ki = Kp/Ti = 1.67/10 = 0.167
Kd = 0

# temp_control_imc.yaml (excerpt - full file available)
- name: heater_output_pct
  data_type: float
  generator:
    type: derived
    expression: >
      pid(
        pv=temp_pv_degc,
        sp=temp_sp_degc,
        Kp=1.67,
        Ki=0.167,
        Kd=0.0,
        dt=30,
        output_min=0,
        output_max=100,
        anti_windup=True
      )

Working example: /examples/cheme_course/L07_tuning/temp_control_imc.yaml

Comparison:

Tuning Method Kp Ki Kd Response Overshoot Robustness
Ziegler-Nichols 4.0 1.0 4.0 Fast ~20% Low
IMC (ฮป=ฮธ) 1.67 0.167 0 Moderate <5% High
IMC (ฮป=3ฮธ) 0.83 0.083 0 Slow 0% Very High

Example 3: Tuning Comparison Study

Generate side-by-side comparison data:

# tuning_comparison.yaml
# Simulates same process with 3 different tuning methods
# Output: Combined dataset for performance analysis

Working example: /examples/cheme_course/L07_tuning/tuning_comparison.yaml

Analysis metrics: - IAE (Integral Absolute Error) - smaller is better - Rise time (10% โ†’ 90%) - faster is better - Overshoot (%) - smaller is better - Settling time (2% band) - faster is better - Total valve movement - less wear is better


Data Engineering Insights

Why tuning matters for data engineers:

  1. Detect detuned controllers in historian data
  2. High IAE โ†’ controller not optimized
  3. Excessive valve movement โ†’ Kp too high
  4. Slow response โ†’ Ki too low

  5. Generate training data for ML-based tuning

  6. Simulate 100s of processes with different tunings
  7. Label data with performance metrics
  8. Train model to suggest Kp/Ki/Kd from step response

  9. Validate re-tuning before deployment

  10. Simulate proposed tuning on historical disturbances
  11. Estimate improvement in IAE, overshoot
  12. Prevent production trials

  13. Digital twin for operator training

  14. Let operators "tune" simulated process
  15. Learn consequences of aggressive vs conservative tuning
  16. No risk to real plant

Exercises

Exercise 1: Apply ZN Tuning

For a process with K=2.0, ฯ„=5 min, ฮธ=1 min: - Calculate Kp, Ki, Kd using ZN rules - Modify temp_control_zn.yaml with new parameters - Measure overshoot and settling time

Exercise 2: IMC Lambda Sweep

Test IMC tuning with ฮป = [ฮธ, 2ฮธ, 3ฮธ, 5ฮธ]: - How does increasing ฮป affect settling time? - What is the tradeoff between speed and robustness?

Exercise 3: Comparison Metrics

Run all 3 tuning methods on same disturbance: - Calculate IAE for each - Which method minimizes overshoot? - Which method has least valve movement?

Exercise 4: Noisy Process

Add measurement noise to PV (ยฑ0.5ยฐC random): - Does ZN's derivative term amplify noise? - Is IMC (PI-only) more robust to noise?


Reflection: Real Plant Scenarios

When to use each method:

Scenario Recommended Method
Fast, clean process (flow, pressure) Ziegler-Nichols
Slow, noisy process (temperature) IMC with ฮป=2ฮธ or 3ฮธ
Critical safety loop (no overshoot) IMC with ฮป=5ฮธ
Unknown process model Manual tuning or relay auto-tune

Red flags in tuned data: - Overshoot > 30% โ†’ Too aggressive (reduce Kp) - Slow settling (>5ฯ„) โ†’ Too conservative (increase Kp) - Oscillations โ†’ Kp too high or wrong sign - Steady offset with PI โ†’ Ki=0 (forgot integral!)


Summary

Key Takeaways: - โœ… Ziegler-Nichols: Fast, aggressive (20% overshoot) - โœ… IMC: Conservative, robust (tune with ฮป) - โœ… ฮป = ฮธ โ†’ balanced, ฮป = 3ฮธ โ†’ very robust - โœ… Always compare methods on same disturbance - โœ… Simulation prevents risky plant tuning trials

Next Lesson: L08: Disturbances + Setpoints - Load rejection vs setpoint tracking


Additional Resources