我试图使用从Teensy 3.2接收到的Python(PyQtGraph)尽快绘制数据,该Python通过串行通信发送模拟数据。该代码可以充分绘制较高频率的测试波形(约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 }
有人对如何加快速度有任何建议吗?
正如上面MR所建议的,如果在发送之前打包更多数据而不是一次发送两个字节的数据包,则可能会更好。
但是,您看到的可怕性能与在计算机上读取数据的方式有关。如果您仅从串行端口读取两个字节并将它们附加到绘图上,那么最终的开销将是巨大的。
如果改用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进行处理,巧合的是,我发现了这个问题,该问题与您的非常相似。因此,值得信赖的是numpy解决方案。
不幸的是,便携式示波器的电池没电了,所以我无法正确测试上面的代码。但是我认为从那里开始一个好的解决方案应该是可行的。
我没有详细检查Teensy代码,但快速浏览一下,我认为您用于提供ADC速度的中断定时器可能太紧了。您忘了考虑随每个数据字节移动的起始位和停止位,并且您没有考虑完成AD转换所花费的时间(我想这应该很小,可能是10微秒)。考虑到所有因素,我认为您可能需要增加心跳,以确保您不会引入不规则的采样时间。Teensy应该有可能获得更快的采样率,但是要做到这一点,您需要使用完全不同的方法。我猜是另一个问题的好话题…