python在工业过程控制场景模拟第四十五篇:多组PID参数仿真数据对比,自动筛选最优整定参数组合。
2026/8/3 11:26:57 网站建设 项目流程

PID 参数自动寻优与仿真对比系统 —— 基于 OOP 的多组参数筛选实战

"调 PID 这件事,新手靠蒙,老手靠手感,高手靠数据。但在 DCS 上一个个试参数组合,每次都要等半小时看响应——效率太低了。如果把整条响应曲线变成一组数字指标,让程序帮你从几十组参数里挑出最好的那一组,你只需要做最后的决定。"

—— 哈尔滨工程大学《工业过程控制》课程核心思想延伸

一、实际应用场景描述

在过程控制领域,PID 参数整定是每个控制工程师的必修课。典型的工作场景如下:

┌──────────────────────────────────────────────┐

│ 控制室操作站 │

│ │

│ 工程师: "Kp=1.5, Ti=20, Td=5 这组怎么样?" │

│ │

│ ┌──────────────────────────────────────────┐ │

│ │ 趋势画面 │ │

│ │ SP ────┐ │ │

│ │ └─┐ │ │

│ │ ├─┐ PV │ │

│ │ │ └─────┐ │ │

│ │ │ └──┐ │ │

│ │ │ └─── 稳态 │ │

│ │ MV ─┐ │ │ │

│ │ └────┘ │ │

│ └──────────────────────────────────────────┘ │

│ │

│ 工程师: "超调有点大,Kp降到1.2试试..." │

│ (再等 30 分钟) │

└──────────────────────────────────────────────┘

常见整定方法及其局限

方法 原理 优点 局限

Ziegler-Nichols 阶跃响应 从 S 曲线求 L 和 T 简单直观 对噪声敏感,参数偏激进

Z-N 临界振荡 找临界增益 K_u 和周期 T_u 无需模型 需要让系统振荡,有风险

Cohen-Coon 基于一阶+滞后模型 对大滞后效果好 模型不准时失效

IMC 内模 基于模型解析设计 参数有明确物理意义 依赖精确模型

仿真对比法 多组参数仿真+量化评分 安全、全面、可重复 需要仿真模型

哈尔滨工程大学《工业过程控制》课程(彭秀艳教授主讲,国家级一流本科课程)在第六章"PID 控制律及参数整定"中系统讲解了多种整定方法:

"PID 参数整定的本质是优化问题——在稳定性、快速性和准确性之间寻找最佳平衡点。不同的整定方法给出了不同的经验公式,但面对具体对象时,最可靠的方式是通过仿真对比,用量化指标来验证哪组参数真正适合。"

二、引入痛点

2.1 现场的真实困境

场景 现场发生了什么 根因

整定耗时 "一组参数试下来要 40 分钟,一天只能试十几组" 在线整定受限于过程动态

主观评判 "我觉得这组比那组好"——同事不同意 缺乏量化评分标准

顾此失彼 "超调小了但调节时间太长" 多目标权衡靠人工

遗忘记录 "上周试过一组不错的参数,忘了记下来" 没有系统化归档

新人畏难 "Z-N 公式算出来 Kp=3.2,一投就振荡" 理论值需要结合实际修正

2.2 核心矛盾

人的眼睛擅长看趋势图,但不擅长做量化比较。两组响应曲线,一组超调 8%、调节时间 120s,另一组超调 5%、调节时间 150s——哪组更好?这取决于你对超调和速度的权重偏好。但如果你有 50 组参数要比较,人工逐一看图是不现实的。你需要一个"评分函数",把每条曲线变成一个分数。

2.3 我们要解决什么

用一段 Python 程序,构建一个PID 参数自动寻优与仿真对比系统,实现:

1. 多组参数批量仿真 —— 自动遍历参数组合矩阵

2. 一阶+纯滞后过程模型 —— 标准工业对象仿真

3. 量化评分体系 —— ISE / IAE / ITAE / 超调惩罚 / 调节时间

4. 自动排序筛选 —— 综合评分 TOP N

5. 结果可视化 —— 响应对比图 + 参数散点图

6. 面向对象设计 —— 分层清晰,可扩展

三、核心逻辑讲解

3.1 理论基础:PID 性能评价指标

本工具基于哈工程《工业过程控制》第六章"PID 控制律及参数整定":

① 误差积分指标

指标 公式 特点

ISE \int_0^\infty e^2(t)dt 惩罚大偏差,对后期小偏差不敏感

IAE \int_0^\infty \|e(t)\|dt 均匀惩罚所有偏差

ITAE \int_0^\infty t\|e(t)\|dt 强烈惩罚后期偏差,鼓励快速收敛

② 综合评分函数

Score = w_1 \cdot \frac{IAE_{norm}}{IAE_{norm} + IAE} + w_2 \cdot (1 - \frac{\sigma}{\sigma_{max}}) + w_3 \cdot (1 - \frac{t_s}{t_{s,max}})

其中:

- IAE_{norm} 是归一化常数

- \sigma 是超调量百分比

- t_s 是调节时间(进入 ±2% 并保持不变)

- w_1, w_2, w_3 是权重系数

③ 参数扫描策略

Kp ∈ [0.5, 1.0, 1.5, 2.0, 2.5, 3.0]

Ti ∈ [10, 20, 30, 40, 50]

Td ∈ [0, 2, 5, 8, 10]

总共 6 × 5 × 5 = 150 组组合

3.2 系统数据流

┌──────────────────────────────┐

│ 参数矩阵定义 │

│ Kp × Ti × Td 组合 │

└──────────────┬───────────────┘

┌──────────────▼───────────────┐

│ ① 批量仿真 │

│ 每组参数跑一遍阶跃响应 │

└──────────────┬───────────────┘

┌──────────────▼───────────────┐

│ ② 指标计算 │

│ ISE / IAE / ITAE / σ / ts │

└──────────────┬───────────────┘

┌──────────────▼───────────────┐

│ ③ 综合评分 │

│ 加权求和 → 0~100 分 │

└──────────────┬───────────────┘

┌──────────────▼───────────────┐

│ ④ 排序筛选 │

│ TOP N + Pareto 前沿 │

└──────────────┬───────────────┘

┌──────────────▼───────────────┐

│ ⑤ 可视化 & 报告 │

│ 响应对比 + 参数分布 │

└──────────────────────────────┘

四、代码讲解(面向对象设计)

4.1 类结构总览

类名 职责 设计模式

"PIDParams" 单组 PID 参数(dataclass) 值对象

"ProcessParams" 过程模型参数(值对象) 值对象

"ScoringWeights" 评分权重配置(值对象) 值对象

"SimulationConfig" 仿真配置(值对象) 值对象

"PIDController" 位置式 PID(含抗积分饱和) 封装

"ProcessModel" 一阶+纯滞后仿真模型 封装

"StepResponseSimulator" 单组参数仿真器 命令模式

"MetricsCalculator" 性能指标计算器 策略模式

"Scorer" 综合评分器 策略模式

"ParameterSweeper" 参数扫描与批量仿真 工厂模式

"ResultRepository" 结果存储与查询 仓储模式

"ReportGenerator" 分析报告生成器 模板方法

"AutoTuningSystem" 系统编排器(聚合根) 聚合根

4.2 数据模型层

from dataclasses import dataclass

from typing import List, Tuple, Optional, Dict, Callable

import numpy as np

import itertools

import csv

from pathlib import Path

from enum import Enum

class MetricType(Enum):

"""性能指标类型"""

ISE = "ISE"

IAE = "IAE"

ITAE = "ITAE"

OVERSHOOT = "Overshoot"

SETTLING_TIME = "SettlingTime"

@dataclass(frozen=True)

class PIDParams:

"""单组 PID 参数 —— 值对象"""

Kp: float

Ti: float

Td: float

dt: float = 1.0

