☰
工厂多屏电子看板同步方案:PLC采集+WebSocket实时推送实践
2026/10/1 14:43:19 网站建设 项目流程

做工厂的电子看板项目,很多人第一反应是“不就是一堆图表嘛”,等真正接手就会发现,最磨人的不是图表画得漂不漂亮,而是“多块大屏同步显示”这六个字。上个月我做完一个上海汽车零部件工厂的可视化电子看板项目,车间正中央一组4块55寸屏拼成的显示墙,实时刷产量、设备OEE、报警、能耗这几路数据,前后干了两周半,写页面只用了一周,剩下的时间全耗在数据采集、多屏同步和现场调试上。我直接把这套项目的完整拆解思路、技术方案、关键代码和踩坑记录摊开讲,给准备做同类工厂大屏的朋友一份可以直接参考的落地指南。

这类项目有一个很常见的坑:客户说“要同步显示”,但他心里想的“同步”和工程师理解的“同步”很可能是两码事。所以我不急着讲代码,先讲需求拆解,再讲技术选型,最后一步步讲调试和排障。如果你正在为车间大屏项目头痛,按这个顺序走一遍,能少走很多弯路。

1. 项目背景与需求拆解

1.1 这块看板到底展示什么

客户是上海一家做汽车零配件的工厂,车间里注塑机、压铸机、CNC加工中心加起来二十多台,原来车间管理层要看生产进度,得跑到电脑前翻MES报表,或者让统计员定时去抄数。这次他们想在大厅和产线通道装一组屏,把关键生产数据滚动展示出来,让现场人员一抬头就知道当前设备状态和产量。

物理环境是这样的:车间正中间一面2×2的55寸拼接屏墙,另外在车间的东侧和西侧各有一台独立大屏。数据内容我最终梳理成五类:设备状态(运行/待机/报警)、当日产量与班次目标达成率、各线体OEE实时值、设备报警列表、车间温度与能耗趋势。这五类数据听起来简单,但每一类背后的数据来源和处理方式都不一样。产量和OEE可以从MES或者PLC里取,报警要单独轮询PLC的诊断缓冲区,能耗则要看电表走的是什么协议。

这个阶段最忌讳的是上来就画原型。我用了两天时间拉着客户的生产主管和设备维护工程师一起过了一遍数据清单,每一条都明确标注“数据从哪个PLC读”“字段类型是什么”“更新频率要求多少”。后来调试期一半的故障定位,靠的都是这张确认过的数据清单。

1.2 “同步”其实是两种完全不同的需求

客户说“要同步显示”,我建议你把这里的“同步”拆开看。第一种是画面复制式的同步,多块屏显示完全一样的画面,比如电视墙播同一个宣传片,这种要的是把一份视频信号分发到多个屏。第二种是数据一致性的同步,每块屏布局可以不同,但展示的底层数据必须是同一份,不能出现左边屏幕产量显示1000、右边屏幕显示999这种尴尬。

现在做的这个项目,两种需求同时存在:拼接墙本身是4块物理屏拼成一个大画面,属于第一种;车间东西两侧的独立大屏,布局不同、展示模块不同,但数据必须和拼接墙完全一致,属于第二种。技术上要把两件事分开处理:拼接墙的拼接由显卡和拼接控制器完成,数据一致性则由一套统一的数据推送机制解决。

如果需求阶段不把这个区别逼出来,后面十有八九会选错方案。我们当时跟供应商确认过拼接墙支持直连模式,才敢放心走“多个浏览器窗口 + 同一套数据服务”的路线。

1.3 需求阶段必须问清的三个问题

接这类项目,我固定要问客户三个问题,每个问题都能直接淘汰掉一批错误方案。

第一,数据从哪来。有没有现成的MES数据库、上位机组态软件,还是数据都躺在PLC里?如果有PLC点位表,点位表是不是最新的、有没有人维护?很多老工厂的设备是不同年代买的,有的西门子、有的三菱、有的走Modbus,没有一份统一的点位表,后面联调就是灾难。

