论坛» 嵌入式开发» MCU

嵌入式开发日记(3)——利用Python接收并处理维特智能JY61传感器数据

菜鸟
2019-08-08 10:12 1楼

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




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

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

image.png

性能参数:image.png

经测试,用在我的项目里精度已经足够。采用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() #处理完一轮后关闭端口


截图示意:

image.png

读取全部数据:

#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()

截图示意:

image.pngimage.png

image.png

结果对比:

输出:

(-0.2060546875, -0.11181640625, 3.00634765625)

(0.0, 0.0, 0.0)

(-2.0928955078125, 3.9166259765625, 0.0)

截图示意:

image.png

上位机

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



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


#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) #调用程序进行处理

image.png
image.pngimage.png

image.png

附上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;

}

}

}

截图示意:

image.pngimage.pngimage.png需要注意的问题有:

1、利用python的pyserial模块


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


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


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


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


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


7、如果当try后的语句执行时发生异常,python就跳回到try并执行第一个匹配该异常的except子句,异常处理完毕,控制流就通过整个try语句(除非在处理异常时又引发新的异常)。


8、如果在try后的语句里发生了异常,却没有匹配的except子句,异常将被递交到上层的try,或者到程序的最上层(这样将结束程序,并打印缺省的出错信息)。


9、如果在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

---------------------

版权声明:本文为CSDN博主「Initial-T」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/wulitaotao96/article/details/89303763



高工
2019-08-08 14:09 2楼

真详细

工程师
2019-08-08 14:13 3楼

不错不错

工程师
2019-08-11 20:48 4楼

转载的这片文章质量挺高

共4条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册]