mv_min: float = 0.0

mv_max: float = 100.0

anti_windup: bool = True

def key(self) -> str:

"""唯一键"""

return f"Kp{self.Kp}_Ti{self.Ti}_Td{self.Td}"

@dataclass(frozen=True)

class ProcessParams:

"""过程模型参数 —— 一阶+纯滞后"""

K: float = 1.0 # 过程增益

tau: float = 30.0 # 时间常数 (s)

theta: float = 10.0 # 纯滞后 (s)

dt: float = 1.0 # 仿真步长 (s)

@dataclass(frozen=True)

class ScoringWeights:

"""评分权重"""

ise: float = 0.15

iae: float = 0.25

itae: float = 0.20

overshoot: float = 0.25

settling_time: float = 0.15

def validate(self) -> bool:

"""权重之和应为 1"""

total = self.ise + self.iae + self.itae + self.overshoot + self.settling_time

return abs(total - 1.0) < 0.01

@dataclass(frozen=True)

class SimulationConfig:

"""仿真配置"""

sim_duration: float = 600.0 # 仿真时长 (s)

step_time: float = 50.0 # 阶跃施加时间 (s)

sp_initial: float = 50.0 # 初始设定值

sp_final: float = 80.0 # 阶跃后设定值

settling_band: float = 0.02 # 调节时间判定带 (±2%)

4.3 PID 控制器

class PIDController:

"""

位置式 PID 控制器(含抗积分饱和)

离散化公式:

P = Kp * e

I = Kp/Ti * ∫e·dt

D = -Kp*Td * de/dt (对 PV 微分)

"""

def __init__(self, params: PIDParams):

self.p = params

self.reset()

def reset(self):

self._integral = 0.0

self._prev_pv = 0.0

self._first = True

def compute(self, setpoint: float, process_value: float) -> float:

dt = self.p.dt

error = setpoint - process_value

# 比例项

P = self.p.Kp * error

# 积分项

if self.p.Ti > 0:

self._integral += error * dt

I = (self.p.Kp / self.p.Ti) * self._integral

else:

I = 0.0

# 微分项 (对 PV 微分,避免 SP 阶跃冲击)

if self.p.Td > 0 and not self._first:

D = -self.p.Kp * self.p.Td * (process_value - self._prev_pv) / dt

else:

D = 0.0

mv = P + I + D

# 抗积分饱和: 钳位后反算

mv_clipped = max(self.p.mv_min, min(self.p.mv_max, mv))

if self.p.anti_windup and self.p.Ti > 0:

if abs(mv - mv_clipped) > 1e-9:

allowed_I = (mv_clipped - P - D) / (self.p.Kp / self.p.Ti)

self._integral = allowed_I

self._prev_pv = process_value

self._first = False

return mv_clipped

4.4 过程模型

class ProcessModel:

"""

一阶惯性 + 纯滞后过程模型

离散方法: 一阶惯性法

G(s) = K / (τs + 1) · e^(-θs)

"""

def __init__(self, params: ProcessParams):

self.p = params

self.reset()

def reset(self):

self._output = 0.0

self._alpha = self.p.dt / (self.p.tau + self.p.dt)

delay_steps = max(1, int(round(self.p.theta / self.p.dt)))

self._delay_buffer = [0.0] * (delay_steps + 1)

def step(self, mv: float) -> float:

"""

执行一个仿真步

Args:

mv: 控制器输出 (%)

Returns:

过程输出 PV

"""

# 一阶惯性

target = self.p.K * mv

self._output += self._alpha * (target - self._output)

# 纯滞后

self._delay_buffer.append(self._output)

delayed = self._delay_buffer.pop(0)

return delayed

4.5 单组参数仿真器

class StepResponseSimulator:

"""

单组参数阶跃响应仿真器 —— 命令模式

执行一次完整的阶跃响应仿真,返回时间序列数据

"""

def __init__(self, process: ProcessModel, sim_cfg: SimulationConfig):

self.process = process

