Performance 如何在Python中高效地从串行端口读取、编码和存储数据?
该程序的目的是:从串行端口读取串行数据,将其转换为int并将其存储到列表中。稍后将使用matplotlib实时处理和打印这些数据 问题是:Performance 如何在Python中高效地从串行端口读取、编码和存储数据?,performance,python-3.x,serialization,matplotlib,serial-port,Performance,Python 3.x,Serialization,Matplotlib,Serial Port,该程序的目的是:从串行端口读取串行数据,将其转换为int并将其存储到列表中。稍后将使用matplotlib实时处理和打印这些数据 问题是: 创建列表的时间太长(可能需要几秒钟) 分钟)。 ps:列表的大小为n=1024 在花了那么多时间创建列表后,我得到了以下信息: “(…) 文件“C:\Users\ianmc\AppData\Local\Programs\Python\Python35-32\lib\site packages\matplotlib\lines.py”,第645行,在recac
ps:列表的大小为n=1024
“(…)
文件“C:\Users\ianmc\AppData\Local\Programs\Python\Python35-32\lib\site packages\matplotlib\lines.py”,第645行,在recache中
raise RUNTIMERROR('xdata和ydata的长度必须相同')
运行时错误:扩展数据和ydata的长度必须相同“ 可能情节争论的长度不同,但为什么呢
import serial
import math
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
dt = 0.01
Fs = 44000.0 # sample rate
timestep = 1.0/Fs # sample spacing (1/sample rate)
n = 1024 # size of the array data (BUFFER SIZE)
t = np.arange(0, (n/100), dt) # t range
w = 10000 # frequency of the input
#initialize data list
data = [0] * n
#initialize magnitude list
magnitude = [0] * (n//2)
# open and prepare serial port
ser = serial.Serial('COM4', 9600, timeout=8,
parity=serial.PARITY_EVEN, rtscts=1)
# calculates the fft dB
def createMagnitudeDB(magnitude):
magnitudeDB = 20 * (math.log(magnitude,10))
return magnitudeDB
def update(data):
# update the curves with the incoming data
timeCurve.set_ydata(data[0])
freqCurve.set_ydata(data[1])
return timeCurve, freqCurve,
def generateData():
# simulate new data coming in
while True:
# THE EFFICIENCY PROBLEM IS IN THIS FOR
# Read data from arduino
for i in range(n): #while you are taking data
PreData = ser.readline()
data.append(int(PreData))
# fft computing and normalization
magnitude = np.fft.fft(data)/n
magnitude = np.abs(magnitude[range(n//2)])
# calculates the fft dB
magnitude_dB = [createMagnitudeDB(x) for x in magnitude]
yield (data, magnitude)
fig = plt.figure()
# create time graph axis
timeGraph = plt.subplot(2, 1, 1)
timeGraph.set_ylim(-200, 200)
timeGraph.set_xlim([0, n])
timeGraph.set_xlabel('Time')
timeGraph.set_ylabel('Amplitude')
# create frequency graph axis
freqGraph = plt.subplot(2, 1, 2)
freqGraph.set_ylim([0, 70])
freqGraph.set_xlim([0, (Fs/2)])
freqGraph.set_xlabel('Freq (Hz)')
freqGraph.set_ylabel('Magnitude')
#get frequency range
k = np.arange(n)
T = n/Fs
freq = k/T # two sides frequency range
freq = freq[range(n//2)] # one side frequency range
# plot the curves
timeCurve, = timeGraph.plot(np.linspace(0, 1, n),'b')
freqCurve, = freqGraph.plot(freq, magnitude,'g')
# animate the curves
ani = animation.FuncAnimation(fig, update, generateData,
interval = 10, blit=True)
# open window
plt.show()
# close serial connection
ser.close()
另一条信息:如果不使用串行端口,而是生成自己的数据,则程序工作正常,如下所示:
def generateData():
# simulate new data coming in
while True:
nse = np.random.randn(len(t))
r = np.exp(-t/0.05)
cnse = np.convolve(nse, r)*dt
cnse = cnse[:len(t)]
data = 100*np.sin(2*np.pi*t) + 500*cnse
# fft computing and normalization
magnitude = np.fft.fft(data)/n
magnitude = np.abs(magnitude[range(n//2)])
# calculates the fft dB
magnitude_dB = [createMagnitudeDB(x) for x in magnitude]
yield (data, magnitude)
唯一的区别是generateData函数和不使用open/close串行命令,这就是为什么我不理解另一个代码上的“xdata和ydata必须是相同长度”错误(两个代码上的数据长度相同)
编辑:
该程序通过蓝牙从正在读取模拟引脚的arduino接收数据。我测量了arduino的读取时间,读取每个值需要“0.015毫秒”。读取和打印每个值需要“3.1ms”。计算机读取每个值的时间为“100ms”
这意味着创建列表需要n*100(ms),即n是列表的元素数。我可以使用的最小“n”是256,创建列表需要25.6秒。这对于实时图形来说太长了。将1024个项目添加到列表并不需要几秒钟。从您的设备读取1024行数据可能需要几秒钟或几分钟,但我认为我们对此无能为力 在
generateData
中,您将项目附加到data
中,但从不删除它们,因此data
的增长没有限制。我猜您希望在循环的每次迭代中从一开始就重新创建数据
:
# Read data from arduino
data = []
for i in range(n): #while you are taking data
PreData = ser.readline()
data.append(int(PreData))
问题1:
从外部很难判断您的数据采集需要多长时间,这是“太长”还是正常。串行连接的速度是每秒9600位,但我们不知道您读取了多少位。您可以使用time
模块(start=time.time()。在这种情况下,也可以在每次读取数据后更新图形
然而,真正的问题可能在于,您试图每10毫秒调用一次generateData()
(在animation.FuncAnimation(…,interval=10,…)
中指定)。这可能会导致问题,因为数据获取肯定需要更长的时间。也就是说,即使一次只读取一位,读取1024位也需要大约100毫秒。
因此,它可能有助于将间隔设置为更合理的数字,或者根本不使用FuncAnimation
,只需在读取数据后绘制数据即可
问题2:
问题是,您使用长度为n
,timeCurve,=timeGraph.plot(np.linspace(0,1,n))的数组初始化时间图
此时,数组数据中已经有n
零。在第一次迭代中,附加从串行端口读取的另一个n
编号。因此,在这一点上,data
是2*n
长的
现在的问题是,您只更新绘图的ydata(timeCurve.set_ydata(数据[0])
),因此x轴仍有n
值(在开始时隐式设置),这与要设置到该绘图y轴的2*n
值不匹配
解决方案:
第一个选项是在每次迭代中重新初始化数据,使其始终具有n
条目
第二个选项是通过以下方式更新绘图:
timeCurve.set_data(np.arange(len(data[0])), data[0])
将x和y数据都设置为相同长度的数组
我测量了读取时间,它从“0.06秒”到“0.16秒”不等,平均值约为0.1秒。追加时间是打印“0.000000秒”。我不知道我是否做错了什么,或者是速度太快了。这意味着如果我创建一个包含1024个数字的列表,需要1024*0.1=102.4s才能创建该列表。这是一个巨大的延迟,使得无法绘制实时图形。即使我将“n”更改为256,也会有25.6s的延迟。我认为无法绘制每次我读取一个数据时都要用图形表示,因为要计算FFT,我需要一个数字序列。我用一些关于计时的新信息更新了帖子。问题实际上是串行端口的读取时间。波特率的增加能让事情变得更好吗?如果数据读取需要约0.1秒,这将完全符合我的预测。Appe将数据发送到列表确实是一个非常快速的操作,不能用时间准确测量,所以不用担心。您是否确实尝试了脚本并测量了创建列表的102秒?增加波特率可能会起作用。请注意,设备必须设置为该波特率,设置过高的波特率可能会使其变得不重要无法与设备通信。所有这些问题实际上都是针对您的配置的,我认为您应该创建一个新的(无任何matplotlib打印)然后问一个关于它的新问题,具体说明你的确切硬件。所以可能不是回答这样一个问题的最佳地点,但是如果你选择在这里发布,请确保使用arduino
和bluetooth
标记。我刚刚预测的102。但是无论如何,谢谢你,我会按照你的建议去做。这可能是因为我使用bluetooth来接收数据吗串行数据?pr