I am trying to show real time voltage data using a dynamic graph that shows the recorded voltage over time and display the plot inside of a Tkinter window that has other widgets.
(我正在尝试使用动态图显示实时电压数据,该图显示随时间变化的记录电压,并在具有其他小部件的Tkinter窗口内部显示图。)
This program also has to take various actions with switches and relays based on the measured values as well as sending out notifications of these actions via text messages and emails. (该程序还必须根据测量值对开关和继电器采取各种措施,并通过短信和电子邮件发送这些措施的通知。)
After many iterations on different methods, I settled on Matplotlib using clear() and draw() in each loop to update the plot and place just the email sending into a sub-process so that the internet delay does not stop the voltage sampling for a long period of time. (在使用不同方法进行多次迭代之后,我在每个循环中使用clear()和draw()来确定Matplotlib的状态,以更新绘图并将电子邮件发送到子进程中,从而使Internet延迟不会停止电压采样。时间长了。)
It is working quite well plotting 3 traces of 500 points each at about 1/4 second plot update rate on a Raspberry Pi 4. (它在Raspberry Pi 4上以大约1/4秒的绘图更新速率绘制3条500点的迹线的效果非常好。)
However, as I let the program run, I am finding that the loop time is getting longer and longer, slowing down the loop time from 1/4 second to 2.5 seconds after 16 hours.
(但是,当我让程序运行时,我发现循环时间越来越长,将循环时间从1/4秒降低到16小时后的2.5秒。)
Also, the Virtual Memory size has grown from 105MB to 500MB. (此外,虚拟内存大小已从105MB增加到500MB。)
I added code to isolate the culprit and narrowed it down to the clear() call to Matplotlib.
(我添加了代码来隔离罪魁祸首,并将其范围缩小到对Matplotlib的clear()调用。)
Here is a plot showing the time it took for each component of the loop over 3 hours of the program running in a while loop. (这是一个图表,显示了在while循环中运行程序3个小时后,循环的每个组件花费的时间。)
Time components of each loop over time
(每个循环随时间变化的时间分量)
Here you can see that the time it took for the call to clear() (red line) increased from 0.055 seconds to 0.7 seconds over 3 hours of the loop running.
(在这里您可以看到,在循环运行3小时后,调用clear()(红线)所花费的时间从0.055秒增加到0.7秒。)
All the other components of the loop stayed pretty much constant except for the calls to plot() having a couple of big spikes. (循环的所有其他组件几乎保持不变,除了对plot()的调用有几个大的尖峰。)
But the time taken by the clear() call keeps on increasing and is not acceptable. (但是clear()调用所花费的时间不断增加,这是不可接受的。)
It is most likely the culprit for the memory leakage too. (也是内存泄漏的元凶。)
Below are segments of the code I have related to matplotlib, including the code to measure the time for the graph above:
(以下是与matplotlib相关的代码段,包括用于测量上图时间的代码:)
from tkinter import *
from tkinter import ttk
from tkinter import messagebox
import tkinter as tk
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
root = Tk()
root.wm_title("Title")
# This is the initialization code for the plot
figure3 = plt.Figure(figsize=(9.9,5.8), dpi=100)
ax3 = figure3.add_subplot(111)
ax3.plot(time_plot,v1_plot,"r-", label="Phase 1") # Red line
ax3.plot(time_plot,v2_plot,"g-", label="Phase 2") # Green line
ax3.plot(time_plot,v3_plot,"b-", label="Phase 3") # Blue line
scatter3 = FigureCanvasTkAgg(figure3, root)
scatter3.get_tk_widget().grid(column=0, row=0, rowspan=2)
ax3.legend(loc=6)
ax3.set_xlabel('Time (secs)')
ax3.set_ylabel('Voltage (V)')
ax3.set_title(' EndTime = '+datetime.now().strftime("%H:%M:%S"))
ax3.grid()
scatter3.draw()
loopclock = time.time()
pclock = 0.0
# Main Loop
t4=t5=t6=t7=t8=0.0
while running:
c1 = time.time()
CheckADC() # reads the ADC & update lists for the plot call
c2 = time.time()
t1 = c2 - c1
Flowchart_Actions()
# This segment update the plot on the screen
ax3.clear() #- call to clear the previous plot **This is the culprit**
c3 = time.time()
t2 = c3 - c2
ax3.plot(time_plot,v1_plot,"r-", label="Phase 1") # Red line
ax3.plot(time_plot,v2_plot,"g-", label="Phase 2") # Green line
ax3.plot(time_plot,v3_plot,"b-", label="Phase 3") # Blue line
c4 = time.time()
t3 = c4 - c3
ax3.legend(loc=6) # places the legend
c5 = time.time()
t4 = c5 - c4
ax3.set_xlabel('Time (secs)') #places the axis labels
ax3.set_ylabel('Voltage (V)')
c6 = time.time()
t5 = c6 - c5
looptime = time.time() - loopclock
loopclock = time.time()
ax3.set_title(' EndTime = ' + datetime.now().strftime("%H:%M:%S")+
" LT="+f"{looptime:.2f}|{SamplesPerCycle:2d}"+
f"
{t1:.3f}|{t2:.3f}|{t3:.3f}|{t4:.3f}|{t5:.3f}|{t6:.3f}|{t7:.3f}|{t8:.3f}") # places the title
ax3.grid()
c7 = time.time()
t6 = c7 - c6
scatter3.draw() # draws the whole chart to the Tkinter window
c8 = time.time()
t7 = c8 - c7
root.update() # Tkinter’s update refresh of the window
c9 = time.time()
t8 = c9 - c8
if time.time() > pclock + 30.0:
pclock = time.time()
print(f"{t1:.4f}, {t2:.4f}, {t3:.4f}, {t4:.4f}, {t5:.4f}, {t6:.4f}, {t7:.4f}, {t8:.4f}")
Would much appreciate any pointers on how I can fix this CPU and memory leak.
(非常感谢任何有关如何解决此CPU和内存泄漏的指示。)
I found suggestions on making the plotting in a separate process so that all the memory is retrieved when the task is terminated. (我发现了在单独的过程中进行绘图的建议,以便在任务终止时可以检索所有内存。)
But terminating the task that plots to the screen every loop may blank the display or make it flicker. (但是,终止每个循环绘制到屏幕上的任务可能会使显示空白或使其闪烁。)
Wondering if there is a better way to do dynamic plotting using python and matplotlib. (想知道是否有更好的方法使用python和matplotlib进行动态绘图。)
I am using python 3.7 and Matplotlib 3.0.2. (我正在使用python 3.7和Matplotlib 3.0.2。)
ask by nanowiz translate from so