很多工控软件开发同行都遇到过:几十个点的时候Modbus TCP跑得飞快,点表扩到上千个,超时、卡顿、数据刷新慢全来了。其实吧,瓶颈不在网卡带宽,而在请求读得太碎、在链路上排起了长队。把连续地址合并成批量读,再按重要性做分组轮询,问题基本就能解开。
一千个点,为什么读一遍要好几秒?
先算笔账。Modbus TCP虽然跑在以太网上,但大多数从站同一时间只认真正到达的一条请求,处理完才回响应。如果上位机对每个点都发一条读命令,一千个点就是一千次请求往返。
就算单次往返只要10毫秒,串行下来也是10秒起步。经过网关转串口的设备更惨,下面挂着RS485总线,9600波特率下一个来回几十毫秒很正常。你在软件里开再多线程也没用,反而把从站冲得更慢。
地址不连续,还能不能批量读?
能,关键看你愿不愿意为中间的空隙买单。读保持寄存器用03功能码,一条报文最多读125个寄存器;读输入寄存器用04,线圈和离散量则分别用01、02。
实际做法是把点表按地址排序后做区间合并:相邻点之间的空隙小于阈值(比如8个寄存器),就合并成一次读取,中间用不上的数据直接丢弃;空隙太大就切成两段。比起每个点单独请求,多读几个空寄存器的代价几乎可以忽略。
分组时有三条原则我一直死守
- 同一台设备一个通道,通道内严格串行,别在同一连接上并发;
- 批量长度卡着从站能力来,有的老设备只支持一次读32个甚至更少,要在驱动里做成可配置;
- 读和写分开排队,写指令优先级高、立即插队,避免被轮询周期压住。
个别设备超时,为什么会拖垮整个系统?
这是轮询调度里最阴的一个坑。某台从站掉线后,如果超时设得很长,调度器会一直傻等它,后面所有健康设备的数据全被拖住,表现就是“全厂数据都不刷新了”。
处理手段其实不复杂:
1. 超时要短。局域网内设1秒足够,经网关的设备按现场实测放宽到2到3秒;
2. 失败重试限次数,连续2到3次失败就把这台设备标记为离线,调度时快速跳过;
3. 熔断后低频探活,离线设备每30秒试一次,恢复了再拉回正常轮询;
4. 并发总数加闸门,用SemaphoreSlim限制同时在线的通道数,防止设备多了把本机端口和线程池耗尽。
数据新鲜度和设备负担,怎么找平衡?
不是所有点都值得500毫秒刷一次。我习惯把点表分成三层:
- 关键量,比如电流、压力、运行状态,500毫秒到1秒;
- 普通工艺量,比如温度、计数,2到5秒;
- 慢速参数,比如配方号、软件版本,10秒到1分钟,甚至连上就读一次。
每个数据都带上最后更新时间和质量戳,界面显示多久没刷新一目了然。我之前做过一个汽车零部件焊接车间的项目,四十多块电表和温控表经网关走Modbus TCP,合并区间加分级轮询之后,一轮全量采集从8秒压到1秒以内,断网的表也不再拖累其他设备。
今天回去可以做个小实验:用Wireshark抓一次上位机和PLC之间的报文,统计只读一两个寄存器的请求占比。如果这类“碎读”超过一半,说明你的调度层还有一大块优化空间。需要现成的采集调度框架,也可以找我们聊聊:免费咨询,或致电 13761638786。


