使用 Python 最大化来自 Teensy 3.2 的实时绘图数据的串行通信速度 [英] Maximizing serial communication speed for live plotting data from Teensy 3.2 using Python

查看:21
本文介绍了使用 Python 最大化来自 Teensy 3.2 的实时绘图数据的串行通信速度的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用从通过串行通信发送模拟数据的 Teensy 3.2 接收的 Python (PyQtGraph) 尽快绘制数据.该代码可以充分绘制更高频率的测试波形(约 5kHz 的正弦波),但该图需要近 30 秒才能显示频率的变化.例如,如果测试波形被关闭,它会继续绘制正弦波额外的半分钟.

I'm trying to plot data as quickly as possible with Python (PyQtGraph) received from a Teensy 3.2 which is sending analog data over a serial communication. The code can sufficiently plot a test waveform of higher frequencies (sine wave of about 5kHz), but it takes nearly 30 seconds for the plot to show a change in frequency. For example, if the test waveform is turned off, it continues to plot the sine wave for an additional half minute.

我尝试执行串行刷新"以清除 Python 端和青少年端的缓冲区,但是,这严重减慢了绘图速度,并且我的绘图的频率响应下降到了单赫兹.

I've tried performing a "serial flush" to clear out the buffer on both the Python side and Teensy side, however, that seriously slows down the plot and the frequency response of my plot goes down to single hertz.

Python(绘图)方面:

Python (Plotting) Side:

# Import libraries
from numpy import *
from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pg
import serial
import re

# Create object serial port
portName = "COM8"                      
baudrate = 115200 
ser = serial.Serial(portName,baudrate)

### START QtApp #####
app = QtGui.QApplication([])           
####################

win = pg.GraphicsWindow(title="Signal from serial port") # creates a window
p = win.addPlot(title="Realtime plot")  # creates empty space for the plot in the window
curve = p.plot()                        # create an empty "plot" (a curve to plot)
windowWidth = 100                       # width of the window displaying the curve - this is the time scale of the plot
Xm = linspace(0,0,windowWidth)          # create array of zeros that is the size of the window width   
ptr = -windowWidth                      # set first x position

# Realtime data plot. Each time this function is called, the data display is updated
def update():

    global curve, ptr, Xm    
    Xm[:-1] = Xm[1:]                    # shift data in the temporal mean 1 sample left 
    if ser.isOpen():                    # make sure there is data coming in
        b1 = ser.read(1)                # read the first byte of data
        b2 = ser.read(1)                # read the second byte of data
        data = b1 + b2                  # concatenate the two bytes
        data_int = int.from_bytes(data, byteorder='big')

        Xm[-1] = data_int                     # stack the data in the array
        ptr += 1                              # update x position for displaying the curve
        curve.setData(Xm)                     # set the curve with this data
        curve.setPos(ptr,0)                   # set x-y position in the graph to 0 and most recent data point - this creates the scrolling of the plot 
        QtGui.QApplication.processEvents()    # process the plot

### MAIN PROGRAM #####    
# this is a brutal infinite loop calling realtime data plot
while True: update()

### END QtApp ####
pg.QtGui.QApplication.exec_() 
##################

Teensy 3.2 侧:

Teensy 3.2 Side:

const int sensorPin = A9;
uint16_t sensorValue = 0;
byte b1;
byte b2;
int flag = 0;
IntervalTimer heartBeatTimer;

void setup() 
{
  analogReadRes(12);
  Serial.begin(115200);
  heartBeatTimer.begin(heartBeat, 140); // (1 / 115200 Baud) * 16 bits / integer = 139us per 16 bits sent. Interrupt at 140 us to synchronize with baud rate.
  pinMode(13, OUTPUT);
}

void heartBeat() 
{
  flag = 1;                           // Interrupt routine every 140us
}


void loop() 
{
  if (flag == 1) {
    sensorValue = analogRead(sensorPin);  // read the analog pin as a 16 bit integer
    b1 = (sensorValue >> 8) & 0xFF;       // break up the reading to two bytes
    b2 = sensorValue & 0xFF;              // get the second byte
    Serial.write(b1);                     // write the first byte (trying to speed things up by sending only strictly necessary data)
    Serial.write(b2);                     // write the second byte
    digitalWrite(13, HIGH);               // just to make sure we're interrupting correctly
    flag = 0;                             // wait for next interrupt
  }
  digitalWrite(13, LOW);                  // just to make sure we're interrupting correctly 
}

有人对如何加快速度有任何建议吗?

Does anyone have any suggestions on how to speed things up?

推荐答案

正如 MR 所建议的 以上 如果您在发送之前打包更多数据而不是一次发送两个字节的数据包,您可能会更好.

As M.R. suggested above you'd be probably better off if you pack more data before sending it through instead of sending a two-byte packet at a time.

但是您看到的可怕性能更多地与您在计算机上读取数据的方式有关.如果您只从串行端口读取两个字节并将它们附加到绘图中,则最终的开销会很大.

But the horrible performance you see has more to do with the way you read data on your computer. If you read just two bytes from your serial port and attach them to the plot the overhead you end up with is huge.

如果您改为处理 RX 缓冲区上可用的尽可能多的字节,您可以获得几乎实时的性能.

If you instead process as many bytes as you have available on your RX buffer you can get almost real-time performance.

只需更改您的更新功能:

Just change your update function:

def update():

    global curve, ptr, Xm    

    if ser.inWaiting() > 0                         # Check for data not for an open port
        b1 = ser.read(ser.inWaiting())             # Read all data available at once
        if len(b1) % 2 != 0:                       # Odd length, drop 1 byte
            b1 = b1[:-1]
        data_type = dtype(uint16)
        data_int = fromstring(b1, dtype=data_type) # Convert bytes to numpy array
        data_int = data_int.byteswap()             # Swap bytes for big endian
        Xm = append(Xm, data_int)              
        ptr += len(data_int)                              
        Xm[:-len(data_int)] = Xm[len(data_int):]   # Scroll plot
        curve.setData(Xm[(len(Xm)-windowWidth):])                     
        curve.setPos(ptr,0)                   
        QtGui.QApplication.processEvents()   

在玩弄一次迭代两个字节的想法后,我认为应该可以用 numpy 来完成它,巧合的是我发现 这个问题,与您的非常相似.因此,numpy 解决方案功不可没.

After toying a bit with the idea of iterating the bytes two at a time I thought it should be possible to do it with numpy, and coincidentally I found this question, which is very similar to yours. So credit goes there for the numpy solution.

不幸的是,我的便携式示波器的电池没电了,所以我无法正确测试上面的代码.但我认为一个好的解决方案应该是可行的.

Unfortunately, the battery of my portable scope died so I could not test the code above properly. But I think a good solution should be workable from there.

我没有详细检查 Teensy 代码,但快速浏览一下,我认为您用来为 ADC 提供节奏的中断计时器可能有点太紧了.您忘记考虑与每个数据字节一起传输的开始和停止位,并且您没有考虑完成 AD 转换所需的时间(我想这应该非常小,可能是 10 微秒).考虑到所有因素,我认为您可能需要增加心跳以确保不会引入不规则的采样时间.使用 Teensy 应该可以获得更快的采样率,但要做到这一点,您需要使用完全不同的方法.我猜另一个问题的好话题......

I did not check the Teensy code in detail, but at a quick glance, I think the timer for the interrupt you're using to give the tempo for the ADC might be a bit too tight. You forgot to consider the start and stop bits that travel with each data byte and you are not accounting for the time it takes to get the AD conversion done (I guess that should be very small, maybe 10 microseconds). All things considered, I think you might need to increase the heartbeat to be sure you're not introducing irregular sampling times. It should be possible to get much faster sampling rates with the Teensy, but to do that you need to use a completely different approach. A nice topic for another question I guess...

这篇关于使用 Python 最大化来自 Teensy 3.2 的实时绘图数据的串行通信速度的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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