第二,谁会看、看什么。产线班组长盯着的是当班产量和报警,车间主任看重的是OEE和设备利用率,厂长可能更关心整体趋势。视角不同,大屏上的信息层级和布局重心就完全不同。这个项目我们把产量和OEE放在第一屏最显眼的位置,报警列表放在右下角,就是这个逻辑。

第三,断电断网之后怎么办。工厂晚上断电、节假日关机非常正常,客户希望第二天上班一启动,大屏能自己恢复吗,还是接受人工去点一下?这决定了你要不要做开机自启、看板守护脚本、服务端自动重启这些额外功能。这个项目客户明确要求“开机自己恢复”,后来我在自启脚本上花的时间比写页面还多。

2. 系统架构与技术选型

2.1 数据链路整体设计

我最后落地的链路是这样的:车间设备层(PLC、电表、传感器)→ 采集层(一台Linux工控机上的Python采集脚本)→ 缓存层(Redis)→ 推送层(Node.js WebSocket服务)→ 展示层(各块屏上的Chrome浏览器全屏页面)。

为什么中间要插一层Redis,而不是采集脚本直接推给浏览器?因为采集脚本和展示端是不同生命周期的程序,采集脚本要不停适配PLC协议,展示端要不停改布局和图表,两者耦合在一起,任何一边改动都要重新部署整套系统。用Redis做中转后,采集脚本只负责把数据写进Redis,推送服务只负责定时从Redis读数据并广播给前端,各管一段。另一个原因是调试方便,Redis有现成的可视化管理工具,打开就能看每个键的值在不在变,比对着黑窗口敲命令高效太多。

前端展示层每块屏是一个独立的浏览器窗口,理论上这些窗口可以分布在不同机器上,也可以像这次一样集中在同一台工控机上用显卡多输出。多窗口的好处是某块屏卡死了不会带崩其他屏,重启某一屏只需要关掉那个窗口重新打开。

2.2 PLC数据采集:协议与寄存器细节

设备层里西门子S7-1200居多,我用的是python-snap7这个库直接走S7协议读取,不需要额外买网关硬件。连接参数里有几个容易踩坑的地方:S7-1200的机架号一般是0,槽号一般是1,填错就会连接报错;读取DB块数据用db_read,比如读DB1从偏移0开始的4个字节,返回的是原始字节数组。

西门子的多字节数值默认是大端存储,读到一个DInt类的数据,直接拿struct.unpack('>i', data)解包才对。这个细节我们开始没注意,读出来的产量数值翻了好几倍,绕了好大一圈才发现是字节序问题。之前排查的时候把责任都推给点位表,结果点位表没背这个锅,锅在自己代码里。

老设备里有几台走Modbus TCP,联调时我用网络调试助手先手动发报文验证寄存器地址,再写采集逻辑。Modbus协议里寄存器地址和厂商点位表里的地址经常有偏差,比如点位表写“保持寄存器40001”,实际报文里对应的地址却是0。这类偏差只能现场对着一台运转中的设备反复读、反复看数值变化,不能光看文档。

还有一个经验:核对点位时间最好选在工厂午休或者设备批量开动的时段,因为有些寄存器是设备开机运行才有数值变化,停机时读出来全是0,容易让人误判成地址填错。

2.3 Redis + WebSocket:数据缓存与广播通道

采集脚本把每个点位写进Redis,键名按“产线:设备:属性”的规则设计,例如production:line1:output、equipment:press1:status。推送服务每隔2秒从Redis把所有键聚合成一个JSON快照,然后通过WebSocket广播给所有在线客户端。快照设计成完整状态而不是增量消息,这是一个关键决定——客户端中途断线重连后,下一个推送周期自动就能追平到最新状态,不需要补发历史消息。

WebSocket服务端核心逻辑很简单:

const WebSocket = require('ws'); const { getSnapshot } = require('./snapshot'); const wss = new WebSocket.Server({ port: 8080 }); setInterval(() => { const snapshot = getSnapshot(); const payload = JSON.stringify(snapshot); for (const client of wss.clients) { if (client.readyState === WebSocket.OPEN) { client.send(payload); } } }, 2000);

为什么推送比HTTP轮询更适合多屏同步?因为轮询模式下,每块屏到达服务器的时间点不一样,哪怕数据值相同,屏幕上数字变化的瞬间也会错开,给人“没同步”的感觉。WebSocket是服务端在同一时刻把同一份数据推给所有客户端,各屏拿到的完全一样,同步感会明显好很多。要细究的话,各客户端渲染还需要一点时间差,但局域网内这个差值在毫秒级,肉眼分辨不出来。

2.4 可视化前端:Vue + ECharts 搭配细节

前端选了Vue 3做框架,ECharts 5做图表。大屏页面的边框、标题栏这些装饰性组件不用完全自己写,DataV的开源组件库基本够用,能省不少时间。页面按1920×1080设计,每块屏独立一个页面,页面内部用Grid布局按比例划分模块,不要直接做一张3840×2160的大图再整体缩放,那样字体和曲线会发虚。

刷新性能上有两个细节值得单独说。一是动态图表的setOption要传true作为第二个参数,也就是notMerge模式,避免旧数据和残留动画造成闪烁;二是实时刷新的图表建议直接关掉入场动画,不关的话每隔2秒图表重播一次动画,整面墙看起来像在不停抽动,长时间盯屏眼睛很难受。折线图、柱状图这类高频更新的图,我统一设置animation: false,只在页面初始加载时让它动一次。

拼接屏还有一个布局上的经验:不要设计横跨两块物理屏的图表。拼接墙中间有一道物理缝隙,折线图或柱状图从缝隙穿过去,画面会被生生切断,非常难看。我最后所有图表都严格落在单块屏的范围内,宁可多分几个模块,也不做跨屏元素。

3. 多屏同步显示的实现方案

3.1 先分清数据同步还是画面同步

上一节说的两种“同步”,在技术实现上分道扬镳。数据同步讲究的是各屏拿到的值一致、刷新节奏一致,核心靠一个“中央消息源”广播给所有人;画面同步讲究的是像素级一致,要么用HDMI分配器把一份画面复制到多个显示器,要么用视频流让所有终端看同一路画面。

做方案的时候先回答一个问题:如果一块屏上的产量数字变了,其他屏上的数字要不要在同一瞬间变?答案如果是“要”,那你需要的是数据同步;如果客户说“我只要大家看到的内容一模一样就行,管他什么数据”,那可能视频流或HDMI分配器更省事。这里最怕的是把需求理解反,前期辛辛苦苦做了数据推送,最后客户说我要的是所有屏都播同一个3D车间漫游画面,那就得返工。

我在这个项目里也跟客户确认过:拼接墙的画面是各屏显示不同模块组成整体,还是所有屏显示同一个数字大样?最终确认是前者,所以拼接墙走“独立浏览器 + 独立页面布局”,整体视觉上像一整面墙,但每块屏的页面内容是独立的。

3.2 方案A:WebSocket广播加本地渲染

刚才提到推送服务,对应到前端就是每块屏的浏览器连同一个WebSocket地址,收到快照后各自渲染自己的图表。这是本项目的主方案。前端核心逻辑只有几行:

const ws = new WebSocket('ws://192.168.1.200:8080/ws'); ws.onmessage = (event) => { const snapshot = JSON.parse(event.data); outputChart.setOption({ series: [{ data: [snapshot.production.line1.output] }] }, true); oeeChart.setOption({ series: [{ data: [{ value: snapshot.oee.line1 }] }] }, true); };

