之前介绍过,项目的第一项操作是要采集帕金森病人的震颤数据,整体架构图如下:

这里采用的是一款基于MPU6050的JY61型号传感器,好处在于内置卡尔曼滤波,直接可以通过串口输出六轴数据,大大方便了编程效率。模块如图:

 

其产品介绍如下(摘自使用说明书):

 此六轴模块采用高精度的陀螺加速度计 MPU6050,通过处理器读取 MPU6050 的测量数据
然后通过串口输出,同时精心的 PCB 布局和工艺保证了 MPU6050 收到外接的干扰最小,
测量的精度最高。
 模块内部自带电压稳定电路,可以兼容 3.3V/5V 的嵌入式系统,连接方便。
 采用先进的数字滤波技术,能有效降低测量噪声,提高测量精度。
 模块内部集成了姿态解算器,配合动态卡尔曼滤波算法,能够在动态环境下准确输出
模块的当前姿态,姿态测量精度 0.05 度,稳定性极高,性能甚至优于某些专业的倾角
仪!
 采用邮票孔镀金工艺,品质保证,可嵌入用户的 PCB 板中。

性能参数:

、电压:3.3V~5V
2、电流:<10mA
3、体积:51.3mm X 36mm X 15mm
4、焊盘间距:上下 100mil(2.54mm),左右 600mil(15.24mm)
5、测量维度:加速度:3 维,角速度:3 维,角度:3 维
6、量程:加速度:±16g,角速度:±2000deg/s,角度 X Z 轴±180° Y 轴±90°。
7、分辨率:加速度:0.0005g,角速度:0.61°/s。
8、测量精度:静态 0.05°,动态 0.1°。
9、数据输出内容:时间、加速度、角速度、角度。
10、数据输出频率:100HZ(波特率 115200)/20HZ(波特率 9600)。
11、波特率:9600kps、115200kps(默认)。
12、数据接口:串口(TTL/232 电平)可选。

经测试,用在我的项目里精度已经足够。采用TTL转USB线后,与我的linux开发板相连接,发现问题在于官方给出的例程不能直接拿来使用,考虑到python的通用性,因此利用python改写读取程序后分享给大家。

无论是windows还是linux,亲测都可以通过python成功运行。注意是python3版本。


第一版:由于自带例程没有python样例,因此尝试自己读取处理。

注意:

1 安装的包名叫pyserial而不是serial

2 在linux下不用安装驱动,直接利用串口调试工具即可看到串口号

读取加速度:

#Initial-T 2019.4
# -*- coding: UTF-8 -*-
import serial
def get_acc(self):          #定义处理加速度函数

    try:
        axh = int(acchex[6:8],16)     #注意字符串转成16进制的方法
        axl = int(acchex[4:6],16)
    except IOError:                      
        print("ReadError: gyro_acc")
        return (0, 0, 0)
    else:                             #利用转换公式
        k_acc = 16                       
        acc_x = (axh << 8 | axl) / 32768 * k_acc # 以acc_x为例
        if acc_x >= k_acc: 
            acc_x -= 2 * k_acc
    return acc_x
while(1):
    ser = serial.Serial("com6", 115200, timeout=0.5)  # 打开端口
    print(ser.is_open)
    acchex = (ser.read(11).hex())         #以16进制接收数据
    acc = get_acc(acchex)                 #处理数据
    print(acchex) 
    print(acc)     
    ser.close()                           #处理完一轮后关闭端口

读取全部数据:

#Initial-T 2019.4
# -*- coding: UTF-8 -*-
import serial
def get_acc(self):                                          #加速度

    try:
        axh = int(datahex[6:8],16)
        axl = int(datahex[4:6], 16)
        ayh = int(datahex[10:12], 16)
        ayl = int(datahex[8:10], 16)
        azh = int(datahex[14:16], 16)
        azl = int(datahex[12:14], 16)
    except IOError:
        print("ReadError: gyro_acc")
        return (0, 0, 0)
    else:
        k_acc = 16

        acc_x = (axh << 8 | axl) / 32768 * k_acc
        acc_y = (ayh << 8 | ayl) / 32768 * k_acc
        acc_z = (azh << 8 | azl) / 32768 * k_acc
        if acc_x >= k_acc:
            acc_x -= 2 * k_acc
        if acc_y >= k_acc:
            acc_y -= 2 * k_acc
        if acc_z >= k_acc:
            acc_z-= 2 * k_acc
    return acc_x,acc_y,acc_z
