简体   繁体   English

什么是绘制实时串行数据的最有效的Python IPC机制?

[英]What is most efficient Python IPC mechanism for plotting real-time serial data?

What is the fastest Python mechanism for getting data read off of a serial port, to a separate process which is plotting that data? 从串行端口读取数据到绘制该数据的单独进程的最快Python机制是什么?

I am plotting eeg data in real-time that I read off of a serial port. 我正在实时绘制从串口读取的eeg数据。 The serial port reading and packet unpacking code works fine, in that if I read and store the data, then later plot the stored data, it looks great. 串行端口读取和数据包拆包代码工作正常,因为如果我读取并存储数据,然后再绘制存储的数据,则看起来不错。 Like this: 像这样:

note: device generates test sine wave for debugging 注意:设备会生成测试正弦波进行调试

在此处输入图片说明

I am using pyQtGraph for the plotting. 我正在使用pyQtGraph进行绘图。 Updating the plot in the same process that I read the serial data in is not an option because the slight delay between serial read() calls causes the serial buffer to overflow and bad check-sums ensue. 不能以与读取串行数据相同的过程来更新绘图,因为串行read()调用之间的微小延迟会导致串行缓冲区溢出并导致错误的校验和。 pyQtGraph has provisions for rendering the graph on a separate process, which is great, but the bottle-neck seems to be in the inter-process communication. pyQtGraph提供了在单独的进程上呈现图的规定,这很好,但是瓶颈似乎在进程间通信中。 I have tried various configuration of Pipe() and Queue(), all of which result in laggy, flickering graph updates. 我尝试了Pipe()和Queue()的各种配置,所有这些配置都会导致图形更新缓慢,闪烁。 So far, the smoothest, most consistent method of getting new values from the serial port to the graph seems to be through shared memory, like so: 到目前为止,从串行端口向图形获取新值的最流畅,最一致的方法似乎是通过共享内存,如下所示:

from pyqtgraph.Qt import QtGui
import pyqtgraph as pg
from multiprocessing import Process, Array, Value, Pipe
from serial_interface import EEG64Board
from collections import deque

def serialLoop(arr):
    eeg = EEG64Board(port='/dev/ttyACM0')
    eeg.openSerial() 
    eeg.sendTest('1')        #Tells the eeg device to start sending data
    while True:
        data = eeg.readEEG() #Returns an array of the 8 latest values, one per channel
        if data != False:    #Returns False if bad checksum
            val.value = data[7] 

val = Value('d',0.0)
q = deque([],500)

def graphLoop():
    global val,q
    plt = pg.plot(q)
    while True:
        q.append(val.value)
        plt.plot(q,clear=True)
        QtGui.QApplication.processEvents()

serial_proc = Process(target=serialLoop, args=(val,), name='serial_proc')
serial_proc.start()

try:
    while True:
        graphLoop()
except KeyboardInterrupt:
    print('interrupted')

The above code performs real-time plotting by simply pulling the latest value recorded by the serialLoop and appending it to a deque. 上面的代码通过简单地提取serialLoop记录的最新值并将其附加到双端队列来执行实时绘图。 While the plot updates smoothly, it is only grabbing about 1 in 4 values, as seen in the resulting plot: 尽管该图平滑更新,但它仅捕获了四分之一的值,如结果图所示:

在此处输入图片说明

So, what multi-process or thread structure would you recommend, and then what form of IPC should be used between them? 那么,您将建议哪种多进程或线程结构,然后在它们之间使用哪种形式的IPC?

Update: 更新:

I am receiving 2,000 samples per second. 我每秒接收2,000个样本。 I am thinking that if I update the display at 100 fps and add 20 new samples per frame then I should be good. 我在想,如果我以100 fps更新显示并每帧添加20个新样本,那我应该很好。 What is the best Python multithreading mechanism for implementing this? 实现此目的的最佳Python多线程机制是什么?

This may not be the most efficient, but the following code achieves 100 fps for one plot, or 20 fps for 8 plots. 这可能不是最有效的,但是下面的代码可在一幅图中达到100 fps,或在八幅图中达到20 fps。 The idea is very simple: share an array, index, and lock. 这个想法很简单:共享一个数组,索引和锁。 Serial fills array and increments index while is has lock, plotting process periodically grabs all of the new values from the array and decrements index, again, under lock. 当具有锁定时,串行填充数组并递增索引,绘图过程会定期在锁定下定期从数组和递减索引中获取所有新值。

from pyqtgraph.Qt import QtGui
import pyqtgraph as pg
from multiprocessing import Process, Array, Value, Lock
from serial_interface import EEG64Board
from collections import deque

def serialLoop(arr,idx,lock):
    eeg = EEG64Board(port='/dev/ttyACM0')
    eeg.openSerial() 
    eeg.sendTest('1')        #Tells the eeg device to start sending data
    while True:
        data = eeg.readEEG() #Returns an array of the 8 latest values, one per channel
        if data != False:    #Returns False if bad checksum
            lock.acquire()
            for i in range(8):
                arr[i][idx.value] = data[i] 
            idx.value += 1
            lock.release()
    eeg.sendTest('2') 

arr = [Array('d',range(1024)) for i in range(8)]
idx = Value('i', 0)
q = [deque([],500) for i in range(8)]
iq = deque([],500)
lock = Lock()

lastUpdate = pg.ptime.time()
avgFps = 0.0

def graphLoop():
    global val,q,lock,arr,iq, lastUpdate, avgFps
    win = pg.GraphicsWindow()
    plt = list()
    for i in range(8):
        plt += [win.addPlot(row=(i+1), col=0, colspan=3)]
    #iplt = pg.plot(iq)
    counter = 0
    while True:
        lock.acquire()
        #time.sleep(.01)
        for i in range(idx.value):
            for j in range(8):
                q[j].append(arr[j][i])        
        idx.value = 0
        lock.release()
        for i in range(8):
            plt[i].plot(q[i],clear=True)
        QtGui.QApplication.processEvents()
        counter += 1

        now = pg.ptime.time()
        fps = 1.0 / (now - lastUpdate)
        lastUpdate = now
        avgFps = avgFps * 0.8 + fps * 0.2

serial_proc = Process(target=serialLoop, args=(arr,idx,lock), name='serial_proc')
serial_proc.start()

graphLoop()

serial_proc.terminate()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM