如何提高循环重绘绘图的性能?

How to improve performance of plot redraw in a loop?

我正在从串行端口读取加速度计传感器数据,并尝试使用 matplot 库实时绘制它。我写了下面的代码来做我想做的,但速度很慢。此代码需要 5-6 秒来绘制 100 个样本,但我需要在不到 1 秒的时间内完成它。

import matplotlib.pyplot as plt
import numpy as np

x = np.zeros((1000,))
data = np.zeros((1000,))

plt.ion()
fig, ax = plt.subplots()
ax.set_ylim(-2, 2)
ax.set_xlim(0, 1000)
l, = ax.plot(data)

import serial
ser = serial.Serial('COM3',9600)
index = 0
while True:
    b = ser.readline().decode("utf-8") # reading samples
    temp = b[:-1].split()
    print(temp[0], temp[1])
    x[index] = index
    data[index] = temp[0]
    print(index, temp[0])
    l.set_xdata(x[0:index])
    l.set_ydata(data[0:index])
    index += 1
    fig.canvas.draw()
    fig.canvas.flush_events()

如何提高这段代码的性能?

我需要它足够快,这样我就可以在来自多个传感器的原始数据之上实时看到经过过滤(平滑)的数据版本。


这是程序提前终止时分析器的屏幕截图。

这似乎是对 Matplotlib 性能的限制。它不是为实时高速绘图而制作的。你应该看看 pyqtgraph。 有examples条可以修改。

它使用 OpenGL 进行绘图,对于简单的线图,每秒可以达到几百个点。

我仍然认为您可能不需要单独绘制每个数据点,但即使只是调整我在评论中链接的 blitting 示例,我也得到 155 fps。

import time
import matplotlib.pyplot as plt
import numpy as np

x = np.zeros((1000,))
data = np.zeros((1000,))

fig, ax = plt.subplots()
ax.set_ylim(-2, 2)
ax.set_xlim(0, 1000)

# animated=True tells matplotlib to only draw the artist when we
# explicitly request it
l, = ax.plot(data, animated=True)

# make sure the window is raised, but the script keeps going
plt.show(block=False)

# stop to admire our empty window axes and ensure it is rendered at
# least once.
#
# We need to fully draw the figure at its final size on the screen
# before we continue on so that :
#  a) we have the correctly sized and drawn background to grab
#  b) we have a cached renderer so that ``ax.draw_artist`` works
# so we spin the event loop to let the backend process any pending operations
plt.pause(0.1)

# get copy of entire figure (everything inside fig.bbox) sans animated artist
bg = fig.canvas.copy_from_bbox(fig.bbox)
# draw the animated artist, this uses a cached renderer
ax.draw_artist(l)
# show the result to the screen, this pushes the updated RGBA buffer from the
# renderer to the GUI framework so you can see it
fig.canvas.blit(fig.bbox)

N = 1000
tic = time.time()
index = 0
for ii in range(N):
    # reset the background back in the canvas state, screen unchanged
    fig.canvas.restore_region(bg)

    # update the artist, neither the canvas state nor the screen have changed
    # b = ser.readline().decode("utf-8") # reading samples
    b = f'{2 * np.random.rand()} abcd'
    temp = b[:-1].split()
    x[index] = index
    data[index] = temp[0]
    l.set_xdata(x[0:index])
    l.set_ydata(data[0:index])
    index += 1

    # re-render the artist, updating the canvas state, but not the screen
    ax.draw_artist(l)
    # copy the image to the GUI state, but screen might not be changed yet
    fig.canvas.blit(fig.bbox)
    # flush any pending GUI events, re-painting the screen if needed
    fig.canvas.flush_events()
    # you can put a pause in if you want to slow things down
    # plt.pause(.1)

toc = time.time()
fps = N / (toc - tic)
print(f'fps = {fps}')