简介:一份面向工业自动化与空调压缩机控制场景的完整工程包,包含PLC控制程序与基于VC++开发的上位机监控软件,适合PLC编程初学者、C++上位机开发者及设备维护人员参考。包内既有压缩机启停、参数采集与故障判断等PLC逻辑,也有MFC对话框程序、串口通信与数据解析实现,能够帮助理解工业设备从底层控制到上层显示的完整链路。压缩包共58个文件,以C++源代码(14个h与13个cpp)为核心,同时包含编译生成的obj、pdb、exe及界面资源文件如图标、位图等,整体仅2.64MB,结构便于对照阅读。目前已有745人学习下载。通过该项目可直观看到PLC与上位机如何通过串口协议交换数据,并借鉴其界面布局、报警提示与数据管理模块,对快速上手工业监控软件开发很有价值。
1. 自己编的压缩机PLC程序:这不是教学例程,而是一份能落地的现场控制程序
之前帮某厂区改造空压站时,原厂程序把压缩机搞成了「一旦压力波动就频繁启停」,电机发热、接触器打火,压力还稳不住。后来我用某款中型PLC重写了一套压缩机PLC程序,把压力加卸载、变频器(VC)调速、报警联锁、手动/自动切换全部收进一个工程里,配合触摸屏组态,从仿真到带载运行用了不到一周。这套程序不是DEMO,模块划分、I/O分配、状态机都是按现场接线来的,拿到手上改改点位就能用。适合三类人:想把手动启停改成自动闭环的电气工程师、在学PLC但缺完整项目参照的开发者、以及手里有旧压缩机想翻新控制系统的同行。
2. 整体技术架构:PLC选型、程序目录与I/O地址规划
拿到程序先别急着看代码,先把这个工程的骨架摸清楚。这套程序基于某款国产中型PLC编写,编程软件支持梯形图(LD)和结构化文本(ST)混编,CPU自带以太网口和RS485口,模拟量模块走背板扩展。程序里控制逻辑主体用的是ST,因为压缩机的时序、滞环比较、通讯状态机用文本写更直观;少部分启停互锁逻辑退回LD,方便现场电工维护时直接看触点关系。
2.1 技术栈与编程方式:LD与ST混编的实际考量
选型思路很实在:压缩机控制不需要高端运动控制,但模拟量通道、通讯口和掉电保持区是刚需。程序里大量使用定时器、边沿检测和状态字位操作,这类逻辑用ST表达比梯形图少写很多网络。比如「启动脉冲」这种边沿逻辑,LD要放两个串联触点加一个脉冲输出指令,ST里一句R_TRIG就完事。但急停回路、门联锁这类安全相关回路,我保留在LD里,因为电工拿万用表量线路时,梯形图能直接对应物理接线,ST反而不直观。
项目中ST块负责算法,LD块负责硬接线逻辑,数据块统一管理变量。编程软件里没做任何特殊配置,就是用标准工程模板,在OB1主任务里轮询调用各功能块。这点对新人有参考价值:复杂项目别把代码全塞进OB1,像这里按功能切成FC_StartStop、FC_LoadUnload、FC_VC_Drive、FC_Alarm,调试时单独禁用某个FC就能定位问题。
2.2 程序文件结构:OB、FB、FC与数据块的分工
整个工程目录展开后大概是这样的:
Compros_PLC/ ├── OB1 // 主任务,周期扫描 ├── FB_Compros // 单机控制封装功能块 ├── FC_StartStop // 启停时序与联锁 ├── FC_LoadUnload // 压力加卸载滞环控制 ├── FC_VC_Drive // 变频器(VC)通讯状态机 ├── FC_Alarm // 报警生成与故障保持 ├── DB_Main // 全局运行数据区 ├── DB_AlarmState // 故障保持区(掉电保持) ├── DB_VC_Para // 变频器寄存器映射参数 └── HMIScreens/ // 触摸屏画面源文件 ├── SC_Home.uix // 主画面:启停、压力、电流 ├── SC_Para.uix // 参数设定:压力目标、死区 └── SC_Alarm.uix // 报警列表与复位设计逻辑很清晰:FB_Compros是把一台压缩机当成一个对象封装,内部调用启停、加卸载、VC控制这三个FC;DB_Main放运行状态和设定值,DB_AlarmState用掉电保持属性存故障记录。以后做多机组扩展时,直接实例化多个FB_Compros,每个实例绑定自己的I/O和DB,不需要复制代码。
2.3 I/O清单与内部地址映射表:改程序前先核对这张表
动手改任何点位前,先看程序里的地址映射表。这台设备的物理点位如下:
| 信号类型 | 点号 | 地址 | 说明 |
|---|---|---|---|
| DI 启动按钮 | I0.0 | 常开,上升沿 | 自动模式启动指令 |
| DI 停止按钮 | I0.1 | 常闭 | 程序内取反处理 |
| DI 主回路反馈 | I0.2 | 接触器辅助触点 | 确认主接触器真实吸合 |
| DI 油压正常 | I0.3 | 压力开关 | 预润滑完成信号 |
| DI 变频器故障 | I0.4 | VC故障输出端子 | 硬接点故障联锁 |
| AI 排气压力 | IW64 | 4-20mA,量程0-1.6MPa | 加卸载控制依据 |
| AI 电机电流 | IW66 | 4-20mA,量程0-200A | 过载保护与运行监视 |
| DO 主接触器 | Q0.0 | 控制主电机启停 | 需中间继电器隔离 |
| DO 加载电磁阀 | Q0.1 | 控制加卸载阀 | 得电加载 |
| DO 预润滑泵 | Q0.2 | 启动前先建立油压 | 时序执行 |
| DO VC运行指令 | Q0.3 | 干接点或通讯字 | 控制变频器启停 |
内部存储器里,MW10是压力滤波值、MW12是电流滤波值、MD20是压力目标值、MD24是死区。这些地址和HMI画面变量一一对应,改偏移前必须同步改触摸屏的变量表,否则画面上按钮按了没反应,压力曲线显示的还是旧通道。
程序注释里专门写了「地线没接好时模拟量读数会周期性跳动」,这句不是玩笑,后面故障排查章会细讲。整体结构熟悉后,看核心控制逻辑就有抓手了。
3. 核心控制逻辑拆解:从启动等待到变频器(VC)闭环调节的全链路
这一章是整套程序的重头戏。压缩机的控制本质是一个有限状态机:待机 → 预润滑 → 启动主电机 → 加载/卸载调节 → 正常停机 → 故障停机。每个状态之间都有条件判断和延时确认,防止信号抖动造成状态误跳。
3.1 启动与停机时序:预润滑、反馈确认与延时判断
压缩机启动最忌讳「给出指令就认为已经启动」。程序里用了一个三步确认链:启动指令 → 开预润滑泵并等待油压开关闭合 → 延时后合主接触器 → 等待接触器辅助触点反馈。每一步都有超时判断,任何一步在规定时间内没有完成,直接进入故障状态并保持报警。
// 启停时序核心逻辑(结构化文本) IF start_cmd AND NOT running THEN step := 1; // 进入预润滑步骤 END_IF; CASE step OF 1: // 预润滑阶段:启动油泵,等待油压开关 oil_pump_cmd := TRUE; IF oil_pressure_ok THEN // 油压开关DI已经闭合 ton_prelub(IN := TRUE, PT := T#5s); // 继续润滑5秒 IF ton_prelub.Q THEN step := 2; END_IF; ELSIF prelub_timeout THEN // 20秒未建立油压 fault_code := 10; // 故障码10:油压建立失败 step := 99; END_IF; 2: // 启动主电机:合接触器,等待反馈 main_contact_cmd := TRUE; IF main_feedback THEN // I0.2接触器辅助触点闭合 ton_confirm(IN := TRUE, PT := T#3s); // 稳定3秒防抖动 IF ton_confirm.Q THEN running := TRUE; step := 3; END_IF; ELSIF start_timeout THEN // 5秒内无反馈 fault_code := 11; step := 99; END_IF; 3: // 运行状态:交给加卸载模块处理 // 正常运行逻辑 99: // 故障停机状态 main_contact_cmd := FALSE; oil_pump_cmd := FALSE; // 等待人工复位 END_CASE;这里的核心技巧是每个状态转移都要确认“反馈已经稳定一段时间”,而不是收到信号立即跳转。ton_confirm的3秒就是为了过滤接触器辅助触点在吸合瞬间的抖动。fault_code是整数故障码,HMI上通过查表显示中文报警信息,比一堆BOOL报警位更容易排查。
3.2 压力加卸载控制:带死区与模拟量滤波的滞环调节
压缩机不需要连续PID调节出口压力,常用做法是装/卸载控制:压力低于下限就加载,高于上限就卸载,中间留一个死区,防止在目标值附近反复跳动。这里用了滞环比较,配合压力模拟量的一阶滤波。
// 加卸载滞环控制,带模拟量滤波 // 模拟量采样原始值:RAW_PRESSURE,滤波系数FILTER_K filtered_pressure := filtered_pressure + (RAW_PRESSURE - filtered_pressure) * FILTER_K; // 目标压力SET_P,死区DEAD_ZONE,来自HMI设定 IF running THEN IF filtered_pressure < (SET_P - DEAD_ZONE) THEN load_valve := TRUE; // 压力偏低,加载 ELSIF filtered_pressure > (SET_P + DEAD_ZONE) THEN load_valve := FALSE; // 压力偏高,卸载 END_IF; END_IF;FILTER_K是滤波系数,一般取0.1到0.3之间。取值太大滤波效果差,取值太小压力响应变慢。调试时先设0.2,观察趋势图里滤波曲线是否平滑。DEAD_ZONE建议设置为量程的2%~3%,比如量程1.6MPa,死区取0.04MPa左右。需要说明的是,如果现场管网本身频繁波动,单纯加大死区不是办法,可能要考虑加装缓冲罐或调整卸载回路的泄放速度。
3.3 变频器(VC)通讯控制:控制字状态机与掉线恢复
VC变频器是这套程序的另一个核心。这里没有用干接点启停,而是通过RS485走Modbus RTU协议,发送控制字来控制变频器运行和设定频率。通讯控制的难点在于状态管理,不能简单丢一个启动命令就完事,必须时刻知道变频器当前处于什么状态。
// VC变频器控制字状态机(简化版) CASE vc_state OF 0: // 通讯待机,等待允许运行 IF vc_enable THEN vc_ctrl_word := 16#047F; // 控制字:启动+使能 vc_state := 1; END_IF; 1: // 已发送启动,等待状态字反馈 IF (vc_status_word AND 16#0001) = 16#0001 THEN // 状态字第0位为1,表示变频器已准备 vc_state := 2; ELSIF comm_timeout_cnt > 3 THEN // 连续3个扫描周期没有收到应答 vc_ctrl_word := 16#0000; // 撤掉启动指令 fault_code := 30; // 故障码30:VC通讯中断 vc_state := 0; END_IF; 2: // 运行中,持续监视状态和频率反馈 freq_set := target_freq; // 目标频率来自压力偏差映射 // 正常循环内持续写入设定频率 END_CASE;16#047F这个控制字里,第0位是启动、第1位使能、第7位故障复位,具体位定义要看对应变频器手册。vc_status_word是变频器返回的状态字,第0位为1代表“准备好”,第2位是“运行中”。通讯状态机里最关键的是掉线恢复逻辑:连续多次请求无响应后,必须强制撤销控制字并将状态打回待机,绝不能停在“等待反馈”这个中间态,否则变频器可能在通讯恢复后自行运行,非常危险。
3.4 报警联锁与故障保持:按优先级执行和手动复位
报警处理的设计原则是:故障发生后必须保持,不能因为信号瞬间恢复就自动清除。程序里故障分两级——一级故障直接停机(油压丢失、接触器反馈丢失、VC通讯断),二级故障只报警不跳机(电机过流预警、压力过高报警)。
// 故障处理与保持逻辑 // 故障条件触发后,fault_hold置位并保持 IF (oil_pressure_lost OR main_feedback_lost OR vc_comm_fault) THEN fault_hold := TRUE; fault_code := MAX(fault_code, 10); // 故障码越大优先级越高 END_IF; // 手动复位按钮,且故障条件已消失 IF reset_btn AND fault_hold THEN IF NOT (oil_pressure_lost OR main_feedback_lost OR vc_comm_fault) THEN fault_hold := FALSE; fault_code := 0; step := 0; // 状态机回到待机 END_IF; END_IF;故障保持用fault_hold单个BOOL位配合fault_code实现,故障码同时承担优先级功能:同时发生多个故障时,显示码更大的那个。复位逻辑要求所有故障源都消失后才能复位,防止按下复位按钮后设备立即再次跳机。这里有个细节:fault_code用MAX而不是覆盖赋值,是为了保留优先级高的故障,避免低级别故障覆盖掉关键信息。
4. 运行与调试:从仿真验证到连接组态画面的实际步骤
很多人拿到程序第一反应是直接下载到PLC里接设备,这是最容易翻车的操作。这章按我的调试习惯,从头到尾走一遍完整流程。
4.1 仿真运行:不接设备如何验证逻辑正确性
编程软件自带仿真器,可以在电脑上模拟PLC运行。先把程序编译下载到虚拟PLC,然后在变量监控表里手动强制I/O信号,观察状态机的跳转是否符合预期。
# 仿真操作顺序(以软件内置仿真器为例) 1. 编译工程,确认无错误 2. 启动仿真器,下载程序到虚拟PLC 3. 打开变量监控表,强制 I0.0 = TRUE(启动按钮) 4. 观察 step 从 0 → 1,oil_pump_cmd 变为 TRUE 5. 强制 I0.3 = TRUE(油压正常),等待 5 秒定时器 6. 观察 step 变为 2,main_contact_cmd 变为 TRUE 7. 强制 I0.2 = TRUE(主接触器反馈),等待 3 秒 8. 观察 running 变量变为 TRUE,step 变为 3仿真阶段最重要的是把每个分支都走到,包括超时分支:试一下强制油压开关一直不闭合,看20秒后fault_code是否变为10。仿真器里能直接修改DB块里的定时器预设值,我把预润滑时间从5秒临时改成2秒,加速验证整个时序链路。
跟实际设备联调相比,仿真少了模拟量信号源。压力变送器没接时,程序里RAW_PRESSURE是0,会导致未运行时加载阀动作。所以仿真时同时强制filtered_pressure为一个正常值,比如1.0MPa,保证控制逻辑处在正常工况。
4.2 通信配置与点位核对:触摸屏画面与PLC变量绑定
程序里预留给HMI的变量都集中放在DB_Main里,触摸屏组态时只需要关联这些变量,不用去翻散落的地址。组态软件里新建画面,按钮关联DB_Main.start_cmd(BOOL,按下置位,释放复位),指示灯关联DB_Main.running,压力表关联DB_Main.filtered_pressure,报警列表关联DB_AlarmState.fault_code。
这里最常见的坑是数据地址类型不匹配。比如PLC里filtered_pressure是REAL类型,触摸屏上建数值显示时选了16位整数,几十兆帕的压力能显示成几百。组态前先在PLC变量表里确认每个变量的类型和字节长度,再做一次通信测试:PLC侧强制变量为设定值,看触摸屏上显示的数值是否一致。
HMI画面和PLC的通信链路是网线直连,设置PLC的IP为192.168.0.10,触摸屏IP设为192.168.0.20,端口默认。通信建立后,在触摸屏上启动按钮,仿真PLC里观察start_cmd是否变TRUE,这个闭环测试跑通后,系统联调才有意义。
4.3 现场带载验证:从点动到自动模式的测试顺序
现场通电后的测试顺序,我习惯按「信号验证 → 手动模式单动作 → 自动模式空载 → 自动模式带载」四步走。信号验证阶段,用万用表在PLC侧逐一测量DI点对地电压,确认无源干接点接线正确。然后切手动模式,单独点动预润滑泵电机,看电机转向是否正确;再点动主接触器,确认接触器吸合无异常噪音。
自动模式空载前,把加载电磁阀的气路手动切断,防止一开机就往管网打气。空载运行半小时,观察电流曲线和压力曲线是否平稳。确认没问题后,接上气路,逐步打开出口阀门,让压缩机加载,观察压力从0.1MPa慢慢升到0.7MPa的过程中,加载阀在压力低时是否打开、压力到设定值死区上沿时是否卸载。整个流程我要求全程盯住触摸屏的趋势曲线,出现问题随时打回手动模式。
5. 踩坑与排查:现场压缩机程序常见的故障记录
这套程序前后在现场跑了几个月,也踩了不少坑。挑五个代表性最强的问题,按「现象 → 原因 → 解决」的方式记下来,给后来人当个参考。
5.1 模拟量跳变导致的误报警:信号地环路问题
现象:压缩机稳定运行时,触摸屏上压力数值在设定值附近来回跳,幅度有0.05MPa,偶尔触发压力过高报警导致跳机。
原因:压力变送器信号线屏蔽层在PLC侧和变送器侧两端都接地,形成地环路,变频器运行时杂散电流串进信号线,4-20mA信号里叠加了交流分量。
解决:把变送器侧的屏蔽层剪断,只在PLC侧单端接地。同时在程序里增加一阶滤波,FILTER_K从0.2改到0.15,压力曲线明显平稳。这个案例说明滤波参数不能盲目调,先解决物理层的干扰,滤波只是辅助手段。
5.2 启动瞬间接触器抖动:PLC输出驱动能力不足
现象:主接触器在吸合时出现明显的打火抖动,声音断断续续,像继电器在快速通断。
原因:PLC的DO输出模块额定电流只有0.5A,而接触器线圈的吸合电流峰值达到1.2A左右,超过模块驱动能力,导致输出晶体管过热保护、反复通断。
解决:在PLC输出和接触器线圈之间加中间继电器。中间继电器线圈电流几十毫安,PLC驱动绰绰有余,接触器线圈由中间继电器的触点去控制。改完之后抖动彻底消失。这也解释了为什么程序里Q0.0、Q0.1这类输出点都预留了外部继电器接线位置。
5.3 变频器通讯掉线后的恢复乱套:状态机重入问题
现象:VC变频器偶尔报通讯故障后,按复位按钮能复位,但压缩机重新启动时,变频器不动作,必须断电重启变频器才恢复。
原因:通讯掉线时程序直接把vc_state打回0,但变频器侧可能还停在“运行中”的内部状态。这时再次发送启动控制字,变频器因为内部状态不一致,拒绝执行。
解决:在掉线保护逻辑里,除了撤掉控制字,还要发送一个“快速停车”命令(控制字16#0002),让变频器内部状态同步复位。并且在重新发送启动命令前,先延时1秒发送一个故障复位命令。从那以后,我再也没遇到过掉线后必须断电重启的情况。通讯状态机的每个分支,都要考虑对端设备内部状态的复位问题,这是PLC通讯编程里容易漏掉的一环。
5.4 在线修改定时器却不生效:在线值与启动值混淆
现象:把预润滑时间从5秒改成10秒,下载到PLC后,实际运行还是5秒就跳过了。
原因:PLC定时器有两种时间参数,一个是启动值,一个是当前在线值。在线修改的是启动值,但定时器已经在运行中,它读到的是旧的在线值(也就是原先的5秒),直到定时器复位再触发才会使用新的启动值。
解决:改完定时器参数后,先把程序切到停止模式再下载一次,或者手动复位定时器(ton_prelub(IN := FALSE)强制复位)。如果是运行中调整,我一般直接在HMI上给定时器预设值做成DB变量,触摸屏里改了立即生效,不用反复下载程序。这个设计在后面二次开发章节会进一步展开。
5.5 压力震荡不停加载卸载:压力反馈滞后与死区不匹配
现象:压缩机加载后压力快速上升,卸载后压力又快速下降,加载阀和卸载阀频繁切换,十几秒就循环一次。
原因:管网容积小,压力变化速度快,加载/卸载带来的压力波动幅度大于程序设定的死区范围。触发的死区不足以覆盖压力惯性的影响。
解决:把死区从0.04MPa加大到0.07MPa,同时把加载阀得电到压力上升的响应时间考虑进去——程序里加了一个“最小装载时间”的延时,强制加载后至少保持5秒才允许卸载。这招本质是牺牲一点压力精度,换取系统的稳定周期。对空压站这类应用,压力波动0.07MPa完全可接受。
6. 二次开发与扩展:把单机控制升级为多机组轮换与节能改造
这套程序跑顺之后,下一步往往是把一台压缩机扩展成一个空压站的多机组联动系统。这章给几个可以直接动手的改造方向。
6.1 将单机控制改造成可复用功能块
目前的FB_Compros已经封装了单台压缩机的完整控制逻辑,但内部还依赖全局变量DB_Main。改成可复用,第一步是把所有依赖改成功能块自身的内部变量和输入输出参数,比如启动指令start_cmd从全局变量改为FB的输入引脚。
// 可复用功能块接口定义(示意) FUNCTION_BLOCK FB_Compros VAR_INPUT start_cmd : BOOL; // 来自轮换调度模块 stop_cmd : BOOL; reset_btn : BOOL; pressure_raw : INT; // 本机压力变送器采集值 END_VAR VAR_OUTPUT running : BOOL; fault_code : INT; load_valve : BOOL; END_VAR改完之后,在OB1里声明3个实例,每个实例分配不同的I/O和定时器上下文,互不干扰。这样程序的复用性立刻提升,新增一台压缩机不需要复制代码,只需要实例化功能块并绑定地址。
6.2 增加机组轮换与运行时间统计
多机组控制的核心是两个维度:平均磨损和节能。轮换调度模块记录每台压缩机的累计运行时间,每隔固定周期(比如48小时)把运行时间最长的机组切换为备用机,把运行时间最短的机组拉起来运行。程序里用掉电保持DB区存每个机组的运行秒数,断电不会清零。
运行时间统计功能块:
// 运行时间累积逻辑(掉电保持区域) IF compros_1.running THEN run_seconds_1 := run_seconds_1 + 1; // 每秒累加一次 END_IF; // 换算为小时并写入HMI显示 run_hours_1 := run_seconds_1 / 3600;轮换动作的要点是切换前必须保证另一台机组已经加载到位,否则管网压力会掉得厉害。程序里加一个“并网运行5分钟后再卸载旧机”的交叉过渡逻辑,压力波动控制在0.02MPa以内。
6.3 联动上位机与节能数据分析
如果现场有多台压缩机,把每台的电流、排气压力、加载率这些数据汇总到上位机做趋势分析,就有了节能优化的数据基础。做法是通过PLC的以太网口把DB_Main里的数据映射到OPC服务器,上位机组态软件直接采集。
实际改造中,我还习惯在程序里增加一个“加载率统计”功能块:每分钟算一次加载时间占比,存到掉电保持区。连续记录一周后,就能看出压缩机的容量配置是否合理——如果加载率长期低于30%,说明压缩机选大了,应该考虑加变频器降低运行频率。这套程序跑下来,某厂的压缩机加载率从原来的频繁启停变成稳定的45%左右,电费肉眼可见地降下来。
从那以后,我每次给新站点部署这套程序,都强制把I/O映射表重新核对一遍,再花十分钟看一下状态机的复位分支是否覆盖了所有异常路径。这个习惯帮我挡掉了不少现场翻车的风险。希望帮到你。
本文还有配套的精品资源,点击获取