简介:HMIMaker是一套面向易显科技工控机与VGA工控板的上位机人机界面组态软件,服务于工业自动化、嵌入式显示与设备监控场景。它以“所见即所得”的拖拽式设计,配合按钮、位开关、字符控件、实时曲线、动态图片等图形化元素,让开发者无需编写界面代码即可快速搭建交互面板,并通过串口以简单指令与单片机或PLC通信,适合追求快速交付显示界面的软硬件工程师。
压缩包内含1个doc格式的技术文档,容量903KB,便于查阅与离线使用。文档围绕软件核心能力展开,包括可视化界面组态、通信协议开放性、实时数据库、可编程命令语言、权限安全机制,以及离线/在线模拟与仿真功能,内容结构完整,可作为人机界面开发选型与设计参考。
该资源已有1515人浏览学习,文档提炼了HMIMaker的功能模块,对实时监控、历史/实时曲线、报表、报警等典型应用做了说明,能帮助读者快速评估软件与自身工控项目的匹配度,缩短人机界面的调试与开发周期。
1. 别把组态软件当画图工具:HMIMaker真正的工作量在下游
第一次接触上位机人机界面组态软件HMIMaker时,我的第一反应是:这不就是工业版画图工具吗。直到用它在一条小产线上做设备监控,我才发现自己低估了两件事:一是画面拖拽只占整个项目的一成工作量,二是真正决定项目成败的变量命名、通信调试和数据落库,几乎都在画面之外。HMIMaker这一类组态软件,解决的核心问题是让画面控件和现场数据实时联动:你拖一个按钮上去,它要能真正写PLC寄存器;你放一个曲线图,它要能持续拿到现场数据并画出来。适合做设备监控、产线看板、能源管理这类需要频繁调整画面的项目,尤其适合没有专职上位机开发团队的小团队。如果你只是要一张静态展示图,别上组态软件;如果你要让操作员通过屏幕去控制现场设备,那这篇文章就值得读完。
2. 拆开HMIMaker的三层模型:画面、变量和通信驱动之间是什么关系
我一般会先让新手把组态软件想成三个独立的层,而不是一个整体。UI层只管画和显示,变量层负责数据中转,驱动层负责跟设备说话。这样在后面遇到通信问题时才不至于去画面里找原因。很多现场问题其实是驱动参数没调好,但因为你把按钮和PLC寄存器直接理解成了一根硬线,就会下意识怀疑是画面坏了。
2.1 一个画面背后是三层结构:UI、变量、驱动各司其职
UI层包含的是你看到的按钮、指示灯、数值框、趋势图。这些控件不直接读PLC地址,而是绑定一个变量名。变量层是工程的数据字典,每个变量有地址、数据类型、读写属性和轮询周期。驱动层根据变量表生成轮询请求,把设备寄存器变化映射到变量值,再触发UI刷新。以启动按钮为例:操作员按下按钮,UI层把变量“reactor_start”置为true,驱动层收到变量变更后写PLC地址;PLC返回后,驱动层把“reactor_running”刷新为true,指示灯变色。如果以后换了PLC,通常只要换驱动配置和地址映射,画面可以原样保留,这就是三层模型的直接收益。
理解这个模型还有一个隐藏价值:变量是全局唯一的。同一个变量被多个画面引用,值只有一个,修改一处,所有引用的位置同时变。所以变量表里的命名规范比画面美观更重要。我见过一个项目,画面上显示温度和实际PLC温度差了10倍,原因是变量表里有两处“temp1”,一个挂在模拟量输入,一个挂在测试脚本里,画面绑定到了后者。这种问题在单层思维下很难发现,但如果先查变量表,五分钟就能定位。
2.2 组态软件和直接写上位机程序,本质区别是声明式配置
组态软件把“画面长什么样”和“什么时候刷新”配置化,运行引擎负责解释执行。直接写C#上位机,你得自己维护线程、委托、事件订阅;而组态软件把这部分封装进引擎,代价是复杂的算法流程和特殊交互逻辑反而难做。我的判断标准是:如果项目80%的工作是“信号-显示-操作-记录”,用组态软件效率高;如果里面有大量图像处理或自定义算法逻辑,还是老老实实写代码。做PID整定画面,组态软件要用脚本实现;用Python从零搭一个,可以直接拿现成的科学计算库,融合度高得多。所以选型不是看名气,而是看控制逻辑是不是集中在画面交互上。
这个判断直接影响你对HMIMaker这类软件的使用深度。如果你只是拿它做静态数据展示,那它和一张网页没有区别;如果你要做工艺流程图、设备联动、报警追忆,那它才真正体现价值。我见过有人用组态软件硬写一套排产算法,最后每次升级都痛苦不堪,因为组态软件不是为复杂业务逻辑设计的。反过来,也有人把简单画面用纯代码写,一个按钮点击事件牵出十几个委托,维护成本也高。两者本质上是不同工具在不同复杂度区间的分工。
2.3 选型时我用四个指标测HMIMaker这类组态软件
我一般会在安装后半小时内做一个小测试,而不是只看宣传页。先建一个2000变量的测试工程,跑5分钟,观察页面切换是否卡顿;如果切换时白屏超过半秒,就要警惕。然后看工程文件格式,优先选文本格式,方便用Git做版本管理;二进制格式的工程文件在合并和差异对比时非常痛苦。再看驱动开放度:除了自带驱动,是否支持自定义协议插件,很多非标设备要你在串口报文上做文章,没有插件意味着只能等官方驱动或者放弃。最后看运行环境兼容性:老产线工控机经常是嵌入式Windows系统,缺运行库、分辨率也低,最好拿目标设备跑一遍画面。把这四个指标列成一张表,选型时逐个打分,比我当时靠朋友推荐靠谱得多。
| 指标 | 测试方式 | 及格线 |
|---|---|---|
| 变量容量 | 建2000点变量并切换页面 | 切换无白屏、不卡顿 |
| 工程文件格式 | 保存后查看工程目录 | 画面和变量表至少是文本文件 |
| 驱动开放性 | 在文档里找协议插件入口 | 支持加载自定义脚本或扩展模块 |
| 运行环境兼容性 | 在目标工控机上运行 | 半小时无崩溃、无缺库报错 |
这里要提醒一句:变量容量这个指标,不要只看宣传册上的上限。实际瓶颈往往不是变量数量,而是页面同时刷新时绘制引擎的吞吐能力。所以测试时一定要模拟真实操作:打开多个画面来回切换,同时让十几个变量快速变化,观察画面帧率。很多组态软件小变量跑得很顺,一到同时变化的大数据量就卡成幻灯片。这也是我在选型时坚持做实测而不是看参数表的原因。
3. 用HMIMaker起一个最小可运行工程:目录、画面、变量和模拟调试顺序
很多人第一次打开组态软件直接拖控件,拖完才发现没建变量表,又回去补。我现在的习惯永远是先建工程目录、再变量表、后画面。这一章按这个顺序走一遍,不依赖任何硬件,你在自己电脑上就能跑通。顺序乱是新手最容易踩的坑,因为变量表是画面的数据骨架,没有变量表,画面里的控件就只是死图形。
3.1 工程目录和文件类型:先分清哪些能手工改
常见的组态工程目录会把配置和运行时数据分开。以下是一个典型的文本化工程结构,我用它作为新建工程的基准。
my_hmi/ ├── project.json # 工程配置:分辨率、刷新周期、启动画面 ├── screens/ │ └── main.xml # 画面文件:控件布局和图元定义 ├── variables/ │ └── tags.csv # 变量表:名称、地址、数据类型、读写属性 ├── drivers/ │ └── modbus.json # 驱动配置:串口/网络参数、轮询表 ├── scripts/ │ └── startup.py # 启动脚本:变量初始化、登录校验 └── data/ └── history/ # 运行时生成的历史数据目录工程配置文件和画面文件是手工维护的重点,历史数据目录是运行时自动生成的,提交版本时应该忽略。project.json里的分辨率要按目标工控机屏幕设置,不要用开发机的缩放比例。刷新周期一般设200ms,对大多数过程量足够;如果要显示快速变化的流量或频率,再把对应变量的轮询周期单独调到50ms。这个结构最大的好处是:变量表是CSV,可以批量导入导出;画面是XML,可以用脚本生成和校验。
3.2 用XML描述画面:控件布局是嵌套关系而不是散点
HMIMaker这类组态软件的画面文件通常采用嵌套结构描述控件,而不是一堆绝对坐标的散点。父容器负责整体定位,子控件基于父容器做相对偏移。这样后续调整机组位置时,只需要改父容器坐标,不需要动内部每个控件。以下是一个最小的主画面片段。
<screen name="main" width="1280" height="800"> <panel x="20" y="20" width="300" height="200" title="1号机组"> <numericText x="10" y="40" variable="temp_reactor" precision="1" unit="°C"/> <button x="10" y="80" text="启动" variable="reactor_start"/> <indicator x="10" y="120" variable="reactor_running" colorOn="#00FF00"/> </panel> </screen>这个片段里,numericText负责显示数值,button负责写变量,indicator负责根据开关量变色。所有控件都通过variable属性绑定变量,画面本身不关心这个变量是来自PLC还是模拟器。precision定义显示小数位,unit只是显示后缀,不会影响数值计算。把按钮放进panel而不是直接放在screen下,是为了后续复制整个机组时,只需要改panel的坐标和内部变量名。如果你要做一个12个机组的监控画面,这种嵌套方式能让你的复制工作量少一大半。
3.3 变量表与驱动映射:地址写错是最大的坑
变量表是工程的核心,我要求所有变量名必须语义化,并且加前缀区分用途:温度用temp_,压力用press_,设备控制用设备名加动作。这样一来,画面绑定变量时不容易串,脚本里引用也更清晰。一个典型的变量表条目如下。
[ {"name":"temp_reactor","address":"%MW100","dataType":"float","writable":false,"pollRate":200}, {"name":"reactor_start","address":"%MW200","dataType":"bool","writable":true,"pollRate":50} ]这里的%MW100是某系列PLC保持寄存器地址,float表示占两个连续寄存器,bool对应一个位或字地址。writable标记这个变量是否允许画面写入。pollRate是驱动轮询周期,单位毫秒,反应釜温度这种缓变量用200ms足够,启动按钮这类操作信号用50ms是为了让按钮按下去后有更快的反馈。实际项目里我会把write型变量单独列一张表,不许普通画面对其赋值,防止操作员误触发。变量表检查不光是看名字和地址,还要检查数据类型与寄存器长度是否匹配,否则读出来的数值会乱跳。
3.4 模拟运行前先跑一次变量一致性检查
没有PLC时,HMIMaker通常提供模拟运行模式,变量值按设定的初值或规律变化。但在那之前,我会先跑一个变量表检查脚本,把最明显的错误挡在编译之外。这个脚本读tags.csv,检查空变量名、重复地址和非法前缀。
# check_tags.py - 运行前给变量表做一致性检查 import csv, sys names = set() with open("variables/tags.csv", newline="", encoding="utf-8") as f: for row in csv.DictReader(f): if not row["name"]: sys.exit("发现空变量名") if row["name"] in names: sys.exit("重复变量名: " + row["name"]) if not row["address"].startswith("%M"): sys.exit("地址格式不正确: " + row["address"]) names.add(row["name"]) print("变量表检查通过,共", len(names), "个变量")这个脚本重点检查三个地方:变量名不能空,变量名全局不能重复,地址必须符合本项目的设备地址规范。跑完后再开启模拟运行,哪怕没有真实设备,也能验证画面刷新、按钮动作和报警逻辑。模拟运行的价值是让你在进现场前就把画面逻辑调通,而不是等到PLC接线完毕再去改画面。
4. 把现场数据接进HMIMaker:驱动参数、实时曲线与历史落库的取舍
工程跑通后,下一步就是接真实数据。这一章的内容决定画面在车间里到底能不能用,也是组态软件最容易被当成“黑匣子”的地方。很多人担心通信连不上,其实大部分通信问题不是设备坏了,而是驱动参数没有和设备特性匹配。
4.1 通信驱动:轮询周期、超时和从站响应延迟怎么配合
组态软件的通信驱动通常是周期轮询模式:驱动按设定的时间间隔,向PLC发送读取请求,获得响应后再刷新变量。你如果把所有变量都设成50ms轮询,现场PLC会不堪重负。正确的做法是把变量分到不同轮询组,缓变量用200ms,快变量用50ms,报警条件类的开关量夹在中间用100ms。这样既保证实时性,又减轻了总线负载。
| 参数 | 常见值 | 影响 |
|---|---|---|
| 轮询周期 | 50~500ms | 越短越占总线资源,越长画面刷新越慢 |
| 超时 | 500~2000ms | 太短容易误报通信故障,太长会影响操作反馈 |
| 重试次数 | 1~3次 | 重试过多会让故障状态延迟暴露 |
| 从站响应延迟 | 0~50ms | 针对PLC扫描周期长的设备,避免频繁超时 |
我一般先按200ms默认轮询,然后看实际响应时间和总线负载再往下调。如果PLC程序扫描周期是20ms,而你的驱动把轮询周期设成50ms且连续请求多个功能码,从站就会因为来不及响应而偶发超时。第一次遇到这种问题,别去怀疑网线,先用万用表思路检查驱动日志里的响应时间,再看是否需要把读写请求错开。写操作不要和读操作挤在同一个轮询组里,写变量应通过事件触发,而不是周期轮询,否则同样一个按钮可能被重复写入多次。
4.2 实时曲线:环形缓冲区与时间轴压缩
实时曲线是组态软件里最吃性能的控件。一条曲线如果每一个刷新周期都往列表里塞点,半天就会把内存耗尽。我的做法是在数据源和曲线控件之间加一个环形缓冲区,只保留最近一段时间的数据,画图时按界面像素宽度抽样。
# curve_buffer.py - 环形缓冲区,只保留最近6000个点 from collections import deque class CurveBuffer: def __init__(self, size=6000): self.size = size self.data = deque(maxlen=size) def append(self, ts, value): self.data.append((ts, value))deque的maxlen参数收到上限后会自动丢弃最旧的数据,不需要手动清理。6000个点对于常见的5分钟曲线,在200ms采样率下足够用。关键是时间轴压缩:画面宽度只有800像素,而缓冲区里有6000个点,不能直接全画。要按像素宽度等间隔抽样,每列像素取区间内的最大值和最小值,绘制成竖线,这样曲线既不会漏峰,也不会因为点太密而卡顿。这个技巧比我见过很多默认全部绘制的案例要有效得多。
4.3 历史数据落库:缓存队列与批量写入
实时曲线管的是最近几分钟,历史追忆需要保存更长时间。很多组态软件运行一段时间后会越跑越慢,就是因为历史数据落库时机没做对。最稳妥的办法是在内存里维护一个缓存队列,积攒到一定条数或时间间隔后再批量写入磁盘。直接每个周期打开文件写一条,磁盘IO会成为瓶颈,而且可能出现文件句柄泄漏。
# history_flush.py - 每5秒批量写入一次历史缓存 import csv import time cache = [] def collect(ts, tag, value): cache.append((ts, tag, value)) if len(cache) >= 500: flush("history.csv") def flush(file_path): with open(file_path, "a", newline="", encoding="utf-8") as f: writer = csv.writer(f) writer.writerows(cache) cache.clear()这里的500条或5秒阈值需要根据现场数据量调整。数据量大的项目建议单独开一个写盘线程,避免在UI刷新线程里做文件操作。历史目录最好按天分文件,例如history_20250318.csv,方便后续归档和清理。时间戳用设备时间还是本地时间要提前定好,如果后续要做工艺分析和追溯,时间基准不一致会非常痛苦。我一般会同时记录PLC内部时钟和上位机接收时间,两者都存,排查时用来对账。
4.4 报警阈值判断:延迟报警避免抖动误报
报警是组态软件里用户感知最直接的功能,但也是最容易被刷屏的功能。液位在阈值附近波动时,如果每个周期都触发报警,操作员几分钟就会被刷屏淹没。所以报警要加延迟判断,只有连续超过阈值一定时间后才触发。常见做法是设置一个延迟时间,比如液位高于80%保持5秒后报警。这段延迟让瞬时的干扰不影响判断,也让真正的故障不会被漏掉。报警配置里还要考虑恢复阈值和恢复时间,恢复阈值最好和报警阈值拉开一点滞回区间,否则设备会在报警点附近反复报警、恢复、再报警。
5. 组态画面上线前避坑:5个高频翻车点与排查清单
我把自己做过的项目里翻车最频繁的五个场景列出来,每条都按现象、原因、解决来写。很多都是小问题,但定位成本高。你先看现象,再对着找原因,就能省半天。这些坑没有玄学,背后都是运行时机制和工程组织方式的边界问题。
5.1 画面周期性闪烁、CPU占用高
现象:切换画面后没问题,但运行几分钟开始闪烁,任务管理器里CPU占到30%以上。原因:某个数值框绑定了变量,刷新周期设为20ms,而画面自身绘制周期是50ms,刷新请求把绘制队列塞满,造成闪烁。解决:把刷新周期改到100ms以上,开启画面双缓冲,不要在一个刷新回调里同时修改多个控件属性。我通常把变量分组:模拟量200ms、开关量100ms,而不是所有变量同一速度。这个坑在工控机性能偏弱时会更明显,因为老CPU的绘制能力跟不上高频刷新。
5.2 修改变量名后工程里还有旧引用
现象:在变量表里把“temp1”改名为“temp_reactor”,运行后提示变量不存在,报错指向画面文件。原因:变量表是独立文件,画面控件绑定的变量名不会自动跟着改,尤其脚本里的字符串引用完全靠人工维护。解决:把画面和脚本文件统一用文本格式保存,用文本搜索做全工程替换;再用一致性校验脚本扫描画面XML里的variable字段和变量表做差集,把找不到的变量名一次性列出来。这一步我会放在每次发布前,比肉眼检查可靠得多。以前我觉得改变量名是小事,直到有一次漏改了一处脚本里的引用,导致设备在自动模式下没有按预期停机。
5.3 Modbus通信偶发超时,按钮按下去没反应
现象:通信状态灯偶尔变红,读取数据时有时无,点击启动按钮偶发无效,过几秒又恢复。原因:驱动轮询周期设成50ms,而PLC通信任务扫描周期是20ms,多个功能码串行请求时从站来不及响应,触发超时。解决:把轮询周期放宽到200ms,设置从站响应延迟为10ms,超时改成1000ms,并把写操作与读轮询分开。这类问题往往不是硬件故障,先把驱动参数调宽再看。如果你用示波器看总线报文,会发现大量重试报文挤在一起,这不是设备坏了,是请求频率超出了从站的处理能力。
5.4 换高分屏后画面控件错位
现象:在开发机上正常,换到目标工控机的1920x1080高分屏后按钮位置溢出、文字变大。原因:工程按1024x768固定坐标开发,没有启用整体缩放,系统DPI缩放叠加了画面缩放。解决:在工程配置里设置基准分辨率和缩放策略,画面容器使用相对坐标,字体用固定字号并关闭系统DPI缩放。现在我先确认目标屏幕分辨率再建工程,而不是开发完再适配。如果项目里既有老触摸屏又有新显示器,就按最小屏幕设计,再允许等比放大,绝不能按开发机分辨率定版。
5.5 连续运行几天后内存缓慢增长
现象:开机时占用200MB,三天后到1.5GB,页面切换越来越卡。原因:报警列表或历史曲线缓存只加不删,通常是列表控件绑定了数组但没有限制行数,或者历史缓存队列在收到新数据后没有清理。解决:给报警列表加最大行数,超过自动删除旧行;历史曲线用环形缓冲;数据落库改成每5秒批量写入,而不是每个周期打开文件。定位这类问题,我会用系统自带性能监视器看进程私有字节,如果只增不减,优先查缓存类控件和脚本里用append创建的列表。这个坑最隐蔽,因为平时测试不会连续跑三天,建议上线前做一次24小时老化测试。
6. 进阶用法:文本化工程配置,用脚本批量生成重复画面
如果你在做的项目里有几十个非常相似的机组画面,比如水泵、搅拌器、反应釜,我的建议是不要让组态软件里的复制粘贴成为主力。复制粘贴的问题在于漏改变量名和坐标错位,肉眼很难发现。我把这类重复画面做成文本模板,再用一份设备清单批量生成对应的画面文件。这样每次新增一台设备,只需要往清单里加一行,再跑一下脚本。
# generate_pump_screens.py - 从设备清单批量生成机组画面 import csv def gen_screen(pump_id, tag): return f'''<screen name="pump_{pump_id}" width="1024" height="600"> <numericText x="20" y="30" variable="{tag}_pressure" precision="2" unit="MPa"/> <button x="20" y="80" text="启动" variable="{tag}_start"/> <indicator x="160" y="80" variable="{tag}_running" colorOn="#00FF00"/> </screen>''' with open("device_list.csv", newline="", encoding="utf-8") as f: rows = list(csv.DictReader(f)) for row in rows: with open(f"screens/pump_{row['id']}.xml", "w", encoding="utf-8") as fp: fp.write(gen_screen(row["id"], row["tag"])) print(f"已生成 {len(rows)} 个画面文件")脚本生成的画面文件直接放进组态工程目录,刷新后就能被识别。设备清单里只需要维护id和tag,tag是变量前缀,脚本会自动拼出pressure、start、running这些标准变量名。生成完之后,再跑一次变量一致性校验,检查画面引用的变量是否都在变量表里。这套方法我第一次是赶进度时被逼出来的,当时一个项目有40个相同回路,手动改到一半开始漏变量,后来用模板生成重做,两小时搞定,还顺手做了变量校验。以后再遇到重复画面,我都会先问数据清单从哪来,而不是直接复制。希望帮到你。
本文还有配套的精品资源,点击获取