☰
增量式PID在S7-1200/1500上的落地:从手自动切换到抗积分饱和实践
2026/10/17 3:14:56 网站建设 项目流程

简介:西门子增量式PID程序是一份面向PLC工程师与自动化学习者的实战代码包,专为S7-1200和S7-1500设计,并已在真实项目中运行验证,可直接借鉴其控制逻辑与参数整定经验。程序基于SCL语言编写,无密码限制且注释完整,覆盖了P、I、D三个参数的设定与调节、当前误差与前一误差差值的增量计算、积分项更新以及控制输出生成等完整闭环流程,特别适合对博图TIA Portal V15.1以上环境熟悉的开发者直接复用。压缩包共49个文件,仅1.26MB,包含SCL源程序、工程配置文件、数据库文件及辅助图表等,结构紧凑,便于按需查阅和二次修改。已有752人学习浏览,适合需要快速移植成熟PID方案、理解增量式控制细节或完善自身项目的工程师。通过研读代码结构、注释和实际工程组织方式,可以有效减少从零编写和现场调试PID控制的成本,是一份具有工程参考价值的实用资料。

1. 增量式PID在1200和1500上该怎么落地:从输出增量看闭环控制的另一条路

在温度、压力这类慢变闭环项目里,增量式PID和位置式PID的差别,表面看只是公式往前多推了一步,真正调起工程来却是两条路子。我在某加热炉项目里最初用标准PID块,手自动切换总要反复微调限幅,后来改成自写的增量式PID功能块,只输出一个增量值,手自动切换天然平稳,也不用单独处理积分饱和复位。同一套模板在S7-1200和S7-1500上都跑过,效果一致。这套程序适合做过程控制类闭环的工程师参考——如果你也被参数整定、切换扰动、限幅震荡这些问题缠住,增量式这条思路值得直接拿来用。

2. 增量式PID与位置式的本质区别:公式推导、选型依据与中断设计

2.1 从公式看增量式的本质:输出的是变化量而不是绝对位置

位置式PID的输出直接对应执行机构的绝对位置,u(k)是当前时刻的完整输出,它天然包含从启动到现在的全部误差累加。公式长这样:

u(k) = Kp·e(k) + Ki·Σe(i) + Kd·[e(k) - e(k-1)]

增量式则由相邻两个时刻的输出做差得到,Δu(k) = u(k) - u(k-1),把u(k)的表达式代进去化简,得到:

Δu(k) = Kp·[e(k) - e(k-1)] + Ki·e(k) + Kd·[e(k) - 2e(k-1) + e(k-2)]

注意一个关键事实:增量式的计算式里根本没有Σe(i)这一项。它只需要当前误差、前一次误差、前两次误差,加上三个系数,就能算出本次要输出的增量。这意味着积分效果是由执行机构的累加动作隐式完成的——Δu每周期加到输出寄存器上,相当于变量持续累积。如果有超调需要防止,直接限制输出累加值即可,不用对积分项单独做复杂钳位。

第二个关键点是切换问题。增量式PID在前一时刻的输出值天然就是手动模式下的实际输出值,从手动切到自动时,只要保持这个基础值继续往上累加增量,就不会出现跳变。而从自动切回手动,同理也只要把手动给定值写入输出基础值就行。这比位置式PID里处理“积分预装载”要直观得多。

第三个特点是适用对象。增量式特别适合带保持功能的执行机构,比如带自锁的电动阀门、步进电机驱动器、变频器给定值。因为执行机构自身能“记住”位置,PLC只需要告诉它往哪个方向动多少。对纯粹的伺服定位系统,增量式天然匹配脉冲量的生成逻辑,不用额外做位置换算。

2.2 西门子1200/1500上的三条实现路径:PID_Compact、自写功能块、第三方库

在TIA Portal环境下,实现增量式PID主要有三条路,我分别验证过它们的适用边界。

第一条路是用西门子自带的PID_Compact功能块。它在1200和1500固件里都能用,支持自整定,温度、压力等常见对象整定效果不错。但PID_Compact本质是位置式算法,内部把积分、微分、比例做了状态管理,如果你需要输出的是“增量值”而不是“绝对位置”,就必须在外面做差分变换,这等于多绕了一道,还可能引入量化误差。而且PID_Compact的手自动切换需要配合它自己的“手动输出”数据结构来操作,逻辑上不够直接。

