用Python串口實時顯示數據並繪圖pyqtgraph


 

https://blog.csdn.net/RNG_uzi_/article/details/96736432

 

其他方法如 Processing   SerialChart

http://www.starlino.com/serialchart

https://github.com/starlino/serialchart

 

 

用Python串口實時顯示數據並繪圖

使用pyserial進行串口傳輸

一、安裝pyserial以及基本用法

在cmd下輸入命令pip install pyserial
注:升級pip后會出現 "‘E:\Anaconda3\Scripts\pip-script.py’ is not present."錯誤
使用 easy_install pip命令就能解決,換一條重新能執行安裝的命令

常用方法:
ser = serial.Serial(0) 是打開第一個串口
print ser.portstr 能看到第一個串口的標識,windows下是COM1
ser.write(“hello") 就是往串口里面寫數據
ser.close() 就是關閉ser表示的串口
ser.open() 會打開這個串口
ser = serial.Serial(‘COM1’, 115200) 來設置波特率,當然還有專門的函數
data = ser.read()可以讀一個字符
data = ser.read(20) 是讀20個字符
data = ser.readline() 是讀一行,以\n結束,要是沒有\n就一直讀,阻塞。
data = ser.readlines()ser.xreadlines()都需要設置超時時間
ser.baudrate = 9600 設置波特率
ser 來查看當前串口的狀態
ser.isOpen() 看看這個串口是否已經被打開

串行口的屬性:
name:設備名字
portstr:已廢棄,用name代替
port:讀或者寫端口
baudrate:波特率
bytesize:字節大小
parity:校驗位
stopbits:停止位
timeout:讀超時設置
writeTimeout:寫超時
xonxoff:軟件流控
rtscts:硬件流控
dsrdtr:硬件流控
interCharTimeout:字符間隔超時

注意:使用開發板printf函數,以\n結束。使用serial.readline()進行讀取數據,使用decode對其進行編碼,編碼之后使用split(’ ')[i]函數進行讀取某個字節。

二、最基本的串口代碼

import serial
portx="COM5"
bps=9600
timex=5
#串口執行到這已經打開 再用open命令會報錯
ser = serial.Serial(portx, int(bps), timeout=1, parity=serial.PARITY_NONE,stopbits=1)
if (ser.isOpen()):
    print("open success")
     # 向端口些數據 字符串必須譯碼
    ser.write("hello".encode()) 
    while (True):
        line = ser.readline()  
        if(line):
            print(line)
            line=0
else:
	print("open failed")
ser.close()#關閉端口

三、pyqtgraph的使用

pip install pyqtgraph#顯示波形的界面
pip install PyQt5#界面要Qt的支持

pyqtgraph是Python平台上一種功能強大的2D/3D繪圖庫,相對於matplotlib庫,由於內部實現方式上,使用了高速計算的numpy信號處理庫以及Qt的GraphicsView框架,因此,它在大數據量的數字處理和快速顯示方面有着巨大的優勢,它適合於需要快速繪圖更新、視頻或實時交互性的操作場合。另外,它不僅為各種數據提供了快速可交互式的圖形顯示,同時也提供了用於快速開發應用程序的各種小工具,如屬性樹、流程圖等小部件,在數學、科學和工程領域都有着廣泛的應用。

import pyqtgraph as pg
import numpy as np
import array

app = pg.mkQApp()#建立app
win = pg.GraphicsWindow()#建立窗口
win.setWindowTitle(u'pyqtgraph逐點畫波形圖')
win.resize(800, 500)#小窗口大小

data = array.array('d') #可動態改變數組的大小,double型數組
historyLength = 100#橫坐標長度
p = win.addPlot()#把圖p加入到窗口中
p.showGrid(x=True, y=True)#把X和Y的表格打開
p.setRange(xRange=[0,historyLength], yRange=[-1.2, 1.2], padding=0)
p.setLabel(axis='left', text='y / V')#靠左
p.setLabel(axis='bottom', text='x / point')
p.setTitle('y = sin(x)')#表格的名字
curve = p.plot()#繪制一個圖形
idx = 0
def plotData():
    global idx#內部作用域想改變外部域變量
    tmp = np.sin(np.pi / 50 * idx)
    if len(data)<historyLength:
        data.append(tmp)
    else:
        data[:-1] = data[1:]#前移
        data[-1] = tmp
    curve.setData(data)
    idx += 1

timer = pg.QtCore.QTimer()
timer.timeout.connect(plotData)#定時調用plotData函數
timer.start(50)#多少ms調用一次

app.exec_()

在這里插入圖片描述
四、通過多線程實現串口數據的實時繪圖import pyqtgraph as pg

主要是開了一個線程去處理串口 剩下的和上面內容一樣 就不過多解釋了 直接上代碼

import array
import serial
import threading
import pyqtgraph as pg import numpy as np import time i = 0 def Serial(): while(True): n = mSerial.inWaiting() if(n): if data!=" ": dat = int.from_bytes(mSerial.readline(1),byteorder='little') # 格式轉換 n=0 global i; if i < historyLength: data[i] = dat i = i+1 else: data[:-1] = data[1:] data[i-1] = dat def plotData(): curve.setData(data) if __name__ == "__main__": app = pg.mkQApp() # 建立app win = pg.GraphicsWindow() # 建立窗口 win.setWindowTitle(u'pyqtgraph逐點畫波形圖') win.resize(800, 500) # 小窗口大小 data = array.array('i') # 可動態改變數組的大小,double型數組 historyLength = 200 # 橫坐標長度 a = 0 data=np.zeros(historyLength).__array__('d')#把數組長度定下來 p = win.addPlot() # 把圖p加入到窗口中 p.showGrid(x=True, y=True) # 把X和Y的表格打開 p.setRange(xRange=[0, historyLength], yRange=[0, 255], padding=0) p.setLabel(axis='left', text='y / V') # 靠左 p.setLabel(axis='bottom', text='x / point') p.setTitle('semg') # 表格的名字 curve = p.plot() # 繪制一個圖形 curve.setData(data) portx = 'COM24' bps = 19200 # 串口執行到這已經打開 再用open命令會報錯 mSerial = serial.Serial(portx, int(bps)) if (mSerial.isOpen()): print("open success") mSerial.write("hello".encode()) # 向端口些數據 字符串必須譯碼 mSerial.flushInput() # 清空緩沖區 else: print("open failed") serial.close() # 關閉端口 th1 = threading.Thread(target=Serial)#目標函數一定不能帶()被這個BUG搞了好久 th1.start() timer = pg.QtCore.QTimer() timer.timeout.connect(plotData) # 定時刷新數據顯示 timer.start(50) # 多少ms調用一次 app.exec_()

效果如圖

在這里插入圖片描述

五、與下位機通訊實現波形實時監測

在這里與第四階段基本相同,需要注意的是,如果收數據直接畫圖的話,波形會出現問題。所以串口傳輸數據時使用循環隊列(先進先出),數據來之后先進隊列,之后再定時器調用函數,出隊列,更新圖。理論上刷新數據的時間需要大於下位機發送數據的間隔時間,否則隊列會越來越大,而且圖的刷新不連貫。再就是有一個小問題,因為正弦波有負值,我又沒找到很好的把Byte轉為char的方法,所以只能手動代碼處理,先轉成int類型,再把第八位(符號位)清零,得到絕對值。然后再取負,得到我們需要的數據。但發現Python無法進行移位操作,python是int類型是無精度類型,不會發生溢出而進行截取的情況,所以只能先轉為二進制在移位,太麻煩,直接通過減去一個數的方法來實現了。然后直接上代碼吧

import pyqtgraph as pg
import array
import serial
import threading
import numpy as np
from queue import Queue
import time


i = 0
q = Queue(maxsize=0)
def Serial():
    global i;
    global q;
    while(True):
        n = mSerial.inWaiting()
        if(n):
            dat = int.from_bytes(mSerial.readline(1),byteorder='little')  # 格式轉換
            if(dat>>7):
                dat =256-dat
                dat =0-dat
            q.put(dat)

def plotData():
    global i;
    if i < historyLength:
        data[i] = q.get()
        i = i+1
    else:
        data[:-1] = data[1:]
        data[i-1] = q.get()
    curve.setData(data)


if __name__ == "__main__":
    app = pg.mkQApp()  # 建立app
    win = pg.GraphicsWindow()  # 建立窗口
    win.setWindowTitle(u'pyqtgraph逐點畫波形圖')
    win.resize(800, 500)  # 小窗口大小
    data = array.array('i')  # 可動態改變數組的大小,double型數組
    historyLength = 100  # 橫坐標長度
    a = 0
    data=np.zeros(historyLength).__array__('d')#把數組長度定下來
    p = win.addPlot()  # 把圖p加入到窗口中
    p.showGrid(x=True, y=True)  # 把X和Y的表格打開
    p.setRange(xRange=[0, historyLength], yRange=[-50, 50], padding=0)
    p.setLabel(axis='left', text='y / V')  # 靠左
    p.setLabel(axis='bottom', text='x / point')
    p.setTitle('semg')  # 表格的名字
    curve = p.plot()  # 繪制一個圖形
    curve.setData(data)
    portx = 'COM25'
    bps = 19200
    # 串口執行到這已經打開 再用open命令會報錯
    mSerial = serial.Serial(portx, int(bps))
    if (mSerial.isOpen()):
        dat = 0xff;
        dat >> 2;
        print("open success")
        # 向端口些數據 字符串必須譯碼
        mSerial.write("hello".encode())
        mSerial.flushInput()  # 清空緩沖區
    else:
        print("open failed")
        serial.close()  # 關閉端口
    th1 = threading.Thread(target=Serial)
    th1.start()
    timer = pg.QtCore.QTimer()
    timer.timeout.connect(plotData)  # 定時刷新數據顯示
    timer.start(1)  # 多少ms調用一次
    app.exec_()

Python+pyqtgraph數據可視化:自定義坐標軸信息

方法1

其原則是,直接使用pyqtgraph庫提供的軸項類AxisItem,定義它的一個實例對象,調用該類的setTicks函數設置橫坐標軸的字符信息,代碼如下:
在這里插入圖片描述
代碼簡要說明如下:

1、第14-16行,產生波形的x、y數據及對應的x軸的字符信息列表

2、第20行,將x數值及字對應字符組成一個元組的列表

3、第25-26行,創建軸項類AxisItem的實例對象strAxis,並調用setTicks函數設置橫坐標的字符信息

4、第29-30行,使用strAxis創建繪圖對象並繪制波形圖

方法2

其原則是,以pyqtgraph庫提供的軸項類AxisItem作為基類,在程序中創建一個自定義的軸項類,類中重定義函數tickStrings來實現橫坐標刻度的的字符信息顯示,代碼如下:
在這里插入圖片描述
代碼簡要說明如下:

1、第13-29行,以pyqtgraph庫提供的AxisItem作為基類自定義了一個軸項類MyStringAxis,在類中重定義tickStrings函數,實現橫坐標刻度的的字符信息顯示

2、第33-35行,產生波形的x、y數據及對應的x軸的字符信息列表

3、第39行,將x數值及字對應字符組成一個元組的列表

4、第42-44行,創建自定義類MyStringAxis的實例對象strAxis,用其創建繪圖對象並繪制波形圖

Python+pyqtgraph數據可視化之多條曲線繪制方法

pyqtgraph是Python平台上一種功能強大的2D/3D繪圖庫,相對於matplotlib庫,由於其在內部實現方式上,使用了高速計算的numpy信號處理庫以及Qt的GraphicsView框架,因此它在大數據量的處理及快速顯示方面有着天然的優勢,非常適合於需要快速繪圖更新、視頻或實時交互性的操作場合,在數學、科學和工程領域都有着廣泛的應用。

對於多條曲線的快速繪制方式,有兩種方案可供選擇,一種是將多條曲線合並顯示在一幅繪圖區域上,另一種方案是將多條曲線顯示在不同的繪圖區域上,對於這兩種繪制方案,下面通過例子來演示在Python語言中使用pyqtgraph庫編程實現的方法。

方案1:將多條曲線合並顯示在一幅繪圖區域
程序的運行效果如下圖所示:
在這里插入圖片描述
例子在圖形的一幅繪圖區域上顯示了3條曲線,分別為正弦曲線、余弦曲線及sinc函數曲線。
主要實現代碼如下:
在這里插入圖片描述
代碼主要部分說明如下:

1、第13行,創建應用程序實例app

2、第15-17行,創建一個圖形顯示窗口win,設置窗口屬性,如窗口標題、窗口大小等

3、第19-22行,使用numpy庫產生3條曲線即正弦曲線、余弦曲線及sinc函數曲線的x、y數據

4、第24行,在win上添加一個繪圖區域對象p,參數中設置了X、Y軸label及圖形標題等

5、第25-27行,在p上分別添加3條不同顏色的曲線

6、第28-29行,設置繪圖區域的網格及坐標軸范圍屬性

7、第31行,使用app.exec_()函數運行實例,進入消息循環

方案2:將多條曲線顯示在不同的繪圖區域
程序的運行效果如下圖所示:
在這里插入圖片描述
對於上面的正弦曲線、余弦曲線及sinc函數曲線分別顯示在了圖形的3個繪圖區域上。
主要實現代碼如下:
在這里插入圖片描述
代碼主要部分說明如下:

1、第13-22行,和上例完全相同

2、第24-27行,在win上分別添加了3個繪圖區域對象p1、p2、p3,其中,第26行,說明在繪圖區域的下一行創建p3,第27行的參數“colspan=2”說明p3占的列寬為2列

3、第29-32行,在for循環中分別在3個繪圖區域對象上繪制不同的曲線,並設置繪圖區域的網格及坐標軸范圍等屬性

4、第34行,使用app.exec_()函數運行實例,進入消息循環

備注
在第2種方案中創建繪圖區域對象p時,也可以在參數中直接指定p處於的位置及行寬、列寬等信息,如將上面例子中第26、27行合並寫為如下代碼也是等價的。

歡迎加關注,共同交流。

感謝前輩:
https://blog.csdn.net/up1012/article/details/81010312
http://www.360doc.com/content/18/1007/18/29771970_792744060.shtml

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM