资讯动态

西门子S7-1200/1500增量式PID程序:ST语言实现温度控制方案

发布时间:2026/10/17 7:23:07 来源:尧图企业网站定制
简介这是一份面向西门子1200与1500系列PLC的增量式PID控制程序源自真实工程项目主要适合工业自动化工程师、PLC开发人员及自动控制学习者使用解决实际生产中的闭环调节与稳定控制问题。程序基于博途TIA PortalV15.1及以上版本编写压缩包内提供完整博途项目文件、SCL源程序、数据块及HMI相关文件全程带注释且无密码可直接打开查看、移植或二次修改。资源共49个文件核心包括.ap15_1工程文件、.scl程序代码、.db数据块另有.cfg/.xml配置文件以及日志、图标等辅助内容整体仅1.26MB体积小巧、下载快捷。目前已有752人浏览学习且该程序已在现场运行验证参考价值较高。通过该资源可以系统掌握增量式PID的编程架构、比例/积分/微分参数整定思路以及误差实时处理与输出更新逻辑还能参考实际项目的模块划分、变量命名和注释风格对理解SCL语言和博途工程组织也有实际帮助。1. 西门子增量式PID程序一套能让1200和1500通吃的温度控制方案做自动化这行的都知道博途里自带PID_Compact指令但实际项目里真正敢直接把它用到工艺控制上的并不多。原因很简单PID_Compact封装太深参数整定像个黑匣子而且不同CPU之间升级迁移、版本兼容的问题也够折腾一阵子。这篇要拆的项目是一套手写的西门子增量式PID程序在S7-1200和S7-1500两款PLC上都能直接跑已经在实际产线的温度控制场景里稳定运行过。它没有用系统自带的PID块而是用ST语言从底层实现了增量式PID算法的核心逻辑配合数据块做参数管理和手自动切换。这套程序适合谁用如果你是做设备集成、非标自动化或者过程控制的工程师手头正好是1200或1500的CPU又要面对温度、压力、流量这类慢过程的闭环调节那这份程序比你去啃PID_Compact的底层帮助文档要省事得多。它能帮你把PID控制从“黑匣子”变成自己可控的逻辑从采样周期、微分增益到输出限幅每一个参数都看得见、改得动。2. 增量式PID原理与架构选型为什么不用自带指令算法怎么落到PLC里2.1 增量式与位置式的本质差异先厘清一个基础概念。增量式PID和位置式PID的区别不在比例、积分、微分的计算方式上而在输出含义上。位置式PID直接输出控制量的绝对值u(k)一旦积分项饱和或者偏差突变输出会有大幅度跳变这对阀门、变频器这类执行机构很不友好。增量式PID输出的只是控制量的变化量Δu(k)执行机构在当前基础上叠加这个增量天然具备抗冲击能力。增量式公式写出来是这样的Δu(k) Kp * [e(k) - e(k-1)] Ki * e(k) Kd * [e(k) - 2*e(k-1) e(k-2)]对应的实际输出是u(k) u(k-1) Δu(k)从公式能看出增量式算法只需要保存最近三次的偏差值e(k)、e(k-1)、e(k-2)不需要累加历史偏差这就从结构上规避了积分饱和的问题。而且Δu是增量即使计算出现异常输出也只会产生小幅波动不会让阀门从全开瞬间变全关。2.2 在西门子PLC上的实现方案选择S7-1200和S7-1500都支持PID_Compact为什么还要自己写我在项目里遇到过两个痛点第一PID_Compact的调试面板和参数结构在1200和1500之间的版本适配有细微差异换CPU型号就要重新验证第二PID_Compact是封装好的黑盒输出限幅、微分滤波、抗积分饱和这些细节不开放排查问题的时候很难定位到底是哪一环出了问题。自己写增量式PID技术路线上要考虑的点如下项目选择原因编程语言ST结构化文本算法实现清晰便于移植和二次修改函数块类型FB函数块生成独立背景DB支持多重实例方便多回路复用循环扫描OB30循环中断固定采样周期不依赖主程序扫描周期参数存放背景数据块每个控制回路一套参数互不干扰手自动切换内置于FB手动模式直通操作值自动模式下增量叠加这里要特别说采样周期的选择。PID算法对采样周期Ts有硬性要求Ts必须小于被控对象时间常数的1/5到1/10。温度控制一般取1到5秒压力控制取100到500毫秒流量控制取50到200毫秒。程序里我把采样周期放在中断组织块里实现让它独立于主程序运行这样主程序怎么改都不会影响PID的运算节拍。2.3 程序整体架构整个PID程序分三层最上层是OB30中断组织块负责按固定周期调用PID计算中间层是FB_PID_Incremental功能块封装了PID算法核心逻辑最底层是物理I/O映射把模拟量输入模块读到的工程量转换为偏差值把PID计算出的增量累加到操作值并输出到模拟量模块。OB30循环中断Ts500ms | --- FB_PID_Incremental背景DBDB_PID_Temp | --- 模拟量输入PV过程值 --- 设定值SP来自HMI或上位机 --- 输出操作值MV到阀门/变频器 --- 手自动切换Auto_Man_Switch这个架构最核心的设计是FB块的复用能力。一个PID回路用一套背景DB如果现场有4个温度区就调用同一个FB四次配上四个不同的背景DB。在博途里这叫做多重实例或者参数化调用后面第3章会看到具体代码。3. 程序落地FB块设计与核心代码逐段拆解3.1 功能块接口定义打开博途新建一个FB块语言选ST名字叫FB_PID_Incremental。接口参数是这样定义的FUNCTION_BLOCK FB_PID_Incremental VAR_INPUT SP_Real : REAL; // 设定值来自HMI PV_Real : REAL; // 过程值来自模拟量输入转换 Auto_Man_Switch : BOOL; // TRUE自动模式FALSE手动模式 Man_Value : REAL; // 手动模式下的操作值 Kp : REAL : 2.0; // 比例增益 Ki : REAL : 0.1; // 积分增益针对Ts归一化后的值 Kd : REAL : 0.0; // 微分增益 Ts : REAL : 0.5; // 采样周期单位秒 Out_Max : REAL : 100.0; // 输出上限 Out_Min : REAL : 0.0; // 输出下限 END_VAR VAR_OUTPUT MV_Out : REAL; // 操作值输出到模拟量输出模块 Delta_U : REAL; // 本次增量值用于监视 In_Service : BOOL; // 功能块正在自动运行标志 END_VAR VAR e_k1 : REAL : 0.0; // 上一次偏差 e(k-1) e_k2 : REAL : 0.0; // 上上次偏差 e(k-2) MV_Auto : REAL : 0.0; // 自动模式下的内部叠加值 END_VAR接口设计的几个考虑点所有PID参数都用REAL类型不用INT避免量程转换带来的精度损失。Kp、Ki、Kd三个参数开放成输入变量可以直接在背景DB在线监视界面修改调试时不用反复下载程序。Out_Max和Out_Min是输出限幅参数防止阀门在自动模式下一次跳变过大。3.2 增量式PID算法核心实现进入FB块主体核心算法在主程序区不是初始化区逐行执行// 自动模式执行增量式PID计算 IF Auto_Man_Switch THEN // 第一步计算当前偏差 e_k : SP_Real - PV_Real; // 第二步增量式PID公式 Delta_U : Kp * (e_k - e_k1) Ki * e_k Kd * (e_k - 2.0 * e_k1 e_k2); // 第三步累加到内部操作值 MV_Auto : MV_Auto Delta_U; // 第四步输出限幅处理 IF MV_Auto Out_Max THEN MV_Auto : Out_Max; ELSIF MV_Auto Out_Min THEN MV_Auto : Out_Min; END_IF; // 第五步将操作值送到输出变量 MV_Out : MV_Auto; // 第六步更新偏差历史准备下一次计算 e_k2 : e_k1; e_k1 : e_k; In_Service : TRUE; ELSE // 手动模式直接透传手动值同时同步内部状态 MV_Out : Man_Value; MV_Auto : Man_Value; e_k1 : 0.0; e_k2 : 0.0; Delta_U : 0.0; In_Service : FALSE; END_IF;这段代码的逻辑要说一下第一步计算偏差做的是SP减PV方向也就是正作用。如果现场是反作用回路比如阀门开大温度反而降低的特殊工艺只需要把SP_Real和PV_Real互换或者在前面加一个负号。第二步是算法的灵魂。注意这里Ki前面没有乘以Ts的显式写法因为我约定的Ki参数本身就是经过归一化的值——也就是说在整定的时候我直接把Ts的影响并入了Ki数值中。这样做的好处是目标程序里的公式简洁坏处是Ki的量纲不直观。如果你喜欢教科书式的写法可以把公式改成Ki * Ts * e_k那就要把Ts作为一个单独参数传入并参与乘法。第三步的MV_Auto累加是增量式的核心特征。它替代了位置式PID里的积分项但又不是传统意义上的积分而是一个不断累积的操作基准值。这个基准值配合输出限幅保证了执行机构不会跑出安全范围。3.3 模拟量输入信号的处理PID算法本身算的是实数但PLC读到的温度变送器信号是4到20毫安电流通过模拟量模块转换后变成0到27648的整数。所以必须在调用PID之前完成量程转换。这里我给了一个标准化函数FC_AI_ScaleFUNCTION FC_AI_Scale : REAL VAR_INPUT Raw_Value : INT; // 模拟量模块原始值 AI_Min : REAL; // 量程下限例如0.0 AI_Max : REAL; // 量程上限例如100.0 END_VAR VAR_TEMP Raw_Float : REAL; Range_Scale : REAL; END_VAR BEGIN Raw_Float : INT_TO_REAL(Raw_Value); // 0~27648线性映射到工程量同时处理超量程如断线时读到32767 IF Raw_Value 27648 THEN FC_AI_Scale : AI_Max; ELSIF Raw_Value 0 THEN FC_AI_Scale : AI_Min; ELSE Range_Scale : (AI_Max - AI_Min) / 27648.0; FC_AI_Scale : AI_Min Raw_Float * Range_Scale; END_IF; END_FUNCTION这里有个工程细节值得注意模拟量模块断线或者变送器故障时读到的原始值会跳到32767超上限如果不做限制转换后的过程值会飙到极大值PID会把阀门往反方向打到极限。所以我在转换函数里加了上限判断。3.4 在OB30循环中断里调用PID创建一个循环中断组织块命名为OB30中断周期设成500毫秒。在OB30里写调用代码// 温度区1的PID控制回路 DB_PID_Temp_Zone1.SP_Real : HMI_Temp_SP_Zone1; DB_PID_Temp_Zone1.PV_Real : FC_AI_Scale(Raw_Value : AI_Raw_Temp_Zone1, AI_Min : 0.0, AI_Max : 100.0); DB_PID_Temp_Zone1.Auto_Man_Switch : Zone1_Auto_Man; DB_PID_Temp_Zone1.Man_Value : HMI_Man_Value_Zone1; FB_PID_Incremental.Instance : DB_PID_Temp_Zone1.Instance; FB_PID_Incremental(SP_Real : DB_PID_Temp_Zone1.SP_Real, PV_Real : DB_PID_Temp_Zone1.PV_Real, Auto_Man_Switch : DB_PID_Temp_Zone1.Auto_Man_Switch, Man_Value : DB_PID_Temp_Zone1.Man_Value, Kp : 1.8, Ki : 0.05, Kd : 0.0, Out_Max : 100.0, Out_Min : 0.0);在博途里FB块的背景DB地址有精细符号寻址和多重实例两种方式。上面这种写法用的是多重实例方式——在OB30的局部变量表中定义了一个静态调用。你也可以直接在OB30里写一条FB_PID_Incremental_Instance(...)的调用语句然后把接口参数连到全局DB变量上两种方式效果一样看个人习惯。4. 参数整定实战与避坑指南现场调试的血泪经验4.1 工程上常用的整定步骤拿到这套增量式PID程序不要急着把Kp、Ki、Kd填上就投自动。我调试过各种温控设备总结了一套相对稳的整定流程第一步手动模式到稳态。把Auto_Man_Switch切到手动手动给一个操作值让被控对象稳定在某个工作点比如给阀门40%开度让温度稳定在60度左右。第二步临界比例度法初调Kp。把Ki和Kd先设成0只保留比例项。手动模式把设定值改成目标值然后切自动。观察过程值的响应曲线——如果等幅振荡说明Kp已经到临界值记下这个Kp值最终Kp取它的40%到60%。如果没有振荡但响应太慢逐步加大Kp每次加0.2到0.5直到出现轻微振荡再往回调。第三步加入积分消除静差。保持上述Kp不动逐步增加Ki。每增加一点观察稳态时过程值和设定值是否重合。这里有个判断技巧如果过程值到稳态后和设定值之间有固定偏差就是Ki太小如果出现缓慢的周期波动就是Ki偏大。以采样周期0.5秒为例我常用的Ki初始值是Kp的1/20到1/50。第四步按需加微分。温度控制的大惯性系统一般可以不加Kd如果超调抑制不住再加一个很小的Kd典型值在Kp的1/10到1/30。Kd加多了输出会抖阀门会频繁动作反而烧执行器。4.2 避坑指南三个最常踩的坑第一个坑输出限幅值设成了0到100但执行机构实际收到的是0到27648。这会造成PID输出100%时阀门只开一半。原因是对应关系搞混。程序里Out_Max和Out_Min是PID内部标准化的百分比量程但模拟量输出模块要的是原始整数。正确做法是在PID输出后面再级联一个工程量转原始值的函数把0到100的百分比映射到0到27648。解决办法是在OB30中PID调用后面加一行转换QW_Temp_Zone1 : REAL_TO_INT(DB_PID_Temp_Zone1.MV_Out / 100.0 * 27648.0);第二个坑手自动切换瞬间输出跳变。现象是手动模式下给了阀门50%开度切自动的一瞬间阀门猛地开到100%或者关到0。原因是手动模式切自动时MV_Auto被直接赋值为Man_Value但PID内部残留的e_k1和e_k2没有清零。如果切换前手自动状态一直保持偏差剩下的偏差历史会在切换后第一拍产生一个很大的Δu叠加在MV_Auto上就造成输出跳变。解决办法看我上面代码的手动分支切换手动时把e_k1清零、e_k2清零、Delta_U清零。同时把MV_Auto同步成当前的手动值。这样切自动后第一步的偏差计算是从零开始的增量不会突变。第三个坑程序下载后FB块的背景DB被初始化导致输出归零。现象是在线监视时一切正常但只要把程序停止再运行或者做一次完全下载阀门就会全关影响到现场。原因是背景DB的保持性设置。默认新建的DB是非保持性的CPU冷启动或者程序重新下载后所有变量回到初始值。MV_Auto回到0PID在自动模式第一拍就会从0开始累加阀门从全关慢慢爬升。解决办法是把背景DB的设置改成“保持性”打开DB_PID_Temp_Zone1 - 属性 - 保持性 - 在掉电保持中保持这样CPU重启后MV_Auto恢复成最后的值。或者在上电初始化程序里把MV_Auto读入之前保存的EEPROM/RETAIN值。4.3 在线调试的监视方法博途在线监视时我一般强制打开背景DB的监视表同时观察三组变量一是偏差相关变量e_k、e_k1、e_k2确认偏差数据是实时更新的如果e_k1一直不变说明PID采样可能没进中断OB30。二是Delta_U增量值它在稳态时应该趋近于零或在小范围内波动。如果Delta_U恒定输出正值或负值说明存在恒定偏差积分在起作用还需要微调Ki。三是MV_Auto操作值它应该平缓变化。如果MV_Auto在一个扫描周期内从10跳到90说明Kp或Kd过大或者输出限幅设得太宽了。5. 实际项目应用从温度区到多回路系统的适配5.1 单回路温度控制的完整接线示例我在某条产线的加热炉项目上跑过这套程序控制对象是电加热丝执行机构是固态继电器配合PWM驱动反馈元件是热电偶配温度变送器。整个信号链路如下热电偶 - 温度变送器(4~20mA) - 模拟量输入模块(SM1231 AI) - FC_AI_Scale - FB_PID_Incremental - 模拟量输出模块 - 固态继电器 - 电加热丝这里有个改动点标准的模拟量输出模块输出的是4到20毫安或0到10伏电压固态继电器用的是PWM触发。所以我在PID输出后面不是直接送给模拟量输出而是先做PWM转换——把0到100的输出百分比折算成导通时间写进一个定时中断里控制固态继电器的通断。PWM转换的思路设定一个PWM周期比如2秒输出百分比50%就意味着在2秒内导通1秒、关断1秒。对于加热这种大惯性对象20Hz以下频率的PWM是完全够用的周期太长会导致温度波动周期太短会频繁开关导致固态继电器损坏。5.2 扩展成多回路温控的工程做法加热炉往往不止一个温区这套增量式PID程序的多回路扩展方式我之前提过——复制FB调用配新的背景DB。在博途里操作是这样的第一步在程序资源里复制FB_PID_Incremental生成FB_PID_Incremental_Zone2或者直接复用同一个FB。推荐复用同一个FB因为算法完全一样。第二步在DB文件夹里新建两个背景DB比如DB_PID_Temp_Zone1和DB_PID_Temp_Zone2都分配给FB_PID_Incremental。第三步在OB30里写两次调用各自绑定自己的背景DB和输入输出变量。示意图如下OB30 ├── FB_PID_Incremental(DB_PID_Temp_Zone1) 温区1 ├── FB_PID_Incremental(DB_PID_Temp_Zone2) 温区2 ├── FB_PID_Incremental(DB_PID_Temp_Zone3) 温区3 └── FB_PID_Incremental(DB_PID_Temp_Zone4) 温区4注意如果四路的PID参数差异比较大每路的Kp、Ki、Kd都要独立整定如果四路是完全一样的对称结构可以调好一路后把参数复制到其他路微调即可。5.3 1500PLC上的迁移要点这套程序在1200和1500上是通用的但迁移时有几个要确认的点。第一OB30的中断周期在1500上可以设得更短CPU处理能力更强对于需要更快响应速度的流量控制场景可以把Ts设到100毫秒甚至50毫秒。第二1500PLC支持IEC定时器如果你想把采样周期改成用定时器事件触发而不是循环中断在1500上要注意定时器分辨率和中断抖动的问题。建议还是用循环中断省去时间同步的麻烦。第三1200和1500的地址空间不同。1200的模拟量输入输出地址从IW64或QW80开始1500的模拟量模块地址可能在不同槽位上迁移时要把程序里的I/O地址全局替换。6. 二次开发让这套PID程序适配更多场景的技巧到了这个阶段你已经能把程序跑起来了接下来聊怎么改。二次开发的核心是把通用性和特殊性分离。针对特殊工艺的改造最容易踩的一个点是对执行机构类型做适配。目前程序输出的是0到100的百分比对应线性阀门或者连续调节的变频器。如果在现场碰到快开阀、等百分比阀这样有非线性特性的执行机构纯粹的输出百分比跟不上流量特性会导致PID参数在低开度区准确、高开度区失调。处理方法是在PID输出后面加一个非线性补偿查表函数FUNCTION FC_Valve_Compensate : REAL VAR_INPUT MV_Percent : REAL; // PID输出的百分比 END_VAR VAR_TEMP Comp_Tab : ARRAY[0..10] OF REAL; // 补偿曲线表 END_VAR补偿表的思路是把阀门的实际流量特性标定出来——在手动模式下给不同的开度值记录对应的实际流量或温度变化率得到一组离散映射然后在线性PID输出和真实输出之间做插值补偿。这样PID的线性模型假设就重新成立了。针对执行机构频繁动作的场景可以加一个输出变化率限制。目前程序里只有输出限幅也就是限制MV_Auto的绝对范围没有限制它每次的增量幅度。在实际项目里变频器驱动的风机如果PID增量太大转速会突变对机械结构有冲击。我给程序加了一个增量限幅系数// 在Delta_U计算之后执行增量限幅 IF Delta_U Delta_Max THEN Delta_U : Delta_Max; ELSIF Delta_U -Delta_Max THEN Delta_U : -Delta_Max; END_IF;Delta_Max作为TH输入加到FB接口里我一般取输出量程的2%到5%作为每步最大增量。这样就算是Kp调得偏大输出也会被限制在平滑变化的范围之内保护后端的执行机构。第三个可扩展方向是前馈补偿。如果现场有可测的扰动量——比如蒸汽加热系统里进料流量变化会直接影响温度可以把扰动量引入到操作值里做一个静态前馈补偿。前馈叠加到MV_Auto上PID只负责修正前馈的偏差部分。这样扰动量一出现输出立即响应不用等温度偏差产生了再靠PID慢慢拉回来。我习惯的做法是在FB的VAR_INPUT里增加一个FF_FeedForward变量在自动模式计算完成后MV_Out : MV_Auto FF_FeedForward; // 再次限幅 IF MV_Out Out_Max THEN MV_Out : Out_Max; END_IF; IF MV_Out Out_Min THEN MV_Out : Out_Min; END_IF;前馈量用标定好的比例系数乘以扰动量实测值算出这个比例系数同样可以在线调整。最后想分享一个小教训做完任何一项改动不管是自己写还是抄网上的都不要跳过全流程验证。从那以后我每次拿到新的PID需求都强制走一遍“手动标定特性曲线→纯比例临界振荡→依次加积分微分→切自动联调”这个流程宁可多花两小时在现场,也不再迷信一次就能把参数填好。调试PID这件事方法论比经验重要经验比玄学重要。希望这套增量式PID程序能帮你少走几个弯路。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