模糊PID控制加热器系统仿真

5456 字
27 分钟
模糊PID控制加热器系统仿真

模糊PID控制加热器系统仿真

目录

引言

本文提供了由自适应模糊PID控制器和Smith预估器控制的加热器系统仿真模型的完整算法仿真。该系统旨在通过根据设定值变化调整加热器的功率输出,最小化超调量、稳态误差和稳定时间来调节加热器的温度。


系统概述

该仿真包含几个相互连接的组件:

  1. 加热器系统: 模拟加热器的热力学特性,包括热输入、环境热损失和功率限制。
  2. PID控制器: 传统的比例-积分-微分控制器,根据误差反馈计算控制信号。
  3. 模糊PID控制器: 通过使用模糊逻辑根据当前误差和误差率自适应调整PID参数(Kp、Ki、Kd)来增强PID控制器。
  4. Smith预估器: 补偿系统延迟,并在存在死区时间的情况下提高控制器的性能。
  5. 仿真框架: 协调这些组件之间的交互,应用设定值更改并记录系统性能指标。
  6. 用户界面: 可视化系统响应、控制信号、PID参数和性能指标,并允许通过交互式滑块进行实时调整。

数学模型

隶属度函数

隶属度函数(MFs)是模糊逻辑的基础,定义了每个输入在每个模糊集中的隶属程度。该仿真使用三种类型的隶属度函数:S形、Z形和三角形。

S形函数

定义:

S形函数用于模拟从低隶属度值逐渐增加到高隶属度值的模糊集。

对于给定的输入 [latex]x[/latex],参数 [latex]a[/latex] 和 [latex]b[/latex] 定义了过渡:

[latex]\displaystyle \text{s_shaped}(x, a, b) =\begin{cases} 0 & \text{if } x \leq a \ 2 \left( \frac{x - a}{b - a} \right)^2 & \text{if } a < x \leq \frac{a + b}{2} \1 - 2 \left( \frac{b - x}{b - a} \right)^2 & \text{if } \frac{a + b}{2} < x < b \1 & \text{if } x \geq b \end{cases} [/latex]

Z形函数

定义:

Z形函数模拟从高隶属度值平滑递减到低隶属度值的模糊集。

对于给定的输入 [latex]x[/latex],参数 [latex]a[/latex] 和 [latex]b[/latex] 定义了过渡:

[latex]\displaystyle \text{z_shaped}(x, a, b) = \begin{cases} 1 & \text{if } x \leq a \ 1 - 2 \left( \frac{x - a}{b - a} \right)^2 & \text{if } a < x \leq \frac{a + b}{2} \ 2 \left( \frac{b - x}{b - a} \right)^2 & \text{if } \frac{a + b}{2} < x < b \ 0 & \text{if } x \geq b \end{cases} [/latex]

三角形函数

定义:

三角形函数创建一个简单的、对称的模糊集,在中心点达到峰值。

对于给定的输入 [latex]x[/latex],参数 [latex]a[/latex], [latex]b[/latex], 和 [latex]c[/latex] 定义了三角形:

[latex]\displaystyle \text{tri}(x, a, b, c) = \begin{cases} 0 & \text{if } x \leq a \text{ or } x \geq c \ \frac{x - a}{b - a} & \text{if } a < x \leq b \ \frac{c - x}{c - b} & \text{if } b < x < c \ 0 & \text{otherwise} \end{cases} [/latex]

模糊PID控制器

模糊PID控制器使用模糊逻辑根据当前误差 ([latex]E[/latex]) 和误差导数 ([latex]dE[/latex]) 自适应地调整PID参数。

模糊化

过程:

  1. 输入变量:

    • 误差 ([latex]E[/latex]): 设定值与过程变量之间的偏差。
    • 误差导数 ([latex]dE[/latex]): 误差的变化率。
  2. 模糊化:

    • 每个输入变量都被分配到七个模糊集的隶属度:NB(负大)、NM(负中)、NS(负小)、ZE(零)、PS(正小)、PM(正中)、PB(正大)。
    • 使用上面定义的隶属度函数计算隶属度。

数学表示:

[latex]\displaystyle \muE(\text{Label}) = \text{MembershipFunction}(E, \text{parameters}) [/latex] [latex]\displaystyle \mu{dE}(\text{Label}) = \text{MembershipFunction}(dE, \text{parameters}) [/latex]

规则库

结构:

一个 7x7 规则矩阵定义了 [latex]E[/latex] 和 [latex]dE[/latex] 的组合如何映射到 PID 参数的调整 ([latex]\Delta K_p[/latex], [latex]\Delta K_i[/latex], [latex]\Delta K_d[/latex])。

规则示例:

如果 [latex]E[/latex] 为 NB 且 [latex]dE[/latex] 为 PB,则:

[latex]\displaystyle \begin{aligned} \Delta K_p &= \text{output_map_Kp}['PB'] \ &= 0.5 \end{aligned} [/latex] [latex]\displaystyle \begin{aligned} \Delta K_i &= \text{output_map_Ki}['ZE'] \ &= 0.0 \end{aligned} [/latex] [latex]\displaystyle \begin{aligned} \Delta K_d &= \text{output_map_Kd}['NS'] \ &= -0.05 \end{aligned} [/latex] 规则应用:

