如何提升循环中matplotlib实时绘图的重绘性能
matplotlib实时串口绘图性能优化方案
核心卡顿原因
你当前代码的性能瓶颈主要来自三个方面:
- 循环内的
printIO操作占了大量阻塞时间 - 每次采样都触发全画布重绘,未变化的坐标轴、刻度等元素重复渲染浪费资源
- 冗余的x轴数据重复赋值、更新操作
具体优化措施
- 移除所有非必要的打印操作,调试时可按需开启日志输出,正常运行时关闭
- 初始化时一次性设置好x轴数据,后续循环仅更新y轴采样数据
- 启用blit局部重绘机制,仅重绘变化的曲线区域,跳过固定元素的渲染
- 可根据采样率设置绘图间隔,比如每采5-10个点更新一次画布,在不损失实时观感的前提下进一步降低开销
优化后代码
import matplotlib.pyplot as plt import numpy as np import serial # 初始化数据缓冲区 BUFFER_SIZE = 1000 x = np.arange(BUFFER_SIZE) # 一次性初始化x轴,不需要后续修改 raw_data = np.zeros(BUFFER_SIZE) # 如果要加滤波数据,再加一个缓冲区即可 # filtered_data = np.zeros(BUFFER_SIZE) # 初始化绘图,改用更快的Qt5Agg后端(需要安装pyqt5,不需要可以删掉这行) plt.switch_backend('Qt5Agg') plt.ion() fig, ax = plt.subplots() ax.set_ylim(-2, 2) ax.set_xlim(0, BUFFER_SIZE) raw_line, = ax.plot(x, raw_data, label='原始数据') # 滤波曲线可以一起初始化 # filtered_line, = ax.plot(x, filtered_data, label='滤波后数据', c='orange') ax.legend() # 预缓存画布背景,供blit使用 fig.canvas.draw() background = fig.canvas.copy_from_bbox(ax.bbox) # 初始化串口 ser = serial.Serial('COM3', 9600, timeout=1) index = 0 # 每N个点更新一次绘图,可根据需求调整 UPDATE_INTERVAL = 5 while True: b = ser.readline().decode("utf-8").strip() if not b: continue temp = b.split() current_val = float(temp[0]) raw_data[index] = current_val # 如果做滤波,这里计算滤波后的值存入filtered_data[index]即可 index += 1 # 缓冲区满了左移覆盖,也可以改成循环覆盖模式,按需调整 if index >= BUFFER_SIZE: raw_data[:-1] = raw_data[1:] index = BUFFER_SIZE - 1 # 到更新间隔再执行绘图操作 if index % UPDATE_INTERVAL == 0: # 恢复缓存的背景 fig.canvas.restore_region(background) # 只更新曲线数据 raw_line.set_ydata(raw_data) # filtered_line.set_ydata(filtered_data) # 重绘曲线部分 ax.draw_artist(raw_line) # ax.draw_artist(filtered_line) # 只刷新曲线所在的区域 fig.canvas.blit(ax.bbox) fig.canvas.flush_events()
以上优化完成后,100个采样点的绘制耗时可以控制在200ms以内,完全满足1秒以内的要求,叠加滤波曲线后也不会带来明显的性能损耗。
内容的提问来源于stack exchange,提问作者Quazi Irfan
相关产品推荐
相关产品推荐

