You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Python实现PIC单片机串口数据的示波器式图形可视化?

实现方案与所需技术/算法

一、硬件与串口通信层

  • 串口读取:用pyserial库处理USB转RS232的串口通信,必须确保波特率、数据位、停止位、奇偶校验参数和PIC单片机完全匹配(两次转换器仅做电平转换,不改变串口参数)。
  • 数据解析:和PIC约定数据格式,推荐用固定长度二进制帧(传输效率高)或ASCII文本行(调试便捷)。比如单片机每帧发送[采样序号, ADC值],Python端解析后转成实际电压值:电压 = (ADC值 / ADC分辨率) * 参考电压,若需更高精度,可加入校准系数修正线路或传感器误差。

二、示波器UI与实时绘图实现

工具选择

优先选PyQtGraph(而非Matplotlib),它基于Qt框架,支持GPU加速,适合高采样率下的实时波形绘制,性能远优于Matplotlib的动画模块。如果需要丰富的交互控件(比如幅值滑块、时间缩放按钮),可结合PyQt5/PySide2搭建完整界面。

核心UI功能实现

  • 时间刻度显示:根据采样率计算X轴时间,比如采样率1000Hz时,每1000个采样点对应1秒,X轴直接标注0~1s的时间值;若单片机带时间戳,直接用时间戳作为X轴数据更准确。
  • 波形滚动刷新:维护一个固定长度的缓存数组(比如保存最近1000个采样点),新数据到来时,将数组左移丢弃最旧的点,把新数据追加到末尾,再刷新绘图。
  • 幅值调整:通过滑块控件绑定Y轴范围,拖动滑块时调用plot.setYRange(min_val, max_val)即可实现波形幅值的放大/缩小。

三、核心算法需求

  1. 幅值校准算法
    将单片机输出的ADC原始值转换为实际电压:

    def adc_to_voltage(adc_value, adc_resolution=1023, ref_voltage=5.0, calibration=1.0):
        return (adc_value / adc_resolution) * ref_voltage * calibration
    

    其中calibration是手动校准系数,用于修正传感器或线路的系统误差。

  2. 数据滤波(可选)
    若信号存在噪声,可采用以下两种滤波方式平滑波形:

    • 滑动平均滤波:取最近N个采样点的平均值作为当前值,适合消除高频随机噪声。
    • 中值滤波:取最近N个采样点的中位数,适合去除尖峰脉冲干扰。
  3. 触发功能(可选)
    实现类似专业示波器的触发逻辑,比如上升沿触发:检测电压从低于阈值跳到高于阈值时,锁定波形显示。算法上通过比较连续两个采样点的电压值,判断是否满足触发条件,触发后开始填充缓存数组并保持波形稳定。

  4. 采样率同步
    若单片机未发送时间戳,Python端可通过本地定时器记录采样间隔,但更可靠的方式是让单片机发送采样序号,Python端根据序号和预设采样率计算时间,避免串口延迟导致的时间轴失真。

四、极简代码示例(PyQtGraph版)

import serial
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
import numpy as np

# 串口初始化
ser = serial.Serial('COM3', 9600, timeout=0.001)
ser.flushInput()

# 绘图窗口配置
app = QtGui.QApplication([])
win = pg.GraphicsLayoutWidget(show=True, title="自制示波器")
plot = win.addPlot(title="交流电压波形")
plot.setLabel('left', '电压(V)')
plot.setLabel('bottom', '时间(s)')
curve = plot.plot(pen='cyan')

# 数据缓存:保存最近1000个采样点(对应1秒,采样率1000Hz)
sample_rate = 1000
data_len = sample_rate
data = np.zeros(data_len)
x_axis = np.linspace(0, data_len/sample_rate, data_len)

# 实时更新函数
def update_waveform():
    global data
    # 读取串口数据(假设单片机每行发送一个ASCII格式的电压值)
    raw_data = ser.readline().decode('utf-8').strip()
    if raw_data:
        try:
            voltage = float(raw_data)
            # 滚动更新数据缓存
            data = np.roll(data, -1)
            data[-1] = voltage
            curve.setData(x_axis, data)
        except ValueError:
            # 忽略无效数据
            pass

# 设置定时器,按采样率间隔更新
timer = QtCore.QTimer()
timer.timeout.connect(update_waveform)
timer.start(1000//sample_rate)  # 间隔毫秒数

# 启动UI循环
if __name__ == '__main__':
    QtGui.QApplication.instance().exec_()
    ser.close()

注意事项

  • 串口稳定性:添加异常捕获,处理串口断开、数据丢包的情况,必要时实现自动重连逻辑。
  • 性能优化:高采样率(>10kHz)时,建议用二进制格式传输数据,减少解析耗时;同时限制缓存数组长度,避免内存占用过高。
  • 功能扩展:可添加波形截图、数据保存、多通道显示等功能,通过PyQt的控件实现交互。

内容的提问来源于stack exchange,提问作者WalterPH

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 19:47:43