对于 [latex]E[/latex] 和 [latex]dE[/latex] 的每个组合:

  1. 确定隶属度 [latex]\muE[\text{Label}][/latex] 和 [latex]\mu{dE}[\text{Label}][/latex]。
  2. 应用最小运算符计算触发强度

[latex]\displaystyle \begin{aligned} \alpha &= \min\Bigl(\mu_E[\text{Label}E], \ &\quad \mu{dE}[\text{Label}_{dE}]\Bigr) \end{aligned} [/latex]

  1. 将对 [latex]\Delta K_p[/latex], [latex]\Delta K_i[/latex], 和 [latex]\Delta K_d[/latex] 的贡献加权 [latex]\alpha[/latex] 并聚合。

解模糊

方法: 加权平均

对于每个 PID 参数调整:

[latex]\displaystyle \Delta K_p = \frac{\sum (\Delta K_p^{(i)} \cdot \alpha_i)}{\sum \alpha_i} [/latex] [latex]\displaystyle \Delta K_i = \frac{\sum (\Delta K_i^{(i)} \cdot \alpha_i)}{\sum \alpha_i} [/latex] [latex]\displaystyle \Delta K_d = \frac{\sum (\Delta K_d^{(i)} \cdot \alpha_i)}{\sum \alpha_i} [/latex] 如果分母为零(没有触发规则),则调整默认为零。

PID控制器

传统的PID控制器根据误差反馈计算控制信号。

控制律:

[latex]\displaystyle u(t) = K_p \cdot e(t) + Ki \cdot \int{0}^{t} e(\tau) d\tau + K_d \cdot \frac{de(t)}{dt} [/latex] 离散实现:

给定采样时间为 [latex]\Delta t[/latex] 的离散时间步长:

[latex]\displaystyle \text{积分项} += e(t) \cdot \Delta t [/latex] [latex]\displaystyle \text{微分项} = \frac{e(t) - e(t-1)}{\Delta t} [/latex] [latex]\displaystyle u(t) = K_p \cdot e(t) + K_i \cdot \text{积分项} + K_d \cdot \text{微分项} [/latex] 参数约束:

每个 PID 参数都被限制在指定的最小和最大边界内,以确保稳定性。

[latex]\displaystyle Kp \in [Kp{\text{min}}, Kp_{\text{max}}] [/latex] [latex]\displaystyle Ki \in [Ki{\text{min}}, Ki_{\text{max}}] [/latex] [latex]\displaystyle Kd \in [Kd{\text{min}}, Kd_{\text{max}}] [/latex]

加热器系统动力学

模拟加热器的热行为,考虑热输入、环境损失和系统约束。

参数:

状态变量:

  • [latex]\text{delay_buffer}[/latex]: FIFO 缓冲区,用于模拟死区时间。

动态方程 (欧拉离散化):

[latex]\displaystyle \frac{dy}{dt} = \frac{K \cdot u_{\text{delayed}}(t) - y(t)}{\tau} - h \cdot (y(t) - Ta) [/latex] [latex]\displaystyle y(t+1) = y(t) + \left( \frac{K \cdot u{\text{delayed}}(t) - y(t)}{\tau} - h \cdot (y(t) - T_a) \right) \cdot T_s [/latex] 控制信号处理:

  1. 限幅: 确保 [latex]u(t)[/latex] 位于 [latex][P{\text{min}}, P{\text{max}}][/latex] 内。
  2. 速率限制: 限制功率变化:

[latex]\displaystyle \begin{aligned} u(t) = \text{clip}\Bigl(&u(t), \ &u(t-1) - \Delta P{\text{max}}, \ &u(t-1) + \Delta P{\text{max}}\Bigr) \end{aligned} [/latex]

  1. 死区时间: 将 [latex]u(t)[/latex] 插入延迟缓冲区并检索 [latex]u_{\text{delayed}}(t)[/latex]。

Smith预估器

Smith预估器通过使用无死区时间的系统模型来预测未来状态并相应地调整控制信号,从而预测系统延迟。

参数:

  • 与加热器系统共享相同的参数,除了 [latex]T_d = 0[/latex](无死区时间)。

预测方程:

[latex]\displaystyle \frac{dy{\text{model}}}{dt} = \frac{K \cdot u(t) - y{\text{model}}(t)}{\tau} - h \cdot (y_{\text{model}}(t) - Ta) [/latex] [latex]\displaystyle y{\text{model}}(t+1) = y{\text{model}}(t) + \left( \frac{K \cdot u(t) - y{\text{model}}(t)}{\tau} - h \cdot (y_{\text{model}}(t) - T_a) \right) \cdot T_s [/latex] 补偿机制:

[latex]\displaystyle u{\text{compensated}}(t) = u{\text{PID}}(t) + (SV(t) - y{\text{model}}(t)) [/latex] 其中 [latex]SV(t)[/latex] 是当前设定值,[latex]y{\text{model}}(t)[/latex] 是预测温度。


仿真流程

仿真程序协调加热器系统、模糊PID控制器和Smith预估器在离散时间步长上的交互。

