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

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

问题描述

我试图使用从Teensy 3.2接收到的Python(PyQtGraph)尽快绘制数据,该Python通过串行通信发送模拟数据.该代码可以充分绘制出更高频率的测试波形(约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端和Teensy端的缓冲区,但是,这会严重减慢绘图速度,并且绘图的频率响应会降至单赫兹.

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.

如果您处理的字节数尽可能多,则可以获得几乎实时的性能.

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做到这一点,巧合的是,我发现

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天全站免登陆