☰
西门子博途PID自写SCL源码:从公式到抗积分饱和完整实现
2026/10/15 13:10:09 网站建设 项目流程

简介:西门子博途环境下的PID控制SCL源代码文档,面向PLC工程师与自动化开发者,解决在TIA Portal中用SCL语言实现自定义PID功能块的需求。其中包含一个完整FUNCTION_BLOCK "PID"源代码,输入、输出、内部变量定义齐全,并启用S7_Optimized_Access,便于直接参考移植到博途项目。资源包共1个doc文件,大小约19KB,以Word文档形式保留代码与注释,方便查阅、复制和二次编辑。已有4180人学习/下载,适合正在学习PID算法或需要封装控制功能块的工程师使用。代码覆盖比例、积分、微分计算,支持手动/自动切换、积分保持、死区设置、输出限幅、过程变量校正及干扰前馈等,可按GAIN、TI、TD等参数灵活调整,帮助理解PID内部机制并快速搭建自定义控制器,减少基础算法重复开发时间。

1. 西门子博途 PID 与 SCL 源代码:先分清是想看官方算法还是自写控制逻辑

第一次在博途里双击 PID_Compact,想看看它的 SCL 源代码,结果发现块内部灰成一片,这是很多做 S7-1200/1500 项目的人都遇到过的场景。功能块默认开了查询保护,接口全亮、算法不见。找源代码这个需求背后,通常不是出于兴趣,而是现场调试卡住了:要么自整定结果不符合工艺,要么想把 PID 逻辑完整移植到另一套系统。

这篇笔记不聊怎么绕过保护,只做两件正经事:第一,说清官方块和 SCL 源代码之间的边界;第二,给一套可以直接建块编译的 SCL 源码,让比例、积分、微分和抗积分饱和都能看得见、改得动。最后你会得到一个自己可控的 PID FB,并且知道哪些坑会让它在现场翻车。

2. 博途 PID 块和 SCL 源代码的边界:为什么官方块是个黑匣子

2.1 官方 PID_Compact 为什么看不到算法

在博途里,PID_Compact 是一条标准的系统功能块,拖到 OB 里后会生成一个背景数据块。块能正常工作,但块的实现部分默认被勾选了 Know-how protection。意思很直接:你可以看到接口列表,也就是 VAR_INPUT、VAR_OUTPUT、VAR_IN_OUT 和静态变量,但看不到中间的计算语句;用“从块生成源文本”导出的,只是接口声明和调用关系,算法段是空的。

这个设计不是某个项目的 bug,而是厂商在发布库时把核心算法封装了,避免用户改动后影响后续固件升级。另外一个现实原因是,PID_Compact 内部依赖一部分工艺对象和隐式变量,即使你真想把它完整移植到别的 PLC 品牌,也很难脱离 TIA 环境运行。我见过有开发者花整整两天研究 DB 优化访问和块的加密属性,最后还是按授权渠道提交解除申请,流程非常长。

所以我的建议是:官方块适合“作为整体使用”,SCL 源代码则适合“自己掌控算法”。如果你的工艺只是普通温度、压力、流量回路,自写 SCL 完全够用,而且可调余地更大。

提示:块受保护不是故障,强行修改系统块既影响后续版本升级,也不符合正常的技术边界。把精力放在自写一个可控的 PID FB 上,反而是现场工程师更划算的路线。

2.2 自写 SCL 和官方块怎么选:一张表看清差异

不是所有项目都必须换掉官方块。做技术选型时,我一般看四条:算法可见性、自整定需求、系统迁移需求、输出形式复杂度。把这四个问题列成一张表,判断就很快。

维度官方 PID_Compact自写 SCL 源代码
算法可见性接口可见,内部被保护完全可读,可加注释可改公式
自整定/自适应自带,适合常规回路要自己补充归一化和整定逻辑
跨系统移植依赖 TIA 环境代码可导出为文本,移植方便
特殊输出常规模拟量和阀门定位可随意加分程、时间比例、前馈
调参通道工艺对象/HMIHMI 直接写背景 DB,方式更直接
代码稳定性官方升级维护完全由自己负责,换人换岗有成本

如果现场只是温度、液位、压力这类常规连续控制回路,我建议保留官方块,毕竟它的自整定和诊断功能很成熟。但如果你需要给输出加前馈、把控制逻辑复制到另一个 DCS 里做仿真、或者要给学生和后来者讲透 PID 内部计算,自写 SCL 源代码才是真正解决问题的手段。

2.3 从 PID 公式到 SCL 变量:量纲、周期、离散化

自写 SCL 之前,至少要能看懂离散化公式。连续域里最常见的理想式 PID 是下面这个形式:

