我试图使用arduino和pyserial,numpy和matplotlib绘制加速度计的串行数据。问题是当GUI打开时,输入数据速率变得非常慢并且绘图也非常慢,如果我不打开GUI并且只是在命令窗口上打印数据,则接收的数据很快。 Plz帮忙!
这是我的python代码:
import serial
import matplotlib.pyplot as plt
import numpy as np
import time
ser = serial.Serial('COM3', 9600, timeout=0) #sets up serial connection (make sure baud rate is correct - matches Arduino)
ser.flushInput()
ser.flushOutput()
plt.ion() #sets plot to animation mode
length = 500 #determines length of data taking session (in data points)
x = [0]*length #create empty variable of length of test
y = 0
z = 0
fig, ax = plt.subplots()
line, = ax.plot(np.random.randn(100))
plt.show(block=False)
xline, = plt.plot(x) #sets up future lines to be modified
plt.ylim(30,120) #sets the y axis limits
#for i in range(length): #while you are taking data
tstart = time.time()
n = 0
while time.time()-tstart < 5:
y = (ser.readline().decode('utf-8')[:-2])
if not (len(y)==0):
z = float(y)
x.append(float(z)) #add new value as int to current list
del x[0]
xline.set_xdata(np.arange(len(x))) #sets xdata to new list length
xline.set_ydata(x) #sets ydata to new list
# ax.draw_artist(ax.patch)
# ax.draw_artist(line)
# fig.canvas.update()
fig.canvas.draw()
fig.canvas.flush_events()
#plt.pause(0.0001) #in seconds #draws new plot
#plt.show()
n +=1
print (z)
print(n)
ser.close() #closes serial connection (very important to do this! if you have an error partway through the code, type this into the cmd line to close the connection)
答案 0 :(得分:0)
有几件事可以加快这个过程。当我们尝试使用arduino读出地震检波器时,我们遇到了同样的问题。
主要技巧是在绘图前缓冲十个或更多值,而不是为每个新事件绘制。
使用动画包而不是手动绘图也可能加快这个过程。
查看我们的github代码: https://gist.github.com/ibab/10011590
为什么要将z 2次投射浮动:
z = float(y)
x.append(float(z))
答案 1 :(得分:0)
根据我的经验,用10 000点重绘一行可能需要5毫秒,但这取决于后端和计算机。如果你想要更快地做到这一点,那么你就麻烦了。另一方面,由于感知延迟,不需要或者每秒更新图表超过50次。
因此,正如MaxNoe
所说,如果数据是快速数据,则应缓冲数据。您可以使用固定缓冲区,也可以使用超时缓冲区。 (似乎您仍然收到稳定的数据流,否则您会在ser.readline
没有超时的情况下遇到问题。)
您还可以采取其他一些措施来加快代码速度。在不知道数据量的情况下,我无法说明这些是否会对性能产生任何影响。分析是关键(如James Mills
所述)。
首先,您应该将x
和y
作为ndarray
(NumPy数组),以避免将列表转换为数组的开销。基本上是:
# initializing the array
x = np.zeros(length)
# adding one item to the array:
# shift the existing items and add an item to the end
x[:-1] = x[1:]
x[-1] = float(z)
另请注意,由于set_xdata
保持不变,因此无需每轮xdata
。databuf = []
# collect data until you have something and at least 20 ms has gone
t0 = time.time()
while time.time() - t0 < 0.02 or len(databuf) == 0:
ln = ser.readline()
try:
databuf.append(float(ln[:-2]))
except:
# something went wrong, but now we don't care
pass
n_newpoints = len(databuf)
x[:-n_newpoints] = x[n_newpoints:]
x[-n_newpoints:] = databuf
# draw etc ...
。在循环之前做一次。
如果你最后使用缓冲,那么基于时间的缓冲在每个刷新周期大致采用这种方式:
databuf
此处{{1}}是一个列表,但由于它是一个简短的列表,转换开销无关紧要。
通过这些改进,您应该能够流畅地更新10000点的图形,而不会使计算机停止运转。如果你在性能非常适中的机器上进行此操作(RaspberryPi等),那么配方就是降低更新频率。
答案 2 :(得分:0)
当从arduino的串行端口绘制数据时,我遇到了类似的速度问题。解决方案在以下地址中描述:http://asaf.pm/wordpress/?p=113#comment-273 在这种情况下,我实现大约700fps绘制一个包含50个数据点的数组。
答案 3 :(得分:0)
我认为您面临这些性能问题的主要原因是matplotlib并非主要用于实时数据。通过脚本创建出色的数据图表确实很有用,但是添加单个样本并重绘可能不是matplotlib的亮点。
请考虑其他用于实时查看数据的选项,例如(无耻的插头)lognplot:https://github.com/windelbouwman/lognplot
可以在以下位置找到另一个有用的实时数据查看程序列表:https://arduino.stackexchange.com/questions/1180/serial-data-plotting-programs