发布时间:2026/8/26 18:34:44
STM32F103 温控系统上位机开发:Python串口通信与实时波形显示(附源码) STM32F103温控系统上位机开发实战Python串口通信与数据可视化在嵌入式系统开发中温度控制是一个经典而实用的课题。当STM32F103作为下位机完成温度采集和PID控制后如何通过上位机实现数据的可视化监控和交互控制就成为提升系统实用性的关键环节。本文将深入讲解如何使用Python构建功能完整的温控系统上位机涵盖串口通信协议设计、实时曲线绘制、历史数据回放等核心功能。1. 系统架构与通信协议设计一个完整的温控系统通常由下位机STM32F103、温度传感器如DS18B20、执行机构半导体制冷器/PTC加热片和上位机构成。上下位机之间通过串口进行数据交换因此首先需要设计一套简洁高效的通信协议。1.1 串口通信参数配置在Python中我们可以使用PySerial库实现串口通信。基本参数配置如下import serial ser serial.Serial( portCOM3, # 串口号根据实际连接修改 baudrate115200, # 波特率需与下位机一致 bytesize8, # 数据位 parityN, # 无校验 stopbits1, # 停止位 timeout1 # 超时时间(秒) )1.2 自定义通信协议设计为保证数据传输的可靠性我们设计如下通信帧格式字节位置内容说明00xAA帧头标识1数据长度有效数据字节数2命令类型0x01:上传数据 0x02:下传设定3~n数据内容有效数据n1校验和前面所有字节的累加和下位机上传的温度数据包示例十六进制AA 04 01 00 19 00 1E C2解析0xAA: 帧头0x04: 数据长度4字节0x01: 上传数据命令0x0019: 当前温度(25.0℃)0x001E: 目标温度(30.0℃)0xC2: 校验和(0xAA0x040x010x000x190x000x1E)2. PyQt5界面设计与实时数据显示PyQt5提供了丰富的UI组件非常适合开发专业的上位机界面。我们将创建一个包含温度曲线显示区、控制面板和数据记录区的综合界面。2.1 主界面框架搭建from PyQt5.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QWidget, QHBoxLayout, QGroupBox) from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas import matplotlib.pyplot as plt class MainWindow(QMainWindow): def __init__(self): super().__init__() # 主窗口设置 self.setWindowTitle(STM32温控系统监控平台) self.setGeometry(100, 100, 1000, 600) # 创建中央部件和布局 central_widget QWidget() self.setCentralWidget(central_widget) main_layout QHBoxLayout(central_widget) # 左侧控制面板 control_panel self.create_control_panel() main_layout.addWidget(control_panel, stretch1) # 右侧数据显示区 data_display self.create_data_display() main_layout.addWidget(data_display, stretch3) def create_control_panel(self): 创建左侧控制面板 panel QGroupBox(控制面板) layout QVBoxLayout() # 这里添加各种控制组件... panel.setLayout(layout) return panel def create_data_display(self): 创建右侧数据显示区 container QWidget() layout QVBoxLayout() # 创建Matplotlib图形 self.figure, self.ax plt.subplots(figsize(10, 6)) self.canvas FigureCanvas(self.figure) layout.addWidget(self.canvas) # 其他数据显示组件... container.setLayout(layout) return container2.2 实时曲线绘制实现使用Matplotlib实现动态曲线更新是上位机的核心功能之一。我们通过QTimer定时器实现曲线的实时刷新from PyQt5.QtCore import QTimer import numpy as np class MainWindow(QMainWindow): def __init__(self): # ...其他初始化代码... # 初始化数据存储 self.time_data np.arange(0, 300, 1) # 300个时间点 self.current_temp np.zeros(300) # 当前温度数组 self.target_temp np.zeros(300) # 目标温度数组 self.pointer 0 # 数据指针 # 初始化曲线 self.line_current, self.ax.plot(self.time_data, self.current_temp, r-, label当前温度) self.line_target, self.ax.plot(self.time_data, self.target_temp, b--, label目标温度) self.ax.legend() self.ax.set_xlabel(时间(s)) self.ax.set_ylabel(温度(℃)) self.ax.set_ylim(0, 100) # 温度范围 # 设置定时器 self.timer QTimer() self.timer.timeout.connect(self.update_plot) self.timer.start(1000) # 1秒刷新一次 def update_plot(self): 更新曲线数据 if self.pointer 300: # 从串口读取新数据并更新数组 new_data self.read_serial_data() if new_data: self.current_temp[self.pointer] new_data[current] self.target_temp[self.pointer] new_data[target] self.pointer 1 else: # 滚动显示 self.current_temp[:-1] self.current_temp[1:] self.target_temp[:-1] self.target_temp[1:] self.current_temp[-1] new_data[current] self.target_temp[-1] new_data[target] # 更新曲线数据 self.line_current.set_ydata(self.current_temp) self.line_target.set_ydata(self.target_temp) # 调整X轴范围 if self.pointer 300: self.ax.set_xlim(0, 300) else: self.ax.set_xlim(self.pointer-300, self.pointer) self.canvas.draw()3. 数据记录与历史回放功能完整的监控系统需要具备数据记录和回放功能便于后续分析系统性能。3.1 SQLite数据库存储使用轻量级的SQLite数据库存储历史数据import sqlite3 from datetime import datetime class DataLogger: def __init__(self): self.conn sqlite3.connect(temperature_data.db) self.cursor self.conn.cursor() self.create_table() def create_table(self): 创建数据表 self.cursor.execute( CREATE TABLE IF NOT EXISTS temperature_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp TEXT NOT NULL, current_temp REAL NOT NULL, target_temp REAL NOT NULL, pid_output REAL ) ) self.conn.commit() def log_data(self, current, target, pid_out): 记录数据 timestamp datetime.now().strftime(%Y-%m-%d %H:%M:%S) self.cursor.execute( INSERT INTO temperature_log (timestamp, current_temp, target_temp, pid_output) VALUES (?, ?, ?, ?) , (timestamp, current, target, pid_out)) self.conn.commit() def get_history_data(self, start_time, end_time): 查询历史数据 self.cursor.execute( SELECT timestamp, current_temp, target_temp FROM temperature_log WHERE timestamp BETWEEN ? AND ? ORDER BY timestamp , (start_time, end_time)) return self.cursor.fetchall()3.2 历史数据可视化添加历史数据选择界面和回放功能from PyQt5.QtWidgets import (QDateEdit, QTimeEdit, QPushButton, QDialog, QLabel) class HistoryDialog(QDialog): def __init__(self, parentNone): super().__init__(parent) self.setWindowTitle(历史数据查询) self.setGeometry(200, 200, 400, 200) layout QVBoxLayout() # 日期时间选择 layout.addWidget(QLabel(开始时间:)) self.start_datetime QDateTimeEdit(self) layout.addWidget(self.start_datetime) layout.addWidget(QLabel(结束时间:)) self.end_datetime QDateTimeEdit(self) layout.addWidget(self.end_datetime) # 查询按钮 self.query_btn QPushButton(查询, self) self.query_btn.clicked.connect(self.query_data) layout.addWidget(self.query_btn) self.setLayout(layout) def query_data(self): 查询并显示历史数据 start self.start_datetime.dateTime().toString(yyyy-MM-dd hh:mm:ss) end self.end_datetime.dateTime().toString(yyyy-MM-dd hh:mm:ss) # 获取数据 data self.parent().data_logger.get_history_data(start, end) # 绘制历史曲线 self.parent().plot_history_data(data) self.close()4. PID参数整定与系统优化通过上位机可以实时调整PID参数观察系统响应实现更好的控制效果。4.1 PID参数下发实现在下位机程序中我们需要实现接收PID参数并更新的功能。上位机发送的PID参数包格式如下AA 0C 02 00 00 3F 80 00 00 3E 80 00 00 3D 80 XX解析0xAA: 帧头0x0C: 数据长度12字节0x02: 参数设置命令0x00003F80: Kp (1.0的IEEE754浮点表示)0x00003E80: Ki (0.25)0x00003D80: Kd (0.0625)XX: 校验和Python实现代码import struct def send_pid_params(self, kp, ki, kd): 发送PID参数到下位机 # 将浮点数转换为字节 kp_bytes struct.pack(f, kp) ki_bytes struct.pack(f, ki) kd_bytes struct.pack(f, kd) # 构建数据包 data bytearray() data.append(0xAA) # 帧头 data.append(0x0C) # 数据长度 data.append(0x02) # 命令类型 # 添加PID参数 data.extend(kp_bytes) data.extend(ki_bytes) data.extend(kd_bytes) # 计算校验和 checksum sum(data) 0xFF data.append(checksum) # 发送数据 if self.ser.is_open: self.ser.write(data)4.2 系统响应分析工具添加阶跃响应测试功能帮助分析系统动态特性def step_response_test(self): 阶跃响应测试 # 保存当前目标温度 original_target self.target_temp[self.pointer-1] if self.pointer 0 else 25.0 # 设置新的目标温度(阶跃变化) new_target original_target 10.0 # 10℃阶跃 self.send_target_temp(new_target) # 记录响应过程 self.response_data { time: [], temperature: [], start_time: time.time() } # 启动专用定时器记录数据 self.response_timer QTimer() self.response_timer.timeout.connect(self.record_response_data) self.response_timer.start(100) # 100ms采样一次 def record_response_data(self): 记录响应数据 elapsed time.time() - self.response_data[start_time] current_temp self.current_temp[self.pointer-1] if self.pointer 0 else 0 self.response_data[time].append(elapsed) self.response_data[temperature].append(current_temp) # 10秒后停止记录 if elapsed 10.0: self.response_timer.stop() self.plot_response_curve() def plot_response_curve(self): 绘制响应曲线 fig, ax plt.subplots() ax.plot(self.response_data[time], self.response_data[temperature]) ax.set_xlabel(时间(s)) ax.set_ylabel(温度(℃)) ax.set_title(阶跃响应曲线) ax.grid(True) fig.show()5. 系统集成与调试技巧完成各个模块开发后需要进行系统集成和调试。以下是几个实用的调试技巧串口调试助手辅助调试先用串口调试助手验证通信协议的正确性数据日志记录将所有收发数据记录到文件便于分析通信问题模拟数据模式在没有硬件连接时可以使用模拟数据进行界面测试异常处理完善串口通信的异常处理机制避免程序崩溃def serial_thread(self): 串口通信线程 while self.running: try: if self.ser.in_waiting: data self.ser.read(self.ser.in_waiting) self.process_serial_data(data) # 记录原始数据 with open(serial_log.txt, ab) as f: f.write(data) except serial.SerialException as e: print(f串口错误: {e}) time.sleep(1) except Exception as e: print(f未知错误: {e})通过以上步骤我们构建了一个功能完善的STM32温控系统上位机实现了数据监控、历史回放、PID参数调整等核心功能。这种架构也可以扩展到其他类型的监控系统开发中具有很好的通用性和可扩展性。

相关新闻

2026/8/26 19:00:43

微信Hook还能做吗?PC微信4.1.12.26自动化适配实测

最近一直在研究PC端微信自动化,这次主要针对微信Windows客户端4.1.12.26版本进行了适配。网上关于“微信Hook”的文章不少,但很多内容仍然停留在微信2.x、3.x版本,或者只是把DLL注入、Inline Hook、HTTP接口等概念重新整理了一遍。真正落到新…

2026/8/26 19:00:43

OpenClaw批量部署多机操作指南,TopClaw全自动免费装中文统一版本

为什么需要批量部署?从“一台台折腾”到“一键搞定” 相信不少朋友和我一样,最初接触OpenClaw时,都是在自己的一台电脑上慢慢摸索。但真到了要管理多台机器的时候,那感觉就像从开手动挡小车突然换成开公交车——手忙脚乱。每台机器…

2026/8/26 19:00:43

QML 粒子进度条:Canvas 模拟粒子流动与火花喷散

目录 Demo 粒子进度条 演示代码(粒子填充) 关键逻辑解析(粒子填充) 演示代码(末端火花) 关键逻辑解析(末端火花) 演示代码(气泡上升) 关键逻辑解析(气泡上升) 运行验证 扩展复用方向 工程下载 粒子进度条在普通渐变填充之上叠加一层 Canvas 粒子动画——粒子沿填充…

2026/8/26 19:00:43

企业出海如何突破传播壁垒?传播易全域媒体解决方案靠谱吗?

在全球市场竞争持续加剧的当下,突破地域传播壁垒、精准传递品牌价值、构建国际化品牌形象,已成为中国企业出海布局的核心要务。海外媒体生态复杂、地域文化多元、受众认知差异明显,多数企业常因传播渠道零散、内容适配不足、投放风控薄弱&…

2026/8/26 18:55:42

Nacos‑Client 与 Nacos‑Server

一句话总结:Nacos‑Server 是独立运行的服务端;Nacos‑Client 是嵌入业务项目里的客户端 SDK,两者配合完成注册中心 配置中心能力。表格对比项Nacos‑ServerNacos‑Client角色服务端(注册 & 配置的中央服务器)客户…

2026/8/26 9:13:28

[光学原理与应用-521]:对光的错误理解与纠偏

首先光是一种能量的载体和形态,宏观上观察到的光是由无数个微观的光量子组成的,每个光子在产生的瞬间,其在真空的空间中以确定不变的速度沿着一个初始的方向一直向前,在微观层面,每个光量子的运动轨迹是以波函数所展现…

2026/8/25 11:48:27

SIP通话转接原理与REFER方法实战解析

1. 通话转接不是“挂断再拨号”,而是SIP会话的动态重定向你有没有遇到过这样的场景:客服坐席A正在和客户通电话,突然需要把这通对话无缝转给专家坐席B,客户完全感知不到中间的断连——既没听到忙音,也没被要求重新拨号…

2026/8/25 16:56:43

Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

1. 为什么选择Kolla-ansible来部署单节点OpenStack?如果你正在寻找一种能把OpenStack从“概念”快速变成“可用的实验环境”的方法,那么Kolla-ansible几乎是当前最主流、最省心的选择。我见过太多人卡在手动编译依赖、配置服务、处理版本冲突的泥潭里&am…

2026/8/26 0:04:32

Python random 模块常用函数详解:从入门到实战

目录 1. 引言2. 准备工作3. 基础随机函数4. 序列相关函数5. 随机种子与复现6. 实战案例7. 注意事项8. 常见问题与排查9. 总结 1. 引言 摘要: 本文系统介绍 Python 标准库 random 模块中最常用的随机数生成函数。内容涵盖基础随机函数(random()、unifor…

2026/8/26 1:19:35

JSON总结

JSON概念 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,主要用于跟服务器进行交换数据。它基于ECMAScript的一个子集。 JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C、C、C#、Java、JavaScr…

2026/8/26 1:19:35

保存连接sse 是什么原理,为什么不会一直请求

“保持连接”用的是 SSE(Server-Sent Events),本质是一个没有马上结束的 HTTP 请求。 过程是: 拷贝机发送一次请求: GET /api/code-sync/events服务器返回: Content-Type: text/event-stream但不关闭响应&…

2026/8/24 13:42:17

实测才敢推 AI论文网站 2026最新测评与推荐

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。一、综…

2026/8/24 18:13:48

2026必备!AI论文网站测评:最新推荐与深度对比

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

2026/8/25 1:08:14

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…