3

所以这就是交易,我有一个模块通过串行端口以 9600 波特发送数据,我正在使用 matplotlib 实时绘制该数据。我写了这段代码

#! python

############ import section ###################
import serial
import struct
import numpy as np
import matplotlib.pyplot as plt
###############################################

############ Serial Configuration #############

com = serial.Serial(14,9600)
print ("Successfully opened " + com.portstr)

###############################################
def get_new_values():
    s = com.read(20)
    raw_data =  struct.unpack('!BBBBBBBBBBBBBBBBBBBB', s)
    j = 0;
    norm_data = []
    for i in range(0,20,2):
        big_byte = raw_data[i+1]+(raw_data[i]*256)
        norm_data.append(big_byte)
        j = j+1
    return norm_data

x = range(0,10,1)
y = get_new_values()

plt.ion()
line, = plt.plot(x,y)

while(1):
    a = get_new_values()
    line.set_ydata(a)  
    plt.draw()

com.close()
print ("Port Closed Successfully")

我得到 20 个字节,然后生成 10 个大字节(2 字节数据在传输时被拆分为两个 1 字节值)。但我只是注意到我没有从中获得实时值。如果这很重要,我在 Windows 7 Home Basic 上。

任何线索为什么会这样?

编辑

此外,每当我点击情节时,它就会挂起。

4

1 回答 1

2

我知道下面的代码对于您的简单问题来说似乎很长而且过于复杂,但是手动优化通常是更多的代码和更多的潜在错误。这就是为什么过早优化几乎总是错误的原因。

__init__其中设置绘图,设置轴、画布、线条(从屏幕外绘制的线条开始)和动画前背景的参考。另外__init__注册回调以处理调整大小和关闭。调整窗口大小时,on_resize需要回调来更新背景(用于 blit)。回调使用on_close锁来更新运行状态。我并没有消除所有的竞争条件,但这可以防止_tkinter.TclError由于尝试对终止的 Tk 应用程序进行 blit 引起的。我只用 Tk 进行了测试,并且只在一台机器上进行了测试。YMMV,我愿意接受建议。

run方法中,我添加了对canvas.flush_events(). 如果您尝试拖动窗口并调整其大小,这应该可以防止绘图窗口挂起。此方法中的 while 循环调用self.get_new_values()以设置绘图中的数据。然后它使用配置的方法更新绘图。如果self.blit为真,则使用canvas.blit,否则使用pyplot.draw

变量spf(每帧样本数)控制一帧中绘制的样本数。您可以在您的实现中使用它get_new_values来确定要读取的字节数(例如2 * self.spf,每个样本 2 个字节)。我已将默认值设置为 120,如果您的数据速率为每秒 600 个样本,则为每秒 5 帧。您必须在图表中找到最大化吞吐量与时间分辨率的最佳点,同时还要跟上传入的数据。

将数据读入 NumPy 数组而不是使用 Python 列表可能会加快处理速度。此外,它还可以让您轻松访问对信号进行下采样和分析的工具。您可以直接从字节字符串读入 NumPy 数组,但请确保您获得正确的字节顺序:

>>> data = b'\x01\xff' #big endian 256 + 255 = 511
>>> np.little_endian   #my machine is little endian
True
>>> y = np.fromstring(data, dtype=np.uint16); y  #so this is wrong
array([65281], dtype=uint16)
>>> if np.little_endian: y = y.byteswap()
>>> y #fixed
array([511], dtype=uint16)

代码:

from __future__ import division
from matplotlib import pyplot
import threading

class Main(object):
    def __init__(self, samples_per_frame=120, blit=True):
        self.blit = blit
        self.spf = samples_per_frame
        pyplot.ion()
        self.ax = pyplot.subplot(111)
        self.line, = self.ax.plot(range(self.spf), [-1] * self.spf)
        self.ax.axis([0, self.spf, 0, 65536])
        pyplot.draw()
        self.canvas = self.ax.figure.canvas
        self.background = self.canvas.copy_from_bbox(self.ax.bbox)

        self.canvas.mpl_connect('resize_event', self.on_resize)
        self.canvas.mpl_connect('close_event', self.on_close)
        self.lock = threading.Lock()
        self.run()

    def get_new_values(self):
        import time
        import random
        #simulate receiving data at 9600 bps (no cntrl/crc)
        time.sleep(2 * self.spf * 8 / 9600)
        y = [random.randrange(65536) for i in range(self.spf)]
        self.line.set_ydata(y)

    def on_resize(self, event):
        self.line.set_ydata([-1] * self.spf)
        pyplot.draw()
        self.background = self.canvas.copy_from_bbox(self.ax.bbox)

    def on_close(self, event):
        with self.lock:
            self.running = False

    def run(self):
        with self.lock:
            self.running = True
        while self.running:
            self.canvas.flush_events()
            with self.lock:
                self.get_new_values()
            if self.running:
                if self.blit:
                    #blit for a higher frame rate
                    self.canvas.restore_region(self.background)
                    self.ax.draw_artist(self.line)
                    self.canvas.blit(self.ax.bbox)
                else:
                    #versus a regular draw
                    pyplot.draw()

if __name__ == '__main__':
    Main(samples_per_frame=120, blit=True)
于 2011-10-06T08:33:39.337 回答