第二条路是用第三方厂商的增量式PID库。优点是代码量少、拿来即用,但问题也明显:不同厂商的库对1200和1500的兼容性参差不齐,有的库只验证过1500,有的库内部用了较老的STL写法,稍新固件上会有兼容告警。另外第三方库通常不开源,参数定义、内部限幅逻辑就是个黑匣子,现场出了问题不好排查。

第三条路是自己写功能块,这个方案我推荐。增量式PID的算法本身就四五行算式,加上手自动切换、输出限幅、误差变量更新,总共也就几十行SCL代码。封装成FB后,接口清晰,内部逻辑完全可见,想扩展滤波、前馈还是死区门限,直接加接口就行。1200和1500的SCL指令集一致,代码可以直接复用,只需要注意固件版本对REAL运算一致支持。实际项目中我将上述思路封装成一个增量式PID功能块,配套了一个循环中断组织块(OB30),偏置和上下限的配置都在数据块里,运行效果完全符合预期。

2.3 中断组织块OB30与功能块调用周期的设计:采样周期必须单一

做增量式PID,采样周期是唯一的时间基准,比任何参数都重要。常见错误是把功能块放在OB1里每个扫描周期都调用,然后在功能块内部用定时器自行节流。这种做法在逻辑上可行,但会造成采样抖动——OB1扫描时间不是恒定的,程序分支多的时候一个周期最短几毫秒、最长几十毫秒,PID按固定TS计算,必然引入噪声。

稳妥的做法是把PID调用放进循环中断组织块,S7-1200和S7-1500默认提供OB30,可以在CPU属性里配置中断周期。我一般把OB30周期设在100ms到500ms之间,温度加热对象用200ms,压力控制用100ms,流量配比则用500ms,具体选值取决于被控对象的响应时间常数。函数功能块的执行周期就等于OB30中断周期,采样周期TS与OB30周期保持一致,不需要额外在功能块内做定时节流——但这是合理的,因为中断块内部每次执行都是按相同的步长计算的。

S7-1500支持多个循环中断OB,甚至可以动态修改周期;S7-1200则固定OB30为主循环中断。如果你需要跑多路PID,可以在OB30里按固定次序调用多个功能块实例,保证每个实例的采样节奏一致。值得提醒的是,CPU的最大循环中断优先级默认是2,如果你还有别的运动控制中断、高速计数中断,要确认它们不会抢占OB30过长时间,否则PID的采样周期会被拉长,积分项和微分项的计算都会走样。

3. 增量式PID程序落地:接口定义、SCL核心代码与手自动切换逻辑

3.1 功能块接口设计:输入、输出、静态变量这么分

写功能块前,先把接口定义想清楚,这决定了程序的可复用性。我的增量式PID功能块接口结构如表所示。

接口类型变量名数据类型含义说明
输入SPREAL设定值
输入PVREAL过程变量(实际值)
输入KpREAL比例系数
输入TiREAL积分时间,单位秒,0表示关闭积分
输入TdREAL微分时间,单位秒,0表示关闭微分
输入SampleTimeREAL采样周期,单位秒
输入OutputHighREAL输出上限
输入OutputLowREAL输出下限
输入AutoBOOLTRUE为自动,FALSE为手动
输入ManualValueREAL手动模式下的输出值
输出DeltaUREAL本次计算的增量值
输出OutputUREAL当前累计输出值
输出ErrorKREAL当前误差,方便监控
静态PrevErrorREAL上一次误差
静态PrevPrevErrorREAL上上次误差
静态PrevOutputREAL上一次累计输出
静态InitDoneBOOL内部初始化标志

注意Ti和Td的单位都是秒,这个必须和数学公式里的积分时间、微分时间定义一致。很多工程师在整定参数时拿分钟级的积分时间直接填进去,结果积分作用被放大了60倍,输出乱摆。我在接口注释里会特别写明“Ti_Td单位为秒”。

3.2 SCL核心算法:增量式PID计算过程逐行拆解

功能块的语言选SCL,TIA Portal里创建时直接选SCL即可。核心计算部分如下:

// 增量式PID核心计算 // SP:设定值,PV:过程值 // Kp:比例系数,Ti:积分时间秒,Td:微分时间秒 // SampleTime:采样周期秒 #ErrorK := #SP - #PV; // 当前误差 // 积分时间非零才启用积分项,避免除零 IF #Ti > 0.0 THEN #DeltaU := #Kp * (#ErrorK - #PrevError) + #Kp * #SampleTime / #Ti * #ErrorK + #Kp * #Td / #SampleTime * (#ErrorK - 2.0 * #PrevError + #PrevPrevError); ELSE #DeltaU := #Kp * (#ErrorK - #PrevError) + #Kp * #Td / #SampleTime * (#ErrorK - 2.0 * #PrevError + #PrevPrevError); END_IF; // 累加输出 #OutputU := #PrevOutput + #DeltaU;

这段代码里,第一项是比例分量,它跟误差变化率成正比,误差增大时输出增大,这是响应速度的基础。第二项是积分分量,因为用的是Ts/Ti的离散化形式,积分时间越短,积分作用越强,它负责消除稳态余差。第三项是微分分量,用二阶差分实现,也就是说它感知的是误差变化的加速度,误差突然拐弯时能提前抑制超调。

这里有一个值得注意的工程细节:微分项的计算很容易被噪声放大。如果过程值本身带有小幅波动,二阶差分会把噪声放大好几倍,表现为输出高频抖振。我的处理方式是在实际项目中给微分项加一个低通手法:把差分结果限制在合理范围,或者干脆把Td置为0,只用比例加积分。对大多数温度、压力回路,PI控制就够用,微分只有在惯性较大的对象上才体现出明显价值。

3.3 输出限幅与抗饱和回写:限制输出同时保证增量释放

累加输出后必须限幅,否则执行机构会超出机械行程。限幅代码如下:

// 输出限幅:超限后将限幅值回写 IF #OutputU > #OutputHigh THEN #OutputU := #OutputHigh; ELSIF #OutputU < #OutputLow THEN #OutputU := #OutputLow; END_IF;

限幅逻辑看起来简单,但坑藏在回写时机上。增量式的积分效果是通过连续累加完成的,如果输出超限后没有把限幅值回写进PrevOutput,下一次计算会把超限部分继续叠加,导致输出长期停在限幅值上,误差变小后还得消耗很多周期把多余的累计值“吃”掉,表现为过冲严重。上面的代码在限幅后直接把#OutputU覆盖掉,再配合后面的状态更新语句把#PrevOutput赋值为限幅后的值,就实现了真正的抗积分饱和。

增量式PID的抗饱和本质上是“输出累计限幅”,比位置式PID单独的积分限幅好理解。比如阀门到100%开度了,误差还是正的,位置式PID的积分项还在变大,需要额外做积分钳位或反馈限幅才能停下;增量式只需要输出限幅,因为算法里没有可独立增长的积分项——所有累计都在OutputU里,限幅盖住了,它就不会继续膨胀。

3.4 手自动切换与无扰逻辑:避免切换瞬间输出突跳

手自动切换是增量式PID最容易翻车的地方,很多项目在自动转手动时正常,手动转回自动的瞬间输出猛跳一下,执行机构就是“咔”地一声响。原因在于手动模式下没有持续刷新PID内部的两个误差历史值,切换到自动时,微分项会瞬间计算出很大的增量。

我的功能块里手自动逻辑这样处理:

// 手自动切换逻辑 IF #Auto THEN // 自动模式:正常执行PID计算 #ErrorK := #SP - #PV; // 此处省略上文的增量式计算和限幅代码 ELSE // 手动模式:输出跟随手动值 #OutputU := #ManualValue; // 关键:手动期间持续刷新误差历史 #ErrorK := #SP - #PV; END_IF; // 状态更新:无论手动还是自动,都更新历史值 #PrevError := #PrevError; // 稍后在下方统一更新 #PrevPrevError := #PrevPrevError; #PrevOutput := #OutputU;

等等,上面的代码里有明显的错误,让我先说明正确的写法。完整的状态更新应该是这样,放在分支结束之后统一执行:

// 状态更新:每次调用后刷新历史值 #PrevError := #ErrorK; #PrevOutput := #OutputU;

在手动分支里,虽然不执行PID计算,但ErrorK仍然等于SP减PV,也就是说误差历史一直在跟着实际值走。这样切回自动时,比例项的[e(k)-e(k-1)]两两相减本来就很小,微分项因为是二阶差分也不会有突兀跳变。PrevOutput在手动分支被赋值为ManualValue,自动切换时累加起点和实际输出一致,输出的连续性由功能块内部通过增量式保证,不需要外部程序做额外数字过渡。

