来自高通量源的实时数据绘制

Real time data plotting from a high throughput source

提问人:Leo 提问时间:6/21/2022 最后编辑:norok2Leo 更新时间:6/22/2022 访问量:2981

问:

我想以一种快速更新的方式绘制实时图。

我拥有的数据:

  • 通过串行端口以 62.5 Hz 到达
  • 数据对应于 32 个传感器(因此绘制 32 条线与时间的关系)。
  • 32 点 *62.5Hz = 2000 点/秒

我当前的绘图循环的问题在于它的运行速度低于 62.5[Hz],这意味着我错过了一些来自串行端口的数据。

我正在寻找这个问题的任何解决方案,允许:

  • 要保存串口的所有数据。
  • 绘制数据(甚至跳过几个点/使用平均值/消除旧点并只保留最新的点)

这是我的代码,我使用随机数据来模拟串口数据。

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

#extra plot debugging
hz_ = [] #list of speed
time_=[] #list for time vs Hz plot


 #store all data generated
store_data = np.zeros((1, 33))
 #only data to plot 
to_plot = np.zeros((1, 33)) 
 #color each line 
colours = [f"C{i}" for i in range (1,33)]

fig,ax = plt.subplots(1,1, figsize=(10,8))
ax.set_xlabel('time(s)')
ax.set_ylabel('y')
ax.set_ylim([0, 300])
ax.set_xlim([0, 200])

start_time = time.time()
for i in range (100):
    loop_time = time.time()
     #make data with col0=time and col[1:11] = y values
    data = np.random.randint(1,255,(1,32)).astype(float) #simulated data, usually comes in at 62.5 [Hz]
    data =  np.insert(data, 0, time.time()-start_time).reshape(1,33) #adding time for first column
    store_data = np.append(store_data, data , axis=0)
    to_plot = store_data[-100:,]
    
    for i in range(1, to_plot.shape[1]):
        ax.plot(to_plot[:,0], to_plot[:,i],c = colours[i-1], marker=(5, 2), linewidth=0, label=i)
        #ax.lines = ax.lines[-33:] #This soluition speeds it up, to clear old code. 

    fig.canvas.draw()  
    fig.canvas.flush_events()
    Hz = 1/(time.time()-loop_time)
     #for time vs Hz plot
    hz_.append(Hz)
    time_.append( time.time()-start_time)
    print(1/(time.time()-loop_time), "Hz - frequncy program loops at")
   
 #extra fig showing how speed drops off vs time
fig,ax = plt.subplots(1,1, figsize=(10,8))
fig.suptitle('Decreasingn Speed vs Time', fontsize=20)
ax.set_xlabel('time(s)')
ax.set_ylabel('Hz')

ax.plot(time_, hz_)
fig.show()

enter image description here

我也在使用时尝试过

ax.lines = ax.lines[-33:]

去掉较旧的点,这加快了绘图速度,但仍然比我获取数据的速度慢。enter image description here

任何确保我收集所有数据并绘制一般趋势线(因此即使不是所有点)的库/解决方案都可以。也许是并行运行、采集数据和绘图的东西?

麻木 matplotlib 的

评论

0赞 sramazoth 6/21/2022
您仅限于 Python,还是可以使用其他语言?因为对于快速的数据处理和 GUI 显示,Python 可能很难完成这项工作。

答:

3赞 norok2 6/21/2022 #1

您可以尝试使用两个单独的进程:

  • 一个用于获取和存储数据
  • 一个用于绘制数据

下面有两个基本脚本来理解这个想法。 您首先运行它开始生成数字并将它们保存在文件中。 然后,在同一目录中,您可以运行它将读取文件的最后一部分并更新 Matplotlib 图。gen.pyplot.py

下面是生成数据的脚本:gen.py

#!/usr/bin/env python3

import time
import random

LIMIT_TIME = 100  # s
DATA_FILENAME = "data.txt"


def gen_data(filename, limit_time):
    start_time = time.time()
    elapsed_time = time.time() - start_time
    with open(filename, "w") as f:
        while elapsed_time < limit_time:
            f.write(f"{time.time():30.12f} {random.random():30.12f}\n")  # produces 64 bytes
            f.flush()
            elapsed = time.time() - start_time
            

gen_data(DATA_FILENAME, LIMIT_TIME)

这是绘制数据的脚本(从这个脚本重新设计):plot.py

#!/usr/bin/env python3


import io
import time
import matplotlib.pyplot as plt
import matplotlib as mpl
import matplotlib.animation


BUFFER_LEN = 64
DATA_FILENAME = "data.txt"
PLOT_LIMIT = 20
ANIM_FILENAME = "video.gif"


fig, ax = plt.subplots(1, 1, figsize=(10,8))
ax.set_title("Plot of random numbers from `gen.py`")
ax.set_xlabel("time / s")
ax.set_ylabel("random number / #")
ax.set_ylim([0, 1])


def get_data(filename, buffer_len, delay=0.0):
    with open(filename, "r") as f:
        f.seek(0, io.SEEK_END)
        data = f.read(buffer_len)
        if delay:
            time.sleep(delay)
    return data


def animate(i, xs, ys, limit=PLOT_LIMIT, verbose=False):
    # grab the data
    try:
        data = get_data(DATA_FILENAME, BUFFER_LEN)
        if verbose:
            print(data)
        x, y = map(float, data.split())
        if x > xs[-1]:
            # Add x and y to lists
            xs.append(x)
            ys.append(y)
            # Limit x and y lists to 10 items
            xs = xs[-limit:]
            ys = ys[-limit:]
        else:
            print(f"W: {time.time()} :: STALE!")
    except ValueError:
        print(f"W: {time.time()} :: EXCEPTION!")
    else:
        # Draw x and y lists
        ax.clear()
        ax.set_ylim([0, 1])
        ax.plot(xs, ys)


# save video (only to attach here) 
#anim = mpl.animation.FuncAnimation(fig, animate, fargs=([time.time()], [None]), interval=1, frames=3 * PLOT_LIMIT, repeat=False)
#anim.save(ANIM_FILENAME, writer='imagemagick', fps=10)
#print(f"I: Saved to `{ANIM_FILENAME}`")

# show interactively
anim = mpl.animation.FuncAnimation(fig, animate, fargs=([time.time()], [None]), interval=1)
plt.show()
plt.close()

Anim

请注意,我还包含并注释掉了我用于生成上面动画 GIF 的代码部分。

我相信这应该足以让你继续前进。