使用 Python 最大化来自 Teensy 3.2 的实时绘图数据的串行通信速度

Posted

技术标签:

【中文标题】使用 Python 最大化来自 Teensy 3.2 的实时绘图数据的串行通信速度【英文标题】:Maximizing serial communication speed for live plotting data from Teensy 3.2 using Python 【发布时间】:2019-11-19 12:22:05 【问题描述】:

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

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

Python(绘图)端:

# 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 侧面:

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 

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

【问题讨论】:

我建议您在here 中提问。一种简单的加速方法,不是将传感器值作为单个消息发送,而是将它们放在一个像对象一样的数组中,将其序列化并立即发送。 【参考方案1】:

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

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

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

只需更改您的更新功能:

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 来完成,巧合的是,我发现了 this question,它与你的非常相似。因此,numpy 解决方案值得称赞。

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

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

【讨论】:

马科斯,这效果更好。谢谢!我认为我在发送方方面还有一些工作要做,以进一步提高效率。我在您的建议中更改的一件事是从“curve.setData(Xm)”到“curve.setData(Xm[(len(Xm)-windowWidth):])”,这似乎允许调整时间尺度。 不客气。我很高兴它有助于改善一点。我错过了正确的滚动,我会用你的评论更新我的答案。

以上是关于使用 Python 最大化来自 Teensy 3.2 的实时绘图数据的串行通信速度的主要内容,如果未能解决你的问题,请参考以下文章

详解如何基于Arduino兼容板Teensy LC实现虚拟键盘与鼠标

详解如何基于Arduino兼容板Teensy LC实现虚拟键盘与鼠标

python numpy maxpool:给定一个数组和来自argmax的索引,返回最大值

Neopixel的系列问题

kali linux SET工具包

Python:来自多个统计文件的统计摘要