self.sim_cfg = sim_cfg

def run(self, pid_params: PIDParams) -> dict:

"""

对给定 PID 参数执行仿真

Args:

pid_params: PID 参数

Returns:

仿真结果字典,包含时间序列

"""

pid = PIDController(pid_params)

self.process.reset()

pid.reset()

n_steps = int(self.sim_cfg.sim_duration / self.process.p.dt)

step_idx = int(self.sim_cfg.step_time / self.process.p.dt)

time = []

sp = []

pv = []

mv = []

for k in range(n_steps + 1):

t = k * self.process.p.dt

current_sp = self.sim_cfg.sp_final if k >= step_idx else self.sim_cfg.sp_initial

current_pv = self.process.step(mv[-1] if mv else 0.0)

current_mv = pid.compute(current_sp, current_pv)

time.append(t)

sp.append(current_sp)

pv.append(current_pv)

mv.append(current_mv)

return {

'pid_params': pid_params,

'time': np.array(time),

'sp': np.array(sp),

'pv': np.array(pv),

'mv': np.array(mv)

}

4.6 性能指标计算器

class MetricsCalculator:

"""

性能指标计算器 —— 策略模式

计算:

- ISE: 积分平方误差

- IAE: 积分绝对误差

- ITAE: 时间加权积分绝对误差

- Overshoot: 超调量百分比

- SettlingTime: 调节时间

"""

def __init__(self, sim_cfg: SimulationConfig):

self.sim_cfg = sim_cfg

def calculate(self, result: dict) -> dict:

"""

计算所有性能指标

Args:

result: StepResponseSimulator.run() 的返回值

Returns:

指标字典

"""

sp = result['sp']

pv = result['pv']

t = result['time']

# 找到阶跃后的索引

step_mask = sp > self.sim_cfg.sp_initial

pv_after = pv[step_mask]

sp_after = sp[step_mask]

t_after = t[step_mask]

if len(pv_after) == 0:

return self._empty_metrics()

# 误差序列

error = sp_after - pv_after

abs_error = np.abs(error)

# ISE

ise = float(np.sum(error ** 2) * self.sim_cfg.sim_duration / len(error))

# IAE

iae = float(np.sum(abs_error) * self.sim_cfg.sim_duration / len(abs_error))

# ITAE

itae = float(np.sum(t_after * abs_error) * self.sim_cfg.sim_duration / len(abs_error))

# 超调量

final_sp = self.sim_cfg.sp_final

max_pv = float(np.max(pv_after))

overshoot = max(0.0, (max_pv - final_sp) / final_sp * 100.0) if final_sp > 0 else 0.0

# 调节时间: 首次进入 ±band 并永久保持

band = self.sim_cfg.settling_band * final_sp

settled = False

settling_time = float('inf')

for i in range(len(pv_after)):

if abs(pv_after[i] - final_sp) <= band:

# 检查后续是否一直保持在带内

remaining = pv_after[i:]

if np.all(np.abs(remaining - final_sp) <= band):

settling_time = float(t_after[i])

settled = True

break

if not settled:

settling_time = self.sim_cfg.sim_duration

return {

'ise': round(ise, 2),

'iae': round(iae, 2),

'itae': round(itae, 2),

'overshoot_pct': round(overshoot, 2),

'settling_time_s': round(settling_time, 1),

'final_sp': final_sp,

'final_pv': round(float(pv_after[-1]), 2)

}

def _empty_metrics(self) -> dict:

return {

'ise': float('inf'), 'iae': float('inf'), 'itae': float('inf'),

'overshoot_pct': float('inf'), 'settling_time_s': float('inf'),

'final_sp': 0, 'final_pv': 0

}

4.7 综合评分器

class Scorer:

"""

综合评分器

将多个性能指标归一化为 0~100 分

归一化方法: 线性映射到 [0, 100]

分数 = Σ(w_i × normalized_metric_i)

"""

def __init__(self, weights: ScoringWeights, reference_metrics: dict = None):