def get_gyro(self):                                          #陀螺仪

    try:
        wxh = int(datahex[28:30], 16)
        wxl = int(datahex[26:28], 16)
        wyh = int(datahex[32:34], 16)
        wyl = int(datahex[30:32], 16)
        wzh = int(datahex[36:38], 16)
        wzl = int(datahex[34:36], 16)
    except IOError:
        print("ReadError: gyro_acc")
        return (0, 0, 0)
    else:
        k_gyro = 2000

        gyro_x = (wxh << 8 | wxl) / 32768 * k_gyro
        gyro_y = (wyh << 8 | wyl) / 32768 * k_gyro
        gyro_z = (wzh << 8 | wzl) / 32768 * k_gyro
        if gyro_x >= k_gyro:
            gyro_x -= 2 * k_gyro
        if gyro_y >= k_gyro:
            gyro_y -= 2 * k_gyro
        if gyro_z >=k_gyro:
            gyro_z-= 2 * k_gyro
    return gyro_x,gyro_y,gyro_z
def get_angle(self):                                 #角度

    try:
        rxh = int(datahex[50:52], 16)
        rxl = int(datahex[48:50], 16)
        ryh = int(datahex[54:56], 16)
        ryl = int(datahex[52:54], 16)
        rzh = int(datahex[58:60], 16)
        rzl = int(datahex[56:58], 16)
    except IOError:
        print("ReadError: gyro_acc")
        return (0, 0, 0)
    else:
        k_angle = 180

        angle_x = (rxh << 8 | rxl) / 32768 * k_angle
        angle_y = (ryh << 8 | ryl) / 32768 * k_angle
        angle_z = (rzh << 8 | rzl) / 32768 * k_angle
        if angle_x >= k_angle:
            angle_x -= 2 * k_angle
        if angle_y >= k_angle:
            angle_y -= 2 * k_angle
        if angle_z >=k_angle:
            angle_z-= 2 * k_angle
    return angle_x,angle_y,angle_z



ser = serial.Serial("com6", 115200, timeout=0.5)  # 打开端口,改到循环外,避免一直开闭串口
while(1):
    print(ser.is_open)
    datahex = (ser.read(33).hex())
    acc = get_acc(datahex)
    gyro = get_gyro(datahex)
    angle = get_angle(datahex)
    print(datahex)  # 读一个字节
    print(acc)
    print(gyro)
    print(angle)
    #print(acc_x,acc_y,acc_z)


    #ser.close()

 

结果对比:

输出:

(-0.2060546875, -0.11181640625, 3.00634765625)
(0.0, 0.0, 0.0)
(-2.0928955078125, 3.9166259765625, 0.0)

上位机

经对比,处理后数据与自带上位机数据一致。


第二版:增加了校验和的验证,使其传输更加稳定。添加了更多注释便于理解/

#Initial-T 2019.6
import serial
 
ACCData=[0.0]*8
GYROData=[0.0]*8
AngleData=[0.0]*8         #定义三个数组,分别存储加速度角速度与角度的值
 
FrameState = 0            #通过0x后面的值判断属于哪一种情况
Bytenum = 0               #读取到这一段的第几位
CheckSum = 0              #求和校验位         
 
def DueData(inputdata):   #新增的核心程序,对读取的数据进行划分,各自读到对应的数组里
    global  FrameState    #在局部修改全局变量,要进行global的定义
    global  Bytenum
    global  CheckSum
    for data in inputdata:  #在输入的数据进行遍历
        if FrameState==0:   #当未确定状态的时候,进入以下判断
            if data==0x55 and Bytenum==0: #0x55位于第一位时候,开始读取数据,增大bytenum
                CheckSum=data
                Bytenum=1
                continue
            elif data==0x51 and Bytenum==1:#在byte不为0 且 识别到 0x51 的时候,改变frame
                CheckSum+=data
                FrameState=1
                Bytenum=2
            elif data==0x52 and Bytenum==1: #同理
                CheckSum+=data
                FrameState=2
                Bytenum=2
            elif data==0x53 and Bytenum==1:
                CheckSum+=data
                FrameState=3
                Bytenum=2
        elif FrameState==1: # acc    #已确定数据代表加速度
            if Bytenum<10:            # 读取8个数据
                ACCData[Bytenum-2]=data # 从0开始
                CheckSum+=data
                Bytenum+=1
            else:
                if data == (CheckSum&0xff):  #假如校验位正确
                    print(get_acc(ACCData))
                CheckSum=0                  #各数据归零,进行新的循环判断
                Bytenum=0
                FrameState=0
        elif FrameState==2: # gyro
            if Bytenum<10:
                GYROData[Bytenum-2]=data
                CheckSum+=data
                Bytenum+=1
            else:
                if data == (CheckSum&0xff):
                    print(get_gyro(GYROData))
                CheckSum=0
                Bytenum=0
                FrameState=0
        elif FrameState==3: # angle
            if Bytenum<10:
                AngleData[Bytenum-2]=data
                CheckSum+=data
                Bytenum+=1
            else:
                if data == (CheckSum&0xff):
                    print(get_angle(AngleData))
                CheckSum=0
                Bytenum=0
                FrameState=0
 
 
def get_acc(datahex):  #加速度
    axl = datahex[0]                                        
    axh = datahex[1]
    ayl = datahex[2]                                        
    ayh = datahex[3]
    azl = datahex[4]                                        
    azh = datahex[5]
    
    k_acc = 16
 
    acc_x = (axh << 8 | axl) / 32768 * k_acc
    acc_y = (ayh << 8 | ayl) / 32768 * k_acc
    acc_z = (azh << 8 | azl) / 32768 * k_acc
    if acc_x >= k_acc:
        acc_x -= 2 * k_acc
    if acc_y >= k_acc:
        acc_y -= 2 * k_acc
    if acc_z >= k_acc:
        acc_z-= 2 * k_acc
    
    return acc_x,acc_y,acc_z
 
 
def get_gyro(datahex):                                          #陀螺仪
    wxl = datahex[0]                                        
    wxh = datahex[1]
    wyl = datahex[2]                                        
    wyh = datahex[3]
    wzl = datahex[4]                                        
    wzh = datahex[5]
    k_gyro = 2000
 
    gyro_x = (wxh << 8 | wxl) / 32768 * k_gyro
    gyro_y = (wyh << 8 | wyl) / 32768 * k_gyro
    gyro_z = (wzh << 8 | wzl) / 32768 * k_gyro
    if gyro_x >= k_gyro:
        gyro_x -= 2 * k_gyro
    if gyro_y >= k_gyro:
        gyro_y -= 2 * k_gyro
    if gyro_z >=k_gyro:
        gyro_z-= 2 * k_gyro
    return gyro_x,gyro_y,gyro_z
 
 
def get_angle(datahex):                                 #角度
    rxl = datahex[0]                                        
    rxh = datahex[1]
    ryl = datahex[2]                                        
    ryh = datahex[3]
    rzl = datahex[4]                                        
    rzh = datahex[5]
    k_angle = 180
 
    angle_x = (rxh << 8 | rxl) / 32768 * k_angle
    angle_y = (ryh << 8 | ryl) / 32768 * k_angle
    angle_z = (rzh << 8 | rzl) / 32768 * k_angle
    if angle_x >= k_angle:
        angle_x -= 2 * k_angle
    if angle_y >= k_angle:
        angle_y -= 2 * k_angle
    if angle_z >=k_angle:
        angle_z-= 2 * k_angle
 
    return angle_x,angle_y,angle_z
 
 
if __name__=='__main__':  #主函数
    ser = serial.Serial("com8", 115200, timeout=0.5)  # 打开端口,改到循环外
    print(ser.is_open)
    while(1):
        #datahex = (ser.read(33).hex()) #之前转换成了字符串,一位变成了两位
        datahex = ser.read(33)       #不用hex()转化,直接用read读取的即是16进制
        DueData(datahex)            #调用程序进行处理

附上Arduino读取程序样例:

/*
This code is used for connecting arduino to serial mpu6050 module, and test in arduino uno R3 board.
connect map:
arduino   mpu6050 module
VCC    5v/3.3v
TX     RX<-0
TX     TX->1
GND    GND
note: 
because arduino download and mpu6050 are using the same serial port, you need to un-connect 6050 module when you want to download program to arduino.
 Created 14 Nov 2013
 by Zhaowen
 
 serial mpu6050 module can be found in the link below:
 http://item.taobao.com/item.htm?id=19785706431
 */

