做水温控制这种活,最烦的不是设备不转,而是温度像坐过山车:加热器一开就冲过头,一关就掉下来,半天稳不住。我接过不少用汇川PLC做加热项目的咨询,很多人都抱着“上了PID就能一劳永逸”的想法,结果一调试发现全是坑。这里有个很关键的认知要先摆正:工业现场大部分加热管是电阻性的,没法像变频器调速那样直接把功率拉满打半,你能用的往往就是一个开关量输出——要么全功率加热,要么彻底断电。所以PID算出来的结果,必须转换成“开多久、停多久”的开关时间比例,才能真正控制住水温。
这篇文章我准备用汇川PLC的实战项目来讲清楚这件事,核心是PID和开关量输出的配合,以及梯形图程序怎么一步步搭。内容会覆盖硬件选型、模拟量采集、PID参数整定、PWM占空比输出,还有调试时容易踩的坑。不管你是刚接触PLC的新手,还是已经在车间里调过不少设备的老手,这套思路都值得收藏一份。
1. 项目到底解决一个什么问题
1.1 温度控制为什么不是“简单通断”就行
很多第一次做加热项目的人会想:水温高了就断开加热器,低了就合上,这不就完了吗?这种“纯开关量”的控制方式在某些对精度要求极低、水箱又特别大的场合勉强能用,但只要目标温度稍微高一点,或者系统惯性稍小一点,问题立刻暴露——你看到的温度曲线就是一条不断上下波动的锯齿波,设定80度的水,实际温度能在75到85度之间来回荡。
原因也很好理解:加热管一通电,水温不会立刻上升,温度信号有滞后;断电以后,加热管上的余热还会继续往水里传递,温度也不会马上停。纯逻辑判断根本判断不了“当前该提前关还是提前开”,只会等温度过冲了才反应。这时候就需要PID了。
PID的本质是“根据偏差的大小、快慢和历史趋势,计算出合适的输出量”。但问题来了,PID输出的是一个连续值,比如“输出功率58%”。加热管不支持连续功率调节,怎么办?答案就是把连续值变成开关量占空比:在一个固定周期内,58%的输出就是“通0.58秒,断0.42秒”。温度控制因此从“傻通断”进化成“时间比例控制”,这就是这个项目最核心的解决方案。
1.2 适合哪些场景
用PID加开关量控制水温,适合的场景非常典型:
- 电加热水罐、热水锅炉、模具恒温机、清洗线加热槽等;
- 没有模拟量输出模块,或者不想给加热管配可控硅调压器的项目;
- 传感器用PT100、热电偶,加变送器后接到PLC模拟量输入;
- 现场希望用一个屏或者按钮就能设定目标温度,并要求实时显示偏差和输出比例。
这个方案的好处是硬件成本低、接线简单、可靠性高。坏处是加热管工作在“忽通忽断”的状态,如果开关频率太高会直接影响接触器或固态继电器寿命。所以真正要花心思的,一方面是把PID参数调好,另一方面是选一个合理的通断周期。
2. 硬件选型与系统架构
2.1 核心器件清单
我按照一套中规中矩的配置来说,这套配置在小型水加热项目里很常见,器件你换成手册对应型号即可,思路不变。
| 序号 | 器件 | 型号/规格说明 | 用途 |
|---|---|---|---|
| 1 | PLC | 汇川Easy320系列,本体自带网口 | 主控制器,跑PID逻辑 |
| 2 | 模拟量输入 | 本体AI通道或扩展模块 | 读取温度变送器信号 |
| 3 | 温度传感器 | PT100铂电阻,三线制 | 检测水温 |
| 4 | 温度变送器 | 输入PT100,输出4-20mA | 将PT100阻值变化转换为标准电流信号 |
| 5 | 加热管 | 220V/380V,根据功率选 | 加热介质 |
| 6 | 开关量输出 | 晶体管/继电器输出,驱动中间继电器 | 控制加热管通断电 |
| 7 | 固态继电器(SSR) | 过零触发型,电流按加热管功率选 | 无触点通断,频率可以高一些 |
| 8 | 触摸屏 | 可选,走Modbus TCP或以太网 | 设置温度、监控曲线 |
这里有个取舍要说明:用普通接触器控制加热管也可以,但接触器是机械结构,通断寿命有限,动作频率不能太高。我们做PID时间比例控制时,通断周期一般取0.5到2秒,接触器根本扛不住这种频繁动作。所以我做这个项目都会强烈建议换成固态继电器,电热负载用单相或三相固态继电器,体积小、响应快、过零触发还能减少对电网的冲击。
2.2 接线与信号流程
信号链路并不复杂,画出来就三条线:
温度变化 → PT100阻值变化 → 变送器输出4-20mA → PLC模拟量输入通道 → PLC内工程量转换得到实际温度 → PID功能块计算输出百分比 → PWM通道/梯形图脉宽输出 → 固态继电器 → 加热管通断电。
接线时最容易出错的有几个地方:
- PT100变送器的供电和信号地要与PLC模拟量输入模块的公共端对应好,很多模块AI通道共用一个COM端,接反了读数要么不准要么直接烧通道;
- 变送器信号线用屏蔽双绞线,屏蔽层单端接地,不要和动力线捆在一起走线槽,否则温度信号会跳得厉害;
- 固态继电器要加散热器,尤其是连续大电流工作时发热相当可观;
- 控制加热管通断的DO输出,如果是继电器输出,不建议直接驱动大功率交流接触器,最好先驱动一个中间继电器,再让中间继电器控制接触器或固态继电器。
2.3 汇川Easy320平台要注意的事
汇川产品线现在分得比较细,老款的H系列、新款的Easy系列,编程软件和底层逻辑有差异。我用的是Easy320配InoProShop编程软件,编程语言遵循IEC 61131-3,梯形图只是其中一种,这也意味着你可以在同一个工程里混用梯形图、结构化文本和功能块图。
有一个容易被忽略的点:Easy320本体虽然集成了模拟量输入输出和通讯接口,但具体通道数量、量程范围和接线端子定义,不同型号差别很大。做设计的时候不要光看“别人用Easy320我也用”,要先去官网下载该型号的用户手册和选型样本,确认:
- 本体AI通道支持的是4-20mA还是0-10V;
- 模拟量数字量的原始值范围是0~4000还是0~27648;
- PID运算周期和模拟量刷新周期是否匹配。
这些细节直接决定了梯形图里工程量换算的系数,后面我会细讲。
3. PID三个参数与时间比例输出原理
3.1 先把PID输出这件事想明白
很多初学PLC的人对PID有两个误区:一是以为调用一个PID功能块就能自动控制好;二是把PID的输出直接当成一个模拟量接到变频器或调节阀上。实际上,PID功能块就是一个“计算器”,你给它设定值、反馈值、比例系数、积分时间、微分时间,它算出一个数,这个数的物理意义完全取决于你定义。在温度控制项目里,你可以把它定义成“输出百分比0-100%”。
这个百分比,在加热控制中代表的是“在控制周期内,加热器应该通电的时间比例”。举例说,PID算出来输出是60%,周期设为1秒,那么加热管就通电0.6秒、断电0.4秒。把这0.6秒的“ON时间”按周期循环,加热器的平均功率就相当于满功率的60%。这种做法在行业内叫时间比例控制,也叫占空比控制。
为什么要强调“PID输出不是直接给DO”?因为DO只有0或1,而PID输出是0到100的连续数值。中间必须有一个“脉宽调制”环节,才能把连续值翻译成开关量动作。这个翻译环节,你可以用专门的PWM功能块实现,也可以用两个定时器在梯形图里自己做。后面我会给出两种方式的做法。
3.2 控制周期怎么选
时间比例控制的周期是第一个要定的参数。选得太长,比如10秒甚至30秒,水温会跟着“一浪一浪”地波动,实际温度会在设定值附近来回晃动;选得太短,比如0.1秒,虽然温度平稳,但固态继电器频繁动作,发热器件的寿命会受影响。
我的经验是:容量比较小的水箱(几十升以内)、加热管功率占比大、响应快,周期可以取0.5秒;中等容量水箱,取1秒;容量大但加热功率相对小、温度惯性大的系统,可以取2到5秒。大多数现场水温控制,从1秒起步调都问题不大。如果你用接触器而不是固态继电器,那么周期最好拉到3秒以上,否则接触器线圈和触电撑不住。
这里还要顺便说一句“最小开时间”的概念。比如一个周期1秒,PID输出算出来是1%,那么通电时间只有10毫秒,固态继电器可能还没来得及可靠导通就断开了。这种输出对控制没有意义。所以程序里通常要加一个输出下限处理:要么让PID输出最小值不低于5%,要么在PID输出小于某个值时直接视为0输出。等温度接近设定值后,PID稳定在小输出区间时也不要硬去追求极小的占空比,那是钢丝上跳舞,没有实际意义。
3.3 参数整定的基本框架
PID的参数Kp、Ti、Td,是我觉得整套系统里最玄学的部分。不过只要理解了每个参数的性格,就不会太慌:
- Kp比例系数:决定“偏差一出现,立刻输出多大修正量”。Kp太大,系统容易震荡;太小,升温慢,可能到不了设定值。
- Ti积分时间:专门负责消除稳态误差。积分是把历史偏差一点点累积起来,持续输出纠偏。Ti越小,积分作用越强,但太小会引起超调。
- Td微分时间:看到偏差的未来趋势,提前刹车。水温是大惯性对象,微分用得不好会把传感器噪声放大,导致输出像机关枪一样乱跳。所以我做水温控制,通常建议先Td=0,等Kp和Ti调稳了再加微分量。
整定顺序有讲究。第一次调试,先不给积分和微分,只留Kp。把设定值设成一个常用温度,比如60度,从比较小的Kp开始加,观察温度曲线。如果系统出现等幅震荡,记下此时的Kp(临界比例增益)和震荡周期,然后用齐格勒-尼科尔斯经验公式算出一组基础参数。不过在水温这种大惯性对象上,经验公式给出的参数往往偏激进,我会在这个基础参数上再打八折甚至对折,给系统留够余量。
调试中你大概率会看到这些现象:升温太慢,压着Kp加不上去;到了设定值附近总超调,把Ti调大;稳态之后还有0.5度的固定误差,把Ti减小;温度抖动像锯齿,先检查是不是传感器滤波不够或周期太长。
4. 梯形图实操编写
4.1 变量和地址规划
在正式写梯形图之前,先把变量规划好,这一步别偷懒。我在InoProShop里用的是IEC变量风格,输入变量名可以自己定义,然后再映射到物理地址。下面是这个项目的常用变量表,你可直接照搬。
VAR bStart : BOOL; (* 启动按钮 *) bStop : BOOL; (* 停止按钮 *) bRun : BOOL; (* 运行标志 *) bModeAuto : BOOL; (* 手动/自动切换 *) bLowLevel : BOOL; (* 低水位保护 *) bHeatOut : BOOL; (* 加热输出 *) bAlarmHigh : BOOL; (* 高温报警 *) iRawAI : INT; (* 模拟量原始值 *) rPvTemp : REAL; (* 实际水温,单位℃ *) rSetPoint : REAL; (* 目标水温,默认80.0 *) rPidOut : REAL; (* PID输出百分比 0-100 *) rManualOut : REAL; (* 手动模式下的输出百分比 *) rCtrlOut : REAL; (* 最终控制输出百分比 *) rKp : REAL; (* 比例系数 *) rTi : REAL; (* 积分时间,单位秒 *) rTd : REAL; (* 微分时间,单位秒 *) tonCycle : TON; (* 控制周期定时器 *) tonOnTime : TON; (* 导通时间定时器 *) pwmPeriod : TIME; (* PWM周期,如 T#1S *) tonTimeOnMs : TIME; (* 导通时间,由rCtrlOut计算 *) END_VAR变量名你可以按自己的习惯缩写,但务必把单位写在注释里。尤其是Ti和Td这类时间参数,有的库用秒,有的库用毫秒,写混了系统很难稳定,这种问题排查起来最容易让人抓狂。
4.2 模拟量采集与工程量转换
这里先说一下工程量转换的意义。PLC模拟量模块读到的是一个整数,比如0到4000的原始值,这个值并不是温度。温度变送器把PT100信号变成4-20mA,而4-20mA又对应PLC模块的原始值范围,最后要把它还原成0到200℃的真实温度,这正是“工程量换算”要做的事。
我现在以汇川常见“AC信号在CPU内部显示为0~4000”为例(不同模块、不同量程配置会有差异,实际以你选的型号手册为准)来写梯形图逻辑。
第一段梯形图,先读模拟量原始值,再换算成温度:
(* 模拟量读取通道,实际地址按IO映射配置 *) iRawAI := %IW0; (* 换算:0-4000 对应 4-20mA,温度量程 0-200℃ *) rPvTemp := INT_TO_REAL(iRawAI) * 200.0 / 4000.0;如果你的模块量程是0-27648,就把分母改成27648。需要注意:如果原始值里包含“断线检测值”,比如断线时报出-32768,你需要在程序里加一个判断,防止把异常值带入PID计算导致加热失控。常见的做法是:当iRawAI小于量程5%时,输出传感器故障报警并切断加热输出。
这是个很小但非常重要的程序保护。我在实际项目里见过温度信号线松动,模块读到满量程,PID立刻把加热输出降到0,反而没有引发事故;但如果读到的是半量程且还在不断漂移,PID就会按照错误温度反复调节,场面就特别难看。所以模拟量上限、下限的判断要做,而且要在联锁里把加热退出。
4.3 PID功能块调用
打开InoProShop的库管理器,找到控制器自带的PID库,把PID功能块拖到梯形图里,或者直接在LD界面插入功能块实例。不同版本库的PID引脚名称略有差别,但核心引脚逃不出下面几个:
| 引脚 | 说明 |
|---|---|
| SV | 设定值 |
| PV | 过程反馈值 |
| KP | 比例增益 |
| TI | 积分时间 |
| TD | 微分时间 |
| OUT | PID输出 |
| CYCLE | PID运算周期(有的库没有,直接用任务周期) |
我的梯形图写起来大概是这样的:
(* 调用PID功能块,输出结果保存到rPidOut *) PID_Water( SV := rSetPoint, PV := rPvTemp, KP := rKp, TI := rTi, TD := rTd, OUT => rPidOut );有一点必须强调:PID功能块每一次扫描都会执行一次计算,但它的计算周期应该和系统被控对象匹配,不能简单地“PLC扫描多快就跑多快”。水温对象慢,PID运算周期放在100ms到500ms都合适。具体做法是将PID程序放在一个周期性任务里,或者给PID块一个固定的PID运算周期。如果PID周期太短,积分累积得太快,参数很难调;周期太长,又会感觉系统反应迟钝。
另外,如果PID库支持输出限幅,把输出限幅设为0-100%。这一步不做好的话,积分饱和会很严重。什么叫积分饱和?就是温度还远低于设定值时,PID输出一直在100%,积分项慢慢堆积到很大;等温度到达设定值,输出需要往回降,但积分项一时半会儿减不下来,温度就会大幅超调。限幅不能解决全部问题,但能减轻不少。
4.4 用PWM把PID输出变成开关量
PID算出了rPidOut,接下来就是把这个0到100的百分数转成加热管的通断动作。最简洁的办法是用库里的PWM功能块,它通常提供周期和脉宽两个输入引脚:
(* PWM功能块,周期1秒,脉宽由PID输出换算 *) PWM_Heat( Enable := bHeatReady, CycleTime := T#1S, OnTime := TIME_OUT := ULINT_TO_TIME? 这就需要用上位逻辑换算 );不同厂家的PWM块引脚设计不一样,有的需要传“脉冲宽度时间”,有的直接传“占空比百分比”,写之前一定要看库说明。如果实在没有PWM块,或者想彻底搞明白原理,我建议你自己用两个TON定时器做,这也是现场排查问题时最保底的办法。
自己做的思路如下:一个TON负责产生基准周期,另一个TON负责控制加热ON时间。假设周期固定为1秒:
- 第一步,控制周期定时器tonCycle到1秒后复位,同时把tonOnTime复位重启;
- 第二步,计算onTimeMs := rCtrlOut / 100.0 * 1000ms;
- 第三步,比较tonOnTime已经计时的时间是否小于onTimeMs,小于就让加热输出为ON,否则为OFF。
放在梯形图里,逻辑长这样:
(* 周期定时器,PT=1s,到时间自复位 *) tonCycle( IN := bHeatReady AND NOT tonCycle.Q, PT := T#1S ); (* 周期到,复位导通定时器 *) tonOnTime( IN := bHeatReady AND NOT tonOnTime.Q, PT := TIME_TO_ULINT? -- 根据ON时间设定 ); (* 导通时间计算公式 *) onTimeMs := REAL_TO_TIME((rCtrlOut / 100.0) * 1000.0);实际LD里,你只需要把onTimeMs赋给tonOnTime的PT输入,然后在输出比较前取一个判断:如果tonOnTime计时未到onTimeMs,则bHeatOut为ON,否则为OFF。比较指令在梯形图里用“<”或“<=”功能块实现。
我把这段逻辑的最终控制变量写在下面:
bHeatReady := bRun AND bLowLevel AND NOT bAlarmHigh; rCtrlOut := SEL(bModeAuto, rManualOut, rPidOut);这里的SEL是选择函数:bModeAuto为FALSE时,把手动输出当作最终输出;为TRUE时,把PID输出当作最终输出。手动模式是用来调试加热器本身好不好用的,千万别一上来就跑自动PID。
4.5 完整梯形图逻辑串联
把前面几个段落串起来,就是一个可以下载到PLC里运行的骨架。我通常会把程序分成几个梯级写在同一个POU里:
Rung 1:启动和停止控制
(* 启动按钮按下,运行标志置位;停止按钮按下,运行标志复位 *) bRun := (bStart OR bRun) AND NOT bStop;这段是典型自锁电路,不过用赋值指令做也行。你果你喜欢传统触点梯形图,可以在LD里画成按钮并联自己的常开触点,再串联停止按钮的常闭触点。
Rung 2:安全联锁
bAlarmHigh := rPvTemp > rAlarmTemp; bHeatReady := bRun AND (NOT bLowLevel) AND (NOT bAlarmHigh);这里的rAlarmTemp是上限报警温度,一般取设定值加3到5℃。低水位这个信号我强烈建议接到硬接点输入,不要只在触摸屏界面上做一个软开关,真实工况下水位过低但加热器还在烧,那会出大问题。
Rung 3:模拟量读取和温度换算
iRawAI := %IW0; (* 假设物理通道地址为IW0 *) rPvTemp := INT_TO_REAL(iRawAI) * 200.0 / 4000.0;Rung 4:PID输出
PID_Water( SV := rSetPoint, PV := rPvTemp, KP := rKp, TI := rTi, TD := rTd, OUT => rPidOut );Rung 5:手动/自动选择
rCtrlOut := SEL(bModeAuto, rManualOut, rPidOut);Rung 6:PWM脉宽输出
用PWM功能块的直接接;用自写TON方案的,把rCtrlOut换算成通断时间再输出。
Rung 7:实际DO地址绑定
%QY0 := bHeatOut; (* 加热输出 *) %QY1 := bAlarmHigh; (* 报警输出 *)以上是最基础的逻辑。工程上建议再加一个“手无自动时加热输出显示”,把rPidOut和rManualOut都显示在触摸屏上,方便调机时观察。
5. 调试过程与常见问题
5.1 第一次上电,千万别直接跑PID
我在现场的习惯是“先手动,再自动,最后精调”。第一次上电先把手动/自动开关拨到手动,手动输出给一个10%左右的小占空比,确认加热管能正常发热、温度能上去;再切到自动,设定值设低一点,比如30度,看看传感器读数是不是稳定、升温方向是否正确。
很多新手容易犯的错误是:程序一写好就设目标温度80度,直接自动跑。结果传感器接反、变送器量程不对、输出接错等基础问题全堆一起,温度乱跳,你根本分不清是PID的问题还是硬件接线的问题。手动模式就是用来隔离这些故障的。
5.2 温度曲线常见的三种病
第一种病:温度永远到不了设定值,稳态误差一直存在。
这种问题十有八九是积分作用不够。Kp只负责“有偏差就修正”,但偏差小到一定程度以后,修正量也变小,最后系统停在“加热量和散热量相等”的位置上,离设定值差那么一点。此时把Ti调小,让积分加速累积,就能把这部分误差顶上去。如果你用的是不支持积分抗饱和的老旧PID库,还要检查是不是因为饱和限制导致积分根本没法累积。
第二种病:到设定值附近来回震荡,幅度越来越大。
原因通常是Kp太大,或者积分时间太短。我见过有人一上来把Kp设到几百,Ti设成0.1秒,结果温度直接变成发散震荡,加热管一下全开一下全关。保守的做法是先把Kp减半,Ti增一倍,观察一两个周期再调。水温对象惯性大,一次调整完成后至少等几分钟才看效果,别刚看10秒就改参数。
第三种病:温度震荡很小但有规律,比如±0.3度。
这种往往不是PID参数问题,而是控制周期太长。你设了3秒的PWM周期,相当于每3秒才调节一次加热功率,对快响应的小水箱来说自然会产生周期性波动。把周期缩短到1秒,波动就会明显变小。如果传感器本身噪声大,比如热电偶屏蔽没做好,还要先解决采样噪声,不然PID的微分环节会把噪声放大得更厉害。
5.3 常见问题速查表
| 问题现象 | 可能原因 | 处理办法 |
|---|---|---|
| 温度显示乱跳 | 屏蔽线没接好、传感器与动力线同槽 | 检查接线,传感器远离动力线,加滤波 |
| 升温特别慢 | Kp太小、Ti太大、输出上限过低 | 增大Kp,减小Ti,检查输出是否被限幅 |
| 超调严重 | Kp太大、Ti太小、系统接近饱和 | 降低Kp,增大Ti,确认抗积分饱和有效 |
| 温度稳定但始终低于设定值 | 积分作用不足 | 减小Ti,并检查稳态偏差 |
| 加热输出频繁通断 | PWM周期太短 | 增加周期到1秒以上 |
| 自动模式没输出 | 模拟量读数异常、PID输出被限为0 | 查看rPidOut,检查传感器和工程量换算 |
| 手动模式正常,切自动后失控 | 设定值或PID参数设置错误 | 从低目标温度重新开始,逐步提高 |
5.4 关于微分项和传感器滤波
温度控制这种慢对象,多数情况下可以不加微分。微分本质上是“对偏差变化的预测”,但水温的滞后时间很长,偏差变化往往不是立刻反映原因,微分很容易被数字采样噪声骗到。我的习惯是Td先设0,等纯比例和积分把系统调到基本稳定,再看超调趋势。如果超调出现得很突然,而且系统有足够时间提前响应,我才会尝试加一点微分,一般取积分时间的1/8到1/4。
传感器滤波也一样,可以在模拟量读取后加一阶低通滤波或者均值滤波,但不要滤波过度。水温本身是慢变量,加1秒以内的时间常数没问题;可如果滤波太长,反馈值变化太迟钝,PID看到的永远是“过去”的温度,就容易滞后震荡。
6. 最后说点个人经验
做了这么多温度控制项目,我最深的体会是,PID这东西不是什么黑魔法,而是一个需要尊重过程机制的工具。你理解了它输出的是比例、积分、微分三股力量的合成,理解了加热管只能靠时间比例来等效功率,剩下的就是按步骤调试、观察曲线、改参数。
再做扩展的时候,有两个方向很实用:如果现场有变频器或可控硅调压模块,可以把PID输出直接接到模拟量输出上,实现真正的连续功率调节,控制品质会比开关量方式更细腻;如果对象是大水箱、滞后特别大,可以考虑加前馈控制——比如检测到进水量增加时,提前按比例提高输出——避免PID被滞后拖住反应不过来。
如果你准备在汇川PLC上跑这套程序,我的建议是先在办公室用一个小电热水壶或者小型恒温槽验证逻辑,用触摸屏把rPvTemp、rPidOut、rKp、rTi这几个变量显示出来,一边看曲线一边调。等把握了PID的脾气,再上现场的大设备,心里就完全有底了。