用 Qt 打造工业 HMI 水泵监控系统基于 QModbusTcpClient 与 Qt Charts 的零硬件实战指南【免费下载链接】easy-vibe vibe coding 101The first course for AI-native product builders.项目地址: https://gitcode.com/GitHub_Trending/ea/easy-vibe导读本文完整讲解如何从零构建一套工业级HMI人机界面水泵监控系统通过 Qt 官方模块QModbusTcpClient实时读取下位机数据、用 Qt Charts 绘制压力趋势曲线、实现超阈值自动报警并将故障记录持久化到 SQLite 数据库。整个过程在 PC 上用免费的 Modbus Slave 模拟器充当虚拟水泵零硬件、零成本即可复现真实工厂的监控工作流。读完本文你将掌握上位机/下位机通信原理、Modbus TCP 协议实操、Qt 工业界面开发以及借助 AI 编程助手快速完成整套 Qt 应用开发的方法。本文对应仓库文档为 docs/es-es/stage-3/cross-platform/qt-industrial-hmi/index.md属于本仓库跨平台开发实战章节的一部分关于为何工业场景推荐 Qt 桌面应用的选型论证可参见 平台选型章节。第一章认识工业 HMI 与 Qt 开发1.1 什么是上位机与下位机在工业自动化中有两个必须先搞清楚的核心概念上位机与下位机。下位机现场的手脚下位机是直接与物理设备交互的控制器在工厂里通常是一台PLC可编程逻辑控制器或传感器负责读取现场数据温度、压力、流量、液位等控制设备动作启动水泵、关闭阀门、调节转速等按预设逻辑自动执行例如压力超阈值时自动停泵可以把下位机理解为工厂车间里的工人——不需要复杂思考但必须可靠地执行任务。上位机中控室的眼睛和大脑上位机是运行在 PC 或工控机上的监控软件也就是我们今天要构建的HMI人机界面负责实时展示现场数据数字、图表、动画记录历史数据与报警日志让操作员实现远程控制提供数据分析与报表能力可以把上位机理解为工厂的监控中心操作员通过屏幕掌握全厂运行状态。它们如何通信上下位机通过工业通信协议交换数据最常用的就是Modbus——这个诞生于 1979 年的老牌协议因为简单、可靠、几乎所有工业设备都支持至今仍被广泛使用。中控室 车间现场 ┌──────────┐ Modbus 协议 ┌──────────┐ │ 上位机 │ ◄──────────────────► │ 下位机 │ │ (Qt HMI) │ 告诉我当前压力 │ (PLC/传感器)│ │ │ 当前压力 1.20MPa │ │ │ 显示数据 │ │ 读取数据 │ │ 记录日志 │ │ 控制设备 │ │ 触发报警 │ │ 执行保护 │ └──────────┘ └──────────┘1.2 什么是 Modbus 协议Modbus 是工业通信的通用语言它定义了上下位机之间如何对话核心只有两个概念寄存器Register下位机里的数据单元格每个都有地址0、1、2...存一个数字。例如地址0存压力、地址1存温度。读写操作上位机可以读寄存器取数据也可以写寄存器下发控制指令。Modbus 有两种常见变体变体传输层典型场景Modbus RTU串口RS-485/RS-232短距离、设备直连Modbus TCP以太网TCP/IP长距离、网络化通信本教程使用Modbus TCP。因为它是基于网络的上位机应用和下位机模拟器可以运行在同一台机器上无需任何物理接线。1.3 为什么选择 QtQt 是工业软件的一流框架工厂、医院、交通系统里的很多监控界面都是用 Qt 构建的原因很直接优势说明跨平台一套代码可编译到 Windows、Linux 及嵌入式设备内置工业协议支持Qt Serial Bus 原生支持 Modbus无需第三方库图形能力强大Qt Charts 提供专业的实时图表高性能基于 C适合实时数据刷新场景成熟稳定30 余年历史久经工业领域考验在本仓库的平台选型文档中对 Qt 与 Electron、浏览器扩展做了对比Qt 使用 C性能和稳定性好特别适合工业场景工控机普遍运行 WindowsQt 的兼容性很强——这也是本教程推荐 Qt 的原因。1.4 我们要构建什么我们将构建一个水泵监控 HMI 系统模拟真实工厂的水泵压力监控功能说明实时数据读取每秒从下位机读取一次压力压力趋势图折线图展示最近 60 秒压力变化超阈值报警压力超限时弹窗警告 界面变红故障记录所有报警事件入库支持历史查询手动控制一键启/停水泵写下位机寄存器1.5 教程路线图整个流程分为 6 步准备环境与模拟下位机2 分钟安装 Qt 6.5 和 Modbus Slave 模拟器创建 Qt 工程并连接 Modbus3 分钟打通上位机应用与模拟器的通信实现实时读取与显示3 分钟定时读取压力并刷新 UI绘制实时压力趋势图3 分钟用 Qt Charts 画动态折线图实现报警与故障记录3 分钟超阈值报警 SQLite 落库打包部署可选将应用打包成独立可执行程序第二章准备环境与模拟下位机2 分钟2.1 安装 Qt 6.5Qt 提供免费的开源版本足以完成本教程访问 Qt 官方网站下载Qt Online Installer运行安装器登录或注册一个免费 Qt 账号在组件选择中勾选Qt 6.5.x或更新版本Additional Libraries下的Qt Serial BusModbus 支持Additional Libraries下的Qt Charts图表渲染Qt CreatorIDE一般默认勾选点击安装并等待完成提示如果 Qt 已安装但缺少 Serial Bus 或 Charts重新运行 Qt Maintenance Tool 补充组件即可无需重装。2.2 安装 Modbus Slave你的虚拟水泵Modbus Slave 是一款免费的 Modbus 从站模拟器可以在电脑上模拟一个工业设备PLC/传感器让上位机应用有对象可以通信访问 modbustools.com 官网下载 Modbus Slave安装并打开配置连接菜单Connection - Connect选择Modbus TCP/IPIP 地址127.0.0.1本机回环端口502Modbus TCP 默认端口点击OK开始监听设置模拟数据界面会出现一张寄存器表每一行是一个寄存器地址0、1、2...双击地址0的值改成120表示压力 1.20 MPa应用中除以 100双击地址1的值改成350表示温度 35.0°C双击地址2的值改成1水泵状态1运行中0已停止现在 Modbus Slave 就是你的7×24 小时虚拟水泵。保持窗口打开它会持续响应读写请求。动态模拟技巧Modbus Slave 支持自动递增/随机变化。在寄存器值上右键选择 Auto increment 或 Random即可模拟真实的传感器波动。第三章创建 Qt 工程并连接 Modbus3 分钟3.1 新建 Qt 工程打开 Qt Creator新建工程点击File - New Project选择Application (Qt) - Qt Widgets Application工程名设为PumpHMI选择已安装的 Qt 6.5 套件Kit完成创建打开PumpHMI.pro若用 CMake 则是CMakeLists.txt添加关键模块QT core gui widgets serialbus charts sql模块用途serialbus提供QModbusTcpClient用于 Modbus TCP 通信charts提供QChart、QLineSeries用于实时趋势图sql提供QSqlDatabase用于 SQLite 故障记录如果使用 CMake等效配置为find_package(Qt6 REQUIRED COMPONENTS Widgets SerialBus Charts Sql) target_link_libraries(PumpHMI PRIVATE Qt6::Widgets Qt6::SerialBus Qt6::Charts Qt6::Sql)3.2 声明核心成员本仓库教程的核心理念是vibe coding把需求描述清楚让 AI 编程助手Cursor / Trae / Claude Code生成代码。你可以直接向 AI 提出如下需求请帮我编写 mainwindow.h包含水泵监控 HMI 的核心成员 1. QModbusTcpClient 用于 Modbus TCP 通信 2. QTimer 用于定时读取数据 3. QChart QLineSeries 用于实时趋势图 4. QSqlDatabase 用于故障记录存储 5. UI 元素压力标签、状态指示灯、启停按钮、记录表格核心头文件// mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H #include QMainWindow #include QModbusTcpClient #include QModbusDataUnit #include QTimer #include QtCharts #include QSqlDatabase #include QLabel #include QPushButton #include QTableWidget class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent nullptr); ~MainWindow(); private slots: void connectModbus(); // 连接下位机 void readPressure(); // 定时读取压力 void onReadReady(); // 读取回调 void triggerAlarm(float v); // 触发报警 void togglePump(); // 启/停水泵 private: // Modbus 通信 QModbusTcpClient *m_modbusClient nullptr; QTimer *m_pollTimer nullptr; // 实时图表 QChart *m_chart nullptr; QLineSeries *m_series nullptr; QDateTimeAxis *m_axisX nullptr; QValueAxis *m_axisY nullptr; // 数据库 QSqlDatabase m_db; // UI 元素 QLabel *m_pressureLabel nullptr; // 压力显示 QLabel *m_statusLight nullptr; // 状态指示灯 QPushButton *m_pumpButton nullptr; // 启/停按钮 QTableWidget *m_logTable nullptr; // 记录表格 // 报警阈值 float m_alarmThreshold 1.50f; // 超过 1.50 MPa 报警 bool m_pumpRunning false; void setupUI(); void setupDatabase(); void logAlarm(float pressure, const QString message); }; #endif // MAINWINDOW_H3.3 构建 Modbus TCP 连接在mainwindow.cpp中实现连接逻辑// mainwindow.cpp - 连接部分 void MainWindow::connectModbus() { m_modbusClient new QModbusTcpClient(this); // 连接到 Modbus Slave 模拟器 m_modbusClient-setConnectionParameter( QModbusDevice::NetworkPortParameter, 502); m_modbusClient-setConnectionParameter( QModbusDevice::NetworkAddressParameter, 127.0.0.1); m_modbusClient-setTimeout(1000); // 超时 1 秒 m_modbusClient-setNumberOfRetries(3); // 重试 3 次 if (!m_modbusClient-connectDevice()) { statusBar()-showMessage(下位机连接失败, 3000); return; } statusBar()-showMessage(已连接下位机 (127.0.0.1:502), 3000); // 启动定时器每秒读取一次 m_pollTimer new QTimer(this); connect(m_pollTimer, QTimer::timeout, this, MainWindow::readPressure); m_pollTimer-start(1000); // 1000ms 1s }代码要点代码含义QModbusTcpClientQt 内置的 Modbus TCP 客户端与下位机通信NetworkPortParameter, 502连接502端口与 Modbus Slave 配置一致NetworkAddressParameter, 127.0.0.1连接本机回环地址模拟器就在本机运行m_pollTimer-start(1000)每秒调用一次readPressure()3.4 读取压力数据// mainwindow.cpp - 读取部分 void MainWindow::readPressure() { if (!m_modbusClient || m_modbusClient-state() ! QModbusDevice::ConnectedState) return; // 构造读取请求从地址 0 开始读 3 个保持寄存器 QModbusDataUnit readUnit( QModbusDataUnit::HoldingRegisters, // 寄存器类型 0, // 起始地址 3 // 数量 ); // 发送异步读取请求 if (auto *reply m_modbusClient-sendReadRequest(readUnit, 1)) { if (!reply-isFinished()) { connect(reply, QModbusReply::finished, this, MainWindow::onReadReady); } else { delete reply; // 广播请求直接释放 } } } void MainWindow::onReadReady() { auto *reply qobject_castQModbusReply *(sender()); if (!reply) return; if (reply-error() QModbusDevice::NoError) { const QModbusDataUnit unit reply-result(); // 解析数值寄存器值按比例换算成实际单位 float pressure unit.value(0) / 100.0f; // 地址0压力 (MPa) float temperature unit.value(1) / 10.0f; // 地址1温度 (°C) int pumpStatus unit.value(2); // 地址2水泵状态 // 更新 UI m_pressureLabel-setText( QString(%1 MPa).arg(pressure, 0, f, 2)); // 检查报警 if (pressure m_alarmThreshold) { triggerAlarm(pressure); } // 更新趋势图下一章实现 // updateChart(pressure); } else { statusBar()-showMessage( QString(读取失败: %1).arg(reply-errorString()), 2000); } reply-deleteLater(); }Modbus 读取流程readPressure() 由定时器触发 - 构造 QModbusDataUnit读取地址 0-2 - sendReadRequest() 异步发送不阻塞 UI - 下位机返回数据 - onReadReady() 被触发 - 解析寄存器值并刷新 UI这里体现了工业通信的关键设计异步非阻塞。sendReadRequest发出后立即返回UI 线程不会被网络等待卡住数据到达后通过信号槽机制回到onReadReady()处理这正是 Qt 事件循环在实时监控场景下的典型用法。第四章绘制实时压力趋势图3 分钟4.1 初始化图表Qt Charts 提供专业的图表组件同样让 AI 在构造函数中完成初始化请帮我在 MainWindow 构造函数中初始化 Qt Charts 实时折线图 1. 创建 QChart 和 QLineSeries 2. X 轴用 QDateTimeAxis显示最近 60 秒 3. Y 轴用 QValueAxis范围 0-3.0 MPa 4. 线条蓝色宽度 2px 5. 将图表放入 QChartView 并添加到布局核心代码// mainwindow.cpp - 图表初始化 void MainWindow::setupChart() { m_series new QLineSeries(); m_series-setName(压力 (MPa)); m_series-setPen(QPen(QColor(#2196F3), 2)); m_chart new QChart(); m_chart-addSeries(m_series); m_chart-setTitle(实时压力趋势); m_chart-setAnimationOptions(QChart::NoAnimation); // 实时数据禁用动画 // X 轴时间 m_axisX new QDateTimeAxis(); m_axisX-setFormat(HH:mm:ss); m_axisX-setTitleText(时间); m_chart-addAxis(m_axisX, Qt::AlignBottom); m_series-attachAxis(m_axisX); // Y 轴压力 m_axisY new QValueAxis(); m_axisY-setRange(0, 3.0); m_axisY-setTitleText(压力 (MPa)); m_axisY-setLabelFormat(%.1f); m_chart-addAxis(m_axisY, Qt::AlignLeft); m_series-attachAxis(m_axisY); // 创建图表视图 QChartView *chartView new QChartView(m_chart); chartView-setRenderHint(QPainter::Antialiasing); // 添加到布局假设已存在 centralLayout centralLayout-addWidget(chartView); }4.2 实时更新图表每读到一个新的压力值就追加一个点并只保留最近 60 秒的数据// mainwindow.cpp - 图表更新 void MainWindow::updateChart(float pressure) { QDateTime now QDateTime::currentDateTime(); // 添加新点 m_series-append(now.toMSecsSinceEpoch(), pressure); // 只保留最近 60s 的数据 QDateTime cutoff now.addSecs(-60); while (m_series-count() 0 m_series-at(0).x() cutoff.toMSecsSinceEpoch()) { m_series-remove(0); } // 更新 X 轴范围始终显示最近 60s m_axisX-setRange(cutoff, now); }然后在onReadReady()中调用它// 在 onReadReady() 的压力解析之后添加 updateChart(pressure);现在运行程序你会看到一条蓝色曲线实时滚动每秒一个点始终展示最近 60 秒。如果在 Modbus Slave 里手动修改寄存器值曲线会立刻反映变化。性能提示QChart::NoAnimation很重要。实时数据每秒都在更新动画会导致 UI 卡顿这是工业 HMI 的常见实践。第五章报警系统与故障记录3 分钟5.1 超阈值报警当压力超过阈值时需要做到界面变红警示 弹窗提醒 落库记录。// mainwindow.cpp - 报警逻辑 void MainWindow::triggerAlarm(float pressure) { // 界面变红 m_pressureLabel-setStyleSheet( color: white; background-color: #F44336; font-size: 32px; padding: 10px; border-radius: 8px;); // 状态指示灯变红 m_statusLight-setStyleSheet( background-color: #F44336; border-radius: 12px; min-width: 24px; min-height: 24px;); // 弹窗报警仅在刚越限时弹一次避免重复弹窗 static bool alarmActive false; if (!alarmActive) { alarmActive true; QMessageBox::warning(this, 压力报警, QString(当前压力 %1 MPa 超过阈值 %2 MPa\n请立即检查水泵状态。) .arg(pressure, 0, f, 2) .arg(m_alarmThreshold, 0, f, 2)); } // 写入数据库 logAlarm(pressure, QString(压力超限: %1 MPa %2 MPa) .arg(pressure, 0, f, 2) .arg(m_alarmThreshold, 0, f, 2)); // 压力恢复正常时复位 if (pressure m_alarmThreshold) { alarmActive false; m_pressureLabel-setStyleSheet( color: #2196F3; font-size: 32px; padding: 10px;); m_statusLight-setStyleSheet( background-color: #4CAF50; border-radius: 12px; min-width: 24px; min-height: 24px;); } }5.2 SQLite 故障记录工业系统必须记录所有报警事件以便追溯这里使用 SQLite// mainwindow.cpp - 数据库初始化 void MainWindow::setupDatabase() { m_db QSqlDatabase::addDatabase(QSQLITE); m_db.setDatabaseName(pump_alarm_log.db); if (!m_db.open()) { qWarning() 无法打开数据库: m_db.lastError().text(); return; } // 创建报警表 QSqlQuery query; query.exec( CREATE TABLE IF NOT EXISTS alarm_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, pressure REAL, message TEXT ) ); }5.3 记录并展示日志// mainwindow.cpp - 写记录 void MainWindow::logAlarm(float pressure, const QString message) { // 写入数据库 QSqlQuery query; query.prepare( INSERT INTO alarm_log (pressure, message) VALUES (?, ?)); query.addBindValue(pressure); query.addBindValue(message); query.exec(); // 更新屏幕上的表格 int row m_logTable-rowCount(); m_logTable-insertRow(row); m_logTable-setItem(row, 0, new QTableWidgetItem( QDateTime::currentDateTime().toString(yyyy-MM-dd HH:mm:ss))); m_logTable-setItem(row, 1, new QTableWidgetItem(QString::number(pressure, f, 2))); m_logTable-setItem(row, 2, new QTableWidgetItem(message)); // 自动滚动到最后一行 m_logTable-scrollToBottom(); }记录表格有三列时间、压力值、报警信息。每次报警追加一行同时持久化到 SQLite。5.4 手动启/停水泵上位机除了读数据还要能控制下位机方式就是写寄存器// mainwindow.cpp - 水泵控制 void MainWindow::togglePump() { if (!m_modbusClient || m_modbusClient-state() ! QModbusDevice::ConnectedState) return; m_pumpRunning !m_pumpRunning; // 构造写请求向地址 2 写入 1启动或 0停止 QModbusDataUnit writeUnit( QModbusDataUnit::HoldingRegisters, 2, 1); writeUnit.setValue(0, m_pumpRunning ? 1 : 0); if (auto *reply m_modbusClient-sendWriteRequest(writeUnit, 1)) { connect(reply, QModbusReply::finished, this, [this, reply]() { if (reply-error() QModbusDevice::NoError) { m_pumpButton-setText(m_pumpRunning ? 停止水泵 : 启动水泵); m_pumpButton-setStyleSheet(m_pumpRunning ? background-color: #F44336; color: white; padding: 12px; : background-color: #4CAF50; color: white; padding: 12px;); statusBar()-showMessage( m_pumpRunning ? 水泵已启动 : 水泵已停止, 2000); } reply-deleteLater(); }); } }在 Modbus Slave 中点击按钮时可以看到地址2的值在0和1之间切换——这就是上位机对下位机的控制过程。至此读 显 警 记 控五个 HMI 核心能力全部闭环。第六章打包与部署可选6.1 使用 windeployqt / macdeployqt 打包Qt 提供官方部署工具自动收集程序运行所需的动态库。Windows# 先构建 Release 版本然后在编译目录下执行 windeployqt PumpHMI.exewindeployqt会把 Qt 的 DLL、插件、翻译文件等复制到可执行文件旁。打包好的整个目录可以直接分发。macOSmacdeployqt PumpHMI.app -dmg这会生成一个.dmg安装镜像。6.2 用 Qt Installer Framework 制作安装包如果需要专业的安装向导下一步 - 下一步 - 完成可使用 Qt Installer Framework请帮我用 Qt Installer Framework 为 PumpHMI 制作安装程序 1. 创建安装器目录结构config、packages 2. 配置 config.xml安装器名称、版本、安装目录 3. 将 windeployqt 的输出文件放入 packages/com.example.pumphmi/data/ 4. 运行 binarycreator 生成安装器第七章总结与进阶方向恭喜你已经从零构建了一个工业水泵监控 HMI 系统。回顾一下理解了上位机、下位机、Modbus 协议的核心概念用 Modbus Slave 模拟了虚拟水泵无需真实硬件使用 QtQModbusTcpClient构建了上下位机通信用 Qt Charts 绘制了实时滚动的压力趋势图实现了超阈值弹窗报警与 SQLite 故障记录实现了水泵的远程启停控制整个过程没有使用真实工业硬件但架构和功能与真实工厂的 HMI 系统一致。如果把 Modbus Slave 换成真实 PLC这套应用可以直接用于生产场景——这正是本教程的最大价值通过零成本仿真完整走通工业软件的真实工作流。进阶方向多设备监控连接多台下位机用标签页/分屏查看不同设备数据历史回放从 SQLite 读取历史数据配合时间轴控件回放趋势图OPC UA 协议Modbus 适合简单场景复杂工业系统常用 OPC UAQt 也支持Qt OPC UA 模块Web 远程监控用 Qt WebSocket 把实时数据推送到浏览器实现移动端查看AI 预测性维护把历史压力数据喂给机器学习模型提前预测故障用代码守护工业现场每一台设备。参考资源Qt Serial Bus 官方文档QModbusTcpClient、Modbus 相关类Qt 官方 Modbus TCP 客户端示例Qt Charts 官方文档QChart、QLineSeries、QDateTimeAxis、QValueAxisModbus 协议规范官方文档Modbus Slave 模拟器官方下载页Qt Installer Framework 官方文档上述资源在仓库对应文档 docs/es-es/stage-3/cross-platform/qt-industrial-hmi/index.md 的末尾均有完整条目。更多跨平台开发实战Electron、Flutter、React Native、鸿蒙、小程序、浏览器扩展等可进入跨平台开发章节与课程总索引继续学习。【免费下载链接】easy-vibe vibe coding 101The first course for AI-native product builders.项目地址: https://gitcode.com/GitHub_Trending/ea/easy-vibe创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考