每次听到有人说“全自动洗衣机控制程序不就是个顺序启动嘛”,我都想拉他坐下来看看这套仿真工程的细节。说它难吧,确实没到伺服运动控制那种程度;说它简单吧,等你做到水位传感器抖动、脱水时序打架、画面和PLC对不上号的时候,就会明白那些“看得见的简单”背后全是“看不见的细节”。
这套程序我用的环境是 GX Works2 编写梯形图逻辑,GT Designer3 做触摸屏画面,最后通过自带仿真器把 PLC 和 HMI 全套联动跑通。整个过程走下来,最大的感受是:洗衣机控制的关键根本不是“会不会写梯形图”,而是能不能把一个生活常识准确地翻译成时序和互锁。下面我就按实际开发的顺序,从需求拆解到仿真联调,一段一段拆给你看,顺便把那些仿真里才暴露出来的坑也一起说了。
1. 需求拆解:先别碰梯形图,把“洗衣服”翻译成状态机
1.1 顺序控制之外的三个隐藏需求
洗衣机的自动流程,教科书里都会写:进水、洗涤、排水、脱水、结束。但真让你写PLC程序,你会发现这几个词根本不够用,因为中间还夹着很多“隐含动作”。
最典型的是洗涤阶段。波轮洗衣机不是正转到底,而是正转一会儿、停一会儿、反转一会儿、再停一会儿,这样反复循环。如果只写一个“洗涤输出”,那电机要么一直转,要么停下来就再也起不来。所以洗涤这一个阶段,实际上内部还要再拆成四个子状态:正转、暂停、反转、暂停。
第二个隐藏需求是水位判断。进水不能靠时间瞎猜,要等水位开关到位;排水也不能光靠时间,要等水位确实降到安全点,不然带着水强行脱水,电机会过载。水位开关的反馈信号,既是进水的结束条件,也是排水结束和脱水启动的前置条件。
第三个是门锁和安全联锁。洗涤和脱水过程中,门必须锁死;排水阀和进水阀不能同时打开;脱水开始前必须确认水已经排完。这些条件看着不起眼,但少写一个,仿真的时候也许看不出来,真机运行就可能出安全事故。
所以第一步不是开软件,而是在纸上把状态列出来。我一般直接画一个简单的状态表,把每个阶段的进入条件、执行动作、退出条件写清楚。写完了再动程序,后面基本不会乱。
1.2 软元件规划从输入输出倒推
写梯形图之前,先把I/O分配定好。这个项目用的是一台模拟FX3U系列的PLC,分配表如下:
| 类别 | 软元件 | 作用 |
|---|---|---|
| 输入 | X0 | 启动按钮 |
| 输入 | X1 | 停止按钮 |
| 输入 | X2 | 水位开关,达到设定水位后ON |
| 输入 | X3 | 模式选择,OFF为标准、ON为轻柔 |
| 输入 | X4 | 门锁到位反馈 |
| 输出 | Y0 | 进水阀 |
| 输出 | Y1 | 洗涤电机正转 |
| 输出 | Y2 | 洗涤电机反转 |
| 输出 | Y3 | 排水阀 |
| 输出 | Y4 | 脱水电机(含离合器) |
| 输出 | Y5 | 蜂鸣器 |
| 输出 | Y6 | 门锁电磁铁 |
内部软元件这块,我用M500做运行总标志,M501到M504做洗涤四拍控制,D0存放当前阶段代码,D20做洗涤节拍索引,D21做洗涤循环计数,D100到D105存放各阶段的时间基准值,D30存报警代码。
定时器和计数器也要提前规划。FX3U的T0到T199是100ms时基定时器,T200到T245是10ms时基定时器;我这边统一用T0到T6,具体用途后面代码里会体现。计数器用C0做洗涤循环计数。规划这一步很多人会跳过,结果写一半发现软元件打架,回来改程序改到怀疑人生。
2. GX Works2落地梯形图:从软元件规划到STL状态骨架
2.1 工程创建和参数配置别默认一路下一步
打开GX Works2新建工程,选PLC系列时要注意和后面仿真器的匹配。如果选了FX3U,对应的系列就是FX3U/FX3UC;编程语言选“梯形图”就行。工程建好后,先别急着写程序,到“PLC参数”里看一眼软元件设置。
仿真时有一个容易被忽略的点:FX3U的默认输入输出点数、定时器范围够用,但如果你在程序里用了比较指令、INC指令这类功能,要注意PLC类型不能选太低端的FX3G,不然指令集不支持。我这次因为要用比较触点和INC指令,所以选了FX3U。
参数配置里还要注意“注释”和“软元件内存”的设置。GX Works2支持给每个软元件写注释,仿真联调时候,注释会显示在软元件监视窗口里,看起来一目了然。我这套程序的X0、Y0、D0这些都有注释,调试时省了不少事。
2.2 STL步进梯形图的搭建骨架
洗衣机的核心流程用步进梯形图(STL)来实现,这是三菱PLC里很适合顺序控制的写法。每个状态是一个STL触点,状态内部写这个阶段要做的动作,状态之间用SET指令转移。好处是结构清楚,一个状态对应一坨逻辑,改起来不会牵连其他状态。
主流程我划分了这么几个状态:
- S0:待机
- S20:进水
- S21:洗涤
- S22:排水
- S23:脱水
- S24:结束提示
- S30:故障报警
程序开头用M8002(上电初始脉冲)把S0置位,然后从S0开始往下走。上电进入待机这段,很多新手会漏掉M8002,结果一上电PLC不知道自己在哪个状态,直接乱跑。
S0待机状态里的转移条件是这样处理的:
STL S0 LD X0 // 启动按钮 ANI X1 // 停止按钮没按 AND X4 // 门锁已经吸合 SET S20 // 进入进水状态这里把启动按钮、停止按钮、门锁反馈串在一起,相当于一个启动总条件。X4门锁反馈必须为ON才能启动,避免门没关好就开始进水。启动后,程序进入S20进水状态。
进水段的写法也简单,但它带了一个保护定时器:
STL S20 OUT Y0 // 进水阀打开 OUT T4 K3000 // 进水超时保护,5分钟 LD X2 // 水位到位 SET S21 // 进入洗涤状态 LD T4 // 如果5分钟还没到水位 SET S30 // 进入报警状态进水阀Y0在S20内直接OUT,状态一旦转移,Y0会自动断开。这个机制是STL的好处,不用在下一个状态里手动去关上一个状态的输出。T4是保护逻辑,防止水压不足或者水位开关坏了导致一直进水,5分钟没到水位就直接报警。
2.3 洗涤四拍:定时器交替和循环计数
洗涤是整个程序里最绕的部分,因为一个S21状态内部还要跑四个子节拍。我处理方式是用D20做节拍索引,0代表正转、1代表暂停、2代表反转、3代表暂停。进入S21时先确保D20为0,然后靠定时器驱动D20不断变化。
先看洗涤前的时间参数设定。因为标准模式和轻柔模式的时间不一样,而且这个设定必须在启动瞬间锁存,所以我用启动脉冲来做:
LD X0 PLS M0 // 启动脉冲 LD M0 ANI X3 // 标准模式 MOV K70 D100 // 正转7秒 MOV K20 D101 // 暂停2秒 MOV K70 D102 // 反转7秒 MOV K20 D103 // 暂停2秒 MOV K15 D104 // 循环15次 MOV K1800 D105 // 脱水3分钟 LD M0 AND X3 // 轻柔模式 MOV K40 D100 // 正转4秒 MOV K30 D101 // 暂停3秒 MOV K40 D102 // 反转4秒 MOV K30 D103 // 暂停3秒 MOV K10 D104 // 循环10次 MOV K900 D105 // 脱水1.5分钟这里用PLS M0取一个启动上升沿,只在按启动按钮那一下把时间参数写到D100到D105。如果不用脉冲而是直接LD X0去MOV,那运行过程中只要X0一直为ON,模式切换就会实时改时间参数,洗涤到一半突然变节奏,这种问题排查起来特别隐蔽。
洗涤主逻辑这样写:
STL S21 LD= D20 K0 // 正转节拍 OUT T0 D100 // 正转时间 LD T0 MOV K1 D20 // 切到暂停节拍 LD= D20 K1 // 暂停节拍 OUT T1 D101 // 暂停时间 LD T1 MOV K2 D20 // 切到反转节拍 LD= D20 K2 // 反转节拍 OUT T2 D102 // 反转时间 LD T2 MOV K3 D20 // 切到暂停节拍 LD= D20 K3 // 暂停节拍 OUT T3 D103 // 暂停时间 LD T3 MOV K0 D20 // 回到正转节拍这里要重点提醒一下:定时器OUT T0 D100里的D100是定时器设定值,单位是100ms一眼要看清。T0的输出条件LD= D20 K0只有当前节拍是0时才为ON,节拍一切走,定时器条件断开,才能在下一次重新计时。如果你把定时器放在一个一直为ON的常开触点后面,那定时器只会计一次,到点后触点一直ON,再也回不去,这是洗涤程序最常见的死循环原因。
洗衣机的电机正转和反转输出,实际上是在各自节拍里输出的。正转节拍时Y1为ON,反转节拍时Y2为ON。我把Y1放在D20等于0的节拍里,Y2放在D20等于2的节拍里。注意正转和反转绝对不能同时为ON,这个由D20的互斥节拍索引天然保证了。
循环次数计数交给C0。计数脉冲来自反转结束的T2:
LD T2 OUT C0 D104 // 每次反转结束计数一次 LD C0 // 循环次数到了 SET S22 // 进入排水 RST C0 // 复位计数器三菱计数器OUT C0 D104的计数条件是逻辑从OFF到ON的上升沿,所以即使T2为ON保持几个扫描周期,C0也只加一次。C0计满后触点ON,转移去排水,同时在S22状态里把C0复位。
3. 排水、脱水与门锁互锁:仿真最容易翻车的三个细节
3.1 排水结束靠水位下降沿,不是靠时间
洗涤完成后进入S22排水状态,我的处理是直接开排水阀Y3,然后等水位开关X2从ON变成OFF。X2作为常开触点,有水时ON,排空了就OFF。所以转移条件用的是X2的常闭:
STL S22 OUT Y3 // 排水阀打开 OUT T5 K3000 // 排水超时保护5分钟 LDI X2 // 水位已经降下去 SET S23 // 进入脱水 LD T5 SET S30 // 超时报警有人觉得排水排个固定时间比如30秒就够了,反正洗衣机一共就那点水。但仿真和真机不一样,真机里水管高度、排水管堵塞程度都会影响排水速度。如果你用固定时间,水没排完就直接脱水,电机会带水启动,轻则震动,重则烧电机。所以排水结束条件必须是“水位信号消失”,而不是“时间到了”。时间只做超时保护用,这点在实际项目里是硬性要求。
3.2 脱水启动必须先关进水、再开排水
脱水阶段S23有个很容易犯的错:排水阀Y3在S22里开了,到了S23很多人就忘了继续开。因为STL切换后,上一个状态输出自动OFF,所以如果你在S23里不重新OUT Y3,排水阀在脱水那一刻就关了,剩下半管水在桶里,脱水效果大打折扣。
正确做法是在S23里同时保持排水阀打开,并启动脱水输出:
STL S23 OUT Y3 // 排水阀继续开 OUT Y4 // 脱水电机启动 OUT T6 D105 // 脱水时间 LD T6 SET S24 // 进入结束状态这里还有一个顺序逻辑:脱水电机Y4的启动,必须发生在排水阀已经打开的前提下,也就是S22已经运行过一段时间,水位信号已经消失。如果程序里直接把进水、排水和脱水都塞在同一个状态里,仿真时可能看不出来,但真机一开脱水,桶里还有水,离合器硬接高速,那个声音和震动能让隔壁邻居来敲门。
另外注意,脱水输出Y4算是一个高速输出,仿真器里看不到实际旋转,但可以通过监控Y4的通断状态判断时序是否正常。GT官方仿真器对高速脉冲这类功能支持有限,但单纯的通断逻辑没问题。
3.3 门锁联锁:虽然仿真管得松,程序不能松
门锁电磁铁Y6我放在主逻辑里,用运行标志M500控制:
LD M500 OUT Y6 // 门锁吸合M500在启动瞬间置位,在停止或程序结束时复位。这么做意味着只要洗衣机在运行,门锁就一直吸合,不管当前是在进水、洗涤还是脱水。门锁电磁铁的复位时机也很关键,必须在停止按钮按下或者结束状态完成后才断开。
有人会把门锁逻辑也放进STL状态里,结果状态一转移门锁就掉了。洗涤过程中门突然能打开,这在真机里就是安全事故。仿真时可以偷懒,但程序结构必须按真机标准写,这是我认为做仿真项目最重要的心态。
停止逻辑我单独拉了一段:
LD X1 // 停止按钮 ZRST S20 S24 // 复位所有工作状态 SET S0 // 回到待机 RST M500 // 关运行标志ZRST是区间复位指令,一次把S20到S24全部复位。要注意如果当前正在S30报警状态,停止按钮也需要能够退出报警,所以复位区间要把S30也带上,我实际程序里是ZRST S20 S30。这个细节很容易漏,漏了之后一旦报警,按停止键没反应,只能断电复位,体验非常糟糕。
4. GT Designer3画面设计与HMI联动:把程序搬上触摸屏
4.1 画面工程的建立与GOT类型选择
PLC程序写完,开始做触摸屏画面。打开GT Designer3新建工程,第一步选GOT类型。我用的是GT27系列,这个系列在仿真器支持上比较完善,字体和对象类型也丰富。通信设置这块,PLC选FX3U系列,接口选择“CPU直连”,这样才能和GX Works2的仿真PLC建立通信。
很多人建完工程直接画画面,忘了设置系统区和通信参数,导致后来GT Simulator3根本连不上GX Simulator。所以建完工程第一件事,我建议先用“通信设置”向导走一遍,确认控制器类型和连接方式正确。
4.2 按钮、指示灯、阶段显示和报警的映射
画面我做了几个核心区域:启动停止区、状态指示灯区、阶段文字显示区、循环计数显示区、报警区。
启动按钮在主画面上放一个“位开关”,软元件设X0,动作选点动,也就是按下时X0为ON、松开为OFF。停止按钮同样用X1。模式选择开关用“位开关”的交替功能,软元件X3,按一次切换一次,画面上可以用两个状态文本“标准”和“轻柔”来显示当前模式。
状态指示灯直接对应Y0到Y6。进水阀亮绿色,排水阀亮黄色,脱水亮红色,门锁合上亮蓝色。这种颜色区分在现场很实用,仿真时也能一眼看出当前状态。
阶段显示我用“字指示灯”对象,软元件D0。程序里在各个状态里写入对应的阶段代码:
// 在S20里写 MOV K20 D0 // 在S21里写 MOV K21 D0 // 在S22里写 MOV K22 D0 // 在S23里写 MOV K23 D0 // 在S24里写 MOV K24 D0画面的字指示灯对象里设置多行文本,D0等于20时显示“进水”,等于21时显示“洗涤”,以此类推。这样画面上能实时看到流程走到了哪一步。循环计数显示用“数值显示”对象,软元件D21,单位设个“次”,看起来更直观。
报警显示这一块,我用D30存放报警代码。程序里如果进水超时或者排水超时,就往D30里写一个1或2。画面上放一个“报警显示”对象,关联D30,再配置好报警文本。仿真时一旦D30被写入,画面立刻弹出报警信息,这个功能在真机上也非常好用。
4.3 GT Simulator3与GX Simulator的联动流程
画面工程做完,进入最爽也最容易出问题的环节:仿真联动。GX Works2这边的操作是:
- 菜单栏点“调试”里的“开始/停止模拟”,启动GX Simulator2。
- 模拟启动后,梯形图会高亮显示当前执行中的触点,比如S20正在运行,进水阀Y0的状态会变成绿色高亮。
- 打开“软元件监视”窗口,可以强制ON/OFF一些输入点,比如X2水位到位、X4门锁到位。
- 确认PLC程序跑起来之后,打开GT Designer3里的GT Simulator3。
GT Simulator3启动后,默认是以独立HMI的形式模拟。想要和PLC仿真联动,需要在GT Simulator3的设置里选择连接GX Simulator2。具体的菜单位置一般在“模拟”或“通讯设置”里,勾选“使用GX Simulator”或者“PLC仿真器联动”。
联动成功之后,你去点GT Simulator3画面上的启动按钮,GX Works2里的梯形图会同步看到X0变成ON,S0状态开始执行,S20进水状态高亮。这感觉就像真机触摸屏连着PLC一样,整个信号链路全部打通。
5. 联调阶段我踩过的几个坑:从定时器到画面数据不同步
5.1 定时器编号和时基单位混搭
第一个坑来自定时器时基。FX3U的T0到T199是100ms时基,T200到T245是10ms时基。一开始我把正转7秒写成T0 K70,这个没错,K70意味着70个100ms,也就是7秒。但同样的K70如果放到T200里,就只剩0.7秒了。如果你一堆定时器混用,画面上的节奏会突然变快或变慢,还以为程序逻辑错了。
排查这种问题,我习惯在软元件监视窗口里同时看定时器的当前值和设定值。如果当前值从0一直涨到设定值然后触点动作,那说明计时逻辑没问题;如果设定值看起来不对,先查K值的单位是不是100ms。
还有一个附带问题:定时器设定值可以用寄存器D来提供,比如OUT T0 D100。我调试时遇到过D100被误写成0,导致正转节拍瞬间跳过的诡异现象。所以用D做设定值时,一定要在启动脉冲那段程序里确保MOV已经把值写进去了,否则定时器拿到0,基本等于常开。
5.2 计数器不动作或提前动作的根因
洗涤循环计数C0,我最初接在T2的常开触点后面,也就是反转节拍结束时计数。第一次仿真时发现C0到不了设定值,或者有时候一下子加了好几次数,动作看起来乱七八糟。
后来用监视窗口盯着看,发现问题出在反转结束那个瞬间,T2触点在断开、导通的临界状态抖动。因为T2的ON状态不止持续一个扫描周期,如果我又在T2后面串了别的触点,导致OFF到ON的沿判断被干扰,计数就变得不可靠。
解决方法是给计数条件加一个脉冲整形或者直接用T2的边沿触发。三菱的PLS指令可以把一个窄脉冲提取出来,我用一个辅助继电器M601来做计数脉冲,T2一旦导通,M601只ON一个扫描周期,计数于是只加一次。这种沿处理思路,在写任何计数器逻辑时都适用。
5.3 仿真画面数据不刷新的真正原因
GT Simulator3和GX Simulator2这俩协同工作的时候,最容易出现的现象是:画面上的按钮能点,但PLC那边没有任何反应;或者PLC程序一直在跑,但画面上的指示灯和数据永远不变。
我第一次遇到时以为是版本不兼容,折腾了好久才发现是通信设置里选错了连接方式。GX Simulator2模拟出来的PLC默认提供一条虚拟通信通道,GT Simulator3要指定走这条通道,而不是走以太网或者串口。在GT Simulator3的通信目标设置里,把连接目标选成“GX Simulator2”,而不是某个具体的IP地址或COM口,问题就解决了。
还有一次是画面里把水位开关X2设成了“位开关”的动作模式,误设成交替型,结果按一下水位到位,再按一下水位消失,逻辑来回横跳。这个问题不在程序,在画面配置。所以做画面映射时,按钮是点动还是交替一定要分清,启动按钮用点动,模式选择才用交替,别把逻辑搞混。
5.4 双线圈输出和STL未配对
程序做到后面,最容易出现的是“双线圈”问题。比如排水阀Y3,在S22里写了一次,在S23里又写了一次。GX Works2编译时会有警告提示“双线圈”,如果你忽略这个警告,运行结果可能完全出乎意料。
双线圈的机制是:PLC扫描时,同一个输出线圈如果在多处OUT,最终以最后一次扫描为准。所以在S22里OUT Y3,切到S23后,S23里的OUT Y3覆盖了前面的结果。如果S23里忘记OUT Y3,那个输出就一直保持S22最后一次的状态,看起来像“排水阀不受控制”。这个现象仿真时特别能迷惑人,因为画面上的Y3有时候亮有时候灭,似乎没有规律。
解决办法有两种:要么每个输出在全局只写一次,用中间继电器汇总;要么干脆把排水阀的状态在所有相关STL块里都写全。我习惯用第二种,简单直接,只要保证所有可能激活排水阀的状态都写了OUT Y3就行。检查时在程序搜索里搜一遍Y3,看看一共出现在几个地方,每个地方是不是都符合预期。
STL配对问题也是编译期常见的报错。STL开头必须和RET结尾配对,程序末尾要写RET,而且STL块内部不能用MC/MCR主控指令。如果编译直接报错,程序根本进不了仿真,所以这个坑其实是最容易发现的,前提是你愿意看编译输出窗口。很多人编译报错后直接盯着梯形图找半天,其实滚动条往下一拉,错误行号和原因都写得很清楚。
最后再分享一个小技巧
不知道你们做仿真有没有一个习惯,我反正在联调洗衣机这套程序时养成了:每次改动程序后,先把GX Simulator2停掉再重新启动,而不是直接在线修改。因为GX Simulator2的在线修改对STL程序的支持偶尔会犯迷糊,状态跑到一半你改程序,容易产生“旧状态卡死、新状态进不去”的假故障。重启模拟把状态归零,再按一次启动按钮重新走流程,基本上能看到正确的结果。
另外,GT Designer3里给每个画面对象加注释时,别偷懒,一个按钮、一个指示灯注释都写清楚。仿真阶段看不出什么,一旦真机调试,现场的人对着画面问“这个绿灯是啥”的时候,你会感谢当初多打的那几个字。
洗衣机这套控制逻辑,真正写下来其实不算长,但每一段都有它该有的位置和理由。顺序控制项目最大的魅力就在这里:程序本身不复杂,复杂的是把常识、安全、时序这些“人的经验”翻译成PLC能执行、人能维护的逻辑。这篇先聊到这儿,回头有时间我再把报警处理、手动调试模式这些进阶部分补一补。