unsigned char Re_buf[11],counter=0;
unsigned char sign=0;
float a[3],w[3],angle[3],T;
void setup() {
  // initialize serial:
  Serial.begin(115200);
}

void loop() {
  if(sign)
  {  
     sign=0;
     if(Re_buf[0]==0x55)      //检查帧头
     {  
	switch(Re_buf [1])
	{
	case 0x51:
		a[0] = (short(Re_buf [3]<<8| Re_buf [2]))/32768.0*16;
		a[1] = (short(Re_buf [5]<<8| Re_buf [4]))/32768.0*16;
		a[2] = (short(Re_buf [7]<<8| Re_buf [6]))/32768.0*16;
		T = (short(Re_buf [9]<<8| Re_buf [8]))/340.0+36.25;
		break;
	case 0x52:
		w[0] = (short(Re_buf [3]<<8| Re_buf [2]))/32768.0*2000;
		w[1] = (short(Re_buf [5]<<8| Re_buf [4]))/32768.0*2000;
		w[2] = (short(Re_buf [7]<<8| Re_buf [6]))/32768.0*2000;
		T = (short(Re_buf [9]<<8| Re_buf [8]))/340.0+36.25;
		break;
	case 0x53:
        	angle[0] = (short(Re_buf [3]<<8| Re_buf [2]))/32768.0*180;
		angle[1] = (short(Re_buf [5]<<8| Re_buf [4]))/32768.0*180;
		angle[2] = (short(Re_buf [7]<<8| Re_buf [6]))/32768.0*180;
		T = (short(Re_buf [9]<<8| Re_buf [8]))/340.0+36.25;
                Serial.print("a:");
                Serial.print(a[0]);Serial.print(" ");
                Serial.print(a[1]);Serial.print(" ");
                Serial.print(a[2]);Serial.print(" ");
                Serial.print("w:");
                Serial.print(w[0]);Serial.print(" ");
                Serial.print(w[1]);Serial.print(" ");
                Serial.print(w[2]);Serial.print(" ");
                Serial.print("angle:");
                Serial.print(angle[0]);Serial.print(" ");
                Serial.print(angle[1]);Serial.print(" ");
                Serial.print(angle[2]);Serial.print(" ");
                Serial.print("T:");
                Serial.println(T);
                break;
	} 
    }
  } 
}

void serialEvent() {
  while (Serial.available()) {
    
    //char inChar = (char)Serial.read(); Serial.print(inChar); //Output Original Data, use this code 
  
    Re_buf[counter]=(unsigned char)Serial.read();
    if(counter==0&&Re_buf[0]!=0x55) return;      //第0号数据不是帧头              
    counter++;       
    if(counter==11)             //接收到11个数据
    {    
       counter=0;               //重新赋值,准备下一帧数据的接收 
       sign=1;
    }
      
  }
}

需要注意的问题有: 

 

利用python的pyserial模块

注意不能出现占用端口的现象

更改程序要注意端口是否对应

不关闭串口再循环的时候,会出现串口被占用的错误

字符串要进行类型转换才能参与计算

try的工作原理是,当开始一个try语句后,python就在当前程序的上下文中作标记,这样当异常出现时就可以回到这里,try子句先执行,接下来会发生什么依赖于执行时是否出现异常。

如果当try后的语句执行时发生异常,python就跳回到try并执行第一个匹配该异常的except子句,异常处理完毕,控制流就通过整个try语句(除非在处理异常时又引发新的异常)。
如果在try后的语句里发生了异常,却没有匹配的except子句,异常将被递交到上层的try,或者到程序的最上层(这样将结束程序,并打印缺省的出错信息)。
如果在try子句执行时没有发生异常,python将执行else语句后的语句(如果有else的话),然后控制流通过整个try语句。

改进版代码的github地址如下,读取并记录到本地文件。https://github.com/hanjintao1996/jy61-/tree/master

如有帮助请在github点赞,感激不尽。

参考链接: https://www.cnblogs.com/dongxiaodong/p/9992083.html 串口处理

                 https://www.oschina.net/question/89964_62779 常见新手错误

                 https://blog.csdn.net/woxiaozhi/article/details/58603865  字符串整数转换

               https://blog.csdn.net/APP852045932/article/details/87995972 树莓派处理JY901