news 2026/7/9 15:52:14

STM32F103 温控系统上位机开发:Python串口通信与实时波形显示(附源码)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32F103 温控系统上位机开发:Python串口通信与实时波形显示(附源码)

STM32F103温控系统上位机开发实战:Python串口通信与数据可视化

在嵌入式系统开发中,温度控制是一个经典而实用的课题。当STM32F103作为下位机完成温度采集和PID控制后,如何通过上位机实现数据的可视化监控和交互控制,就成为提升系统实用性的关键环节。本文将深入讲解如何使用Python构建功能完整的温控系统上位机,涵盖串口通信协议设计、实时曲线绘制、历史数据回放等核心功能。

1. 系统架构与通信协议设计

一个完整的温控系统通常由下位机(STM32F103)、温度传感器(如DS18B20)、执行机构(半导体制冷器/PTC加热片)和上位机构成。上下位机之间通过串口进行数据交换,因此首先需要设计一套简洁高效的通信协议。

1.1 串口通信参数配置

在Python中,我们可以使用PySerial库实现串口通信。基本参数配置如下:

import serial ser = serial.Serial( port='COM3', # 串口号,根据实际连接修改 baudrate=115200, # 波特率,需与下位机一致 bytesize=8, # 数据位 parity='N', # 无校验 stopbits=1, # 停止位 timeout=1 # 超时时间(秒) )

1.2 自定义通信协议设计

为保证数据传输的可靠性,我们设计如下通信帧格式:

字节位置内容说明
00xAA帧头标识
1数据长度有效数据字节数
2命令类型0x01:上传数据 0x02:下传设定
3~n数据内容有效数据
n+1校验和前面所有字节的累加和

下位机上传的温度数据包示例(十六进制):

AA 04 01 00 19 00 1E C2

解析:

  • 0xAA: 帧头
  • 0x04: 数据长度4字节
  • 0x01: 上传数据命令
  • 0x0019: 当前温度(25.0℃)
  • 0x001E: 目标温度(30.0℃)
  • 0xC2: 校验和(0xAA+0x04+0x01+0x00+0x19+0x00+0x1E)

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, stretch=1) # 右侧数据显示区 data_display = self.create_data_display() main_layout.addWidget(data_display, stretch=3) 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 container

2.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, parent=None): 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. 系统集成与调试技巧

完成各个模块开发后,需要进行系统集成和调试。以下是几个实用的调试技巧:

  1. 串口调试助手辅助调试:先用串口调试助手验证通信协议的正确性
  2. 数据日志记录:将所有收发数据记录到文件,便于分析通信问题
  3. 模拟数据模式:在没有硬件连接时,可以使用模拟数据进行界面测试
  4. 异常处理:完善串口通信的异常处理机制,避免程序崩溃
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参数调整等核心功能。这种架构也可以扩展到其他类型的监控系统开发中,具有很好的通用性和可扩展性。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/9 15:50:52

STM32与欧姆龙G6D-ASI继电器在直流负载控制中的高效应用

1. 项目背景与核心目标在工业自动化和小型电力控制系统中&#xff0c;直流负载管理一直是个既基础又关键的环节。我最近接手的一个项目就遇到了典型问题——传统继电器方案在频繁切换直流负载时&#xff0c;不仅效率低下&#xff0c;还存在触点烧蚀风险。经过多轮方案对比&…

作者头像 李华
网站建设 2026/7/9 15:50:33

PyTorch 2.3 实现多头自注意力:从 QKV 计算到 8 头并行推理代码详解

PyTorch 2.3 实现多头自注意力&#xff1a;从 QKV 计算到 8 头并行推理代码详解在自然语言处理和计算机视觉领域&#xff0c;Transformer 架构已经成为事实上的标准。而多头自注意力机制&#xff08;Multi-Head Self-Attention, MHSA&#xff09;作为 Transformer 的核心组件&a…

作者头像 李华
网站建设 2026/7/9 15:48:14

Cesium for Unreal 官方示例项目部署与核心问题解决方案

1. 项目概述与核心价值 如果你正在尝试将真实世界的地理空间数据引入到虚幻引擎中&#xff0c;构建一个从城市漫游到全球模拟的沉浸式应用&#xff0c;那么Cesium for Unreal Samples这个官方示例项目&#xff0c;绝对是你绕不开的“新手村”和“问题百科全书”。这个项目远不止…

作者头像 李华
网站建设 2026/7/9 15:48:10

H桥电机驱动设计与优化:TC78H651AFNG与PIC18LF45K50实战

1. 项目背景与核心器件选型 在直流有刷电机驱动领域&#xff0c;H桥拓扑结构一直是实现电机正反转控制的标准解决方案。TC78H651AFNG作为东芝新一代DMOS H桥驱动器&#xff0c;其内部集成四个N沟道功率MOSFET&#xff0c;可提供高达3A的持续输出电流&#xff08;峰值5A&#xf…

作者头像 李华
网站建设 2026/7/9 15:46:01

STM32F732IE与MCP3551高精度ADC采集方案详解

1. 项目概述&#xff1a;MCP3551与STM32F732IE的硬件搭档在嵌入式系统开发中&#xff0c;高精度模拟信号采集一直是个经典难题。MCP3551这款22位Δ-Σ ADC芯片与STM32F732IE这款ARM Cortex-M7内核MCU的组合&#xff0c;为需要高精度数据采集的场景提供了性价比极高的解决方案。…

作者头像 李华
网站建设 2026/7/9 15:43:52

东芝TC78H651AFNG与PIC18F4515的直流电机驱动方案

1. 项目背景与核心器件选型在工业自动化和消费电子领域&#xff0c;直流有刷电机驱动方案一直扮演着关键角色。TC78H651AFNG作为东芝新一代H桥驱动器IC&#xff0c;与Microchip的PIC18F4515微控制器组合&#xff0c;构成了一个高效可靠的驱动解决方案。这套组合特别适合需要精确…

作者头像 李华