首页 > 解决方案 > Matplotlib FuncAnimation 抛出“AttributeError:‘NoneType’对象没有属性‘间隔’”

问题描述

我正在尝试在 Python 上运行以下代码,AttributeError: 'NoneType' object has no attribute 'interval'当我关闭当前图形时它会生成错误(以便继续执行程序。

from loadcell import LoadCell
import time
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

def my_program():
    my_loadcell = LoadCell()

    # TODO: prima si comincia a leggere la cella e poi si muove (?)

    batch_index = 0

    forces = []
    timings = []

    my_loadcell.start_reading()
    t0 = time.time()

    def update_function(i):
        if time.time() - t0 < 3:
            nonlocal batch_index
            if my_loadcell.is_batch_ready(batch_index, batch_size=15):
                batch, batch_index = my_loadcell.get_batch(batch_index)
                print(time.time() - t0)
                forces.extend(batch['F'])
                timings.extend(batch['t'] - t0)

                ax.cla()
                ax.plot(timings, forces)
                ax.set_xlabel('Time (s)')
                ax.set_ylabel('Force (N)')
                ax.set_xlim(0, timings[-1] + 0.15 * timings[-1])
                ax.set_ylim(0, max(forces) + 0.10 * max(forces))
                ax.set_title('Force vs. Time')
                ax.grid(True)
                ax.scatter(timings[-1], forces[-1])
                ax.text(timings[-1], forces[-1] + 0.01 * forces[-1], '{} N'.format(round(forces[-1], 3)))

            print('not ready')
        else:
            plt.savefig('./my_fig.svg')ì
            plt.close(fig)
        
        return

    fig = plt.figure(facecolor='#DEDEDE')
    ax = plt.axes()
    ax.set_facecolor('#DEDEDE')

    my_animation = FuncAnimation(fig, update_function, interval=50)

    plt.show(block=True)

    my_loadcell.stop_reading()

    return

my_program()
print('do stuff ....')

报告的代码使用以下占位符模块loadcell.py

from statistics import median
import time
from threading import Thread
import numpy as np
import pandas as pd

#HACK#
import random
def read_placeholder():
    time.sleep(0.0125)
    return random.randint(100000, 111111)

class LoadCell():
    def __init__(self):
        
        # Calibration attributes
        self.is_calibrated = True
        self._slope = 0.0001
        self._y_intercept = 0.5
        self._calibrating_mass = 300
        self._calibration_filename = 'load_cell_calibration.json'

        # Reading attributes
        self._is_reading = False
        self._readings = None
        self._timings = None
        self._started_reading_at = None
        self._read_thread = None

    def _reset_reading_attributes(self):
        self._is_reading = False
        self._readings = None
        self._timings = None
        self._started_reading_at = None
        self._read_thread = None

        return
    
    def _init_reading_attributes(self):
        self._readings = []
        self._timings = []
        self._is_reading = True

        self._read_thread = Thread(target=self._read)
        self._started_reading_at = time.time()

        return

    def start_reading(self):
        self._init_reading_attributes()
        self._read_thread.start()

        return

    def stop_reading(self):
        readings = np.array(self._readings)
        timings = np.array(self._timings)
        
        self._reset_reading_attributes()
        
        weights = self._slope * readings + self._y_intercept
        forces = (weights / 1000) * 9.81
        data = {'t': timings, 'F': forces}

        # TODO: eventualmente aggiungere qui vari filtri e post elaborazione dei dati
        
        df = pd.DataFrame.from_dict(data, orient='index')
        df = df.transpose()

        return df

    def _read(self):
        while self._is_reading:
            try:
                self._readings.append(read_placeholder())
                self._timings.append(time.time())
            except:
                pass

        return

    def is_batch_ready(self, batch_index:int, batch_size:int = 15):
        if self._readings is not None:
            if len(self._readings) - batch_index >= batch_size:
                return True
            else:
                return False
        else:
            return False

    def get_batch(self, batch_index:int, batch_size:int = 15, kernel_size:int = 5):
        batch = np.array(self._readings[batch_index:batch_index + batch_size])
        batch_timings = np.array(self._timings[batch_index:batch_index + batch_size])
        batch_index += batch_size
        
        batch_median = median(batch)
        reading_tolerance = 0.5 # 50%

        for i in range(len(batch)):
            reading = batch[i]
            if abs(reading) > abs(batch_median) * (1 + reading_tolerance) or abs(reading) < abs(batch_median) * (1 - reading_tolerance):
                batch[i] = batch_median
        
        # batch = scipy.signal.medfilt(batch, kernel_size)

        batch = self._slope * batch + self._y_intercept
        batch = (batch / 1000) * 9.81
        batch = pd.DataFrame({'t': batch_timings, 'F': batch})

        return batch, batch_index

首先,我可以使用 Anaconda3 和 Python3.8.8 在我的 PC 上正确运行该脚本,然后在我的 Raspberry Pi (Python3.7.3) 上运行该脚本时我注意到了错误。为了检查我在 PC 上执行脚本的所有内容,将 Python 版本更改为 3.9.7 和 3.7.3,两者都生成相同的 AttributeError。最奇怪的是,当我恢复到 Python3.8.8 时,错误仍然存​​在,而首先特定版本的 Python 工作正常。

感谢大家的帮助!

编辑:经过一些额外的测试,我注意到该错误发生在 3.4.3 版本的 matplotlib 中,而在我安装在计算机上的 3.3.4 版本中没有发生。因此,Python 版本不相关。我仍然想知道:这是 matplotlib 最新版本的问题还是与我自己的代码有关?

标签: pythonpython-3.xmatplotlibraspberry-pimatplotlib-animation

解决方案


推荐阅读