PLC交通灯控制算是工控入门必练的小项目,但真要把东西南北四个方向的红绿灯时序理清楚,还能在触摸屏上同步监控和调试,比想象中容易踩坑。这篇文章我把博途V15 + S7-1200做的一套完整十字路口交通灯控制方案分享出来,核心是用比较指令处理相位切换,触摸屏部分也已全部调通。适合刚接触PLC编程、正在做毕业设计,或者想学HMI联调的朋友参考。
这套程序最方便的地方在于:只要把“当前第几秒”算清楚,剩下所有灯的状态、倒计时、状态码都能靠比较指令一条一条判断出来,逻辑非常直观,后期要改绿灯时长也只用改几个常数,不用像传统定时器方案那样拆了重排。触摸屏上能看到实时灯态、倒计时,还能一键切应急模式,实用性很强。
1. 项目整体思路与方案选型拆解
1.1 先理清十字路口的时序逻辑
做交通灯控制,第一步不是写程序,而是把时序表画明白。常见的两相位十字路口可以这样定义方向:东西方向为A相,南北方向为B相。我采用一个常见且好验证的时序,整个周期40秒:
| 时间段(秒) | 东西方向 | 南北方向 |
|---|---|---|
| 0 ~ 14 | 绿灯,持续15秒 | 红灯 |
| 15 ~ 19 | 黄灯,持续5秒 | 红灯 |
| 20 ~ 34 | 红灯 | 绿灯,持续15秒 |
| 35 ~ 39 | 红灯 | 黄灯,持续5秒 |
40秒后回到0,循环往复。这里有个容易疏忽的点:A相绿灯结束后,B相不能立即转绿,中间必须有5秒黄灯过渡,这是为了给正在路口的车辆一个清空时间。反之亦然。所以一个周期内的安全顺序是:A绿 -> A黄 -> B绿 -> B黄,永远不能出现A绿和B绿同时为真的情况。
1.2 为什么选比较指令而不是纯定时器法
我知道很多教材喜欢用TON定时器串联,比如T1计时15秒后置位Y,T2再计时5秒,以此类推。这种纯定时器方案在简单演示上没问题,但一旦想在中途加一个“急停全红”或“夜间黄闪”,定时器链条就会变得非常凌乱,改起来也痛苦。
我在这套程序中改用“全局秒计数 + 比较指令”的思路:用一个1Hz脉冲信号累加出总秒数,再用比较指令判断当前总秒数落在哪个区间,从而决定每一盏灯的输出状态。这样做的优势有三个:
- 时间轴是全局统一的,所有相位都由同一个“秒数”驱动,不会出现多个定时器各自为政、互相打架的情况。
- 修改绿灯时长只需改比较区间,不用重排定时器网络,维护成本低。
- 触摸屏需要显示倒计时,秒数就在这里直接算,非常顺手。
1.3 硬件选型与I/O规划
这套项目用的是S7-1200系列,型号选CPU 1214C DC/DC/DC,自带14点数字输入、10点数字输出,做交通灯绰绰有余。如果你的CPU是1211C或1212C,点数也够,只是输出点数少一些,布局时稍微挪一挪就行。
触摸屏我选了博途V15下同组态的KTP700 Basic PN,12寸左右的屏做这个画面非常宽裕。当然,用精简系列更便宜,原理一样。I/O分配按最直观的方式排:
| 地址 | 功能 | 说明 |
|---|---|---|
| I0.0 | 启动按钮 | 按下后系统进入自动运行 |
| I0.1 | 停止按钮 | 用于复位和停止运行 |
| I0.2 | 手动/应急切换 | 不接硬件时可在触屏上通过M变量替代 |
| Q0.0 | 东西方向绿灯 | |
| Q0.1 | 东西方向黄灯 | |
| Q0.2 | 东西方向红灯 | |
| Q0.3 | 南北方向绿灯 | |
| Q0.4 | 南北方向黄灯 | |
| Q0.5 | 南北方向红灯 |
实际接线时,输出点外接24V灯组或继电器再去带220V红绿灯都可以,但感性负载一定要并续流二极管,不然PLC输出晶体管容易被反向电动势打坏。这个坑我确实踩过,后面还会细说。
2. 比较指令的核心玩法与程序编写细节
2.1 S7-1200里比较指令到底怎么用
博途V15中,S7-1200的比较指令在LAD里有EQ、NE、GT、GE、LT、LE六种,对应等于、不等于、大于、大于等于、小于、小于等于。它们可以比较两个整数、实数或字符串,但两个输入的数据类型必须一致。
在我的程序里,用得最多的是GE和LE组合。比如判断“当前秒数是否在0到14之间”,梯形图里就是两个比较块串联:第一个块写总秒数 >= 0,第二个块写总秒数 <= 14,两个块的结果做与逻辑,这样一组“区间判断”就完成了。如果后面想改成10秒绿灯,只需把14改成9。
如果你更习惯用SCL,也可以写得很精练。我经常给朋友们演示的版本是这样的:
IF #总秒数 >= 0 AND #总秒数 <= 14 THEN "东西绿灯" := TRUE; ELSE "东西绿灯" := FALSE; END_IF;这段代码的本质仍然是若干次比较运算。初学者不要被LAD里的“块”吓到,它和C语言里的if判断是一个道理,只是图形化表达而已。
2.2 秒脉冲与总秒数累加的实现
既然要用比较指令做相位判断,就得先有一个可靠的“秒发生器”。S7-1200最省事的做法是启用系统时钟存储器。在CPU属性中勾选“启用系统时钟存储器”,并指定一个字节地址,比如MB100,那么M100.4就是1Hz信号,周期正好1秒。再用一个R_TRIG上升沿检测指令,检测到M100.4从0到1的跳变,就把总秒数加1。
这段逻辑如果用SCL写,大约长这样:
"时钟上升沿"(CLK := "M100.4", Q => "秒脉冲"); IF #运行标志 THEN #总秒数 := #总秒数 + 1; IF #总秒数 > 39 THEN #总秒数 := 0; END_IF; END_IF;注意几个细节:
- 不要直接用M100.4去驱动计数器的边沿当作计数信号,因为1Hz信号本身是方波,你需要在“上升沿”那一瞬间加1,而不是在电平为1的整个1秒内持续加。
- 总秒数上限设为39,因为周期是40秒,数到39后必须回0,否则会一直涨上去。
- 停止时要把运行标志清掉,同时把总秒数清零,这样下一次启动一定从绿灯第0秒开始,不会出现上电直接黄灯或红灯的尴尬。
2.3 输出网络的编写顺序与边界值处理
输出网络是整个程序最核心的部分。我的做法是分成两组写:一组管东西方向,一组管南北方向,每组内部再用比较指令划分区间。
以东西方向为例,三个输出分别是东西绿灯、东西黄灯、东西红灯。用SCL写出来的逻辑非常清楚:
// 东西方向 IF #总秒数 >= 0 AND #总秒数 <= 14 THEN "东西绿灯" := TRUE; "东西黄灯" := FALSE; "东西红灯" := FALSE; ELSIF #总秒数 >= 15 AND #总秒数 <= 19 THEN "东西绿灯" := FALSE; "东西黄灯" := TRUE; "东西红灯" := FALSE; ELSIF #总秒数 >= 20 AND #总秒数 <= 39 THEN "东西绿灯" := FALSE; "东西黄灯" := FALSE; "东西红灯" := TRUE; END_IF;南北方向反着写:
// 南北方向 IF #总秒数 >= 20 AND #总秒数 <= 34 THEN "南北绿灯" := TRUE; "南北黄灯" := FALSE; "南北红灯" := FALSE; ELSIF #总秒数 >= 35 AND #总秒数 <= 39 THEN "南北绿灯" := FALSE; "南北黄灯" := TRUE; "南北红灯" := FALSE; ELSE "南北绿灯" := FALSE; "南北黄灯" := FALSE; "南北红灯" := TRUE; END_IF;这里有一个非常容易翻车的地方:边界值必须用“>=”和“<=”仔细对待。比如0到14是绿灯,15到19是黄灯,如果第一次把绿灯写成“<=14”,第二次黄灯也写成“>=15”,那不会重叠;但如果你把灯状态写成独立线圈,中间又没有处理好,就可能在某一个瞬间同时输出绿和红。
还有一点经验分享:不要在输出网络里用置位SET和复位RESET来做相位切换。置位复位虽然写着省事,但一旦忘记复位某一个灯,就会造成两方向同时绿灯的灾难。直接用赋值或线圈方式,让每个扫描周期都重新计算一遍输出状态,逻辑反而最安全。
2.4 触摸屏需要倒计时,怎么在PLC里算出来
触摸屏上少不了一个“剩余秒数”显示,这个数其实就是在PLC里算的。我定义了四个变量:东西剩余、南北剩余,类型都是Int。
当前相位剩余时间的计算思路很简单:找到当前相位区间的上边界,减去当前秒数即可。
// 东西方向当前相位剩余秒数 IF #总秒数 <= 14 THEN #东西剩余 := 14 - #总秒数; ELSIF #总秒数 <= 19 THEN #东西剩余 := 19 - #总秒数; ELSE #东西剩余 := 39 - #总秒数; END_IF;南北方向同理:
IF #总秒数 >= 20 AND #总秒数 <= 34 THEN #南北剩余 := 34 - #总秒数; ELSIF #总秒数 >= 35 AND #总秒数 <= 39 THEN #南北剩余 := 39 - #总秒数; ELSE #南北剩余 := 19 - #总秒数; END_IF;这样做的好处是触摸屏那边根本不用做任何计算,直接显示PLC传上来的数据就行,减轻了HMI的负担,也避免HMI和PLC两边因刷新周期不一致导致数字跳变。
3. 触摸屏组态与联调实战
3.1 触摸屏画面怎么规划才实用
很多初学者做触摸屏,喜欢把画面做得花里胡哨,其实工业画面最重要的是信息清晰、操作明确。这套交通灯项目的HMI画面,我分成了三个页面:
- 主监控画面:中央放一个简化的十字路口示意图,用四个圆形或方形的“图形I/O域”显示东西南北的灯色;旁边放两个IO域显示倒计时;下方放启动按钮和停止按钮。
- 模式切换画面:放“应急全红”按钮、“夜间黄闪”按钮、“解除”按钮,用于不同场景下的手动干预。
- 调试画面:直接显示总秒数、当前相位代码、运行标志等内部变量,方便排查问题,这个画面在真实项目中也建议保留。
图形I/O域显示灯色,是靠PLC侧算出来的“状态码”驱动的。我给东西方向和南北方向各定义了一个整数变量,0表示绿灯,1表示黄灯,2表示红灯。在SCL里这样赋值:
IF #总秒数 <= 14 THEN #东西状态码 := 0; ELSIF #总秒数 <= 19 THEN #东西状态码 := 1; ELSE #东西状态码 := 2; END_IF;HMI端的图形I/O域设置成“输入/输出”,类型选整数,然后在“图形列表”里给0配绿色圆,给1配黄色圆,给2配红色圆。这样灯色就能跟着PLC实时变化,比单纯写个文字“绿灯”直观太多。
3.2 变量连接与通信设置的几个关键点
在博途V15里给S7-1200和KTP700组态通信,其实是在同一个项目里完成的。触摸屏组态时新建一个连接,控制器的类型选S7-1200,接口选“PROFINET”,然后在HMI变量表里直接拖拽PLC变量过来就行。
这里有个容易踩坑的点:如果PLC侧的DB块或变量启用了“优化块访问”,那么HMI可以直接通过符号名访问,很方便。但如果你的变量没有勾选优化块访问,HMI连的时候就要求你使用绝对地址或填写实际偏移量。为了省事,建议新建一个专门给HMI用的DB块,名称简单、数据类型尽量用Bool和Int,然后在右键属性里把“优化的块访问”保持勾选状态。
还要注意IP地址规划。CPU 1214C默认IP是192.168.0.1,KTP700默认IP可能不同,建议组态时就规划好:
- PLC IP:192.168.0.10
- HMI IP:192.168.0.20
- 电脑网卡IP:192.168.0.50
三者必须处于同一网段,但地址不能冲突,否则下载的时候谁也找不到谁。
3.3 用博途V15仿真联调的操作顺序
在没有真实硬件时,可以用PLCSIM和WinCC Runtime在电脑上把整套逻辑跑起来。我的操作顺序是这样的:
- 在博途V15里编译PLC程序,点击“开始仿真”,弹出S7-PLCSIM界面。
- 下载程序到PLCSIM,点击运行。注意S7-PLCSIM界面里模块的指示灯要变绿,CPU状态要设为RUN。
- 回到项目树,在HMI文件夹右键,选择“开始仿真”,此时会启动WinCC Runtime。
- 关键一步:HMI运行起来后,在HMI连接设置里确认连接属性是“S7-PLCSIM”,而不是真实的PN/IE。仿真状态下如果HMI连不上PLC,大多是这一步没选对。
用PLCSIM调试的好处是可以单步看变量变化。我调试时就盯着“总秒数”这个变量从0一直加到39再清零,同时看灯状态变量是否按预期切换。边界点尤其要盯:14到15、19到20、34到35、39到0,这四下最容易出问题。
如果你的PLCSIM启动不了,或者启动后下载失败,先检查是不是用了S7-PLCSIM Advanced。这里可以说清楚:标准的S7-PLCSIM(博途自带)对S7-1200支持得很好,而S7-PLCSIM Advanced主要面向S7-1500和ET200SP,做1200仿真没必要用Advanced,反而经常因为虚拟网卡权限问题各种“启动不了、还没有报错信息”。老老实实用基础版PLCSIM,踏实很多。
4. 上电调试中的常见问题与排查实录
4.1 上电初始化:为什么启动后可能一片红
这套程序在开发过程中,我遇到的第一类问题全部集中在“上电瞬间”和“停止后再启动”这两个场景。因为总秒数是非保持变量,断电后会自动清零,这倒还好。但如果你把运行标志也做成了非保持变量,上电后运行标志默认是FALSE,启动按钮又是普通输入,那按下启动按钮之前,系统当然不会跑,这是对的。
问题往往出在有人习惯把启动按钮做成常闭点,或者停止按钮用了常开触点,导致上电瞬间PLC采集到一个意外的电平信号,运行标志被误置位,系统突然自动跑起来。我的建议是:启动和停止逻辑不要直接依赖输入点本身的电平,而要在PLC内部用边沿判断。
// 用上升沿检测按钮信号 #启动沿(CLK := "I0.0", Q => #启动脉冲); #停止沿(CLK := "I0.1", Q => #停止脉冲); IF #启动脉冲 THEN #运行标志 := TRUE; END_IF; IF #停止脉冲 THEN #运行标志 := FALSE; END_IF;同时,停止后要把总秒数清零,所有灯输出一个确定的复位状态。我更倾向于停止后保持“全红”或“全灭”。从交通安全角度讲,断电逻辑缺失时全灭不如全红安全,所以我在停止逻辑里让所有红灯点亮,而不是全部熄灭。
4.2 比较指令的边界重叠与输出竞争问题
我在调试时专门盯过一组边界值:总秒数等于14和15、19和20的时候,东西方向灯的切换是否干脆利落。结果发现如果程序里某处把绿灯范围写成“<=15”,把黄灯范围写成“>=15”,就会出现同一秒里绿灯和黄灯同时通的情况。这在真实路口的交通灯上是绝对不允许的。
解决的办法就是严格使用开闭区间搭配:绿灯用“>=0 AND <=14”,黄灯用“>=15 AND <=19”,红灯用“>=20 AND <=39”。这样每个整数秒都只属于一个区间,不重叠、不遗漏。SCL代码里只要统一写法和边界,基本不会出这个问题;倒是在LAD里,如果网络分散、边界常量写错,排查起来比较费眼。
还有一个问题:如果我在程序最后一段给输出用了置位,前面某段又用了复位,两个网络在一个扫描周期里先后执行,就会出现所谓的“输出竞争”。S7-1200的扫描周期很短,最终输出状态取决于最后被执行的网络,但这会让维护者非常头大。我最终彻底改成每周期直接计算赋值,不再使用置位复位,从此这类问题绝迹。
4.3 触摸屏连不上PLC的排查速查表
HMI联调阶段最常见的报错就是“无法建立到控制器的连接”,或者触摸屏画面一片灰色。这类问题排查顺序很重要,我整理了一个速查表:
| 现象 | 可能原因 | 排查动作 |
|---|---|---|
| HMI显示连接中断 | IP不在同一网段 | 对比PLC和HMI的IP,ping一下PLC地址 |
| 仿真HMI连不上PLCSIM | 连接接口选成了PN/IE | 在HMI连接里改成“S7-PLCSIM” |
| 变量全是0或灰色 | 变量路径没连对 | 在HMI变量表里重新拖拽PLC变量 |
| 下载HMI时提示找不到设备 | PG/PC接口类型不对 | 下载窗口里选择实际网卡,常用PN/IE |
| HMI显示值跳变 | HMI刷新周期太快 | 把HMI变量采集周期从1秒改到100毫秒或50毫秒 |
还有一个容易被忽略的点:PLC程序中如果开启了“优化块访问”,HMI访问DB变量是直接按符号名走的。如果你在某个DB块属性里取消了这个勾选,那么HMI访问时需要额外的偏移量和绝对地址,一旦组态版本有差异,很容易出问题。所以我个人建议,给HMI用的变量单独建一个DB,并且保持优化块访问开启,少折腾很多。
4.4 现场接线与抗干扰的经验教训
最后再讲一条硬件侧的坑。我第一次用继电器驱动220V交通灯模型时,没给继电器线圈并续流二极管,结果运行时PLC的输出模块偶尔会自己复位,严重时还导致CPU模块通信中断。原因就是继电器线圈断电瞬间会产生反向电动势,直接反击到PLC的DO端。
正确做法是在继电器线圈两端并联一个反向二极管,也就是续流二极管,让反向能量在二极管上消耗掉。如果是固态继电器或直接驱动LED灯组,一般不用太担心这个问题,但只要是感性负载,都必须考虑。另外,整个控制柜的接地也要处理好,PLC的24V电源负极和机柜接地要按说明书接法来做,否则调试中会出现各种“灵异”现象。
还有一个小细节:PLC输出模块的公共端(比如1M、2M)接线时要特别注意极性,S7-1200 DC/DC/DC的输出是晶体管输出,公共端正负极接反了灯也能亮,但容易导致输出点状态指示异常,甚至烧输出。下载程序之前,先把每个输出点的接线图对着手册核对一遍,比烧掉一个点再去查划算得多。
4.5 模式切换与应急控制的扩展做法
这套程序我把运行模式做成一个整数变量,0是自动,1是全红,2是黄闪。自动模式就按总秒数和比较指令跑;全红模式是所有方向红灯点亮,用于紧急封路;黄闪模式是东西南北四个方向黄灯一起以1Hz频率闪烁,通常用在夜间车流量极小的时候。
黄闪的实现也不难,直接拿M100.4这个1Hz信号去驱动四个黄灯输出即可,但要注意此时要把东西南北两个方向的绿灯和红灯全部清零,不能让黄灯和别的灯同时亮。模式切换逻辑可以这样写:
CASE #运行模式 OF 0: // 自动相位逻辑,即上面的输出网络 1: "东西红灯" := TRUE; "南北红灯" := TRUE; "东西绿灯" := FALSE; "东西黄灯" := FALSE; "南北绿灯" := FALSE; "南北黄灯" := FALSE; 2: "东西黄灯" := "M100.4"; "南北黄灯" := "M100.4"; "东西绿灯" := FALSE; "东西红灯" := FALSE; "南北绿灯" := FALSE; "南北红灯" := FALSE; END_CASE;在触摸屏上给模式切换按钮写入1或2,就能立刻切到应急或夜间模式。调试的时候我试过在自动模式运行到第38秒时直接切全红,此时没有出现任何灯同时亮的危险状态,说明模式切换逻辑和输出互锁是有效的。
5. 调试心得与可以继续扩展的方向
做这个交通灯项目,我最大的体会是:PLC编程的难点从来不是某个指令不会用,而是如何把一套逻辑组织得清晰、安全、可维护。比较指令在博途V15里属于最基础的运算指令之一,但一旦把它用在“全局秒数与相位区间”的对应关系上,整套代码的可读性和可维护性会明显上升。哪怕以后要加左转相位、行人按钮、公交车优先,也只需要再增加几个状态区间和相应的比较判断。
如果你打算在此基础上继续做,我建议优先考虑两个扩展方向。第一个是加倒计时显示到真实路口的数码管上,这需要在PLC里额外算倒计时并转成BCD码或段码输出,本质上还是基于当前相位剩余秒数的计算。第二个是增加传感器检测和手动干预,比如按下行人按钮后,在下一个周期插入一个行人绿灯时段,这时要把总秒数的周期做动态调整,比较区间也会更灵活。这两个方向都是很好的进阶练习,也能让这套毕业设计或参赛项目更有亮点。