维特智能九轴加速度计陀螺仪模块 Python 实例程序

购买了 维特智能九轴加速度计、陀螺仪模块 ,型号WT901 。
维特智能九轴加速度计陀螺仪模块 Python 实例程序_第1张图片
红色圈中的就是传感器模块,约为1角硬币大小。图片上面部分是厂家原配的USB转TTL的模块,连线实在是太短了,用起来一点都不方便。我换成了自已以前用过的转接模块,连线要长得多,差不多是原配线的3倍长。

下图是传感器模块放大后的照片。只需要接 GND,VCC,RX,TX四根线就可以和串口通信了。
维特智能九轴加速度计陀螺仪模块 Python 实例程序_第2张图片
在厂家提供的的示例程序中,只找到了VC++ 与 C#的示例程序,没有找到我想要的Python示例程序。

好在厂家提供的说明书中对模块所使用的串口协议做了说明。参照厂家的说明书与C#示例程序,本人用Python实现了与C#功能一样的示例程序。

程序运行界面如下:

维特智能九轴加速度计陀螺仪模块 Python 实例程序_第3张图片
附厂家提供的C#示例程序的运行界面:
维特智能九轴加速度计陀螺仪模块 Python 实例程序_第4张图片
观察一下,可以注意到,传感器读数会有微小的动态变化。此外经过检查,厂家提供的C#程序在显示GPS地速,航向数据时,显示位置有些错位了。在用Python采集数据时,对这个问题也做了处理。当然,由于实际上未接GPS,上面显示的GPS数据都是0,没有实际意义。

完整的python代码

UARTTest.py

# -*- coding:utf-8 -*- 
# 维特的WT901传感器 数据读取实例程序,
# 串口协议规定11个字节一个数据包,包头两个字节,包尾一个字节的检验码,中间8个字节存放回传的实际数据
# 从现有的资料来看,JY901的串口协议与WT901的串口协议应该是一样的

import time
import struct
import serial
import binascii
import threading
import tkinter as tk
from datetime import datetime

#配置类
class Config:
    # 端口号
    serialPort = 'COM8' 
    # 波特率 
    baudRate = 115200  
    #在传感器数据中,最小的包长度是11个字节
    minPackageLen = 11
    
#传感器数据读取类
class SensorReader:
    def __init__(self):
        self.port = serial.Serial(Config.serialPort, Config.baudRate)
        self.port.close()
        if not self.port.isOpen():
            self.port.open()
        self.receiveBuffer = bytearray()
        self.working = False

    #打开
    def open(self):
        if not self.port.isOpen():
            self.port.open()

    #关闭
    def close(self):
        self.port.close()

    #发送数据
    def send(self,data):
        self.port.write(data)

    #接收数据
    def receive(self):        
        while self.working:
            #休眠一个微小的时间,可以避免无谓的CPU占用,在windows上实验时直接从12%下降到接近于0%
            time.sleep(0.001)
            count = self.port.inWaiting()
            if count > 0:
                s = self.port.read(count)
                self.receiveBuffer+= s                

    #开始工作
    def start(self):
        #开始数据读取线程
        t = threading.Thread(target=self.receive)
        #将当前线程设为子线程t的守护线程,这样一来,当前线程结束时会强制子线程结束
        t.setDaemon(True)
        self.working = True
        t.start()

    #停止工作
    def stop(self):
        self.working = False