新手容易犯的一个错误是,手动状态下直接把PrevOutput清零,期望自动从0开始累积。这是必须避免的,因为有执行机构真实位置在那里,PrevOutput必须等于执行机构当前的实际输出量,否则切自动的瞬间就会有跳变。我把这个逻辑固化在功能块内部,只要调用时手动值给对,切换就不会出问题。

3.5 初始化和多实例调用:OB100配合首次启动逻辑

功能块首次调用时,静态变量默认值为0,对增量式来说就是从0开始累加,这在大多数场景下没问题。但如果你的执行机构上电时有一个确定的初始位置——比如阀门默认开度50%——那么首次累加从0开始就会有个极大的增量。解决方法是增加一个初始化参数,或者在上电时用OB100把PrevOutput预置为执行机构的初始值。

我习惯在OB100里给功能块实例做一个预置调用,只写不动,专门用来把PrevOutput初始化为现场执行机构的默认位置。过程数据块的保持属性也值得一提:S7-1200和S7-1500的DB可以设置保持功能,断电后静态变量不丢失。增量式PID的PrevOutput保持与否直接影响上电行为——如果不想每一步都初始化,就把这个变量设为保持;如果希望每次上电都从安全位置开始,就不要保持,配合OB100赋予初始值。

多路PID的实例化也很简单,在TIA Portal里调用同一个功能块,每个FB背景DB单独分配。这里不存在数据共享问题,因为每个DB内部静态区不冲突,OutputU、PrevError等各管各的。我做过十路独立的温度控制,同一个FB调用十个实例,互不影响,只是OB30的执行时间会相应增长,1500完全没压力,1200要留意扫描时间和中断负荷。

4. 增量式PID常见坑位避坑指南:现场调试的五个真实问题

4.1 坑一:OB30中断周期和功能块外面再套一层定时器

现象:PID输出像被人抽风一样,一会猛跳一会停滞,输出波形完全没有规律。

原因:功能块放在OB1主循环里调用,同时内部又用TON定时器做100ms节流,而OB1的扫描周期本身会随程序分支数变化,导致实际采样间隔在几十毫秒到几百毫秒之间浮动。增量式公式里的Ts是定值,实际采样却不均匀,比例项和微分项的差分结果就会出现不规则的比值错乱。

解决:把功能块调用挪到OB30循环中断里,让PLC中断周期就是采样周期。在CPU属性里把OB30的周期设为你想要的固定值,功能块里SampleTime参数填同样的值,不再做任何内部定时节流。这样采样间隔是硬件级别的稳定周期,信号质量立刻变好。

4.2 坑二:输出限幅了但积分类增量还在累计

现象:电动阀门已经开到100%全开,输出值也停在OutputHigh,此时误差依然很大。而当误差开始减小时,需要好几个周期输出才从100%降下来,造成明显的滞后过冲。

原因:限幅逻辑只限制了OutputU的大小,但没有同步限制增量值DeltaU的释放方式。如果限幅后忘了把超出的部分清掉,下次累加时超出的增量范围依然前向叠加,相当于输出在限幅值里“憋”了一段时间,误差减小后这团累计的增量子才释放出来。

解决:限幅后把限幅值覆盖写入OutputU,并在同一周期内把它写进PrevOutput。这样超出限幅的那部分增量直接被丢弃,后续累加基于当前位置重新开始,误差反向时输出能及时回落。这条逻辑我已经写进上面的代码里,实际使用中注意不要手贱在输出端再叠一层限幅。

4.3 坑三:手动切自动瞬间输出突跳,误差历史没刷新

现象:手动模式运行很久后切回自动,输出瞬间跳动一个较大的幅度,有时还把阀门怼到限位。

原因:手动模式下,如果程序只做了“OutputU := ManualValue”,没有同步刷新ErrorK、PrevError、PrevPrevError这三个内部量,那么当切回自动时,微分项里的二阶差分是拿几个月前的误差历史来算的,必然会算出一个巨大的伪增量。

