串行数据保存的定时添加

问题描述

我编写了一个程序来保存来自 arduino 的串行数据,绘制它,为其添加时间戳并将数据保存到 CSV。串行数据每隔几秒发送一次。但是,arduino 程序的时间安排与 Python 程序不一致,并且经常会出现异相,我在 CSV 文件错误列中得到时间戳,这使得处理数据变得困难。

我认为解决方案是由 for 控制的 range 循环,但无法完全弄清楚如何去做。

相关代码如下,串行数据在 1-32 个图之间,但出于空间原因,我在此处将其压缩为 3 个,但我通常会注释掉不相关的,因此需要循环范围。

line=ser.readline()      #ascii
print(line)
seconds = time.time()

line_as_list = line.split(b',')
J = float(line_as_list[0]) 
L = float(line_as_list[1])
M = float(line_as_list[2])
relProb = line_as_list[4] 
relProb_as_list = relProb.split(b'\n')
relProb_float = float(relProb_as_list[0])

# Add x and y to lists
xs.append(seconds)
As.append(J)
bs.append(L)
cs.append(M)


# Limit x and y lists to 20 items
#xs = xs[-20:]
#ys = ys[-20:]

# Draw x and y lists
ax.clear()
ax.plot(xs,As,label="Plot 1")
ax.plot(xs,bs,label="Plot 2")
ax.plot(xs,cs,label="Plot 3")


dateTimeObj = datetime.Now()
f = open('Datafile.csv','a')
line=str(line,"utf-8") ## the UTF-8 bit is essential to make it not have b' '
timestampStr = dateTimeObj.strftime("%d-%b-%Y (%H:%M:%s.%f)")#convert time to string


f.write(timestampStr)#write the time to the file
f.write(",") # seperate the time from the data
f.write(line)
f.close()

解决方法

我怀疑问题在于循环的处理时间太长,您可能不仅会丢失部分行,有时还会丢失整行。当串行缓冲区填满时会发生什么取决于硬件和与之一起使用的特定驱动程序(可能是 USB 串行驱动程序?)。似乎串行输入缓冲区正在填满,并默默地抛出一些数据。我想绘制绘图可能是代码中最慢的部分,因为 matplotilb 使用 CPU 渲染,并且专为定制而不是速度而设计。我看到了解决这个问题的两种解决方案:将您的代码加速到可以跟上的程度,或者将绘制图形的过程与使用线程读取串行端口的过程分开。

您可能可以做很多小事情来加速您的代码,但最大的事情之一可能是研究 matplotlib 的 animation 功能,这些功能旨在最大限度地减少仅更新部分所需的额外处理已更改的图表。

另一种选择(虽然两者实际上可以结合)是将串行端口的读取移动到一个单独的线程,这样图形的绘制就不会中断数据流。基于您的代码的快速示例可能如下所示(从您的代码推断出的一些内容):

import serial
import time
from threading import Thread
import matplotlib.pyplot as plt
import datetime

ser = serial.Serial()
xs = []
As = []
bs = []
cs = []

fig,(ax,) = plt.subplots(1,1)
f = open('Datafile.csv','a')

keep_reading = True

def reader_thread():
    while keep_reading:
        #do processing on img
        
        line=ser.readline()      #ascii
        print(line)
        seconds = time.time()
        
        line_as_list = line.split(b',')
        J = float(line_as_list[0]) 
        L = float(line_as_list[1])
        M = float(line_as_list[2])
        relProb = line_as_list[4] 
        relProb_as_list = relProb.split(b'\n')
        relProb_float = float(relProb_as_list[0])
        
        # Add x and y to lists
        xs.append(seconds)
        As.append(J)
        bs.append(L)
        cs.append(M)
        
        
        # Limit x and y lists to 20 items
        #xs = xs[-20:]
        #ys = ys[-20:]
        dateTimeObj = datetime.now()
        
        line=str(line,"utf-8") ## the UTF-8 bit is essential to make it not have b' '
        timestampStr = dateTimeObj.strftime("%d-%b-%Y (%H:%M:%S.%f)")#convert time to string
        
        
        f.write(timestampStr)#write the time to the file
        f.write(",") # seperate the time from the data
        f.write(line)

try:
    t = Thread(target=reader_thread)
    t.start()
    while True:
        # Draw x and y lists
        ax.clear()
        ax.plot(xs,As,label="Plot 1")
        ax.plot(xs,bs,label="Plot 2")
        ax.plot(xs,cs,label="Plot 3")
        time.sleep(.1) #not strictly necessary
except KeyboardInterrupt: #ctrl-c to quit
    pass
finally:
    keep_reading = False
    t.join()
    ser.close()
    f.close()