#数据解析类
class DataParser:
    def __init__(self,sensorReader,myUI):
        self.r = sensorReader   
        self.u = myUI     
        self.working = False
        self.TimeStart = datetime.now()
        self.iniVariable()

    #流逝的毫秒数,返回浮点数
    def elapseMilliSeconds(self):
        now = datetime.now()
        seconds = time.mktime(now.timetuple()) - time.mktime(self.TimeStart.timetuple())
        #微秒数的差值 
        microSeconds = now.microsecond - self.TimeStart.microsecond
        return seconds* 1000+ microSeconds/1000.0

    #流逝的秒数,返回浮点数
    def elapseSeconds(self):
        return self.elapseMilliSeconds()/1000

    #在缓冲数据中找到第一个包的起始位置
    def findFirstPackage(self,buffer):
        i = 0
        while True:
            if buffer[i] == 0x55 and (buffer[i+1] & 0x50) == 0x50 :
                return i
            if i+2 >= len(buffer):
                return -1
            i+=1

    #处理数据
    def handle(self):        
        #处理接收到的数据
        while self.working:               
            text = ''
            #显示当前收到的数据
            dataLen = len(self.r.receiveBuffer)
            if dataLen >= Config.minPackageLen:
                #去掉第1个包头前的数据
                headerPos = self.findFirstPackage(self.r.receiveBuffer)           
                text += '包头偏移:'+str(headerPos) +'\r\n'
                if headerPos >= 0 :
                    if headerPos > 0:
                        self.r.receiveBuffer[0:headerPos]=b''
                    #取 Config.minPackageLen 整数倍长度的数据
                    if dataLen-headerPos >= Config.minPackageLen : 
                        packageCount =   int((dataLen-headerPos)/Config.minPackageLen)
                        if packageCount > 0:
                            cutLen = packageCount*Config.minPackageLen                        
                            text += '当前收到包数:'+str(packageCount) +'\r\n'
                            temp = self.r.receiveBuffer[0:cutLen]
                            #按16进制字符串的形式显示收到的内容
                            hexStr = str(binascii.b2a_hex(temp))                     
                            text += '16进制原始据:'+hexStr[2:len(hexStr)-1] 
                            self.r.receiveBuffer[0:cutLen]=b''
                            #在窗口的文本框中显示数据
                            self.u.showData(text) 
                            #解析数据,逐个数据包进行解析
                            for i in range(packageCount):                                                    
                                beginIdx =int(i*Config.minPackageLen)
                                endIdx   =int(i*Config.minPackageLen+Config.minPackageLen)                            
                                byteTemp = temp[beginIdx:endIdx]
                                #校验和通过了的数据包才进行解析
                                if self.sbSumCheck(byteTemp):
                                    self.decodeData(byteTemp) 
                                              
            time.sleep(0.005)

    #初始化解析丰关的变量
    def iniVariable(self):           
        self.ChipTime  = [0,0,0,0,0,0,0]
        self.a = [0,0,0,0]        
        self.w = [0,0,0,0]
        self.Angle = [0,0,0,0]
        self.h = [0,0,0,0]
        self.Port = [0,0,0,0]
        self.LastTime = [0,0,0,0,0,0,0,0,0,0]
        self.Temperature = 0
        self.Pressure = 0
        self.Altitude = 0
        self.GroundVelocity = 0
        self.GPSYaw = 0
        self.GPSHeight = 0
        self.Longitude = 0
        self.Latitude = 0

    #解码包中的数据
    def decodeData(self,byteTemp):        
        #记录当前的相对时间
        TimeElapse = self.elapseSeconds();
        #将8个字节的数据解析成4个短整型
        Data = list(struct.unpack("hhhh", byteTemp[2:10]) )

        if byteTemp[1] == 0x50:
            self.ChipTime[0] = (2000 + byteTemp[2])
            self.ChipTime[1] = byteTemp[3]
            self.ChipTime[2] = byteTemp[4]
            self.ChipTime[3] = byteTemp[5]
            self.ChipTime[4] = byteTemp[6]
            self.ChipTime[5] = byteTemp[7]
            self.ChipTime[6] = struct.unpack("h", byteTemp[8:10])[0] 

        if byteTemp[1] == 0x51:
            self.Temperature = Data[3] / 100.0
            Data[0] = Data[0] / 32768.0 * 16
            Data[1] = Data[1] / 32768.0 * 16
            Data[2] = Data[2] / 32768.0 * 16

            self.a[0] = Data[0]
            self.a[1] = Data[1]
            self.a[2] = Data[2]
            self.a[3] = Data[3]
            if ((TimeElapse - self.LastTime[1]) < 0.1):
                return
            self.LastTime[1] = TimeElapse

        if byteTemp[1] == 0x52:
            self.Temperature = Data[3] / 100.0
            Data[0] = Data[0] / 32768.0 * 2000
            Data[1] = Data[1] / 32768.0 * 2000
            Data[2] = Data[2] / 32768.0 * 2000
            self.w[0] = Data[0]
            self.w[1] = Data[1]
            self.w[2] = Data[2]
            self.w[3] = Data[3]
            if ((TimeElapse - self.LastTime[2]) < 0.1):
                return
            self.LastTime[2] = TimeElapse

        if byteTemp[1] == 0x53:
            self.Temperature = Data[3] / 100.0
            Data[0] = Data[0] / 32768.0 * 180
            Data[1] = Data[1] / 32768.0 * 180
            Data[2] = Data[2] / 32768.0 * 180
            self.Angle[0] = Data[0]
            self.Angle[1] = Data[1]
            self.Angle[2] = Data[2]
            self.Angle[3] = Data[3]
            if ((TimeElapse - self.LastTime[3]) < 0.1):
                return
            self.LastTime[3] = TimeElapse

        if byteTemp[1] == 0x54:
            self.Temperature = Data[3] / 100.0
            self.h[0] = Data[0]
            self.h[1] = Data[1]
            self.h[2] = Data[2]
            self.h[3] = Data[3]
            if ((TimeElapse - self.LastTime[4]) < 0.1):
                return
            self.LastTime[4] = TimeElapse

        if byteTemp[1] == 0x55:
            self.Port[0] = Data[0]
            self.Port[1] = Data[1]
            self.Port[2] = Data[2]
            self.Port[3] = Data[3]

        if byteTemp[1] == 0x56:
            self.Pressure = struct.unpack("i", byteTemp[2:6])[0]
            self.Altitude = struct.unpack("i", byteTemp[6:10])[0] / 100.0

        if byteTemp[1] == 0x57:            
            self.Longitude = struct.unpack("i", byteTemp[2:6])[0]
            self.Latitude  = struct.unpack("i", byteTemp[6:10])[0]

        if byteTemp[1] == 0x58:
            self.GPSHeight = struct.unpack("h", byteTemp[2:4])[0] / 10.0
            self.GPSYaw = struct.unpack("h", byteTemp[4:6])[0] / 10.0
            self.GroundVelocity = struct.unpack("h", byteTemp[6:8])[0] / 1e3

        #内嵌的输出函数,可以直接引用方法内部的各种变量,比如 TimeElapse 等
        def output():
            text ='系统时间:'+datetime.now().strftime("%Y-%m-%d %H:%M:%S") + "\r\n"
            text+='片上时间:'+str(self.ChipTime[0]) + "-" + str(self.ChipTime[1]) + "-" + str(self.ChipTime[2]) + "\r\n                    " 
            text+= str(self.ChipTime[3]) + ":" + str(self.ChipTime[4]) + ":" + str(self.ChipTime[5]) + "." + str(self.ChipTime[6]) + "\r\n"      
            text+='相对时间:'+"%.3f" %TimeElapse + "\r\n\r\n"
            
            text+='x轴加速度:'+"%.2f g" %self.a[0] + "\r\n"
            text+='y轴加速度:'+"%.2f g" %self.a[1] + "\r\n"
            text+='z轴加速度:'+"%.2f g" %self.a[2] + "\r\n\r\n"

            text+='x轴角速度:'+"%.2f °/s" %self.w[0] + "\r\n"
            text+='y轴角速度:'+"%.2f °/s" %self.w[1] + "\r\n"
            text+='z轴角速度:'+"%.2f °/s" %self.w[2] + "\r\n\r\n"

            text+='x轴角度:  '+"%.2f °" %self.Angle[0] + "\r\n"
            text+='y轴角度:  '+"%.2f °" %self.Angle[1] + "\r\n"
            text+='z轴角度:  '+"%.2f °" %self.Angle[2] + "\r\n\r\n"

            text+='x轴磁场: '+"%.0f mG" %self.h[0] + "\r\n"
            text+='y轴磁场: '+"%.0f mG" %self.h[1] + "\r\n"
            text+='z轴磁场: '+"%.0f mG" %self.h[2] + "\r\n\r\n"

            text+='温   度:'+"%.2f ℃" %self.Temperature + "\r\n"
            text+='气   压:'+"%.0f Pa" %self.Pressure + "\r\n"
            text+='高   度:'+"%.2f m" %self.Altitude + "\r\n\r\n"

            text+='经   度:'+"%.0f°" %(self.Longitude /10000000) + "%.5f\'" %((self.Longitude % 10000000)/1e5) +"\r\n"
            text+='纬   度:'+"%.0f°" %(self.Latitude / 10000000) + "%.5f\'" %((self.Latitude % 10000000)/1e5) +"\r\n"

            text+='GPS高度:'+"%.1f m" %self.GPSHeight + "\r\n"
            text+='GPS航向:'+"%.1f °" %self.GPSYaw + "\r\n"
            text+='GPS地速:'+"%.3f km/h" %self.GroundVelocity + "\r\n\r\n"

            self.u.showText(text)

        #输出解析得到的内容
        output()

    #检查校验和
    def sbSumCheck(self,byteTemp):
        if (((byteTemp[0]+byteTemp[1]+byteTemp[2]+byteTemp[3]+byteTemp[4]+byteTemp[5]+byteTemp[6]+byteTemp[7]+byteTemp[8]+byteTemp[9])&0xff)==byteTemp[10]) :
            #print('sum check ok!')
            return True
        else:
            print('sum check false!')
            return False

    #开始工作
    def start(self):
        #开启数据解析线程
        t = threading.Thread(target=self.handle)
        #将当前线程设为子线程t的守护线程,这样一来,当前线程结束时会强制子线程结束
        t.setDaemon(True)
        self.working = True
        t.start()

    #停止工作
    def stop(self):
        self.working = False    

