Arduino实时串口数据绘图无法自动更新,需手动关闭窗口问题
解决Matplotlib实时绘图无法自动更新的问题
我明白你的问题了——每次测完数据都得手动关窗口才能看到新图,这确实挺烦的。问题出在你每次循环都重新创建FuncAnimation实例并调用plt.show(),而plt.show()是阻塞式的,必须关掉窗口才会继续执行后面的代码。下面给你调整后的代码,解决这个实时更新的问题:
关键修改点
- 将动画初始化和
plt.show()移到主循环外,只执行一次,避免重复创建窗口 - 用全局变量存储最新的均值数据,让
animate函数定时获取并更新图表 - 移除循环内的绘图相关调用,把更新逻辑交给动画函数处理
- 给串口添加超时设置,防止读取数据时程序卡住
修改后的完整代码
import serial import numpy as np import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation import threading # 全局变量存储最新的均值数据,供动画函数使用 latest_mean = [0, 0] x_vals = [] y_vals = [] plt.style.use('fivethirtyeight') num_averages = 10 filename = "E:/Python/Data/TemperatureReadPlotreadings.csv" port = "COM3" baudrate = 9600 # 初始化串口,添加超时设置防止卡住 arduino = serial.Serial(port, baudrate, timeout=2) arduino.flush() def readdata(): dataline = arduino.readline() try: y = dataline.decode("ascii").strip().split(",") # 确保读取到两组有效数据 if len(y) != 2: raise ValueError("数据格式不完整") for i in range(len(y)): y[i] = float(y[i]) return y except (ValueError, UnicodeDecodeError): arduino.flush() return [0, 0] def save_data(filename, mean, stddev): print("Saving data") # 使用with语句自动管理文件,避免遗漏关闭 with open(filename, 'a') as datafile: datafile.write(f"{mean[0]:6.2f},{stddev[0]:6.3f},{mean[1]:9.2f},{stddev[1]:9.3f}\n") def animate(i): # 引用全局变量获取最新均值 global latest_mean # 过滤初始无效的0值 if latest_mean[0] != 0 or latest_mean[1] != 0: x_vals.append(latest_mean[0]) y_vals.append(latest_mean[1]) plt.cla() plt.plot(x_vals, y_vals, label="LM35") plt.legend(loc='upper left') plt.xlabel('时间计量') plt.ylabel('温度读数') # 初始化动画,间隔设置为10秒(和你的采样周期匹配) ani = FuncAnimation(plt.gcf(), animate, interval=10000) def main_loop(): global latest_mean run = True while run: try: data = np.zeros((2, num_averages)) mean = np.zeros(2) stddev = np.zeros(2) # 读取10组原始数据 for i in range(num_averages): data_new = readdata() data[0, i] = data_new[0] data[1, i] = data_new[1] # 计算均值和标准差 for i in range(2): mean[i] = np.mean(data[i, :]) stddev[i] = np.std(data[i, :]) # 更新全局均值变量,供动画函数使用 latest_mean = mean.copy() save_data(filename, mean, stddev) except KeyboardInterrupt: print('\nExiting') run = False arduino.close() if __name__ == "__main__": # 把数据读取循环放在子线程,避免阻塞绘图主线程 thread = threading.Thread(target=main_loop) thread.daemon = True thread.start() plt.tight_layout() plt.show()
额外说明
- 串口超时:添加
timeout=2让串口在2秒内无数据时返回空,避免程序卡住。 - 文件操作优化:改用
with语句自动关闭文件,比手动调用close()更安全可靠。 - 线程分离:将数据读取的主循环放在子线程中,避免阻塞Matplotlib的绘图主线程,确保动画能正常定时更新。
- 数据校验:增加了数据长度检查,避免读取到不完整或格式错误的数据导致程序报错。
现在运行代码,你会看到一个持续自动更新的绘图窗口,不用再手动关闭旧窗口啦!
内容的提问来源于stack exchange,提问作者CMJE96
相关产品推荐
相关产品推荐

