2. 模糊规则表设计
任务目标
设计一个二维模糊规则表,以误差 \( e \) 和误差变化率 \( ec \) 为输入,输出 PID 参数调整量 \( \Delta K_p \)、\( \Delta K_i \)、\( \Delta K_d \)。规则表采用标准 Mamdani 型,输入输出均使用 7 个对称三角形隶属度函数,论域为 [-3, 3]。
模糊论域与隶属度定义
输入 \( e \)、\( ec \) 与输出 \( \Delta K_p \)、\( \Delta K_i \)、\( \Delta K_d \) 均采用相同论域与语言值:NB(负大)、NM(负中)、NS(负小)、ZO(零)、PS(正小)、PM(正中)、PB(正大)。隶属度函数采用全交叠三角形,相邻函数交叠度为 0.5,保证任意输入至少激活两条规则。
规则表核心设计原则
- \( \Delta K_p \):|e| 大时取大值以快速响应;|e| 中等时取中值;|e| 小时取小值,同时根据 ec 方向微调以避免超调。
- \( \Delta K_i \):|e| 小时增大积分作用以消除稳态误差;|e| 大时减小积分作用以防积分饱和。
- \( \Delta K_d \):|ec| 大时取大值以抑制动态偏差;|ec| 小时取小值以避免噪声放大。
关键完整代码(Python + NumPy)
import numpy as np
# 语言值索引: 0=NB,1=NM,2=NS,3=ZO,4=PS,5=PM,6=PB
labels = ['NB','NM','NS','ZO','PS','PM','PB']
centers = np.array([-3,-2,-1,0,1,2,3]) # 三角形中心
half_width = 1.0 # 半宽,交叠度0.5
def tri_mf(x, center):
"""三角形隶属度函数"""
return max(0.0, 1.0 - abs(x - center) / half_width)
def fuzzify(x):
"""将精确值映射为7维隶属度向量"""
return np.array([tri_mf(x, c) for c in centers])
# ---------- 模糊规则表 (49条规则) ----------
# 每条规则: (e_idx, ec_idx) -> (dKp_idx, dKi_idx, dKd_idx)
# 规则表依据标准Ziegler-Nichols经验修正设计
rule_table = {}
# ΔKp 规则表 (7x7)
dKp_rules = np.array([
[6, 5, 4, 4, 4, 5, 6], # NB
[5, 4, 3, 3, 3, 4, 5], # NM
[4, 3, 2, 2, 2, 3, 4], # NS
[3, 2, 1, 0, 1, 2, 3], # ZO
[4, 3, 2, 2, 2, 3, 4], # PS
[5, 4, 3, 3, 3, 4, 5], # PM
[6, 5, 4, 4, 4, 5, 6], # PB
]) # 行=e, 列=ec
# ΔKi 规则表 (7x7)
dKi_rules = np.array([
[0, 0, 1, 1, 1, 0, 0], # NB
[0, 1, 2, 2, 2, 1, 0], # NM
[1, 2, 3, 3, 3, 2, 1], # NS
[2, 3, 4, 4, 4, 3, 2], # ZO
[1, 2, 3, 3, 3, 2, 1], # PS
[0, 1, 2, 2, 2, 1, 0], # PM
[0, 0, 1, 1, 1, 0, 0], # PB
])
# ΔKd 规则表 (7x7)
dKd_rules = np.array([
[3, 2, 1, 1, 1, 2, 3], # NB
[2, 1, 0, 0, 0, 1, 2], # NM
[1, 0, 0, 0, 0, 0, 1], # NS
[0, 0, 0, 0, 0, 0, 0], # ZO
[1, 0, 0, 0, 0, 0, 1], # PS
[2, 1, 0, 0, 0, 1, 2], # PM
[3, 2, 1, 1, 1, 2, 3], # PB
])
# 构建规则表字典
for i in range(7):
for j in range(7):
rule_table[(i, j)] = (dKp_rules[i, j], dKi_rules[i, j], dKd_rules[i, j])
def fuzzy_inference(e, ec):
"""
模糊推理: 输入精确值 e, ec,输出调整量 ΔKp, ΔKi, ΔKd
采用 Mamdani 推理 + 重心法去模糊化
"""
mu_e = fuzzify(np.clip(e, -3, 3))
mu_ec = fuzzify(np.clip(ec, -3, 3))
# 收集所有规则后件隶属度
out_centers = np.zeros(3) # 加权和
out_weights = np.zeros(3) # 权重和
for i in range(7):
for j in range(7):
# 规则激活度 = min(前件隶属度)
fire_strength = min(mu_e[i], mu_ec[j])
if fire_strength <= 0:
continue
kp_idx, ki_idx, kd_idx = rule_table[(i, j)]
# 重心法累加
out_centers[0] += fire_strength * centers[kp_idx]
out_centers[1] += fire_strength * centers[ki_idx]
out_centers[2] += fire_strength * centers[kd_idx]
out_weights += fire_strength
if np.sum(out_weights) == 0:
return np.array([0.0, 0.0, 0.0])
return out_centers / out_weights
# ---------- 最小验证闭环 ----------
if __name__ == "__main__":
# 测试用例1: e=0, ec=0 (稳态) -> 输出应接近0
dKp, dKi, dKd = fuzzy_inference(0.0, 0.0)
print(f"e=0, ec=0 -> ΔKp={dKp:.3f}, ΔKi={dKi:.3f}, ΔKd={dKd:.3f}")
# 预期: 三者均接近0 (因ZO规则占主导)
# 测试用例2: e=3, ec=0 (大误差) -> ΔKp应较大
dKp, dKi, dKd = fuzzy_inference(3.0, 0.0)
print(f"e=3, ec=0 -> ΔKp={dKp:.3f}, ΔKi={dKi:.3f}, ΔKd={dKd:.3f}")
# 预期: ΔKp接近3, ΔKi接近0, ΔKd接近0
# 测试用例3: e=0, ec=3 (大误差变化率) -> ΔKd应较大
dKp, dKi, dKd = fuzzy_inference(0.0, 3.0)
print(f"e=0, ec=3 -> ΔKp={dKp:.3f}, ΔKi={dKi:.3f}, ΔKd={dKd:.3f}")
# 预期: ΔKd接近3, ΔKp接近0, ΔKi接近0
运行与验证方法
- 运行环境:Python 3.8+,仅需 NumPy(
pip install numpy)。 - 执行方式:将上述代码保存为
fuzzy_rules.py,在终端运行python fuzzy_rules.py。 - 通过条件:
- 测试1输出三个值均接近 0(值 < 0.1);
- 测试2输出 ΔKp 接近 3(2.5~3.0),ΔKi、ΔKd 接近 0;
- 测试3输出 ΔKd 接近 3(2.5~3.0),ΔKp、ΔKi 接近 0。
规则表可视化
常见问题
- 输出超出 [-3,3] 范围:输入 e/ec 超出论域时被 clip 到边界,输出由重心法保证在论域内。
- 规则表对称性:ΔKp 表关于 ZO 行/列对称,ΔKi 表关于 ZO 行对称,ΔKd 表关于 ZO 行/列对称,符合 PID 调参的对称特性。
- 实际应用:推理得到的 ΔKp、ΔKi、ΔKd 需乘以各自量化因子(如 \( K_{up}, K_{ui}, K_{ud} \))后叠加到初始 PID 参数上,量化因子需根据被控对象另行整定。