开源HMI设计工具选型指南:从PLC监控到SCADA上位机实战 上个月我帮一个朋友选型一套小型产线的上位机方案他最初的念头是买一套现成的商业组态软件。结果去打听了一圈授权费用、培训成本、后期维护加起来直接劝退而且很多功能他根本用不上。后来我们挤出两个晚上把几款主流开源HMI设计工具拉出来对比又花了一天半把一套PLC监控页面上线预算几乎归零。这篇文章就是从那两天半的实战里整理出来的重点围绕工业自动化、SCADA上位机、PLC监控场景聊聊开源HMI设计工具到底该怎么选、怎么用。先给个忠告开源HMI工具不是商业软件的简单替代品它是另一条技术路线。选得好两三天就能上线选不好后面改架构的痛苦会远超你的想象。下面我按选型前思考、方案盘点、实操Demo、通信原理、踩坑记录这个顺序讲最后附上我的个人判断。1. 为什么越来越多的工控项目开始放弃商业组态软件1.1 商业HMI软件的核心痛点做自动化项目的工程师多少都碰过这几类商业组态软件。它们功能确实全组态画图、变量管理、报警、历史曲线、配方管理什么都有。但问题也集中在几点授权成本高按点位收费是行业惯例有些软件连开发环境都要单独买授权。一个中型项目大几百上千点的授权费动辄几万。绑定硬件和操作系统很多授权锁定加密狗或网卡换电脑要重新激活运行环境以Windows为主想在Linux工控机上跑非常费劲。扩展性差想给页面加一个非标准控件想接一个私有协议想去掉某个用不到的模块基本都做不到。文档与社区封闭遇到问题大多数时候只能找原厂客服。而原厂客服对中小项目的响应速度经历过的人都懂。所以这几年我越来越倾向于把开源方案放进候选清单里不是在抠成本而是它确实解决了很多工程现场的实际麻烦。1.2 开源方案为什么能站住脚过去大家觉得开源HMI不靠谱主要是早期项目功能太弱、文档太少、界面简陋。但最近五年发生了几个变化Web技术成熟浏览器端SVG组态、WebSocket实时推送、WebGL图表渲染让开源HMI在画面表现力上已经不输传统组态软件。工业通信协议走向标准化Modbus TCP/RTU、OPC UA这些跨厂商协议被越来越广泛地支持开源项目只要支持这些协议就等于打通了市面上绝大多数PLC和仪表。部署方式变轻Docker等容器技术普及之后开源SCADA平台从“搭建一堆依赖”变成了“一条命令启动”实施成本大幅下降。生态互补有采集工具、有数据库、有可视化前端每个环节都有优秀的开源项目可选大家可以像搭积木一样组合出适合自己方案的链路。当然开源不是免费午餐。你已经支付的成本是“学习时间”和“自己维护的精力”这两个我在后面的选型清单里会详细展开。2. 选型前的硬核思考你的HMI到底需要哪几层能力2.1 不要把“画面上有几个仪表盘”当成HMI经常有人拿着Grafana做的漂亮大屏来问“这是不是就是开源HMI”严格来说不是。HMI或者SCADA系统至少包含五层能力采集层从PLC、仪表、传感器读取数据按指定周期轮询支持断线重连。数据映射层把协议侧的寄存器地址、数据块偏移映射成工程意义上的“变量”比如“1号罐温度”“水泵转速”还要解决字节序、数据类型转换问题。画面组态层把图元、按钮、文本框和变量绑定起来支持颜色变化、动画、数值显示、滑动条、趋势图。报警与事件层设置上下限、报警确认、恢复判断、报警记录。历史存储层把数据存进数据库支持按时间段查询和曲线回放。一个项目如果只需要第1、3层那很多轻量工具就够了如果五层都要就不能只看“能画图”否则做到中途会发现报警或者历史记录缺失非常难受。2.2 选型前必须明确的技术约束清单我建议你在选型前先把下面这张表填完再开始比工具。这能帮你过滤掉80%不合适的选项。问题为什么重要点位数量有多少50、200、还是2000以上点位直接影响采集轮询策略、数据库选型、页面加载性能数据刷新速度要求多快1秒、500毫秒、还是毫秒级刷新周期太短会压垮设备网口太长则失去实时监控意义现场用到哪些PLC或仪表不同协议支持程度差异巨大私有协议往往需要额外网关或库需要Web远程访问吗需要浏览器访问就优先Web组态型不需要可以选桌面型历史数据要存多久存多少点决定用SQLite、MySQL还是时序数据库报警需要怎么处理只要闪烁还是要确认、要记录简版报警和工业级报警实现成本差别很大开发团队熟悉什么语言Python、Node.js、Java、C#团队适合的工具完全不同有没有人专职维护这个系统没人维护就选社区活跃、常规部署简单的项目比如20个点位的水泵房监控和2000个点位的车间SCADA前者用轻量Web组态加SQLite就够后者必须考虑分布式采集、历史归档、性能优化甚至考虑是否要引入时序数据库。把这些问题想清楚再去选型才能少走弯路。3. 主流开源HMI/SCADA方案盘点它们到底是什么定位3.1 轻量Web组态型出自带编辑器的场景这一类的代表是FUXA一个基于Web的HMI/SCADA项目自带网页版组态编辑器支持拖拽图元、绑定变量、设置报警、画趋势曲线。它对中小型项目非常友好部署后用户在浏览器里打开IP地址就能开发画面不需要装任何客户端。它的数据接入能力也比较全支持Modbus TCP/RTU、MQTT、OPC UA还有西门子S7等其他协议。我实测下来一个200点以内的项目单台服务器跑得很稳。如果你做的是泵站、污水处理、小型产线监控这类需求可以把它列为第一候选。3.2 流程编排加仪表盘型灵活但需要拼装这一类以Node-RED为代表。Node-RED本身就是一款基于流的编程工具生态里有大量的Modbus、S7、OPC UA、MQTT节点可以把采集逻辑用节点连线的方式搭出来。它的Dashboard模块又能生成基础仪表盘包括图表、开关、表格等。Node-RED最大的优势是灵活几乎所有协议都能通过安装节点来支持很适合做“采集端”和“中转层”。但它本身不是专业的组态软件画面布局能力偏弱复杂联动逻辑要靠写Function节点完成做大型SCADA画面会比较痛苦。业界最常见的用法是让Node-RED专门负责采集数据然后往MQTT或数据库里扔再让其他可视化工具取数据展示。3.3 数据可视化大屏型只做展示是它的强项提到Grafana工控圈都不陌生。它擅长对接时序数据库比如InfluxDB、Prometheus、SQLite、MySQL做出非常美观的实时曲线、仪表盘、告警面板。但注意Grafana不能直接采集Modbus或者S7数据也没有工业组态里“变量绑定图元”的概念。它不是HMI而是数据可视化平台。如果项目只需要把已经存在数据库里的数据做得好看Grafana足够好如果还要实时控制PLC输出、做复杂报警联动就必须在前面加一道采集层比如Node-RED采集后写库再由Grafana展示。它的告警能力相对简单复杂的分级策略或确认流程做起来不如专业SCADA顺手。3.4 全功能开源SCADA平台型适合中大型项目如果系统规模较大需要完整的SCADA功能就要看Rapid SCADA和SCADA-LTS这一类项目。Rapid SCADA采用C#生态包含服务器、通信模块、Web界面、数据库存储以及大量工业设备驱动模块。适合有一定.NET开发能力的团队部署偏传统学习曲线比轻量工具陡。SCADA-LTS基于Java生态提供数据采集、报警、历史存储、趋势分析等功能内置Web界面支持多用户权限和工业协议。适合熟悉Java技术栈、需要全功能SCADA但不想从零搭建的团队。这类平台的特点是“该有的功能都有”但代价是你得投入时间学习它们的架构、配置文件和数据模型。它们更像是传统商业SCADA的开源对应物。3.5 物联网平台型自带SCADA模块的融合方案最后说一下ThingsBoard。它本来是个物联网设备管理平台后来加入了仪表盘和SCADA能力。它的强项在设备管理、数据入库、报警规则以及REST API和MQTT接入。如果你的“HMI”要对接大量物联网设备又需要远程管理、多租户、设备影子之类的功能ThingsBoard会很合适。不过它偏向IoT体系纯做PLC监控时反而显得重配置流程也复杂中小型纯工控项目要慎重评估。3.6 横向对比一句话记住它们的能力边界方案主要语言/生态数据采集画面组态报警联动历史存储适合场景FUXANode.js/WebModbus、MQTT、OPC UA、S7自带组态编辑器拖拽式支持阈值报警内嵌存储中小型监控页面的快速搭建Node-REDNode.js生态节点丰富协议极多Dashboard较弱需自行写逻辑配合数据库采集中转、系统集成GrafanaGo/Web不能直接采集仪表盘/大屏非常强简单告警配合时序数据库数据可视化、大屏Rapid SCADAC#/.NET内置大量工业驱动Web/桌面组态完整报警体系数据库支持良好中大型全功能SCADASCADA-LTSJava支持Modbus、OPC UA等自带Web界面完整报警体系MySQL/MariaDB中大型SCADA的Java选型ThingsBoardJava/WebIoT协议、MQTT、HTTP仪表盘/SCADA模块规则引擎报警内置数据库IoT设备管理融合场景4. 实操篇用开源方案把一套PLC数据搬进浏览器页面4.1 为什么我拿FUXA做演示我平时做中小型监控项目最常选的组合就是FUXA加Modbus模拟器做验证。原因很简单FUXA部署最简单组态界面就在浏览器里新手上手路径最短。下面的操作能在一天内从零跑通而且步骤可以平移到真实PLC环境。先说明一下我下面提到的“FUXA”你可以理解为一类Web组态型开源HMI设计工具的代表。如果你在选型时看到功能类似的其他项目思路和操作流程基本通用。4.2 第一步部署运行环境FUXA支持Docker部署这是最快的方式。在服务器或工控机上装好Docker之后一条命令就能启动docker run -d --name fuxa \ -p 1881:1881 \ -v fuxa_data:/usr/src/app/data \ FUXA官方镜像名称启动后浏览器访问http://服务器IP:1881就能打开组态开发界面。如果你不用Docker也可以直接用Node.js跑源码但那样需要手动装依赖、配置数据库速度慢一些。我这里强调一个易错细节数据卷要挂载好。因为FUXA的画面、变量配置、报警规则都存在数据目录里容器一删就要靠这个数据卷保留配置。我做项目时习惯把数据目录单独放在一块独立磁盘或NAS上避免工控机系统盘故障时整个项目配置丢失。4.3 第二步准备一个Modbus从站做数据源没有真实PLC时可以用Modbus模拟器充当从站。常见做法是装一个Modbus Slave软件在里面创建一张寄存器表手动改变数值模拟现场工况。也可以写一个Python脚本用pymodbus库启动一个虚拟Modbus TCP从站from pymodbus.server import StartTcpServer from pymodbus.datastore import ModbusSequentialDataBlock, ModbusSlaveContext, ModbusServerContext # 建立一段模拟保持寄存器起始地址从0开始共100个寄存器 slave ModbusSlaveContext( diModbusSequentialDataBlock(0, [False] * 100), coModbusSequentialDataBlock(0, [False] * 100), hrModbusSequentialDataBlock(0, [0] * 100), irModbusSequentialDataBlock(0, [0] * 100), ) context ModbusServerContext(slaves{1: slave}, singleFalse) # 启动Modbus TCP Server默认端口502 StartTcpServer(contextcontext, address(0.0.0.0, 502))运行起来后这台机器就是一台虚拟PLC寄存器地址可以直接映射到FUXA里。这里也要提醒一句Linux下运行绑502端口需要root或对应权限Windows下没这个问题实际项目里PLC的Modbus TCP端口通常也是502。4.4 第三步在FUXA里创建设备与变量打开FUXA页面后先创建设备。设备类型选Modbus TCP填从站IP地址、端口502、轮询周期。轮询周期建议先按1秒设后面根据设备响应速度再调。接着创建变量。这是把寄存器地址变成“工程变量”的关键一步变量名写上“1号罐温度”这种便于辨识的名称。数据地址填寄存器偏移量比如保持寄存器40001对应偏移040002对应偏移1。数据类型按PLC寄存器里的实际存储类型选择常见的有Int16、UInt16、Int32、Float32。这一步非常关键选错数据类型读出来的数就是乱码。访问类型按需选只读或读写。要支持控制按钮、远程启停就选读写。我在实际配置时会把变量在纸质的点位表上先整理好把设备IP、寄存器偏移、数据类型、读写属性全部列清楚再到FUXA里录入。这样看起来慢但能大幅度减少配置错误。动辄几十上百个变量的时候脑子直接记绝对会乱。4.5 第四步拖拽图元绑定变量FUXA的组态界面和传统HMI软件类似左侧是图元库中间是画布右侧是属性面板。把数值显示、开关按钮、图形仪表拖到画布上然后在属性里绑定刚才创建的变量。举个实际例子我需要做一个水泵运行状态页面。拖入一个圆形的“指示灯”图元绑定一个布尔变量用颜色表达式让变量为1时显示绿色为0时显示灰色。拖入一个“数值显示”控件绑定温度变量显示单位保持“℃”。拖入一个“启动”按钮绑定一个读写变量按下时写入1实现远程启动动作。绑定完成后切换到运行模式模拟器里修改寄存器值页面上应该能看到实时变化。这种“数值变化驱动图元变化”的机制就是HMI组态的核心图元只是载体变量才是灵魂。4.6 第五步设置报警与联动报警是SCADA不可或缺的部分。FUXA里可以为变量配置报警规则比如1号罐温度上限60度触发报警后页面上的图元会闪烁并在报警列表里出现记录。这里有几个经验报警值建议设置成“阈值滞回区间”比如高报58度触发、55度恢复避免临界值附近反复报警。报警要配置确认机制操作人员点击“确认”后闪烁变常亮但报警记录仍保留。联动逻辑比如温度超限自动解锁某个按钮依赖变量之间的读写控制必要时可以在浏览器端用少量脚本实现不影响组态流程。4.7 第六步多人访问与权限控制Web组态带来的一个直接好处是现场中控室大屏、办公室办公电脑、车间平板可以同时打开同一个页面不需要安装任何客户端。FUXA支持用户管理可以设置不同权限。我的习惯是查看权限给车间主任、值班人员只读页面。操作权限给当班操作员可以点按钮、改设置参数。管理员权限仅给少数维护人员能修改画面、修改报警规则。权限这个事别偷懒。工业现场一旦有人误操作影响的不只是电脑画面可能是真实设备。系统上线前把权限表理清楚再开通账号。5. Modbus、OPC UA还是私有协议HMI与PLC通信的底层逻辑5.1 寄存器与点位理解HMI通信的第一把钥匙PLC里的数据对HMI来说就是一堆“寄存器”。HMI按协议发出读取请求PLC返回寄存器里的数值HMI再把数值映射到画布图元上。以Modbus为例最常用的是保持寄存器和输入寄存器。保持寄存器可以读写通常用来存放控制参数和输出值输入寄存器一般只读用来读取外部模拟量或设备状态。地址编号在不同场合有不同表示法容易把人绕晕区域常见地址表示协议偏移线圈00001开始0x区离散输入10001开始1x区输入寄存器30001开始3x区保持寄存器40001开始4x区在配置开源HMI时多数工具让你填“偏移量”而不是“40001”这种完整地址。比如保持寄存器40001填偏移040002填偏移1千万不要把40001这个数字直接填进偏移里这是新手最容易犯的错配置完读数全乱。5.2 32位数据与字节序为什么会读到天文数字Modbus寄存器一个只有16位也就是一个寄存器最多放一个16位整数。工业现场的浮点温度、累加计量这种32位数据是占用两个连续寄存器的。这时候就涉及字节序大端模式Motorola顺序高字节在前。小端模式Intel顺序低字节在前。还有字序的区别两个寄存器谁在高位、谁在低位。我遇到过一个典型问题读回来的温度在值域范围内但小数点后面几位完全不对或者一会儿正一会儿负。排查到最后就是设备手册里写的是“低字在前高字在后”而组态这边按“高字在前”解析了。解决办法就是配置变量时留个心眼先把一个已知值写进PLC读过来看解析是否正确。比对出正确字节序后把所有同类变量统一设置。千万别凭记忆猜每台设备的Modbus从站实现都可能不一样。5.3 OPC UA跨厂商互通的时代选择OPC UA的价值在于它不关心你底层用的什么PLC、什么协议。PLC厂商或者第三方网关会把所有数据整理成统一的“节点树”上位机只需要浏览节点、订阅节点值就行。用OPC UA接HMI最大的好处是配置简单不需要关心寄存器偏移、字节序、数据类型映射节点名称和工程变量名几乎一一对应而且OPC UA自带加密鉴权比裸奔的Modbus TCP安全很多。开源HMI工具里对OPC UA支持做得好的方案在设备配置时只要填OPC UA服务端地址和节点ID即可。如果你的现场设备比较多、品牌杂我非常建议先把数据汇到一台OPC UA服务器上再让HMI统一对接。这样HMI只需要处理一种协议维护成本低不少。5.4 西门子私有协议S7通信怎么接西门子PLC除了支持Modbus TCP之外它自己的S7通信协议是私有的。好在开源生态里已经有很成熟的通信库比如snap7C/C、Python、Node.js都有绑定。用开源HMI直接连西门子PLC通常需要提供DB块号、起始偏移、数据类型。这和Modbus逻辑相同只是寻址方式换成了“DB号加偏移”的体系。我用这类方案连过S7-1200和S7-1500读取DB块里的温度、压力、速度等变量都很稳定写操作也支持但要注意PLC侧的访问权限设置CPU属性里必须允许从远程通信访问。这里有个排查技巧如果连接失败先查看通信库自身的报错信息常见的多是“机架号/槽号配置错误”“PLC侧访问保护拒绝”“端口被防火墙拦截”这三类逐个排除比盲目改HMI参数高效得多。6. 我在实际项目里踩过的坑以及对应的处理办法6.1 刷新周期打太高设备网口直接拥堵有一次做项目客户说数据要“越快越好”我把Modbus轮询周期调到了100毫秒结果没跑几分钟PLC的网口CPU占用就飙高了连正常程序下载都变慢。复盘原因HMI每100毫秒对整个寄存器区做一次全量轮询报文数量巨大打爆了PLC响应能力。后来改成“分组轮询”策略把实时性要求高的数据放一个组500毫秒一轮普通数据显示放另一个组2秒一轮变化率小的累计量放5秒一轮。实测下来数据一样跟手PLC负载下降了80%以上。6.2 断线重连机制PLC重启后页面“卡死”模拟器测试一切正常现场跑了几天后有一台PLC断电重启HMI页面从此再也不刷新。检查发现通信库在PLC断线后默认不会自动重连必须手动重启设备服务。解决办法在设备配置里找到通信超时和重试设置把“自动重连”打开同时把重试间隔设得温和一些比如5秒重试一次。最好再写一个看门狗定期检查通信状态长时间断线时在页面上显示醒目的“通讯故障”横幅。开源工具往往默认不会做这件事运维和值班人员又不会去翻服务状态页面上的故障提示非常重要。6.3 报警“抖”临界点反复触发温度在59.9度和60.1度之间来回跳动时一个简单的“温度大于60度报警”就会疯了似的频繁触发和恢复报警记录被刷屏操作员也分不清哪些是真实故障。这就是没有设置“滞回区间”造成的。正确做法是高报触发值设为60度恢复值设为55度低报触发值设为0度恢复值设为5度。这样现场设备的正常波动不会频繁跨越报警边界报警记录一眼看过去只有真正的异常。6.4 历史数据越存越多页面打开越来越慢中小型项目最常见的做法是数据直接存在本机数据库比如SQLite。但运行三个月之后历史表积了大量记录趋势页面的查询开始变慢。这时不能一直加索引硬扛应该考虑数据分级策略实时数据写入内存缓存页面查最近几分钟直接从缓存取。当天数据写入按小时分区的数据表。超过三个月的老数据定期归档减小主表的查询压力。如果项目数据量大也可以把历史库换成时序数据库把查询压力交给专门为时间序列优化的引擎。开源组件之间可以通过MQTT或HTTP接口解耦把“采集、存储、展示”做成三段独立的服务哪一段出问题都不影响另外两段。6.5 浏览器缓存改完配置页面还是老样子Web组态的另一个怪毛病是浏览器缓存。组态画面的JS和图片资源更新后现场大屏的浏览器还是旧版本。我经常在客户现场被问“为什么改了画面没生效”其实按下F12打开开发工具勾选“Disable cache”再刷新页面就正常了。为了减少这种问题我一般会在发布时改一下静态资源的版本号或者文件名让浏览器识别为“新资源”。实操层面开发机永远用无痕窗口访问HMI页面现场机器则挂一个大字提示画面不更新时按CtrlF5强制刷新。6.6 常见问题速查表现象可能原因处置方向全部变量断线IP、端口、网络不通服务未启动先ping再telnet 502端口再查日志单个变量数据异常寄存器偏移填错、数据类型不对对照点位表检查偏移和字节序数据抖动但PLC侧正常轮询周期太短、电磁干扰加大轮询间隔检查屏蔽双绞线接地页面加载慢变量过多、图元复杂、数据库查询慢优化轮询分组图元SVG简化历史分表报警刷屏未设置滞回区间加滞回阈值或改为变化率报警7. 写在最后我的选型经验和一个小彩蛋做了好几个开源HMI项目之后我最大的体会是选型不是找“最好的工具”而是找“最匹配当前团队和项目约束的工具”。20个点的小泵站上一个全功能SCADA平台纯属给自己添麻烦2000个点的车间级项目指望一个轻量组态工具撑全场也不现实。先画点位表、列采集清单、理报警需求再回到这篇文章的对比表里去匹配整个过程会顺畅非常多。最后分享一个小彩蛋用这套Web组态方案调试时我会同时打开浏览器的开发者工具切到Network面板过滤WebSocket连接。页面里所有实时变量的数据推送都走WebSocket刷新频率多少、有没有断线重连在Network面板里看得一清二楚。排查“页面为什么没更新”时这个技巧比在HMI代码里打断点还快。如果你已经在用开源HMI做项目下次遇到莫名其妙的刷新问题先别急着怀疑工具打开这个面板看一眼大概率能直接定位到问题。