#图形界面类
class MyUI:
    def __init__(self):
        #创建显示传感器数据的窗口
        self.window = tk.Tk()
        self.window.title('WT901(JY901)传感器')
        self.window.geometry('760x840')

        self.frameTop = tk.Frame(self.window)
        self.frameTop.config( height=100, width=780)
        self.frameTop.place(x=10, y=0)

        self.frameBottom = tk.Frame(self.window)
        self.frameBottom.config( height=720, width=780)
        self.frameBottom.place(x=10, y=110)

        #创建显示数据的文本框
        self.dataBox = tk.Text(self.frameTop,  bg='white', font=('Arial', 12))
        self.dataBox.place(x=4, y=4)        
        
        self.textBox = tk.Text(self.frameBottom,  height=700,bg='white', font=('Arial', 12))
        self.textBox.place(x=4, y=4) 

    #开启UI
    def start(self):
        #开启窗口主循环
        self.window.mainloop()        
        
    #显示数据
    def showData(self,data):
        self.dataBox.delete(0.0,tk.END)
        self.dataBox.insert(tk.INSERT, data) 

    #显示文本
    def showText(self,text):
        self.textBox.delete(0.0,tk.END)
        self.textBox.insert(tk.INSERT, text)
    

 #主线程
if __name__ == '__main__':
    #创建串口操作对象
    r= SensorReader()    
    #开始读取数据
    r.start()    

    #创建UI对象
    u = MyUI()

    #创建数据解析对象
    p= DataParser(r,u)
    #开始解析数据
    p.start()

    #启动UI
    u.start()   
    

有了上述的Python示例程序,稍加修改,就可以用于加速度计、陀螺仪的二次开发。

补充说明:

在实际使用时,请修改python文件开头的参数配置,主要是串口号与波特率要与你实际使用模块的设置要一致。

可以点这里直接下载Python源码

提取码:swa2

你可能感兴趣的:(python,编程语言,智能设备)