2017-07-03 33 views
-1

我一直在寫實驗室工作站自動化程序。我通信的一種儀器稱爲光束分析儀,它基本上讀取來自兩個正交方向(x,y)的光輸入。一旦輸入被讀取,我需要將其轉換爲2D圖像,因爲我使用numpy meshgrid,並且我能夠獲得所需的輸出。 爲了更清晰,請參閱下圖。 x軸和y軸上的兩條高斯線是我的原始輸入,彩色圖是用網格進行處理的。 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 
       ) 

主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的顯示它的圖形用戶界面。我注意到,如果我啓動凸輪,光束輪廓儀的fps減少將近一半。所以,也許QT油漆優化將是一條路要走?

+0

TL; DR。但是,如果我關閉設備滯後停止,並且如果我再次啓動它,它會開始很好,但隨着時間的推移會滯後*聽起來像繪圖代碼無法跟上數據採集速率,因此緩衝區填充越來越多。你多久嘗試一次更新圖片? – kazemakase

+0

我使用插槽功能進行更新。所以無論何時採集和處理完成信號都會發射出去。然後在主線程中的一個插槽啓動並處理圖像更新 – Eduardo

+0

無論您如何觸發更新。例如,如果您每1ms記錄一次數據,並且繪圖需要2ms,那麼您認爲會發生什麼? – kazemakase

回答

1

致電canvas.draw()是昂貴的。您可能會比繪圖命令更快地完成數據採集。這將導致繪畫事件排隊,你的情節會出現滯後。 blog post詳細介紹了一種避免調用canvas.draw()的方法,可用於加速matplotlib實時繪圖。

如果這仍然不夠快,您可能不得不降低採集率,實施某種形式的跳幀機制或使用更好的速度優化的繪圖庫。

+0

非常感謝,看來它解決了這個問題。我只是做更多的測試,看看它是否真的解決了這個問題,但我猜測它確實如此!非常感謝 – Eduardo

+0

更新:經過更多的測試後,我意識到它沒有解決問題。我在劇情中加入了一個fps計數器,並且我注意到在我有大約10fps之前,並且在變化之後我能夠達到14fps左右。這還不夠。也許我應該去其他圖書館,而不是matplotlib? – Eduardo

+0

Matplotlib更專注於創造出版質量數據而不是表現。試着看一下類似['PyQtGraph'](http://www.pyqtgraph.org/)的內容,這本書的寫作速度很快。如果這仍然很慢,也許你還有其他問題。 – user3419537