绘图时使用 pySerial 读取实时值 [英] Reading real time values with pySerial while plotting

查看:64
本文介绍了绘图时使用 pySerial 读取实时值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

#!Python############ 导入部分##################导入序列导入结构将 numpy 导入为 np导入 matplotlib.pyplot 作为 plt######################################################## 串行配置#############com = serial.Serial(14,9600)打印(成功打开"+ com.portstr)############################################def get_new_values():s = com.read(20)raw_data = struct.unpack('!BBBBBBBBBBBBBBBBBBBB', s)j = 0;norm_data = []对于范围内的 i (0,20,2):big_byte = raw_data[i+1]+(raw_data[i]*256)norm_data.append(big_byte)j = j+1返回 norm_datax = 范围(0,10,1)y = get_new_values()plt.ion()线,= plt.plot(x,y)同时(1):a = get_new_values()line.set_ydata(a)plt.draw()com.close()打印(端口关闭成功")

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

知道为什么会这样吗?

编辑

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

解决方案

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

__init__ 中,它设置了绘图,设置了轴、画布、线(从屏幕外绘制的线开始)和动画前背景的参考.另外 __init__ 注册回调来处理调整大小和关闭.当调整窗口大小时,需要 on_resize 回调来更新背景(用于 blit).on_close 回调使用锁来更新运行状态.我没有用这个消除所有的竞争条件,但这可以防止 _tkinter.TclError 尝试 blit 到终止的 Tk 应用程序引起的.我只用 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 数组,但请确保正确使用字节顺序:

<预><代码>>>>数据 = b'\x01\xff' #big endian 256 + 255 = 511>>>np.little_endian #我的机器是小端真的>>>y = np.fromstring(data, dtype=np.uint16);y #所以这是错误的数组([65281],dtype=uint16)>>>如果 np.little_endian: y = y.byteswap()>>>y #固定数组([511],dtype=uint16)

代码:

from __future__ 导入师从 matplotlib 导入 pyplot进口螺纹类主要(对象):def __init__(self, samples_per_frame=120, blit=True):self.blit = blitself.spf = samples_per_framepyplot.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.canvasself.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):导入时间随机导入#simulate 以 9600 bps 接收数据(无 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):使用 self.lock:self.running = False定义运行(自我):使用 self.lock:self.running = 真同时自我运行:self.canvas.flush_events()使用 self.lock:self.get_new_values()如果自运行:如果 self.blit:#blit 以获得更高的帧速率self.canvas.restore_region(self.background)self.ax.draw_artist(self.line)self.canvas.blit(self.ax.bbox)别的:#与常规抽签相比pyplot.draw()如果 __name__ == '__main__':主要(samples_per_frame=120,blit=True)

So here is the deal, I have a module which sends out data over the serial port at 9600 baud and I am using the matplotlib to plot that data in real time. I wrote this code

#! 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")

I get 20 bytes and then make 10 big bytes (2 byte data is split as two 1 byte values while transmitting). But I just noticed that I am not getting real time values from this. I am on windows 7 home basic if that matters.

Any clue why this is happening?

EDIT

Also, whenever I click on the plot it hangs.

解决方案

I know the code below seems long and overly complex for your simple problem, but manual optimization is usually more code and more potential bugs. That's why premature optimization is almost always a mistake.

In __init__ it sets up the plot, setting references for the axis, canvas, line (it starts with a line drawn off screen), and the pre-animation background. Additionally __init__ registers callbacks to handle resizing and shutdown. The on_resize callback is needed to update the background (used for the blit) when the window is resized. The on_close callback uses a lock to update the running status. I haven't eliminated all race conditions with this, but this works to prevent a _tkinter.TclError caused by trying to blit to a terminated Tk app. I only tested with Tk, and on only one machine. YMMV, and I'm open to suggestions.

In the run method I've added a call to canvas.flush_events(). This should keep the plot window from hanging if you try to drag the window around and resize it. The while loop in this method calls self.get_new_values() to set the data in the plot. It then updates the plot using the configured method. If self.blit is true, it uses canvas.blit, else it uses pyplot.draw.

The variable spf (samples per frame) controls how many samples are plotted in a frame. You can use it in your implementation of get_new_values to determine the number of bytes to read (e.g. 2 * self.spf for 2 bytes per sample). I've set the default to 120, which is 5 frames per second if your data rate is 600 samples per second. You have to find the sweet spot that maximizes throughput vs time resolution in the graph while also keeping up with the incoming data.

Reading your data into a NumPy array instead of using a Python list will likely speed up processing. Also, it would give you easy access to tools to downsample and analyze the signal. You can read into a NumPy array directly from a byte string, but make sure you get the endianess right:

>>> 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)

Code:

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)

这篇关于绘图时使用 pySerial 读取实时值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