步骤:

  1. 初始化:

    • 设置初始 PID 参数 ([latex]K{p0}, K{i0}, K_{d0}[/latex])。
    • 定义设定值曲线,以固定间隔更改。
    • 初始化加热器系统、Smith 预估器、模糊 PID 控制器和 PID 控制器。
    • 初始化性能指标。
  2. 时间步进循环 ([latex]t = 0[/latex] 到 [latex]t = \text{TIME_STEPS} - 1[/latex]):

    • 设定值分配: 分配当前设定值 [latex]SV(t)[/latex]。
    • 过程变量 (PV): 检索当前温度 [latex]y(t)[/latex]。
    • 误差计算: [latex]\displaystyle e(t) = SV(t) - PV(t) [/latex] [latex]\displaystyle dE(t) = e(t) - e(t-1) \quad \text{(如果 } t > 0 \text{)} [/latex]
    • 性能指标更新: 在设定值更改间隔,计算超调量、稳态误差和稳定时间。
    • 模糊化: 使用隶属度函数计算 [latex]\muE[/latex] 和 [latex]\mu{dE}[/latex]。
    • 推理: 应用模糊规则确定 [latex]\Delta K_p, \Delta K_i, \Delta K_d[/latex]。
    • 自适应 PID 调整: 更新 PID 参数: [latex]\displaystyle \begin{aligned} K_p(t) &= \text{clip}\bigl(K_p(t-1) + \Delta Kp, \ &\quad Kp{\text{min}}, Kp_{\text{max}}\bigr) \end{aligned} [/latex] [latex]\displaystyle \begin{aligned} K_i(t) &= \text{clip}\bigl(K_i(t-1) + \Delta Ki, \ &\quad Ki{\text{min}}, Ki_{\text{max}}\bigr) \end{aligned} [/latex] [latex]\displaystyle \begin{aligned} K_d(t) &= \text{clip}\bigl(K_d(t-1) + \Delta Kd, \ &\quad Kd{\text{min}}, Kd_{\text{max}}\bigr) \end{aligned} [/latex]
    • PID 控制输出: [latex]\displaystyle \begin{aligned} u_{\text{PID}}(t) &= K_p(t) \cdot e(t) \ &\quad + K_i(t) \cdot \text{Integral}_e \ &\quad + K_d(t) \cdot dE(t) \end{aligned} [/latex]
    • Smith 预估器: 预测无延迟的 PV [latex]\hat{y}(t)[/latex]。
    • 控制补偿: [latex]\displaystyle \begin{aligned} u{\text{compensated}}(t) &= u{\text{PID}}(t) \ &\quad + \bigl(SV(t) - \hat{y}(t)\bigr) \end{aligned} [/latex]
    • 加热器更新: 将 [latex]u_{\text{compensated}}(t)[/latex] 应用于加热器系统以获得 [latex]y(t+1)[/latex] 和 [latex]u(t+1)[/latex]。
    • 数据记录: 存储 PV、SV、控制输出、PID 参数和性能指标。
  3. 最终性能指标: 循环结束后,计算最后一个设定值周期的指标。


性能指标

为了评估控制器的有效性,仿真跟踪每次设定值更改的以下指标:

  1. 超调量 (%): [latex]\displaystyle \begin{aligned} \text{超调量} &= \left( \frac{\max(PV(t)) - SV}{SV} \right) \ &\times 100 \end{aligned} [/latex] 测量 PV 超过设定值的程度。

  2. 稳态误差: [latex]\displaystyle \begin{aligned} \text{稳态误差} &= \frac{1}{N} \sum_{t=T-N}^{T} (SV(t) - PV(t)) \end{aligned} [/latex] 响应最后 [latex]N[/latex] 步的平均误差。

  3. 稳定时间 (步长): PV 保持在设定值的 [latex]\pm2\%[/latex] 以内且之后不再超出此范围所需的步数。


用户界面

交互式图形界面,具有以下功能:

  1. 图表:

    • 系统响应: 显示 PV 和 SV 随时间的变化。
    • 控制信号: 显示控制输出 [latex]u(t)[/latex]。
    • 加热器功率: 可视化应用于加热器的实际功率。
    • 自适应 PID 参数: 跟踪 [latex]K_p, K_i, K_d[/latex] 随时间的变化。
    • 性能指标: 绘制每次设定值更改的超调量、稳态误差和稳定时间。
  2. 滑块:

    • 初始 PID 参数:
      • Kp0: 初始比例增益 (0 到 50)。
      • Ki0: 初始积分增益 (0 到 10)。
      • Kd0: 初始微分增益 (0 到 10)。
    • 最大功率: 调整最大加热功率 (50 到 12,000)。
  3. 交互性:

    • 调整滑块会触发仿真程序使用更新的参数重新运行。
    • 图表动态更新以反映新的仿真结果。

结论

本文概述了与 Smith 预估器集成的模糊 PID 控制加热器系统仿真的算法结构和数学基础。该系统利用模糊逻辑进行自适应 PID 参数整定,在存在系统延迟和设定值变化的情况下增强控制性能。交互式仿真促进了控制动力学和性能指标的实时实验和可视化。

TODO:

  • 其他性能指标: 例如绝对误差积分 (IAE) 或平方误差积分 (ISE)。
  • 高级隶属度函数: 结合高斯或样条基隶属度函数以实现更平滑的过渡。
  • 优化算法: 使用优化算法来微调模糊规则库或 PID 参数范围。
  • 添加更多环境参数: 例如真空值、氩气/氟利昂等气体流量

附件

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
import matplotlib.gridspec as gridspec

定义会员函数#

