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

串口实时仪器数据绘图报错: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']

output image


问题分析

该报错是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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 16:30:54