1. PLC数据监控小程序的诞生背景
车间里那台老设备又抽风了——毫无征兆地停机,触摸屏上连个报警都没有。电工老王抄起万用表在电柜前忙活了半小时,愣是没找出毛病。这种场景对于搞过PLC的朋友来说太熟悉了。传统做法要么是改程序加监控点,要么就是蹲在HMI前玩"大家来找茬",但手速再快也抓不住那些转瞬即逝的异常信号。
这就是我开发这个PLC数据监控小程序的初衷。它能以最短10ms的刷新周期,实时监控西门子、三菱、欧姆龙等主流品牌PLC的内部变量,而且部署简单到连产线上的老张头都能轻松上手。相比每次都要修改PLC程序再下载的繁琐操作,这个小工具简直就是排查偶发故障的神器。
2. 核心功能与技术方案
2.1 多品牌PLC兼容设计
这个小程序最核心的价值在于它支持多种品牌的PLC。在工业现场,我们经常会遇到不同品牌PLC混用的情况。为此,我设计了一个驱动抽象层:
python复制class PLCSpy:
def __init__(self, plc_type):
self.drivers = {
'siemens': self._connect_siemens,
'omron': self._connect_omron,
'mitsubishi': self._connect_mitsubishi,
'ge': self._connect_ge
}
self.conn = self.drivers[plc_type.lower()]()
每种品牌的连接方式都封装成了独立的方法。比如西门子用的是ADS协议,三菱则是MC协议,欧姆龙又不一样。这种设计让后续扩展新品牌变得非常容易。
2.2 高精度采样控制
要实现10ms级别的稳定采样,时间控制是关键。我对比了几种计时方案:
python复制from time import perf_counter, sleep
start = perf_counter()
# 执行采样操作
delta = perf_counter() - start
sleep_time = max(0.01 - delta, 0) # 确保总间隔为10ms
sleep(sleep_time)
这里有几个技术要点:
- 使用perf_counter()而不是time.time(),因为它的精度更高(实测在Win10下精度可达微秒级)
- sleep_time计算时要考虑实际采样耗时,避免累积误差
- 用max函数确保不会出现负数的sleep时间
2.3 数据采集优化技巧
直接逐个读取变量效率太低,特别是需要监控多个点时。为此我实现了批量读取优化:
python复制def _sampling_loop(self, tags):
while self.running:
values = self.conn.read_list(tags) # 批量读取
timestamp = time.time()
# 存储到环形缓冲区
self._save_to_buffer(timestamp, values)
这种方法相比单点读取,效率提升了N倍(N为监控点数)。实测在监控50个变量时,仍能保持10ms左右的采样周期。
3. 品牌特定协议处理
3.1 西门子PLC的ADS协议
西门子PLC通常使用ADS协议通信,pyads库是个不错的选择:
python复制import pyads
def _connect_siemens(self):
plc_ip = '192.168.1.100' # PLC IP地址
port = 851 # ADS端口
self.conn = pyads.Connection(plc_ip, port)
self.conn.open()
注意:西门子PLC需要提前在TIA Portal中启用ADS通信,并设置正确的路由。
3.2 三菱PLC的MC协议
三菱Q系列需要使用MC协议,这是一种基于TCP的二进制协议:
python复制def read_mitsubishi_d(ip, start_addr, length):
cmd = bytearray([
0x50, 0x00, 0x00, 0xFF, 0xFF, 0x03, 0x00,
0x0C, 0x00, 0x00, 0x00
])
cmd.extend(start_addr.to_bytes(2, 'big'))
cmd.extend(length.to_bytes(2, 'big'))
with socket.socket() as s:
s.connect((ip, 5007)) # MC协议默认端口
s.send(cmd)
return s.recv(4 + 2*length)[4:] # 去掉4字节协议头
这里有个大坑:三菱使用大端序(Big-Endian),而西门子是小端序(Little-Endian)。曾经有个项目混用两种PLC,结果数据解析时符号位全乱了,后来在配置表中增加了字节序标记才解决。
3.3 欧姆龙PLC的FINS协议
欧姆龙PLC通常使用FINS协议:
python复制def _connect_omron(self):
fins_header = bytearray([
0x46, 0x49, 0x4E, 0x53, # FINS
0x00, 0x00, 0x00, 0x0C, # 长度
0x00, 0x00, 0x00, 0x00, # 命令码
0x00, 0x00, 0x00, 0x00 # 错误码
])
# 建立TCP连接
self.omron_socket = socket.socket()
self.omron_socket.connect((self.ip, 9600))
4. 数据处理与异常检测
4.1 数据存储策略
10ms采样会产生海量数据,我的解决方案是:
- 使用环形缓冲区保留最近N秒的数据
- 检测到异常时自动保存前后30秒的数据
- 平时以CSV格式存储简化数据
python复制class RingBuffer:
def __init__(self, size):
self.buffer = np.zeros(size)
self.index = 0
self.size = size
def append(self, data):
self.buffer[self.index] = data
self.index = (self.index + 1) % self.size
4.2 实时波形分析
使用numpy可以快速实现波形分析:
python复制def detect_abnormal(data, window=10, threshold=3):
# 计算移动平均和标准差
avg = np.convolve(data, np.ones(window)/window, 'valid')
std = np.std(data[-window:])
# 检测超出3σ的数据点
anomalies = np.where(np.abs(data[-len(avg):] - avg) > threshold*std)[0]
return anomalies
这套算法在电机堵转检测中,成功捕捉到了0.8ms的电流尖峰——比PLC自带的故障检测还要快两倍。
5. 实际应用案例
5.1 变频器异常检测
某产线的变频器偶尔会报过流故障,但PLC记录到的电流值看起来完全正常。使用这个小工具后,我们发现:
- 故障发生时确实有持续约2ms的电流尖峰
- PLC的采样周期是10ms,正好错过了这些瞬态异常
- 尖峰出现在电机启动后的第3-5秒之间
最终发现是制动电阻接线松动导致的,修复后问题彻底解决。
5.2 气动系统泄漏排查
一个气动抓手偶尔会动作缓慢,但压力表显示正常。通过监控:
- 电磁阀输出信号
- 气缸压力传感器
- 流量计信号
发现当抓手动作缓慢时,虽然稳态压力正常,但在换向瞬间压力会短暂下降。这表明气缸密封圈存在轻微泄漏,更换后问题消失。
6. 部署与使用技巧
6.1 快速部署方案
我将核心代码打包成了单文件exe,部署只需三步:
- 将exe文件复制到车间电脑
- 编辑同目录下的config.ini文件
- 双击运行
配置文件示例:
ini复制[PLC]
type = siemens
ip = 192.168.1.100
port = 851
[Tags]
M0.0 = 启动信号
DB1.DBW10 = 温度设定值
DB1.DBD12 = 实际温度
6.2 实用技巧
-
网络优化:如果采样不稳定,可以尝试:
- 使用专用网卡连接PLC
- 禁用网卡节能模式
- 设置更高的进程优先级
-
变量选择:不是所有变量都需要高频采样,重点关注:
- 关键传感器信号
- 执行机构控制信号
- 故障相关标志位
-
存储策略:根据实际情况调整:
- 正常时只保留最近5分钟数据
- 异常时自动保存前后30秒数据
- 重要数据定期导出备份
7. 性能优化经验
7.1 采样周期稳定性
要达到稳定的10ms采样,需要注意:
- 使用高精度计时器(perf_counter)
- 减少不必要的打印输出
- 预分配缓冲区避免动态内存分配
- 使用线程隔离采样和存储操作
实测优化前后的对比:
| 优化项 | 平均周期 | 最大抖动 |
|---|---|---|
| 优化前 | 12.5ms | ±5ms |
| 优化后 | 10.2ms | ±0.8ms |
7.2 多PLC监控方案
当需要同时监控多台PLC时,可以采用:
- 多线程方案:每个PLC一个线程
- 异步IO方案:使用asyncio
- 进程池方案:适合CPU密集型处理
经过测试,对于5台以内的PLC,多线程方案最简单有效:
python复制from threading import Thread
plc_list = ['192.168.1.101', '192.168.1.102', '192.168.1.103']
threads = []
for ip in plc_list:
t = Thread(target=monitor_plc, args=(ip,))
t.start()
threads.append(t)
8. 常见问题排查
8.1 连接失败问题
-
现象:无法连接PLC
- 检查IP地址和端口是否正确
- 确认PLC已启用相应通信服务
- 测试网络连通性(ping/telnet)
-
现象:连接时断时续
- 检查网线和交换机状态
- 禁用网卡节能模式
- 增加连接超时时间
8.2 数据异常问题
-
现象:读取值全为0
- 检查变量地址是否正确
- 确认有读取权限
- 检查PLC程序是否在运行
-
现象:数据跳变异常
- 检查字节序设置
- 确认数据类型匹配(BOOL/INT/REAL)
- 检查信号线是否受干扰
9. 扩展应用方向
这个小工具经过适当改造,还可以用于:
- 设备健康监测:长期记录关键参数,分析设备劣化趋势
- 工艺优化:捕捉生产过程中的参数波动,找出优化空间
- 能耗分析:监控电机、加热器等设备的能耗曲线
- 预测性维护:基于历史数据预测设备故障
比如,我们曾用它分析注塑机的压力曲线,发现某个阶段的压力波动与产品质量相关,通过优化控制参数,不良率降低了30%。
10. 开发心得
在开发这个小工具的过程中,我总结了几个关键经验:
-
协议文档不可全信:实际测试发现某些PLC的协议实现与文档有出入,比如三菱的某些型号对MC协议指令的响应会多几个字节。
-
时间控制要留余量:即使计算出的sleep_time为0,也建议sleep(0)一下,让出CPU时间片,避免占用100%核心。
-
异常处理要周全:工业现场网络环境复杂,必须处理好各种超时和异常,否则程序很容易崩溃。
-
用户界面要简单:车间工程师更喜欢填配置文件,而不是在UI上点点点。所以我把所有配置都做成了ini文件格式。
这套工具现在已经在三个厂区推广使用,帮我们解决了不少疑难杂症。虽然界面简陋,但胜在实用。正如产线上的老张头说的:"能抓到老鼠的就是好猫,管它黑猫白猫呢!"