因为服务端广播的是完整快照,所以所有客户端拿到的数据永远来自同一个来源、同一个时间点。前端要做的只是把快照里的值“塞”进各自对应的图表里。布局上东侧屏可以只展示产量和报警,西侧屏只展示OEE和能耗,拼接墙展示全部,但底层用的都是同一个snapshot对象,数据天然一致。

前端还要写心跳和断线重连。车间环境会断电,第二天早上的开机顺序可能是屏幕先亮、服务器后起,如果浏览器页面不主动重连,客户就只能看到一块白屏。重连逻辑建议用指数退避:第一次1秒后重试,第二次2秒,第三次4秒,最多到30秒,避免服务器还没恢复时几十个客户端疯狂重连把它砸垮。

3.3 方案B:视频流整屏推流

视频流方案适合什么场景?当显示终端是智能电视、专用播放盒,没法跑网页的时候;或者画面必须逐像素一致,比如同一个3D效果图、一段宣传片。技术上可以用FFmpeg采集一个页面,再通过WebRTC或HLS推给所有屏,所有屏播放同一路流,天然同步。

我这次没有选视频流。第一,数据看板的指标是要秒级刷新,视频流再怎么优化也有几百毫秒编码传输延迟,产量数字会“慢半拍”;第二,视频流的运维复杂度明显更高,编码器挂了、码率不够花屏、客户端解码卡顿,哪一个都会变成新的故障点。对工厂数据看板这种场景,网页本地渲染永远是我的第一选择,视频流只作为无法部署网页环境时的兜底方案。

3.4 时间校准:NTP配置与验证

数据同步解决了,还有一个隐藏问题:看板页面上要显示“当前时间”,还要给报警列表打时间戳,如果各台客户端机器的系统时间漂移了几秒钟,那不同屏上显示的“现在时间”就对不上,拍验收照片时非常尴尬。

解决方案是局域网里搭一台NTP服务器,直接复用那台Linux采集服务器,装chrony并开放ntp服务。Windows端用命令手动校时:

w32tm /config /manualpeerlist:"192.168.1.200,0x1" /syncfromflags:manual /update w32tm /resync

校时之后,再用计划任务每小时执行一次w32tm /resync,防止系统时间缓慢漂移。验证方法很简单:做一个只显示当前时间的页面,同时在四块屏上打开,拍照对比,NTP配置正常的话各屏时间差在几十毫秒,肉眼完全看不出差别。

3.5 刷新频率与数据量的参数核算

客户问“你们这个看板多久刷新一次”,不能只回一句“实时”,现场验收时得有工程依据。我按这个项目实际规模算过一笔账:

活动点位大概200个,聚合成一个JSON快照大约10到15KB,每2秒广播一次,单个客户端平均带宽约7.5KB/s。现场最多8个显示终端,总带宽60KB/s左右,千兆局域网的负载连1%都不到。瓶颈完全不在带宽,而在前端渲染效率和服务端聚合快照耗时,这两项在2秒周期下也都是毫秒级操作,整条链路余量非常充足。

刷新间隔我定的是2秒。为什么不设1秒?因为工厂里多数统计指标(产量、OEE)在秒级的变化量本身就很小,1秒刷新只是白白增加渲染压力,还容易造成视觉疲劳。设备状态这类实时性敏感的数据可以单独走1秒通道,趋势类统计算5秒一次。把“哪些数据按什么频率刷新”在前期就定清楚,后面做前端时不会乱。

4. 调试落地的完整流程

4.1 第一轮:先把单屏的数据链路跑通

调试顺序很重要,不要一上来就把四面屏全点亮,出了错都不知道该查哪一段。我的做法是先拿一台笔记本连到局域网,单独打开一块屏的页面,把“PLC → Redis → WebSocket → 前端图表”全链路跑通,再做多屏并行。

