如何在Python中有效地从串口读取,编码和存储数据?

时间:2016-12-13 19:23:42

标签: performance python-3.x serialization matplotlib serial-port

程序的目的:从串口读取串行数据,将它们转换为int并将它们存储到列表中。稍后将使用matplotlib实时处理和打印此数据。

问题:

  1. 列表创建时间太长(可能是几秒钟 分钟)。
    ps:列表的大小是n = 1024。
  2. 在创建列表的所有时间之后,我得到了这个:
    "(...)
    文件" C:\ Users \ ianmc \ AppData \ Local \ Programs \ Python \ Python35-32 \ lib \ site-packages \ matplotlib \ lines.py",第645行,重新计算
    引发RuntimeError(' xdata和ydata的长度必须相同')
    RuntimeError:xdata和ydata的长度必须相同"

    情节论证可能有不同的长度,但为什么呢?

  3. 代码:

    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.015ms"阅读每个值。要阅读和打印每个值,它需要" 3.1ms"。每个值的计算机读取时间为" 100ms"。

    这意味着创建列表需要n * 100(ms),n是列表元素的数量。最低限度的' n'我可以使用256,这将需要25.6秒来创建列表。这对于实时图表来说太长了。

2 个答案:

答案 0 :(得分:0)

将1024个项目附加到列表不需要几秒钟。从您的设备读取1024行可能需要几秒钟或几分钟,但我认为我们无法提供帮助。

generateData中,您将项目附加到data,但您永远不会将其删除,因此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 :(得分:0)

问题1:

很难从外部判断您的数据采集需要多长时间,以及这是否太长'或者只是正常串行连接的速度是每秒9600位,但我们不知道您读入了多少位。您可以使用time模块(start = time.time() stop = time.time() {计算数据采集的时间。 {1}})看看这是否超出了您的预期。在这种情况下,它也可以是在每次读取数据后更新图形的选项。

然而,真正的问题可能在于您尝试每10毫秒调用print (stop - start)(在generateData()中指定)。这可能会导致问题,因为数据采集肯定需要更长的时间。也就是说,即使您一次只读取一位,读取1024位也需要约100毫秒。 因此,它可能有助于将间隔设置为更合理的数字,或者根本不使用animation.FuncAnimation(..., interval = 10, ...),只需在读入数据后绘制数据。

问题2:

问题是您使用长度为FuncAnimationtimeGraph的数组初始化n
此时,您的数组数据中已包含timeCurve, = timeGraph.plot(np.linspace(0, 1, n))个零。在第一次迭代中,您追加从串行端口读取的另一个n个数字。所以此时ndata

现在的问题是你只更新了图的ydata(2*n,所以x轴仍然有timeCurve.set_ydata(data[0])个值(在开头隐含设置),这与{ {1}}要设置为该绘图的y轴的值。

解决方案:

  1. 第一个选项是在每次迭代中重新初始化数据,使其始终具有n个条目。

  2. 第二个选项是通过

    更新图表
    2*n

    将x和y数据都设置为相同长度的数组。