一段时间后实时数据绘图滞后

时间:2017-07-03 13:25:40

标签: python multithreading numpy matplotlib pyqt

我一直在为实验室的工作站自动化编写程序。我传达的一种仪器称为光束分析仪,它基本上从两个正交方向(x,y)读取光输入。一旦读取输入,我需要将其转换为2D图像,因为我使用numpy meshgrid并且我能够获得我想要的输出。 为了更清晰,请参见下图。 x轴和y轴上的两条高斯线是我的原始输入,彩色图是用meshgrid处理后的。 enter image description here

为此,我将软件分为两部分。首先,我创建另一个QT线程,初始化我的设备并在循环中运行获取数据并处理它。然后,该线程向主线程发送一个带有值的信号。

在主线程中,我获取值,绘制图形并更新gui屏幕。

它已经在工作,问题是当我启动光束分析仪读数时,随着时间的推移软件开始变慢。起初我以为是因为数据处理,但它没有意义,因为它在第二个线程中运行,当我启动设备时没有延迟。 好像它是"拯救"内存中的数据越来越慢,这很奇怪,因为我使用set_datadraw方法进行绘图。

注意:如果我关闭软件内部的设备读数,滞后停止,如果我再次启动,它会开始变好但随着时间的推移会滞后。

非常感谢任何传入的帮助!

数据采集线程代码:

class ThreadGraph(QtCore.QThread):
    _signalValues = QtCore.pyqtSignal(float, float, float, float, float, float, float, float)
    _signalGraph = QtCore.pyqtSignal(np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray)
    _signalError = QtCore.pyqtSignal(str)
    BEAMstatus = QtCore.pyqtSignal(str)

    def __init__(self, parent=None):
        super(ThreadGraph, self).__init__(parent)
        self.slit = 0
        self.state = False

    #Thread starts
    def run(self):
        self.init() #Device initialization (Not relevant, therefore omitted)
        time.sleep(0.1)
        while self.state == True: #Thread loop (data acquisition)
            self.emitValues() #Fun to get the data and emit
            time.sleep(0.016)
            self.emitGraph() #Process data into 2D and emit
        try: #When while is over, terminate the thread
            self.beam.close(self.session)
        except RuntimeError as err:
            print err 
        self.quit()

    def emitGraph(self): #Use the data acquired to to generate 2D image and emit
        xx, yy = np.meshgrid(self.slit_data_int[self.slit][0::10], self.slit_data_int[self.slit+1][0::10])
        zz = xx * yy

        self._signalGraph.emit(
            self.slit_data_pos[self.slit][0::10],
            self.slit_data_int[self.slit][0::10],
            self.slit_data_pos[self.slit + 1][0::10],
            self.slit_data_int[self.slit + 1][0::10],
            zz
            )


    def emitValues(self):
        try: #Try to get data from device (data is stored in calculation_result)
            self.slit_data_pos, self.slit_data_int, self.calculation_result, self.power, self.power_saturation, self.power_intensities = self.beam.get_slit_scan_data(self.session)
        except RuntimeError as err:
            self._signalError.emit(str(err))
            return
        else: #emit data to gui main thread
            self._signalValues.emit(
                self.calculation_result[self.slit].peakPosition, 
                self.calculation_result[self.slit + 1].peakPosition,
                self.calculation_result[self.slit].peakIntensity, 
                self.calculation_result[self.slit + 1].peakIntensity,
                self.calculation_result[self.slit].centroidPosition, 
                self.calculation_result[self.slit + 1].centroidPosition,
                self.calculation_result[self.slit].gaussianFitDiameter, 
                self.calculation_result[self.slit + 1].gaussianFitDiameter
                )

Main Gui代码:

class BP209_class(QtGui.QWidget):

    def __init__(self, vbox, slit25, slit5, peakposx, peakposy, peakintx, peakinty, centroidposx, centroidposy, mfdx, mfdy):
        QtGui.QWidget.__init__(self)
        #Initialize a bunch of gui variables
        self.matplotlibWidget = MatplotlibWidget('2d')
        self.vboxBeam = vbox
        self.vboxBeam.addWidget(self.matplotlibWidget)
        self.vboxBeam.addWidget(self.matplotlibWidget.canvastoolbar)
        #Create the thread and connects
        self.thread = ThreadGraph(self)
        self.thread._signalError.connect(self.Error_Handling)
        self.thread._signalValues.connect(self.values_update)
        self.thread._signalGraph.connect(self.graph_update)
        self.thread.BEAMstatus.connect(self.Status)


        #Initialize variables for plots
        self.zz = zeros([750, 750])
        self.im = self.matplotlibWidget.axis.imshow(self.zz, cmap=cm.jet,  origin='upper', vmin=0, vmax=1, aspect='auto', extent=[-5000,5000,-5000,5000])
        self.pv,  = self.matplotlibWidget.axis.plot(np.zeros(750) , np.zeros(750) , color="white" , alpha=0.6, lw=2)
        self.ph,  = self.matplotlibWidget.axis.plot(np.zeros(750) , np.zeros(750), color="white" , alpha=0.6, lw=2)
        self.matplotlibWidget.figure.subplots_adjust(left=0.00, bottom=0.01, right=0.99, top=1, wspace=None, hspace=None)
        self.matplotlibWidget.axis.set_xlim([-5000, 5000])
        self.matplotlibWidget.axis.set_ylim([-5000,5000])

    def __del__(self): #stop thread  
        self.thread.state = False
        self.thread.wait()

    def start(self): #start thread
        if self.thread.state == False:
            self.thread.state = True
            self.thread.start()
        else:
            self.thread.state = False
            self.thread.wait()

    #Slot that receives data from device and plots it
    def graph_update(self, slit_samples_positionsX, slit_samples_intensitiesX, slit_samples_positionsY, slit_samples_intensitiesY, zz):
        self.pv.set_data(np.divide(slit_samples_intensitiesX, 15)-5000, slit_samples_positionsX)
        self.ph.set_data(slit_samples_positionsY, np.divide(slit_samples_intensitiesY, 15)-5000)
        self.im.set_data(zz)
        self.im.autoscale()
        self.matplotlibWidget.canvas.draw()

编辑:我还有一个连接到我的系统的相机,我也使用opencv在gui中显示它。我注意到,如果我启动凸轮,光束分析仪的fps会减少到近一半。那么,也许QT涂料优化是可行的方法?

1 个答案:

答案 0 :(得分:1)

拨打canvas.draw()的费用很高。您可能比绘图命令可以完成的更快地获取数据。这将导致绘制事件排队等候,您的绘图将显得滞后。此blog post详细说明了一种避免调用canvas.draw()的方法,可用于加速matplotlib实时绘图。

如果仍然不够快,您可能不得不降低采集速率,实施某种形式的跳帧机制或使用更好的速度优化的不同绘图库。