u(t) = Kp * e(t) + Kp * (1/Ti) * ∫e(t)dt - Kp * Td * d(PV)/dt

注意最后一项我写成对过程值 PV 求导,而不是对误差 e 求导。这是为了避开设定值阶跃时出现的微分冲击,后面第 4 章会专门说这个坑。

离散到 PLC 的周期扫描里,位置式 PID 可以写成:

rOutput = Kp * rErr + Kp * rIntSum * rTs / rTi + Kp * rTd * (rPVPrev - rFeedback) / rTs

这里有三个关键变量需要统一量纲:设定值和反馈用同一个工程单位;输出用百分比 0~100;采样周期 rTs 的单位是秒。很多自写 PID 翻车,就是因为量纲没统一,Kp 算出来的值要么大得离谱,要么小到没反应。

变量对应的关系大概是:rSetpoint 对应博途 PID_Compact 的 Setpoint,rFeedback 对应 Input,rOutput 对应 Output,rKp 对应参数分配里的比例增益,rTi 是积分时间,rTd 是微分时间。背景数据块里存这些参数,便于在线修改和 HMI 绑定。

3. 在博途 SCL 里建出可复用的 PID 控制器:完整源码与调用方法

3.1 新建 FB 并声明接口:先照抄这一组变量

在博途项目树里右键“添加新块”,选择函数块 FB,语言选 SCL,名称建议写成 FB_PID_SCL。确认后会自动生成一个空块,然后把下面的接口声明复制进去。这里的命名刻意做得直观,因为 HMI 上要和这些标签直接关联。

FUNCTION_BLOCK FB_PID_SCL VAR_INPUT bEnable : BOOL; // 闭环总开关 bManualMode : BOOL; // TRUE 时输出手动值 rSetpoint : REAL; // 设定值,工程单位 rFeedback : REAL; // 过程反馈,工程单位 rManualOut : REAL; // 手动输出值,百分比 rKp : REAL; // 比例增益 rTi : REAL; // 积分时间,单位秒,0 表示关闭 rTd : REAL; // 微分时间,单位秒,0 表示关闭 rTs : REAL; // 采样周期,单位秒 rOutMax : REAL; // 输出上限 rOutMin : REAL; // 输出下限 END_VAR VAR_OUTPUT rOutput : REAL; // PID 输出,百分比 xLimitActive : BOOL; // 限幅是否生效 END_VAR VAR rErr : REAL; // 当前偏差 rPterm : REAL; // 比例项 rIterm : REAL; // 积分项 rDterm : REAL; // 微分项 rIntSum : REAL; // 积分累加值 rRaw : REAL; // 限幅前的输出 rPrevOut : REAL; // 上一周期输出,用于积分饱和判断 rPVPrev : REAL; // 上一周期反馈值,用于微分计算 xIntStop : BOOL; // 积分冻结标志 END_VAR

接口里把控制参数全部声明成 VAR_INPUT,是因为背景数据块会自动保存这些输入值,HMI 可以直接往 DB 里写参数,不需要额外做变量传递。所有浮点量都用 REAL,避免 INT 和 REAL 之间反复转换导致精度损失。

3.2 完整 SCL 源代码:位置式 PID 加抗积分饱和

下面这段代码是核心。它不是教科书式的理想实现,而是针对现场做了三个处理:手动切换自动时输出不突跳、输出限幅时积分冻结、微分项只对反馈求导。

// 停机状态:输出归零并记录跟踪状态 IF NOT bEnable THEN rOutput := 0.0; rPrevOut := 0.0; rIntSum := 0.0; rPVPrev := rFeedback; xLimitActive := FALSE; RETURN; END_IF; // 手动状态:输出直接跟随手动值 IF bManualMode THEN rOutput := rManualOut; rPrevOut := rManualOut; rIntSum := 0.0; rPVPrev := rFeedback; xLimitActive := FALSE; RETURN; END_IF; // 采样周期保护,防止除零 IF rTs <= 0.0 THEN rTs := 0.1; END_IF; // 偏差和比例项 rErr := rSetpoint - rFeedback; rPterm := rKp * rErr; // 积分项:条件积分避免饱和 rIterm := 0.0; IF rTi > 0.0 THEN rIntSum := rIntSum + rErr * rTs / rTi; // 如果输出已经在上限且误差为正,或已经在下限且误差为负,则冻结积分 xIntStop := FALSE; IF rPrevOut >= rOutMax AND rErr > 0.0 THEN xIntStop := TRUE; END_IF; IF rPrevOut <= rOutMin AND rErr < 0.0 THEN xIntStop := TRUE; END_IF; IF NOT xIntStop THEN rIntSum := rIntSum; END_IF; rIterm := rKp * rIntSum; END_IF;