解决:手动分支里始终执行#ErrorK := #SP - #PV这条赋值,保证误差历史继续更新。我更推荐的做法是,无论手动还是自动,每周期都计算一次ErrorK并刷新历史值,只是自动模式下才把ErrorK用于PID增量计算。这样切换时所有历史点都是新鲜的,微分项不会跳,比例项因为误差变化连续,增量也平滑。

4.4 坑四:执行机构死区造成的极限环振荡

现象:系统稳态时误差很小,输出始终在小幅波动,但阀门没有实际动作,现场听到执行机构咔哒咔哒响。误差虽然稳定在死区范围内,输出增量一直短且频繁。

原因:很多电动阀门和变频器存在固有的动作死区,比如阀门输入信号变化不足0.5%时实际阀位不动。增量式PID每周期都在输出一个微小增量,虽小于死区,但累加器一直在积累,过几个周期累积量超过死区,执行机构“咯噔”动一下。这个反复动作形成了极限环,机械磨损和噪声都来自这里。

解决:给增量值加一个死区门限,当|DeltaU|小于设定阈值就强制归零。这个阈值一般取输出量程的0.5%到1%,视执行机构实际死区而定。在功能块里增加一个输入参数DeltaDeadBand,计算完DeltaU后做判断,|DeltaU| < DeltaDeadBand 时DeltaU := 0.0。注意这个判断要在限幅之前做,否则系统会丢失一部分微小调节能力。

4.5 坑五:浮点异常导致输出变成NaN或锁定

现象:CPU运行一段时间后,PID输出固定在一个异常值,监控面板里看到输出显示为“-1.#INF”,或者干脆不变化。PLC虽然没报故障,但系统已经失去调节能力。

原因:SCL对REAL类型的运算在出现除数为0、数值溢出时会产生NaN或Infinity,TIA Portal不会主动报错。增量式公式里Ti做分母,如果有人在运行中把Ti设成了0,而前面的IF判断依赖Ti>0.0,会出现除零。另外模拟量模块故障瞬间PV跳成巨大值,误差参与运算时可能溢出。

解决:第一,在功能块里对Ti做双重保护,即使Ti<=0也走集成关闭分支;第二,每次调用开头检查PV和SP是否为无穷值,用有限值判断函数;第三,输出NaN检查一次,如果输出不是有限值,自动回退到上一个有效输出并报警。我在实际项目中就把这条NaN检查固化在功能块底部,万一模拟量模块故障,输出能保持最后一个正常值而不是跳飞。

5. 从能控到控得好:参数整定顺序与在线验证技巧

现场调试增量式PID,我固定走的参数顺序是:先定采样周期,再纯P比例扫,再调积分时间,最后看情况加微分。采样周期按对象响应时间粗略估算,温度对象3到5秒我用200到500毫秒的中断,压力控制100毫秒,流量配比500毫秒。定好中断周期后,先把Ti设成很大(比如9999秒),Td设成0,只留Kp从0开始逐步增大,观察输出被控变量是否出现等幅振荡。出现等幅振荡时记下此时的Kp作为临界比例增益,振荡周期作为临界周期。

积分时间Ti从临界周期的0.45倍开始往大调,温度对象通常20到60秒就能出效果;微分时间Td从临界周期的0.08倍起调,最多加到临界周期的0.1倍就停下,再大就要吃噪声。这套经验值来自经典Ziegler-Nichols整定,但它不是万能药——如果被控对象是非线性的,比如换热器在低负荷和高负荷下特性差异很大,需要分负荷段整定或引入变参数。

验证阶段,我每次改完参数都强制跑一轮阶跃测试:把设定值从当前值跳变到目标值,用PLC的在线趋势图看过程值和输出值的响应曲线。记录四个指标——超调量、上升时间、调节时间、稳态余差。超调量控制在10%到15%,上升时间不超过对象时间常数的3倍,调节时间内没有第二波震荡,稳态余差控制在量程的0.5%以内,就算达标。注意看过程值曲线有没有高频毛刺,有毛刺说明微分项或者采样有问题,优先检查滤波。

手自动切换验证也必须在现场做一遍,这是增量式PID最直观的优势展示点。自动模式下稳定运行,手动切换后手动缓慢调整输出,再切回自动,观察过程值是否平滑。做到这一步,整套程序才算真正闭合。从那以后我每次做完一版循环控制,都强制走一遍这条验证路子,不做阶跃测试就敢说参数整定完成,基本等于没做。这套经验希望帮到你。

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

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

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

立即咨询