发布时间:2026/7/9 15:52:32
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/7/9 16:53:06

解决PulseView构建报错:libsigrok C++绑定缺失的完整指南

1. 项目概述:当PulseView构建报错“libsigrok C bindings missing”如果你正在尝试从源码构建PulseView——这款由sigrok项目开发的、功能强大的开源逻辑分析仪和示波器软件——并且卡在了CMake配置阶段,屏幕上赫然显示着“libsigrok C bindings missing…

2026/7/9 16:53:06

学生综合成绩测评系统-springboot + vue

本项目为前几天收费帮学妹做的一个项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。 一、项目描述 基于springboot vue的学生综合成绩测评系统 前台登录网址: http://localhost:808…

2026/7/9 16:53:06

ComfyUI-Zluda完整指南:解锁AMD显卡AI绘画的终极方案

ComfyUI-Zluda完整指南:解锁AMD显卡AI绘画的终极方案 【免费下载链接】ComfyUI-Zluda The most powerful and modular stable diffusion GUI, api and backend with a graph/nodes interface. Now ZLUDA enhanced for better AMD GPU performance. 项目地址: http…

2026/7/9 16:53:06

Photoshop 2026安装失败原因与深度排错指南

1. 项目概述:为什么2026版Photoshop安装不是“点下一步”那么简单最近在几个设计类社群里,几乎每天都有人发截图问:“PS 2026安装到87%卡住不动了”“提示‘无法连接Adobe服务器’但网络明明正常”“安装完打开就闪退,连启动界面都…

2026/7/9 16:53:06

CCS生产线全解:2026苏州锂电集成母排自动流程

2026年,全球动力电池市场已突破1.8万亿元规模,储能赛道年复合增长率保持在27%以上。在这个量级下,电池制造早已不是“堆产能”的游戏,而是围绕良率、效率和柔性的精密竞赛。CCS(Cell Contact System,电芯连…

2026/7/9 16:48:05

Proxmox VE 9.1.1部署Windows 11 23H2完整实践指南

1. 项目概述:在Proxmox VE 9.1.1上部署Windows 11 23H1(非26H1,标题存在笔误需先澄清) 先说清楚一个关键事实:标题里写的“Windows11.26H1”并不存在。微软官方发布的Windows 11版本号体系中,2023年10月发…

2026/7/9 1:39:10

国内大模型选型与企业级落地实战指南

我不能提供任何关于访问境外网络信息的技术方案或变通方法。根据中国法律法规和网络管理要求,所有互联网服务必须遵守国家关于网络安全、数据安全和内容安全的规定。ChatGPT及其后续版本(如所谓“GPT-5”)是由境外机构研发的大语言模型&#…

2026/7/9 1:25:56

三步实战方案:高效获取智慧教育平台电子课本PDF的完整流程

三步实战方案:高效获取智慧教育平台电子课本PDF的完整流程 【免费下载链接】tchMaterial-parser 国家中小学智慧教育平台 电子课本下载工具,帮助您从智慧教育平台中获取电子课本的 PDF 文件网址并进行下载,让您更方便地获取课本内容。 项目…

2026/7/9 0:37:00

高精度ADC与STM32L4在工业测量中的优化设计

1. 项目背景与核心器件选型在工业测量和精密仪器领域,模拟信号与数字系统的无缝衔接一直是设计难点。ADS1262作为TI推出的32位精密Δ-Σ ADC,其7nV RMS噪声和3ppm线性度指标,配合STM32L442KC的低功耗特性,构成了理想的信号链解决方…

2026/7/9 0:37:00

ADS1262与STM32F446ZE的高精度数据采集系统设计

1. 为什么需要弥合模拟与数字领域的鸿沟?在嵌入式系统开发中,模拟信号与数字信号的处理一直是个经典难题。我最近在一个工业传感器项目中,就深刻体会到了这种"跨界"处理的挑战。当时需要测量多路微伏级电压信号,同时还要…

2026/7/9 0:37:00

【SpringCloud Alibaba】Spring Boot + Spring Cloud 微服务面试核心20问

大家好,我是 CodeStats。一个在底层技术上“考古”了四年的硬核爱好者,也是 WWAIC(全周项目AI编程) 范式的提出者和实践者。我曾手写过一个完整的 Java Web 框架(从 IoC 容器到嵌入式 Tomcat,代码全开源&a…

2026/7/9 5:30:41

3个高效策略:快速掌握Axure中文界面配置

3个高效策略:快速掌握Axure中文界面配置 【免费下载链接】axure-cn Chinese language file for Axure RP. Axure RP 简体中文语言包。支持 Axure 11、10、9。不定期更新。 项目地址: https://gitcode.com/gh_mirrors/ax/axure-cn 还在为Axure RP的英文界面感…