"""

Args:

weights: 各指标权重

reference_metrics: 参考基准(用于归一化),如果为 None 则在运行时自动确定

"""

self.weights = weights

self.ref = reference_metrics or {}

def score(self, metrics: dict) -> float:

"""

计算综合评分 (0~100)

越低越好: ISE, IAE, ITAE, Overshoot, SettlingTime

→ 转换为: score = 100 - normalized_rank

"""

# 这里使用简单的倒数归一化

# 实际项目中可以用 min-max 归一化

raw_scores = {}

# ISE (越小越好 → 倒数)

raw_scores['ise'] = 1.0 / (1.0 + metrics.get('ise', 1e6))

# IAE

raw_scores['iae'] = 1.0 / (1.0 + metrics.get('iae', 1e6))

# ITAE

raw_scores['itae'] = 1.0 / (1.0 + metrics.get('itae', 1e6))

# Overshoot (0~100%)

raw_scores['overshoot'] = 1.0 - min(1.0, metrics.get('overshoot_pct', 100) / 100.0)

# Settling Time (归一化到 0~1, 假设 600s 为最差)

st = min(600.0, metrics.get('settling_time_s', 600.0))

raw_scores['settling_time'] = 1.0 - st / 600.0

# 加权求和

total = (

self.weights.ise * raw_scores['ise'] +

self.weights.iae * raw_scores['iae'] +

self.weights.itae * raw_scores['itae'] +

self.weights.overshoot * raw_scores['overshoot'] +

self.weights.settling_time * raw_scores['settling_time']

)

return round(total * 100, 2)

4.8 参数扫描器

class ParameterSweeper:

"""

参数扫描与批量仿真 —— 工厂模式

生成参数组合矩阵并逐一仿真

"""

def __init__(self, process_params: ProcessParams, sim_cfg: SimulationConfig):

self.process_params = process_params

self.sim_cfg = sim_cfg

self.results = []

def sweep(self, kp_range: List[float], ti_range: List[float],

td_range: List[float]) -> List[dict]:

"""

执行参数扫描

Args:

kp_range: Kp 取值列表

ti_range: Ti 取值列表

td_range: Td 取值列表

Returns:

所有仿真结果列表

"""

self.results.clear()

process = ProcessModel(self.process_params)

simulator = StepResponseSimulator(process, self.sim_cfg)

total = len(kp_range) * len(ti_range) * len(td_range)

count = 0

for kp in kp_range:

for ti in ti_range:

for td in td_range:

pid_params = PIDParams(Kp=kp, Ti=ti, Td=td, dt=self.process_params.dt)

result = simulator.run(pid_params)

self.results.append(result)

count += 1

return self.results

4.9 结果仓储

class ResultRepository:

"""

结果存储与查询 —— 仓储模式

提供:

- 添加结果

- 按评分排序

- 筛选 TOP N

- 导出 CSV

"""

def __init__(self):

self.entries = []

def add(self, pid_params: PIDParams, metrics: dict, score: float):

"""添加一个结果条目"""

self.entries.append({

'params': pid_params,

'metrics': metrics,

'score': score

})

def top_n(self, n: int = 10, ascending: bool = False) -> List[dict]:

"""返回评分最高的 N 组"""

sorted_entries = sorted(self.entries, key=lambda x: x['score'], reverse=not ascending)

return sorted_entries[:n]

def export_csv(self, filepath: str):

"""导出为 CSV"""

with open(filepath, 'w', newline='', encoding='utf-8') as f:

writer = csv.writer(f)

writer.writerow(['Rank', 'Kp', 'Ti', 'Td', 'Score', 'ISE', 'IAE',

'ITAE', 'Overshoot%', 'SettlingTime'])

for i, entry in enumerate(self.top_n(100)):

m = entry['metrics']

p = entry['params']

writer.writerow([i+1, p.Kp, p.Ti, p.Td, f"{entry['score']:.2f}",

m['ise'], m['iae'], m['itae'],

m['overshoot_pct'], m['settling_time_s']])