这里要重点说明一个设计:代码里先做了一次累加,再判断是否冻结。如果冻结,就保留累加前的值。这个写法比“累加前先判断”更直观,即使没有 xIntStop,也不会让积分无限增长。实际项目中,阀门开到最大仍达不到设定值,温度一直回不去,积分项就会越积越大,最终导致输出被积分“焊死”。条件积分比简单的输出限幅可靠得多。

// 微分项:只对反馈求导,减少设定值阶跃带来的冲击 IF rTd > 0.0 THEN rDterm := rKp * rTd * (rPVPrev - rFeedback) / rTs; ELSE rDterm := 0.0; END_IF; rPVPrev := rFeedback; // 合成输出并限幅 rRaw := rPterm + rIterm + rDterm; IF rRaw > rOutMax THEN rRaw := rOutMax; xLimitActive := TRUE; ELSIF rRaw < rOutMin THEN rRaw := rOutMin; xLimitActive := TRUE; ELSE xLimitActive := FALSE; END_IF; rOutput := rRaw; rPrevOut := rRaw;

参数说明:rKp 的物理含义是“每 1 度误差对应多少百分比输出”。比如 Kp=2,误差 10°C,比例项就是 20%。rTi 的单位是秒,数值越大积分越弱,系统消除静差越慢;rTd 的单位同样是秒,数值越大微分作用越强,但对噪声也越敏感。rTs 必须和调用这个块的 OB 中断周期一致,否则微分项计算出来的值会整体失真。

3.3 调用到 OB30 并形成最小验证闭环

S7-1200/1500 里建议用循环中断 OB 调用 PID,比如 OB30,中断周期设成 100ms。不要在 OB1 里直接调用,因为 OB1 的扫描周期不确定,微分项会被扫描波动拉偏。在 OB30 里写入下面这段调用代码:

"PID_SCL_DB"(bEnable := "HMI_AutoMode", bManualMode := NOT "HMI_AutoMode", rSetpoint := "HMI_Setpoint", rFeedback := "AI_Feedback", rManualOut := "HMI_ManualOut");

调用时不传 rKp、rTi、rTd 这些参数,让它们保留在自己背景数据块里,HMI 直接修改 DB 就行。参数初值放在 OB100 启动时写一次:

"PID_SCL_DB".rKp := 2.0; "PID_SCL_DB".rTi := 20.0; "PID_SCL_DB".rTd := 0.0; "PID_SCL_DB".rTs := 0.1; "PID_SCL_DB".rOutMax := 100.0; "PID_SCL_DB".rOutMin := 0.0;

这个习惯很重要。如果你在 OB30 的调用里直接写rKp := 2.0,那它每个周期都会把 HMI 里改过的值覆盖回去,在线调参永远不生效。把参数初始化放在 OB100,运算调用只传过程变量,就能避免这个问题。

做完基本调用后,可以用一个最简单的静态表来验证逻辑:当 rTi 和 rTd 都是 0 时,输出应当等于Kp * (设定值 - 反馈值)。Kp=2、设定值 50、反馈 40,输出应该是 20;反馈等于设定值时,输出应该是 0。连一个 Watch 表在线监视这几个值,逻辑对错一眼就能看出来。

4. 博途 SCL PID 现场调试最容易翻车的五个坑:现象、原因、解决

4.1 输出反复顶格,阀门开度乱跳:积分饱和

现象:温度一直低于设定值,PID 输出很快冲到 100%,但温度上来后,输出又突然跌到 50% 以下,阀门反复大幅度动作。

原因:积分项不受限幅约束,偏差持续为正,积分累加值不断增大;当反馈越过设定值时,积分还没来得及退下来,输出就被积分项拖着反向运动,形成大的超调和振荡。

解决:使用条件积分,也就是上一周期输出到达上限且误差为正时冻结积分,到达下限且误差为负时也冻结。第 3 章代码里的 xIntStop 判断已经覆盖了这个逻辑。如果现场还是抖,优先检查 rOutMax 和 rOutMin 是否设成了实际阀门行程范围,不要把上下限留得过宽。

4.2 手自动切换瞬间输出突跳,设备动作猛

现象:手动模式输出 55%,切换自动的一瞬间,输出跳到了 80% 或者 30%,管路压力跟着砸一下。

原因:手动状态把 rIntSum 清零了,切换自动后输出等于比例项加积分项加微分项,这个组合值和手动输出 55% 没有任何继承关系,自然就会突变。

解决:手动模式不要简单清零积分项,而要让积分项反向补偿比例项和微分项,让当前输出和手动值一致。用下面这段逻辑替换手动分支:

IF bManualMode THEN rOutput := rManualOut; rPterm := rKp * (rSetpoint - rFeedback); IF ABS(rKp) > 0.01 THEN rIntSum := (rManualOut - rPterm) / rKp; ELSE rIntSum := 0.0; END_IF; rPVPrev := rFeedback; xLimitActive := FALSE; RETURN; END_IF;

这样从手动切自动时,比例项和积分项之和会尽量贴近手动输出,设备就不会猛地跳变。Kp 接近 0 时要做保护,否则除零会直接报错。

4.3 设定值一改,输出瞬间满量程:微分冲击

现象:操作员把设定值从 80 改成 90,温度还没动,PID 输出瞬间冲到 100%,执行机构猛地打开。

原因:如果微分项对误差求导,设定值阶跃瞬间 e 的变化量非常大,(e - ePrev) / rTs会产生一个巨大的尖峰。这个尖峰不是真实过程需求,纯粹是数学上的突变量。

解决:把微分项改为只对反馈值 PV 求导,也就是代码里的rKp * rTd * (rPVPrev - rFeedback) / rTs。设定值变化时,反馈没有变,微分项输出就是 0,自然没有冲击。这个修改是我在自写 SCL 时最常用的一招。

4.4 反馈噪声大,微分项把输出抖成三角波

现象:温度和流量反馈带高频噪声,打开微分项后输出变得很毛糙,阀门高频抖动,执行机构寿命明显缩短。

原因:微分天生对被控量的变化率敏感,噪声的斜率变化非常快,微分项会把噪声放大很多倍,输出自然跟着抖。

解决:先从源头减噪,模拟量模块上设置滤波时间常数,或在程序里对反馈做一阶低通滤波。如果噪声仍然大,就把 rTd 调成 0,靠比例加积分完成控制。必须用微分时,我的习惯是在微分输出后面加一阶惯性滤波,系数取 0.1~0.3,让微分动作柔和下来。

4.5 在线改了 PID 参数但不生效:检查是不是在 OB 里覆盖了参数

现象:触摸屏上把 Kp 从 2.0 改成 5.0,趋势曲线完全没变化,重启 PLC 又恢复原值。

原因:调用 OB 里写了参数赋值,比如"PID_SCL_DB"(rKp := 2.0, ...),每个扫描周期都会把 HMI 写入的 DB 值覆盖回 2.0。HMI 写的是背景 DB,但 OB 里赋值又把输入参数按常量填了一遍。

解决:参数初始化集中在 OB100 写一次,OB30 调用时只传过程变量和模式开关。HMI 直接绑定背景数据块的参数标签,在线修改后立即生效。这样既能避免覆盖,也方便以后把参数列表导出做归档。

5. 给 SCL PID 做一次仿真体检:惯性对象阶跃响应与四个过关条件

5.1 用一阶惯性环节模拟被控对象

我自己的习惯是,写完一个 SCL PID FB 不会立刻下现场,先在 PLCSIM 里把对象模型搭出来做阶跃。没有对象的 PID 调试都属于脑补。下面这个模拟对象块可以放在另一个 OB30 里,用 PID 的输出当输入,把反馈接回 PID:

FUNCTION_BLOCK FB_Plant VAR_INPUT rU : REAL; // 控制器输出 0~100% rT : REAL; // 对象惯性时间常数,单位秒 rK : REAL; // 对象增益 END_VAR VAR_OUTPUT rPV : REAL; // 过程反馈,工程单位 END_VAR VAR rPrevPV : REAL; END_VAR // 后向欧拉法离散一阶惯性环节 rPV := rPrevPV + (rK * rU - rPrevPV) * 0.1 / rT; rPrevPV := rPV; END_FUNCTION_BLOCK

把 PID 的 rTd 设成 0,rTi 先放到较大值,Kp 从 0.5 开始往上加;设定值从 50 跳到 60,观察反馈曲线。正常情况下,温度/流量这类惯性对象会呈现一阶上升曲线,超调量控制在 10% 以内就算及格。

5.2 一页纸验收清单

仿真体检时我会按这四条来验收:第一,无扰动时反馈能稳定到设定值,静差小于 0.5%;第二,Kp 逐步增加,系统不发生持续振荡;第三,加入负载扰动后能在 10 秒内恢复;第四,输出始终在限幅范围内,且没有频繁顶格限幅。

有一年我调试一个温度回路,曲线怎么调都是过阻尼,后来发现是仿真对象里增益符号写反了。对象错了,PID 参数再对也没用。所以仿真跑通后,一定要换一个反向对象或者加死角测试一下。回到标题这件事上,SCL 源代码不是拿到一个包就万事大吉,真正值钱的是你能看懂公式、改掉积分饱和、并亲手验证的那部分。希望你照着这组代码建块后能少走一圈弯路,希望帮到你。

本文还有配套的精品资源,点击获取

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

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

立即咨询