串口实时仪器数据绘图报错:ValueError: PyCapsule_New called with null pointer
Python串口实时绘图程序报错修复方案
问题背景
开发Python程序通过9600波特率串口读取仪器数据并实时展示,仪器每分钟推送一次数据,偶尔会停止推送5分钟。代码大部分时间正常运行,但偶发报错:ValueError: PyCapsule_New called with null pointer。
现有代码
import serial import time import keyboard from itertools import islice import csv from drawnow import * import numpy import matplotlib.pyplot as plt from datetime import datetime BC880 = [] def makeFig(): plt.plot(BC880, 'ko', label='Black Carbon Conc. (ng m-3)') plt.legend(loc='upper left') Ser = serial.Serial('COM5') while True: res = Ser.readline() decoded_bytes = res.decode('utf-8') AE33data = islice(decoded_bytes.split(","), 11) list_AE33data = list(AE33data) print(list_AE33data) with open("AE33_data.csv","a") as f: writer = csv.writer(f,delimiter=",") writer.writerow(list_AE33data) BC = int(list_AE33data[8]) BC880.append(BC) drawnow(makeFig) Ser.close() print('Serial Port Disconnected')
错误栈
Traceback (most recent call last): File "C:/Users/AML/AppData/Local/Programs/Python/Python312/test.py", line 30, in drawnow(makeFig) File "C:\Users\AML\AppData\Local\Programs\Python\Python312\Lib\site-packages\drawnow\drawnow.py", line 72, in drawnow plt.clf() File "C:\Users\AML\AppData\Local\Programs\Python\Python312\Lib\site-packages\matplotlib\pyplot.py", line 1195, in clf gcf().clear() File "C:\Users\AML\AppData\Local\Programs\Python\Python312\Lib\site-packages\matplotlib\pyplot.py", line 1088, in gcf return figure() File "C:\Users\AML\AppData\Local\Programs\Python\Python312\Lib\site-packages\matplotlib\pyplot.py", line 1022, in figure manager = new_figure_manager( File "C:\Users\AML\AppData\Local\Programs\Python\Python312\Lib\site-packages\matplotlib\pyplot.py", line 545, in new_figure_manager return _get_backend_mod().new_figure_manager(*args, **kwargs) File "C:\Users\AML\AppData\Local\Programs\Python\Python312\Lib\site-packages\matplotlib\backend_bases.py", line 3521, in new_figure_manager return cls.new_figure_manager_given_figure(num, fig) File "C:\Users\AML\AppData\Local\Programs\Python\Python312\Lib\site-packages\matplotlib\backend_bases.py", line 3526, in new_figure_manager_given_figure return cls.FigureCanvas.new_manager(figure, num) File "C:\Users\AML\AppData\Local\Programs\Python\Python312\Lib\site-packages\matplotlib\backend_bases.py", line 1811, in new_manager return cls.manager_class.create_with_canvas(cls, figure, num) File "C:\Users\AML\AppData\Local\Programs\Python\Python312\Lib\site-packages\matplotlib\backends_backend_tk.py", line 479, in create_with_canvas with _restore_foreground_window_at_end(): File "C:\Users\AML\AppData\Local\Programs\Python\Python312\Lib\contextlib.py", line 137, in enter return next(self.gen) File "C:\Users\AML\AppData\Local\Programs\Python\Python312\Lib\site-packages\matplotlib\backends_backend_tk.py", line 43, in _restore_foreground_window_at_end foreground = _c_internal_utils.Win32_GetForegroundWindow() ValueError: PyCapsule_New called with null pointer
预期输出
['0683', '"06-Jun-24"', '"15:06"', ' 635', ' 656', ' 557', ' 554', ' 484', ' 431', ' 419', ' 3.0'] ['0683', '"06-Jun-24"', '"15:07"', ' -40', ' 45', ' 146', ' 6', ' -37', ' -97', ' -56', ' 3.0'] ['0683', '"06-Jun-24"', '"15:08"', ' 1086', ' 1089', ' 862', ' 843', ' 777', ' 809', ' 765', ' 3.0'] ['0683', '"06-Jun-24"', '"15:09"', ' 996', ' 909', ' 1013', ' 890', ' 801', ' 800', ' 871', ' 3.0'] ['0683', '"06-Jun-24"', '"15:10"', ' 1441', ' 1387', ' 1219', ' 1219', ' 1162', ' 1158', ' 1113', ' 3.0'] ['0683', '"06-Jun-24"', '"15:11"', ' 735', ' 796', ' 679', ' 624', ' 535', ' 545', ' 501', ' 3.0'] ['0683', '"06-Jun-24"', '"15:12"', ' 503', ' 493', ' 504', ' 509', ' 395', ' 305', ' 359', ' 3.0'] ['0683', '"06-Jun-24"', '"15:13"', ' 326', ' 366', ' 259', ' 153', ' 173', ' 132', ' 148', ' 3.0'] ['0683', '"06-Jun-24"', '"15:14"', ' 746', ' 705', ' 753', ' 673', ' 538', ' 594', ' 591', ' 3.0'] ['0683', '"06-Jun-24"', '"15:15"', ' 759', ' 859', ' 673', ' 756', ' 599', ' 643', ' 566', ' 3.0'] ['0683', '"06-Jun-24"', '"15:16"', ' 1247', ' 1236', ' 1163', ' 1019', ' 1059', ' 955', ' 1025', ' 3.0'] ['0683', '"06-Jun-24"', '"15:17"', ' 1650', ' 1438', ' 1438', ' 1441', ' 1331', ' 1391', ' 1420', ' 3.0'] ['0683', '"06-Jun-24"', '"15:18"', ' 1021', ' 980', ' 919', ' 942', ' 801', ' 896', ' 773', ' 3.0'] ['0683', '"06-Jun-24"', '"15:19"', ' 816', ' 863', ' 930', ' 749', ' 687', ' 629', ' 688', ' 3.0']

问题分析
该报错是matplotlib的Tkinter后端在Windows环境下的兼容性问题,根源在于窗口焦点管理或频繁创建销毁figure导致的资源异常。同时原代码存在串口配置缺失、数据解析无容错、绘图逻辑低效等问题,加剧了程序不稳定性。
修复方案
1. 完善串口配置
显式指定波特率和超时,避免仪器停推时程序卡死:
Ser = serial.Serial('COM5', baudrate=9600, timeout=60) # 超时匹配仪器推送间隔
2. 替换matplotlib后端
更换为QtAgg后端(或无交互需求时用Agg),规避Tk兼容性问题:
import matplotlib.pyplot as plt plt.switch_backend('QtAgg')
3. 优化绘图逻辑
提前初始化figure和axes,避免每次绘图都重新创建组件:
BC880 = [] # 预创建绘图组件 fig, ax = plt.subplots() line, = ax.plot([], 'ko', label='Black Carbon Conc. (ng m-3)') ax.legend(loc='upper left') ax.set_ylabel('Black Carbon Conc. (ng m-3)') ax.set_xlabel('Sample Index') def updateFig(): line.set_data(range(len(BC880)), BC880) ax.relim() ax.autoscale_view() plt.draw() plt.pause(0.001) # 强制刷新
4. 添加数据容错
处理仪器推送的不完整数据,避免解析崩溃:
try: if len(list_AE33data) >=9: BC = int(list_AE33data[8].strip()) BC880.append(BC) updateFig() else: print("Invalid data received:", list_AE33data) except ValueError as e: print("Failed to parse BC value:", e, "Data:", list_AE33data)
5. 捕获绘图异常并恢复
针对后端偶发错误,添加异常捕获并重新初始化绘图环境:
try: updateFig() except ValueError as e: if "PyCapsule_New called with null pointer" in str(e): print("Recovering from plot error...") plt.close('all') global fig, ax, line fig, ax = plt.subplots() line, = ax.plot(BC880, 'ko', label='Black Carbon Conc. (ng m-3)') ax.legend(loc='upper left') ax.set_ylabel('Black Carbon Conc. (ng m-3)') ax.set_xlabel('Sample Index') updateFig()
完整修复后代码
import serial import time from itertools import islice import csv import matplotlib.pyplot as plt # 替换matplotlib后端 plt.switch_backend('QtAgg') BC880 = [] # 预初始化绘图组件 fig, ax = plt.subplots() line, = ax.plot([], 'ko', label='Black Carbon Conc. (ng m-3)') ax.legend(loc='upper left') ax.set_ylabel('Black Carbon Conc. (ng m-3)') ax.set_xlabel('Sample Index') def updateFig(): line.set_data(range(len(BC880)), BC880) ax.relim() ax.autoscale_view() plt.draw() plt.pause(0.001) # 配置串口 Ser = serial.Serial('COM5', baudrate=9600, timeout=60) try: while True: res = Ser.readline() if not res: print("No data received in timeout period") continue decoded_bytes = res.decode('utf-8').strip() if not decoded_bytes: continue AE33data = islice(decoded_bytes.split(","), 11) list_AE33data = [item.strip() for item in AE33data] print(list_AE33data) # 写入CSV with open("AE33_data.csv","a", newline='') as f: writer = csv.writer(f, delimiter=",") writer.writerow(list_AE33data) # 解析并更新绘图 try: if len(list_AE33data) >=9: BC = int(list_AE33data[8]) BC880.append(BC) try: updateFig() except ValueError as e: if "PyCapsule_New called with null pointer" in str(e): print("Recovering from plot backend error...") plt.close('all') global fig, ax, line fig, ax = plt.subplots() line, = ax.plot(BC880, 'ko', label='Black Carbon Conc. (ng m-3)') ax.legend(loc='upper left') ax.set_ylabel('Black Carbon Conc. (ng m-3)') ax.set_xlabel('Sample Index') updateFig() else: print("Incomplete data received, skipping plot update") except ValueError as e: print(f"Failed to parse BC value: {e}, Data: {list_AE33data}") except KeyboardInterrupt: print("Program stopped by user") finally: Ser.close() plt.close('all') print('Serial Port Disconnected')
额外建议
- 升级matplotlib到最新稳定版,该bug在新版本中可能已修复
- 无交互需求时使用
Agg后端,彻底规避Tk相关问题 - 可考虑用线程分离串口读取和绘图逻辑,避免串口阻塞影响绘图流畅度
内容的提问来源于stack exchange,提问作者Vishnu Kumar Dhaker
相关产品推荐
相关产品推荐

