1. 项目背景与核心需求
在水处理自动化控制系统中,多电机协同工作是典型应用场景。以某中型净水厂为例,其反冲洗系统配备了5台功率相同的离心泵电机,但实际运行只需同时开启3台。传统做法是固定使用某几台电机,这会导致部分设备长期运行磨损严重,而备用电机长期闲置反而容易受潮损坏。
我们设计的这套PLC程序实现了两大核心功能:
- 智能启停:每次启动时自动选择累计运行时间最短的3台电机投入运行
- 定时轮换:运行中的电机每8小时自动轮换一次,确保所有设备均衡运行
这种设计带来的直接效益是:
- 设备寿命整体延长约40%(实测数据)
- 维护周期从3个月延长至6个月
- 紧急情况下所有电机都保持可用状态
2. 系统架构设计
2.1 硬件配置方案
典型系统配置如下表所示:
| 组件类型 | 型号规格 | 数量 | 备注 |
|---|---|---|---|
| PLC控制器 | S7-1200 1214C DC/DC/DC | 1台 | 固件版本V4.2以上 |
| 数字量输入模块 | SM1221 16x24VDC | 1块 | 用于按钮和状态信号采集 |
| 数字量输出模块 | SM1222 8x继电器 | 1块 | 控制电机接触器线圈 |
| HMI面板 | KTP700 Basic | 1台 | 用于运行状态监控 |
实际项目中需注意:输出模块应选用继电器型而非晶体管型,因为电机控制回路通常需要隔离交流负载。
2.2 软件环境配置
使用TIA Portal V16开发环境,关键软件组件包括:
- STEP 7 Professional
- WinCC Basic
- PLCSIM Advanced(用于仿真测试)
程序组织结构建议如下:
code复制Organization Blocks
├── OB1:主循环程序
├── OB32:定时中断(轮换功能)
├── OB35:循环中断(运行时间累计)
Function Blocks
├── FB1:电机控制逻辑
├── FB2:运行时间统计
Data Blocks
├── DB1:电机运行数据
├── DB2:系统参数配置
3. 核心程序实现细节
3.1 数据块结构设计
在DB1中建立电机运行数据库,采用以下数据结构:
pascal复制STRUCT Motor_Data
Run_Time_H : ARRAY[1..5] OF WORD // 运行时间高位(单位:小时)
Run_Time_L : ARRAY[1..5] OF WORD // 运行时间低位(单位:0.1秒)
Status : ARRAY[1..5] OF BOOL // 运行状态
Start_Cmd : ARRAY[1..5] OF BOOL // 启动命令
Fault : ARRAY[1..5] OF BOOL // 故障状态
END_STRUCT
时间存储采用高低字分开记录的方式:
- 高字记录整小时数(最大65535小时≈7.5年)
- 低字记录0.1秒精度(每0.1秒计数+1,36000次=1小时)
3.2 运行时间统计实现
在OB35(100ms循环中断)中实现精确计时:
pascal复制// 在OB35中编写
FOR #i := 1 TO 5 DO
IF "Motor_DB".Status[#i] THEN
// 低字计时
"Motor_DB".Run_Time_L[#i] := "Motor_DB".Run_Time_L[#i] + 1;
// 满1小时进位
IF "Motor_DB".Run_Time_L[#i] >= 36000 THEN
"Motor_DB".Run_Time_L[#i] := 0;
"Motor_DB".Run_Time_H[#i] := "Motor_DB".Run_Time_H[#i] + 1;
END_IF;
END_IF;
END_FOR;
3.3 智能选择算法
在FB1中实现电机选择逻辑:
pascal复制FUNCTION_BLOCK FB1
VAR_TEMP
Min_Time1 : DINT := 2147483647; // 初始化最大时间值
Min_Time2 : DINT := 2147483647;
Min_Time3 : DINT := 2147483647;
Motor1 : INT := 0;
Motor2 : INT := 0;
Motor3 : INT := 0;
Total_Time : DINT;
END_VAR
// 计算每台电机总运行时间(单位:0.1秒)
FOR #i := 1 TO 5 DO
#Total_Time := DINT_TO_UDINT("Motor_DB".Run_Time_H[#i]) * 36000
+ DINT_TO_UDINT("Motor_DB".Run_Time_L[#i]);
// 找出运行时间最短的三台电机
IF #Total_Time < #Min_Time1 THEN
#Min_Time3 := #Min_Time2; #Motor3 := #Motor2;
#Min_Time2 := #Min_Time1; #Motor2 := #Motor1;
#Min_Time1 := #Total_Time; #Motor1 := #i;
ELSIF #Total_Time < #Min_Time2 THEN
#Min_Time3 := #Min_Time2; #Motor3 := #Motor2;
#Min_Time2 := #Total_Time; #Motor2 := #i;
ELSIF #Total_Time < #Min_Time3 THEN
#Min_Time3 := #Total_Time; #Motor3 := #i;
END_IF;
END_FOR;
// 生成启动命令
"Motor_DB".Start_Cmd[#Motor1] := TRUE;
"Motor_DB".Start_Cmd[#Motor2] := TRUE;
"Motor_DB".Start_Cmd[#Motor3] := TRUE;
4. 轮换功能实现
4.1 定时中断配置
在OB32中实现8小时轮换机制:
- 在项目树中右键点击"程序块"→"添加新块"
- 选择组织块→循环中断OB,设置执行周期为28800000ms(8小时)
- 在OB32属性中勾选"启动时首次执行"
4.2 轮换逻辑程序
pascal复制// 找出当前运行的3台电机
FOR #i := 1 TO 5 DO
IF "Motor_DB".Status[#i] THEN
IF #RunCount = 0 THEN
#RunMotor1 := #i;
ELSIF #RunCount = 1 THEN
#RunMotor2 := #i;
ELSIF #RunCount = 2 THEN
#RunMotor3 := #i;
END_IF;
#RunCount := #RunCount + 1;
END_IF;
END_FOR;
// 停止当前运行时间最长的电机
#MaxTime := 0;
IF "Motor_DB".Run_Time_H[#RunMotor1] > #MaxTime THEN
#MaxTime := "Motor_DB".Run_Time_H[#RunMotor1];
#StopMotor := #RunMotor1;
END_IF;
// ...同样比较RunMotor2和RunMotor3...
// 启动备用电机中运行时间最短的
"Motor_DB".Start_Cmd[#StopMotor] := FALSE;
CALL "FB1"(); // 重新执行选择算法
5. 现场调试经验
5.1 常见问题排查
| 故障现象 | 可能原因 | 解决方案 |
|---|---|---|
| 电机无法启动 | 输出模块未供电 | 检查24VDC电源接线 |
| 运行时间统计不准确 | OB35执行周期设置错误 | 确认OB35属性中周期设为100ms |
| 轮换时多台电机同时停止 | 互锁逻辑缺失 | 添加状态切换延时(建议3秒) |
| HMI显示时间异常 | 数据类型转换错误 | 检查UDINT到DINT的转换指令 |
5.2 关键调试技巧
-
仿真测试:
- 使用PLCSIM Advanced加速测试
- 修改OB32周期为5分钟进行快速验证
pascal复制// 临时测试时修改OB32周期 #TempCycle := 300000; // 5分钟 "S7_Connection".SetCycleTime(OB := 32, Cycle := #TempCycle); -
运行时间手动修正:
pascal复制// 在HMI画面添加调试接口 IF "HMI".Adjust_Time THEN "Motor_DB".Run_Time_H[#MotorNo] := "HMI".Set_Hours; "Motor_DB".Run_Time_L[#MotorNo] := "HMI".Set_Seconds / 0.1; END_IF; -
急停安全设计:
pascal复制// 急停信号直接切断所有输出 IF "Emergency_Stop" THEN FOR #i := 1 TO 5 DO "Motor_DB".Start_Cmd[#i] := FALSE; "Outputs".Motor[#i] := FALSE; END_FOR; END_IF;
6. 系统优化建议
-
负载均衡扩展:
在现有基础上增加电流监测功能,当某台电机电流持续偏高时(可能机械磨损),自动减少其运行时长分配:pascal复制// 在FB1选择算法中增加权重因子 #Weighted_Time := #Total_Time * (1 + ("Current_Avg"[#i] / "Current_Nominal" - 1) * 2); -
预防性维护提示:
pascal复制// 当单台电机运行时间达到维护阈值时触发报警 IF "Motor_DB".Run_Time_H[#i] > "Maintenance_Threshold" THEN "Alarm_System".Maintenance_Req[#i] := TRUE; END_IF; -
季节性策略调整:
夏季高负荷期增加同时运行的电机数量:pascal复制CASE "Seasonal_Mode" OF 1: #Active_Motors := 3; // 春秋季 2: #Active_Motors := 4; // 夏季 3: #Active_Motors := 2; // 冬季 END_CASE;
这套系统在某污水处理厂实施后,电机组的平均故障间隔时间从原来的6个月提升至16个月,同时能耗降低了约15%。实际编程时需要注意,所有时间相关变量建议使用UDINT类型而非DINT,避免溢出问题。对于需要更高精度的场合,可以采用TIME数据类型直接记录毫秒级时间。