4.10 分析报告生成器

class ReportGenerator:

"""

分析报告生成器

"""

def generate(self, top_results: List[dict], total_scanned: int) -> str:

"""生成文本报告"""

lines = [

"=" * 65,

" PID 参数自动寻优与仿真对比分析报告",

"=" * 65,

"",

f" 扫描参数组合总数: {total_scanned}",

f" 筛选出最优 {len(top_results)} 组",

"",

"-" * 65,

f" {'排名':<4} {'Kp':<8} {'Ti':<8} {'Td':<8} {'评分':<8} {'超调%':<8} {'调节时间':<10}",

"-" * 65

]

for i, entry in enumerate(top_results):

p = entry['params']

m = entry['metrics']

lines.append(f" #{i+1:<3} {p.Kp:<8.1f} {p.Ti:<8.1f} {p.Td:<8.1f} "

f"{entry['score']:<8.2f} {m['overshoot_pct']:<8.2f} {m['settling_time_s']:<10.1f}")

lines.append("")

lines.append(" 建议: 优先验证排名第 1 的参数组合")

lines.append("=" * 65)

return "\n".join(lines)

4.11 系统编排器

class AutoTuningSystem:

"""

PID 自动寻优系统 —— 聚合根

串联: 参数扫描 → 仿真 → 指标计算 → 评分 → 排序 → 报告

"""

def __init__(self, process_params: ProcessParams = None,

sim_cfg: SimulationConfig = None,

scoring_weights: ScoringWeights = None):

self.process_params = process_params or ProcessParams()

self.sim_cfg = sim_cfg or SimulationConfig()

self.scoring_weights = scoring_weights or ScoringWeights()

self.sweeper = ParameterSweeper(self.process_params, self.sim_cfg)

self.metrics_calc = MetricsCalculator(self.sim_cfg)

self.scorer = Scorer(self.scoring_weights)

self.repository = ResultRepository()

self.reporter = ReportGenerator()

def run(self, kp_range: List[float], ti_range: List[float],

td_range: List[float]) -> str:

"""

执行完整的寻优流程

Args:

kp_range: Kp 扫描范围

ti_range: Ti 扫描范围

td_range: Td 扫描范围

Returns:

分析报告

"""

# ① 参数扫描 + 仿真

results = self.sweeper.sweep(kp_range, ti_range, td_range)

# ② 指标计算 + 评分

for result in results:

metrics = self.metrics_calc.calculate(result)

score = self.scorer.score(metrics)

self.repository.add(result['pid_params'], metrics, score)

# ③ 排序

top = self.repository.top_n(10)

# ④ 报告

report = self.reporter.generate(top, len(results))

return report

4.12 完整演示

def demo():

"""完整演示"""

print("=" * 65)

print(" PID 参数自动寻优与仿真对比系统 v1.0")

print(" 基于哈尔滨工程大学《工业过程控制》课程理论")

print("=" * 65)

# 过程模型: 一阶+纯滞后

process = ProcessParams(K=1.0, tau=30.0, theta=10.0, dt=1.0)

# 仿真配置

sim_cfg = SimulationConfig(sim_duration=600.0, step_time=50.0)

# 评分权重

weights = ScoringWeights(ise=0.15, iae=0.25, itae=0.20,

overshoot=0.25, settling_time=0.15)

# 参数扫描范围

kp_range = [0.5, 1.0, 1.5, 2.0, 2.5, 3.0]

ti_range = [10.0, 20.0, 30.0, 40.0, 50.0]

td_range = [0.0, 2.0, 5.0, 8.0, 10.0]

print(f"\n📋 过程模型: G(s) = {process.K}/({process.tau}s+1)·e^(-{process.theta}s)")

print(f"📋 参数扫描: Kp={kp_range}, Ti={ti_range}, Td={td_range}")

print(f

利用AI解决实际问题,如果你觉得这个工具好用,欢迎关注长安牧笛!

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询