如何用Matplotlib实现Arduino MKR1000实时数据的同图更新?
问题核心原因
原有代码的运行逻辑存在两个会导致“每0.1秒生成全新绘图”观感的问题:
- 循环内每次迭代都直接调用
plt.plot(),没有固定的绘图对象引用,每次循环都会在画布上叠加新的图层,长期运行后图层堆积、渲染卡顿 - 没有固定坐标轴的显示范围,每次新增数据时Matplotlib会自动全量缩放坐标轴,视觉上和“重新生成一张新图”的效果高度相似
修改后可实现同图持续更新的代码
import serial import matplotlib.pyplot as plt import numpy as np # 初始化串口连接 ser = serial.Serial('COM7', 9600) plt.close('all') # 提前创建固定画布与坐标轴对象,避免后续重复创建画布 fig, ax = plt.subplots(figsize=[10,8]) # 初始化固定的空折线对象,后续不再重新创建新的绘图图层 line, = ax.plot([], [], lw=2) # 启用Matplotlib交互模式 plt.ion() # 根据Arduino实际输出的传感器值范围固定y轴,避免自动缩放跳动 ax.set_ylim(0, 1023) # 初始x轴显示长度适配最近采集的100个数据点 ax.set_xlim(0, 100) data = np.array([]) while True: # 读取串口数据并去除首尾换行、空白字符 raw_content = ser.readline().decode().strip() try: # 截取有效数值部分转换为浮点数 current_val = float(raw_content[0:4]) except ValueError: # 跳过串口偶发的乱码、不完整数据,避免程序崩溃 continue data = np.append(data, current_val) # 只保留最近100个数据点用于绘图,避免长时间运行内存占用过高 display_data = data[-100:] # 直接更新现有折线的x、y坐标,而非重新绘制新线 line.set_ydata(display_data) line.set_xdata(np.arange(len(display_data))) # 同步调整x轴显示范围,适配当前展示的数据长度 ax.set_xlim(0, len(display_data)) # 刷新画布内容,在原有图面基础上更新最新数据 fig.canvas.draw() fig.canvas.flush_events() plt.pause(0.1)
关键优化点说明
- 全程只创建1次画布、1次折线对象,所有数据更新操作都在原有对象上完成,不会生成新的独立绘图窗口
- 固定y轴量程,避免新增数据时坐标轴自动缩放带来的突兀感
- 增加串口数据的异常捕获逻辑,解决串口通信不稳定时程序直接崩溃的问题
- 采用滑动窗口方式只展示最近固定长度的采集数据,避免累计数据量过大导致绘图卡顿
内容的提问来源于stack exchange,提问作者Davide Semeria
相关产品推荐
相关产品推荐