def s_shaped(x, a, b): if x <= a: return 0 elif a < x <= (a + b) / 2: return 2 * ((x - a) / (b - a)) ** 2 elif (a + b) / 2 < x < b: return 1 - 2 * ((b - x) / (b - a)) ** 2 else: return 1

def z_shaped(x, a, b): if x <= a: return 1 elif a < x <= (a + b) / 2: return 1 - 2 * ((x - a) / (b - a)) ** 2 elif (a + b) / 2 < x < b: return 2 * ((b - x) / (b - a)) ** 2 else: return 0

def tri(x, a, b, c): if x <= a or x >= c: return 0 elif a < x <= b: return (x - a) / (b - a) elif b < x < c: return (c - x) / (c - b) else: return 0

定义模糊控制器#

class FuzzyPIDController: def init(self, E_max=100, dE_max=50): self.E_max = E_max self.dE_max = dE_max # 定义输出的独立映射数值 self.output_map_Kp = { ‘NB’: -0.5, ‘NM’: -0.25, ‘NS’: -0.1, ‘ZE’: 0.0, ‘PS’: 0.1, ‘PM’: 0.25, ‘PB’: 0.5 } self.output_map_Ki = { ‘NB’: -0.3, ‘NM’: -0.15, ‘NS’: -0.05, ‘ZE’: 0.0, ‘PS’: 0.05, ‘PM’: 0.15, ‘PB’: 0.3 } self.output_map_Kd = { ‘NB’: -0.2, ‘NM’: -0.1, ‘NS’: -0.05, ‘ZE’: 0.0, ‘PS’: 0.05, ‘PM’: 0.1, ‘PB’: 0.2 } # 定义规则表 self.rules = [ # NB row [(‘PB’, ‘ZE’, ‘NS’), (‘PM’, ‘PS’, ‘ZE’), (‘PM’, ‘PS’, ‘PS’), (‘PM’, ‘PM’, ‘PS’), (‘PS’, ‘PM’, ‘PS’), (‘ZE’, ‘PM’, ‘PM’), (‘ZE’, ‘PS’, ‘PM’)], # NM row [(‘PM’, ‘PS’, ‘NS’), (‘PM’, ‘PS’, ‘PS’), (‘PM’, ‘PM’, ‘PS’), (‘PS’, ‘PM’, ‘PS’), (‘ZE’, ‘PM’, ‘PM’), (‘ZE’, ‘PS’, ‘PM’), (‘NS’, ‘ZE’, ‘PM’)], # NS row [(‘PM’, ‘PS’, ‘PS’), (‘PM’, ‘PM’, ‘PS’), (‘PS’, ‘PM’, ‘PS’), (‘ZE’, ‘PM’, ‘PM’), (‘ZE’, ‘PS’, ‘PM’), (‘NS’, ‘ZE’, ‘PM’), (‘NM’, ‘NS’, ‘PB’)], # ZE row [(‘PM’, ‘PM’, ‘PS’), (‘PS’, ‘PM’, ‘PS’), (‘ZE’, ‘PM’, ‘PM’), (‘ZE’, ‘ZE’, ‘ZE’), (‘ZE’, ‘NS’, ‘PM’), (‘NM’, ‘NS’, ‘PB’), (‘NM’, ‘NM’, ‘PB’)], # PS row [(‘PS’, ‘PM’, ‘PS’), (‘ZE’, ‘PM’, ‘PM’), (‘ZE’, ‘PS’, ‘PM’), (‘NS’, ‘ZE’, ‘PM’), (‘NM’, ‘NS’, ‘PB’), (‘NM’, ‘NM’, ‘PB’), (‘NB’, ‘NB’, ‘PB’)], # PM row [(‘ZE’, ‘PM’, ‘PM’), (‘ZE’, ‘PS’, ‘PM’), (‘NS’, ‘ZE’, ‘PM’), (‘NM’, ‘NS’, ‘PB’), (‘NM’, ‘NM’, ‘PB’), (‘NB’, ‘NB’, ‘PB’), (‘NB’, ‘NB’, ‘PB’)], # PB row [(‘ZE’, ‘PS’, ‘PM’), (‘NS’, ‘ZE’, ‘PM’), (‘NM’, ‘NS’, ‘PB’), (‘NM’, ‘NM’, ‘PB’), (‘NB’, ‘NB’, ‘PB’), (‘NB’, ‘NB’, ‘PB’), (‘NB’, ‘NB’, ‘PB’)] ] self.E_labels = [‘NB’, ‘NM’, ‘NS’, ‘ZE’, ‘PS’, ‘PM’, ‘PB’] self.dE_labels = [‘NB’, ‘NM’, ‘NS’, ‘ZE’, ‘PS’, ‘PM’, ‘PB’]