具体步骤是这样:第一步核对PLC点位表,确认每台设备的IP、DB号和偏移地址;第二步用网络调试助手或Modbus Poll手动发报文,验证能读到正确数值;第三步跑采集脚本,打开RedisInsight逐键检查数据是否在更新;第四步打开单屏页面看图表刷新是否正常;第五步记录一次端到端延迟,从PLC读到数值到浏览器显示,实测一般小于1秒,作为后面验收的基线。

这一轮我踩了一个很典型的坑:snap7连不上西门子PLC,报错信息看起来像网络问题,我甚至用ping和端口扫描查了半天,后来翻示例代码才发现是机架号和槽号填错,S7-1200要填0, 1。说起来都是细节,但现场调试时这种低级问题最容易卡住进度,因为你不会第一时间往“参数填错”这个方向想。

4.2 第二轮:多屏并行与误差测试

单屏链路通了以后,把所有显示终端接进局域网,登录同一套看板页面。这一步要把所有客户端的系统时间先校一遍,然后做“数据同步误差测试”:把数据源停掉,让画面停在最后一个快照上,然后同时看各屏显示的值是否完全一致。如果所有屏都停了同一个数,说明数据同步是通的。

接下来验证刷新节奏。手机拍两张相隔2秒的照片,对比各屏上的产量数字变化是否在同一节奏上。如果发现明显错位,第一嫌疑是某台机器的CPU或GPU占用过高,导致浏览器主线程卡顿,消息收到但渲染慢了;第二嫌疑是浏览器后台限频,Chrome在非活动标签页会限制定时器和动画帧率。大屏机器不应该让Chrome长时间处于后台或未聚焦状态,要用全屏Kiosk模式跑,并且把相关限频策略关掉。

4.3 第三轮:72小时稳定性验证

工厂大屏是7×24小时运行的,不能调好当天没问题就上线。我给自己定了一个硬性标准:连续运行72小时,无白屏、无自动退出、无内存持续暴涨,才算通过验收。

稳定性验证要重点盯三件事。第一件是浏览器内存曲线,在页面里嵌入一个调试信息条,用performance.memory实时显示内存占用,连续观察是否持续增长。ECharts图表实例如果反复创建不销毁,内存会像温水煮青蛙一样涨上去,一两周之后浏览器必然卡死。第二个是设备重启恢复,我模拟过同时断电服务器和一台客户端,再按顺序恢复供电,验证开机自启和WebSocket自动重连是否正常。第三是WebSocket连接状态,如果方案用的是HTTP轮询,服务器端会积累大量TIME_WAIT连接,时间长了端口资源耗尽,新连接就进不来;WebSocket长连接方案也要通过netstat定期观察连接数,确认没有异常堆积。

服务端我配了systemd服务,设置Restart=always,不管进程怎么退出都会自动拉起。客户端开机自启用一个批处理脚本,延迟30秒等待网络和服务器就绪,再用Chrome的Kiosk模式打开看板URL放全屏,批处理放到Windows启动文件夹里,基本能做到无人干预自动恢复。

4.4 分辨率、拼接与信号线适配

拼接墙部分,工控机上插了两块显卡,共4个HDMI输出,Windows下设置成扩展桌面。关键点是每个浏览器窗口独立全屏到对应的那一块屏上,而不是把浏览器窗口直接拖成4K大小跨四块屏。窗口跨屏后,拼接缝会挡在页面正中间,布局怎么排都是歪的。独立窗口各自全屏后,再通过拼接控制器做边缘校正,视觉上才是一整面墙。

信号线这块是个隐藏大坑。超过10米的HDMI普通铜芯线,经常出现闪屏、黑屏、分辨率自动降低的问题。我们现场有一根12米的线,第一天验收时第三块屏间歇性黑屏,换线之前排查了显卡驱动、拼接控制器、分辨率设置一堆东西,最后问题落在线上。后来超过10米统统换成光纤HDMI线,或者走HDMI转Cat6的延长器,再没出过闪屏问题。线材成本多不了多少,但能省掉大量排查时间。