def fuzzify(self, E, dE):
mu_E = {}
mu_dE = {}
# Fuzzify E
mu_E[&#039;NB&#039;] = z_shaped(E, -self.E_max, -self.E_max / 2)
mu_E[&#039;NM&#039;] = tri(E, -self.E_max, -self.E_max / 2, 0)
mu_E[&#039;NS&#039;] = tri(E, -self.E_max / 2, 0, self.E_max / 4)
mu_E[&#039;ZE&#039;] = tri(E, -self.E_max / 4, 0, self.E_max / 4)
mu_E[&#039;PS&#039;] = tri(E, 0, self.E_max / 4, self.E_max / 2)
mu_E[&#039;PM&#039;] = tri(E, self.E_max / 3, self.E_max / 2, 2 * self.E_max / 3)
mu_E[&#039;PB&#039;] = s_shaped(E, self.E_max / 2, self.E_max)
# Fuzzify dE
mu_dE[&#039;NB&#039;] = z_shaped(dE, -self.dE_max, -self.dE_max / 2)
mu_dE[&#039;NM&#039;] = tri(dE, -self.dE_max, -self.dE_max / 2, 0)
mu_dE[&#039;NS&#039;] = tri(dE, -self.dE_max / 2, 0, self.dE_max / 2)
mu_dE[&#039;ZE&#039;] = tri(dE, -self.dE_max / 4, 0, self.dE_max / 4)
mu_dE[&#039;PS&#039;] = tri(dE, 0, self.dE_max / 4, self.dE_max / 2)
mu_dE[&#039;PM&#039;] = tri(dE, self.dE_max / 3, self.dE_max / 2, 2 * self.dE_max / 3)
mu_dE[&#039;PB&#039;] = s_shaped(dE, self.dE_max / 2, self.dE_max)
return mu_E, mu_dE
def infer(self, mu_E, mu_dE):
# 初始化输出会员度列表
delta_Kp = []
delta_Ki = []
delta_Kd = []
# 遍历所有规则
for i, E_label in enumerate(self.E_labels):
for j, dE_label in enumerate(self.dE_labels):
alpha = min(mu_E[E_label], mu_dE[dE_label]) # 取最小
if alpha &gt; 0:
rule = self.rules[i][j]
delta_Kp.append((self.output_map_Kp[rule[0]], alpha))
delta_Ki.append((self.output_map_Ki[rule[1]], alpha))
delta_Kd.append((self.output_map_Kd[rule[2]], alpha))
# 聚合: 对每个输出变量取加权平均
def weighted_average(output):
numerator = sum([val * alpha for val, alpha in output])
denominator = sum([alpha for _, alpha in output])
if denominator == 0:
return 0
return numerator / denominator
delta_Kp_value = weighted_average(delta_Kp)
delta_Ki_value = weighted_average(delta_Ki)
delta_Kd_value = weighted_average(delta_Kd)
return delta_Kp_value, delta_Ki_value, delta_Kd_value

定义PID控制器#

class PIDController: def init(self, Kp=2.0, Ki=0.5, Kd=0.1, Kp_min=0, Kp_max=10, Ki_min=0, Ki_max=10, Kd_min=0, Kd_max=10): self.Kp = Kp self.Ki = Ki self.Kd = Kd self.Kp_min = Kp_min self.Kp_max = Kp_max self.Ki_min = Ki_min self.Ki_max = Ki_max self.Kd_min = Kd_min self.Kd_max = Kd_max self.integral = 0 self.prev_error = 0

def update_parameters(self, delta_Kp, delta_Ki, delta_Kd):
self.Kp = np.clip(self.Kp + delta_Kp, self.Kp_min, self.Kp_max)
self.Ki = np.clip(self.Ki + delta_Ki, self.Ki_min, self.Ki_max)
self.Kd = np.clip(self.Kd + delta_Kd, self.Kd_min, self.Kd_max)
def compute(self, error, dt=1):
# 积分
self.integral += error * dt
# 微分
derivative = (error - self.prev_error) / dt
self.prev_error = error
return self.Kp * error + self.Ki * self.integral + self.Kd * derivative

定义加热器系统模型,包含环境热量散失、加热功率限制和加热功率变化率限制#

class HeaterSystem: def init(self, K=1.0, tau=10.0, Td=5, Ts=0.05, ambient_temperature=20.0, heat_loss_coefficient=0.1, max_power=100.0, min_power=0.0, power_rate_limit=10.0): self.K = K self.tau = tau self.Td = Td # 滞后时间(延迟步数) self.Ts = Ts # 采样时间 self.ambient_temperature = ambient_temperature # 环境温度 self.heat_loss_coefficient = heat_loss_coefficient # 热量散失系数 self.delay_buffer = [0] * Td # 滞后缓冲区 self.y = ambient_temperature # 初始温度 self.max_power = max_power # 最大加热功率 self.min_power = min_power # 最小加热功率 self.power_rate_limit = power_rate_limit # 加热功率变化率限制 self.prev_delayed_u = 0.0 # 记录上一个延迟的加热功率

def update(self, u):
# 限制控制信号在加热功率范围内
u = np.clip(u, self.min_power, self.max_power)
# 限制加热功率变化率
delta_u = u - self.prev_delayed_u
delta_u = np.clip(delta_u, -self.power_rate_limit, self.power_rate_limit)
u = self.prev_delayed_u + delta_u
# 添加控制输入到延迟缓冲区
self.delay_buffer.append(u)
delayed_u = self.delay_buffer.pop(0)
self.prev_delayed_u = delayed_u # 更新上一个延迟的加热功率
# 欧拉法离散化一阶惯性环节,加上环境热量散失
dy = (self.K * delayed_u - self.y) * self.Ts / self.tau
# 环境热量散失
dy -= (self.y - self.ambient_temperature) * self.heat_loss_coefficient * self.Ts
self.y += dy
return self.y, delayed_u # 返回延迟后的加热功率

定义史密斯预测器#

class SmithPredictor: def init(self, model: HeaterSystem, Td=5): self.model = model self.Td = Td self.model_delay_buffer = [0] * Td self.model_output = self.model.ambient_temperature # 初始预测输出

def predict(self, u):
# 限制控制信号在加热功率范围内
u = np.clip(u, self.model.min_power, self.model.max_power)
# 预测模型不含滞后
dy = (self.model.K * u - self.model_output) * self.model.Ts / self.model.tau
# 环境热量散失
dy -= (self.model_output - self.model.ambient_temperature) * self.model.heat_loss_coefficient * self.model.Ts
self.model_output += dy
return self.model_output

定义仿真函数#

def simulate(Kp0, Ki0, Kd0, E_max=100, dE_max=50, TIME_STEPS=2000, SETPOINT_CHANGE_INTERVAL=500, max_power=12000.0, min_power=0.0, power_rate_limit=100.0, heater_power=0): # 设定点配置:每SETPOINT_CHANGE_INTERVAL步改变一次设定点 SETPOINT_VALUES = [200, 400, 300, 600] num_changes = TIME_STEPS // SETPOINT_CHANGE_INTERVAL SETPOINT_PROFILE = np.concatenate([np.ones(SETPOINT_CHANGE_INTERVAL) * SETPOINT_VALUES[i % len(SETPOINT_VALUES)] for i in range(num_changes)]) if len(SETPOINT_PROFILE) < TIME_STEPS: SETPOINT_PROFILE = np.concatenate([SETPOINT_PROFILE, np.ones(TIME_STEPS - len(SETPOINT_PROFILE)) * SETPOINT_VALUES[-1]])

# 初始化模块
heater = HeaterSystem(K=1, tau=10.0, Td=1, Ts=0.1, ambient_temperature=20.0,
heat_loss_coefficient=0.1, max_power=max_power, min_power=min_power,
power_rate_limit=power_rate_limit)
smith_model = HeaterSystem(K=1, tau=10.0, Td=0, Ts=0.1, ambient_temperature=20.0,
heat_loss_coefficient=0.1, max_power=max_power, min_power=min_power,
power_rate_limit=power_rate_limit) # 模型不含滞后
smith = SmithPredictor(model=smith_model, Td=1)
fuzzy_controller = FuzzyPIDController(E_max=E_max, dE_max=dE_max)
pid = PIDController(Kp=Kp0, Ki=Ki0, Kd=Kd0,
Kp_min=0, Kp_max=50,
Ki_min=0, Ki_max=10,
Kd_min=0, Kd_max=10)
# 初始化记录变量
PV_history = []
SV_history = []
error_history = []
control_history = []
heater_power_history = []
Kp_history = []
Ki_history = []
Kd_history = []
overshoot_history = []
steady_state_error_history = []
settling_time_history = []
# 初始化性能指标
current_setpoint = SETPOINT_PROFILE[0]
response_start_index = 0
overshoot = 0
steady_state_error = 0
settling_time = 0
settling_time_threshold = 0.02 # 2%
within_settle = False
# 仿真循环
for t in range(TIME_STEPS):
SV = SETPOINT_PROFILE[t]
PV = heater.y
error = SV - PV
dE = error - (error_history[-1] if t &gt; 0 else 0)
# 检测设定点变化
if t % SETPOINT_CHANGE_INTERVAL == 0 and t != 0:
# 计算并记录性能指标
# 超调量
max_PV = max(PV_history[response_start_index:t]) if t &gt; response_start_index else PV
overshoot = ((max_PV - current_setpoint) / current_setpoint) * 100 if current_setpoint != 0 else 0
overshoot_history.append(overshoot)
# 稳态误差 (取最后50步的平均误差)
steady_state_error = np.mean(error_history[max(t-50, response_start_index):t]) if t &gt; response_start_index else error
steady_state_error_history.append(steady_state_error)
# 调节时间 (找到第一次进入并保持在±2%范围内的时间步)
target_min = current_setpoint * (1 - settling_time_threshold)
target_max = current_setpoint * (1 + settling_time_threshold)
settling_time = 0
for i in range(response_start_index, t):
if target_min &lt;= PV_history[i] &lt;= target_max:
# Check if it stays within the range for the remaining steps
if all(target_min &lt;= pv &lt;= target_max for pv in PV_history[i:t]):
settling_time = i - response_start_index
break
settling_time_history.append(settling_time)
# 更新当前设定点和响应起始索引
current_setpoint = SV
response_start_index = t
# 模糊控制器调整PID参数
mu_E, mu_dE = fuzzy_controller.fuzzify(error, dE)
delta_Kp, delta_Ki, delta_Kd = fuzzy_controller.infer(mu_E, mu_dE)
if heater_power != 0:
delta_Kp *= (heater_power / max_power) # 根据加热功率调整PID参数
pid.update_parameters(delta_Kp, delta_Ki, delta_Kd)
# PID计算控制输出
u_pid = pid.compute(error, dt=heater.Ts)
# 史密斯预测器预测PV
predicted_PV = smith.predict(u_pid)
# 控制输出补偿
u_compensated = u_pid + (SV - predicted_PV)
# 更新加热器系统,并获取延迟后的加热功率
y, heater_power = heater.update(u_compensated)
# 记录数据
PV_history.append(y)
SV_history.append(SV)
error_history.append(error)
control_history.append(u_compensated)
heater_power_history.append(heater_power)
Kp_history.append(pid.Kp)
Ki_history.append(pid.Ki)
Kd_history.append(pid.Kd)
# 最后一个设定点的性能指标
max_PV = max(PV_history[response_start_index:]) if TIME_STEPS &gt; response_start_index else PV_history[-1]
overshoot = ((max_PV - current_setpoint) / current_setpoint) * 100 if current_setpoint != 0 else 0
overshoot_history.append(overshoot)
steady_state_error = np.mean(error_history[response_start_index:]) if TIME_STEPS &gt; response_start_index else error_history[-1]
steady_state_error_history.append(steady_state_error)
settling_time = 0
target_min = current_setpoint * (1 - settling_time_threshold)
target_max = current_setpoint * (1 + settling_time_threshold)
for i in range(response_start_index, TIME_STEPS):
if target_min &lt;= PV_history[i] &lt;= target_max:
if all(target_min &lt;= pv &lt;= target_max for pv in PV_history[i:]):
settling_time = i - response_start_index
break
settling_time_history.append(settling_time)
return (PV_history, SV_history, control_history, heater_power_history,
Kp_history, Ki_history, Kd_history,
overshoot_history, steady_state_error_history, settling_time_history)

初始化仿真参数#

INITIAL_Kp = 2.0 INITIAL_Ki = 0.5 INITIAL_Kd = 0.1

运行初始仿真#

SETPOINT_CHANGE_INTERVAL=500 results = simulate(INITIAL_Kp, INITIAL_Ki, INITIAL_Kd, TIME_STEPS=2000, SETPOINT_CHANGE_INTERVAL=SETPOINT_CHANGE_INTERVAL, max_power=12000, min_power=0, power_rate_limit=10) PV_history, SV_history, control_history, heater_power_history, Kp_history, Ki_history, Kd_history,
overshoot_history, steady_state_error_history, settling_time_history = results

创建图形和子图,增加一个子图用于展示加热功率和性能指标#

fig = plt.figure(figsize=(15, 20)) gs = gridspec.GridSpec(5, 1, height_ratios=[2, 1, 1, 1, 1], hspace=0.4)

子图1:系统响应#

ax1 = plt.subplot(gs[0]) line1_PV, = ax1.plot(PV_history, label=‘Process Variable (PV)’, color=‘b’) line1_SV, = ax1.plot(SV_history, label=‘Setpoint (SV)’, linestyle=’—’, color=‘r’) ax1.set_ylabel(‘Temperature’) ax1.set_title(‘System Response’) ax1.legend() ax1.grid(True)

子图2:控制输出#

ax2 = plt.subplot(gs[1], sharex=ax1) line2, = ax2.plot(control_history, label=‘Control Output (u)’, color=‘g’) ax2.set_ylabel(‘Control Output’) ax2.set_title(‘Control Signal’) ax2.legend() ax2.grid(True)

子图3:加热功率#

ax3 = plt.subplot(gs[2], sharex=ax1) line3, = ax3.plot(heater_power_history, label=‘Heater Power (u)’, color=‘orange’) ax3.set_ylabel(‘Heater Power’) ax3.set_title(‘Heater Power’) ax3.legend() ax3.grid(True)

子图4:PID参数#

ax4 = plt.subplot(gs[3], sharex=ax1) line4_Kp, = ax4.plot(Kp_history, label=‘Kp’, color=‘m’) line4_Ki, = ax4.plot(Ki_history, label=‘Ki’, color=‘c’) line4_Kd, = ax4.plot(Kd_history, label=‘Kd’, color=‘y’) ax4.set_ylabel(‘PID Parameters’) ax4.set_title(‘Adaptive PID Parameters’) ax4.legend() ax4.grid(True)

子图5:性能指标#

ax5 = plt.subplot(gs[4]) steps = [SETPOINT_CHANGE_INTERVAL * i for i in range(len(overshoot_history))] ax5.plot(steps, overshoot_history, label=‘Overshoot (%)’, marker=‘o’, color=‘purple’) ax5.plot(steps, steady_state_error_history, label=‘Steady-State Error’, marker=‘x’, color=‘brown’) ax5.plot(steps, settling_time_history, label=‘Settling Time (steps)’, marker=’^’, color=‘black’) ax5.set_xlabel(‘Time Step’) ax5.set_ylabel(‘Performance Metrics’) ax5.set_title(‘Performance Metrics per Setpoint Change’) ax5.legend() ax5.grid(True)

调整子图间距以留出滑块空间#

plt.subplots_adjust(bottom=0.35)

添加滑块#

axcolor = ‘lightgoldenrodyellow’ ax_Kp = plt.axes([0.15, 0.25, 0.65, 0.03], facecolor=axcolor) ax_Ki = plt.axes([0.15, 0.18, 0.65, 0.03], facecolor=axcolor) ax_Kd = plt.axes([0.15, 0.11, 0.65, 0.03], facecolor=axcolor) ax_max_power = plt.axes([0.15, 0.04, 0.65, 0.03], facecolor=axcolor)

slider_Kp = Slider(ax_Kp, ‘Kp0’, 0.0, 50.0, valinit=INITIAL_Kp, valstep=0.1) slider_Ki = Slider(ax_Ki, ‘Ki0’, 0.0, 10.0, valinit=INITIAL_Ki, valstep=0.1) slider_Kd = Slider(ax_Kd, ‘Kd0’, 0.0, 10.0, valinit=INITIAL_Kd, valstep=0.1) slider_max_power = Slider(ax_max_power, ‘Max Power’, 50.0, 12000.0, valinit=2000.0, valstep=1.0)

定义更新函数#

def update(val): Kp0 = slider_Kp.val Ki0 = slider_Ki.val Kd0 = slider_Kd.val max_power = slider_max_power.val min_power = 0.0 # 可以根据需要调整最小加热功率 power_rate_limit = 500.0 # 固定变化率限制,可扩展为可调

# 重新运行仿真
results = simulate(Kp0, Ki0, Kd0, TIME_STEPS=2000,
SETPOINT_CHANGE_INTERVAL=SETPOINT_CHANGE_INTERVAL,
max_power=max_power, min_power=min_power,
power_rate_limit=power_rate_limit)
PV, SV, u, heater_power, Kp, Ki, Kd, overshoot, steady_state_error, settling_time = results
# 更新系统响应图
line1_PV.set_ydata(PV)
line1_SV.set_ydata(SV)
ax1.relim()
ax1.autoscale_view()
# 更新控制输出图
line2.set_ydata(u)
ax2.relim()
ax2.autoscale_view()
# 更新加热功率图
line3.set_ydata(heater_power)
ax3.relim()
ax3.autoscale_view()
# 更新PID参数图
line4_Kp.set_ydata(Kp)
line4_Ki.set_ydata(Ki)
line4_Kd.set_ydata(Kd)
ax4.relim()
ax4.autoscale_view()
# 更新性能指标图
steps = [500 * i for i in range(len(overshoot))]
ax5.clear()
ax5.plot(steps, overshoot, label=&#039;Overshoot (%)&#039;, marker=&#039;o&#039;, color=&#039;purple&#039;)
ax5.plot(steps, steady_state_error, label=&#039;Steady-State Error&#039;, marker=&#039;x&#039;, color=&#039;brown&#039;)
ax5.plot(steps, settling_time, label=&#039;Settling Time (steps)&#039;, marker=&#039;^&#039;, color=&#039;black&#039;)
ax5.set_xlabel(&#039;Time Step&#039;)
ax5.set_ylabel(&#039;Performance Metrics&#039;)
ax5.set_title(&#039;Performance Metrics per Setpoint Change&#039;)
ax5.legend()
ax5.grid(True)
fig.canvas.draw_idle()

注册滑块更新事件#

slider_Kp.on_changed(update) slider_Ki.on_changed(update) slider_Kd.on_changed(update) slider_max_power.on_changed(update)

plt.show()

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

模糊PID控制加热器系统仿真
https://www.rwr.ink/index.php/2024/10/12/模糊pid控制加热器系统仿真/
作者
JMFANS
发布于
2024-10-12
许可协议
CC BY-NC-SA 4.0
相关文章智能推荐
1
Brooks MAGNATRAN® LEAP™ 编程手册
未分类Brooks MAGNATRAN® LEAP™ 编程手册 基于官方文档编写 《238485 Leap Manual REV G》— 硬件与用户手册(263页) 《278805 MAGNATRAN® LEAP™ Software Manual REV E》— 软件编程手册(278页) 文档版本 : 综合整理版 v1.0 | 适用固件 : V8.2.x 系列 |
2
Debian 13 新装
未分类Debian 13 (Trixie) 完整自动化配置文档 文档说明 本文档汇总了 Debian 13 系统的完整配置流程,包含系统更新、网络加速、安全防护、开发环境和 Shell 美化等所有功能。 执行顺序 : 从上到下依次执行各代码块 一、系统更新与基础优化 #!/bin/bash # Debian 13 系统全面更新与基础配置 # 1.1 更新软件源并升
3
CE认证标准-电气部分
未分类EN 60204 标准概述 4 适用范围 EN 60204 的本部分适用于机械(包括协同工作的一组机械)的电气、电子和可编程电子设备及系统,不适用于手提工作式机械。 本标准适用于额定电压不超过交流1000V,直流1500V,电源频率不超过200Hz的电子设备、电气设备或工业机器系统。 本标准所论及的设备是从机械电气设备的电源引入处开始的。 本标准不包括用于危
4
电气常用元器件概述
未分类系统组件说明 1513-1 功能:CPU-单晶炉部分逻辑程序的设计,存储及控制。 主要参数:处理器速度、内存容量、I/O通道数量。 通讯接口:以太网、RS485、Profibus等。 分布式IO模块 功能:与1513-1通讯,为各数字量/模拟量的输入及输出提供接口,并实现信号采集和输出。 开关电源 功能:主要使用DC开关电源,即AC/DC电源。主要以DC24
5
Modbus TCP帧结构详解
未分类Modbus TCP是基于TCP/IP协议的Modbus变体,常用于以太网网络中。与Modbus RTU和Modbus ASCII不同,Modbus TCP没有起始和结束静默时间,而是使用TCP/IP协议进行数据传输。以下是Modbus TCP帧结构的详细解释: Modbus TCP帧结构 Modbus TCP帧由以下几个部分组成: MBAP头(Modbus
随机文章随机推荐
Profile Image of the Author
JMFANS
电气工程师,记录技术、学习和生活。
公告
记录工程实践、学习笔记与长期积累。
分类
标签
站点统计
文章
14
分类
1
标签
5
总字数
105,433
运行时长
0 天
最后活动
0 天前
文章目录