5. 常见问题排查实录与工具清单

5.1 高频故障速查表

把现场踩过的坑整理成表格,方便后续维护人员快速定位:

故障现象常见原因排查与解法
各屏右上角时间不一致系统时间漂移,未配置NTP统一NTP服务器,每小时强制校时
数据刷新节奏参差不齐某台机器CPU/GPU占用高,浏览器被限频查看任务管理器,关掉无用程序,用Kiosk模式跑
某块屏白屏或黑屏显卡输出异常,HDMI线过长或接触不良换光纤HDMI线,检查显卡多屏输出设置
PLC数据全部为0访问权限未开放,机架槽号参数错误用snap7单独连接测试,检查DB号和偏移
寄存器数值明显偏大字节序不对,西门子大端数据按小端解了用struct.unpack('>i')大端解包
图表刷新出现残影setOption未用notMerge模式,动画重复播放统一加true参数,实时图表关闭动画
浏览器内存持续增长ECharts实例反复创建不销毁用单例管理图表实例,无用实例及时dispose
重启后看板没自动出现自启脚本失败或者时序太早脚本延时30秒,加日志确认执行状态

5.2 几个印象深刻的故障复盘

第一个是字节序。读西门子S7-1200的DInt变量,用Python的struct.unpack('<i')去解,产量数值翻了好几倍。排查的时候一开始怀疑是点位表给错地址,后来把原始字节打出来一看,发现高字节在前,改成'>i'就正常了。这个故障的教训是:拿到PLC原始数据后,第一步先看字节序和数据类型,别急着套业务逻辑。

第二个是DB偏移地址。点位表上写“DB1.DBX0.0”,指的是DB1里的一个布尔位,我一开始按字节整块去读,读出来的数据串位了。后来逐点位核对,发现有的点位是字对齐、有的是位对齐,必须严格按点位表给的偏移和类型来读。这也是为什么我之前坚持要先拿到一份最新的点位表,没有点位表硬调试,效率会低很多。

第三个是拼接控制器和显卡驱动打架。2×2拼接墙的控制器默认输出一种跨屏模板,Windows那边识别成一个巨大的虚拟桌面,浏览器窗口全屏后经常跑到错误的位置。最后我在显卡驱动里把四路HDMI输出改成各自独立扩展模式,拼接画面由拼接控制器统一处理,两边职责分清之后才稳。这个教训是:拼接墙调试前先确认“拼接的活到底由谁干”,要么显卡拼、要么控制器拼,两边同时开工必然出问题。

5.3 实用的调试设备与工具清单

最后整理一份现场调试常用的工具清单,都是这次项目实际用过的:

  • 网口调试助手 / 网络调试助手:验证Modbus TCP报文、测试WebSocket连接
  • 串口调试助手:处理RS485/RS232协议的PLC网关和电表
  • Modbus Poll:模拟Modbus主站,逐个验证从站寄存器地址和数值
  • Wireshark:抓包看TCP重传和WebSocket帧,排网络层问题特别好用
  • RedisInsight:Redis可视化管理工具,逐个键查看实时数据,比命令行直观太多
  • python-snap7:西门子S7系列PLC的存取库,配合struct处理字节序
  • Chrome Kiosk模式:全屏无地址栏跑看板,防止员工误操作
  • 计划任务 + 批处理:客户端开机自启,配合延时等待网络就绪

做这类工厂大屏项目,从需求调研到验收来回磨的时间往往比写代码还长,但真正让客户认可你的,反而不是图表做得多炫,而是“连续开了一星期不出问题”。多屏同步的功夫全在看不见的地方——数据源可靠性、重连机制、时钟校准、线材质量。我在这项目里把每个环节都过了一遍之后,再去接类似需求,心里基本有了一张明确的清单:先问同步含义,再定数据链路,最后抓时钟和重连。希望这份整理也能帮你少踩几个我踩过的坑。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询