查IC网首页|请登录|马上注册|保存桌面|手机浏览

公司档案

深圳市鹏创达电子有限公司

会员类型: 普通会员

已缴纳保证金: ¥0.00

成立年份: 2015

会员年限: 第3年

证件审核:            资料通过认证

访问人气: 738次

联系方式: 13632549326

即时通讯:     

办公地点: 深圳市福田区华强路现代之窗A座10B

更多信息:展开+

更多>>新闻分类
  • 暂无分类
站内搜索
 
更多>>友情链接
  • 暂无链接
首页 > > STM32 实现 MPU6050 数据读取与倾角检测
STM32 实现 MPU6050 数据读取与倾角检测
发布时间:2021-05-20        浏览次数:88        返回列表

序言

MPU6050 是一个很好玩儿传感器,在四轴、体验、记步等主要用途都能见到这小集成ic的身影,其內部的构造、作用十分丰富多彩,可玩度十分高。另外,对传感器收集到的数据信息开展剖析还能获得很多信息内容,但这时的一些「数学课小窍门」也许会使你伤脑,因此 你能在网络上瘋狂搜索材料,最后发觉了文中。

殊不知很遗憾,小编也是一名数学课渣滓(购物找零都不容易算的那类),因此 本文并不可以教會你「卡尔曼滤波」、「协方差矩阵列阵」等我自身都不明白的物品。实际上假如你仅仅想从传感器读取手机陀螺仪和加速度值,并简易测算一下座标倾斜角(不涉及到姿势解算、四元素等),实际上沒有想像中那麼难。

PS:MPU6050 确实牵涉到很多令人钦佩的数学思想方法,但假如仅仅想要「简单」的座标交角,彻底没必要搬离「加速度手机陀螺仪结合」、「四元素欧拉角」、「手机陀螺仪積分」、「內部的 DMP」等专有名词来忽悠人,用普通高中三角函数专业知识已充足。如果你有更高精密数据信息的要求时,也没必要自身从头开始造轮子,立即移殖现有的库就可以。文中将完成前一种构想 —— 应用简单方式测算粗略地视角

文章内容分成 3 个一部分(试验):

  1. 运用 I2C 协议书从传感器读取 6 个数据信息(三轴陀螺仪 三轴加速度值)

  2. 根据 3 个加速度值,根据反三角函数测算传感器与各纵坐标交角(倾斜角)

  3. 运用测算到倾斜角做一个小玩意儿 —— 根据「重力感应器(歪斜单片机开发板)」操纵流水灯的速率、方位

准备专业知识

  • STM32 基本上开发方式

  • I2C 协议书通信基本上步骤基本原理

  • 基本上物理力学基本常识

  • 简易三角函数专业知识

角速度与加速度

角速度

角速度叙述物件的转动的速度,实际指物件在单位时间内掉转的视角。举个非常简单的事例:时钟上的表针 60 秒旋转一圈(360 度),故表针单位时间内掉转的视角为:360 / 60 = 6 度/秒,即表针旋转的角速度。根据角速度和健身时间能测算出表针掉转视角。

在平面图中,物件一直紧紧围绕着一个「旋转中心」开展转动(如表壳的圆心点),而在三维空间中,物件是紧紧围绕着一根「转动轴」开展转动(例如,烤肉串?),而且绕一切「转动轴」的转动都能够溶解为室内空间直角坐标中 X、Y、Z 三个轴上转动的生成,这也是 MPU6050 所精确测量的三个角速度值,同样,获得三轴角速度份量就可以明确任一转动情况。

加速度

加速度就是指速率转变的速度,稍有物理常识的人都了解:仅有在外面力的作用下,物件的速率才会转变,因此 把加速度简易了解为物件承受力状况好像也没问题。举个事例,用劲把一块石头扔出一瞬间的加速度比捧着石块渐渐地挪动的加速度要大很多。除此之外,加速度一样还可以溶解为三个纵坐标上的份量。

MPU6050 加速度方位

必须留意的一点是,MPU6050 读到加速度与承受力方位反过来。

传感器在静止不动情况遭受往下的作用力,精确测量到的加速度往上(掌握这一点很重要)。同样,假如给一个水准往左边的力,精确测量到的加速度往右边。这也是一些材料中「小箱子和球」实体模型所表述的基本原理。

MPU6050 详细介绍

MPU-60x0 是全世界第一例 9 轴健身运动解决传感器。它集成化了 3 轴MEMS手机陀螺仪,3 轴MEMS加速度计,及其一个可拓展的数据健身运动CPU DMP(Digital Motion Processor)。 MPU-60x0 对手机陀螺仪和加速度计各自用了三个 16 位的 ADC,将其精确测量的模拟量输入转换为可輸出的数据量。为了更好地精准追踪迅速和慢速度的健身运动,传感器的检测范围全是客户可控性的,手机陀螺仪能测范畴为 ±250,±500,±1000,±2000°/秒(dps),加速度计能测范畴为 ±2,±4,±8,±16g。

运用范畴

从上边的剖析可了解,角速度能体现物件的匀速圆周运动情况,加速度可以体现物件的静止不动和匀速直线运动情况,二者相辅相成就能鉴别很繁杂的运动状态,如四轴飞行器的姿势、人走动的体态、空中鼠标的偏移量、手机横屏坚屏这些。可以说基本上全部与健身运动、部位、姿势有关的运用都能够用加速度手机陀螺仪传感器完成。自然,从成本费、开发设计难度系数考虑到并不会彻底那样做。

传感器平面坐标

做为精确测量值的方位参照,传感器座标方位界定如下图,归属于右手坐标系(左手大拇指偏向 x 轴的正方位,无名指偏向 y 轴的正方位,中拇指能偏向 z 轴的正方位):

控制模块电线接法

我是应用企业 STM32F407ZGT6 单片机开发板上集成化的 MP6050 集成ic,可免除布线的不便,假如单片机开发板上沒有相对集成ic就只有应用控制模块了,布线如下图:

注:应用仿真模拟 I2C 时 GPIO 不固定不动,图上的 PB8 和 PB9 能够依据具体情况开展变更,与编码相匹配就可以。

I2C 基本上时钟频率涵数

MPU6050 与 MCU 根据 I2C 系统总线开展通信。用手机软件仿真模拟的方法完成 I2C 最底层基本上时钟频率涵数,包含起止、终止数据信号的造成,及其发送/接受单字节数据信息、检验/发送回复数据信号。

// 【基本】基本上数据信息载入USERsrci2c.h void I2C_Init(void); // I2C 复位 void I2C_Start(void); // 造成 I2C 协议书起始信号 void I2C_Stop(void); // 造成 I2C 协议书完毕数据信号 void I2C_Write_Byte(uint8_t byte); // 发送八位数据信息(不包含回复) uint8_t I2C_Read_Byte(void); // 载入八位数据信息(不包含回复) uint8_t I2C_Read_ACK(void); // 接受回复数据信号 void I2C_Write_ACK(uint8_t ack); // 发送回复数据信号

传感器 I2C 机器设备详细地址

MCU 做为服务器与传感器通信前必须发送 7 位的从设备详细地址,机器设备详细地址的常用招数是固定不动上位,并根据脚位脉冲信号明确底位。查看寄存器指南获知,117 号寄存器 WHO AM I 决策着机器设备详细地址的高 6 位,通电校准数值 ,最少 1 位则由外界脚位 AD0 决策(即一块线路板数最多只有有两个 MPU6050)。查询控制模块或单片机开发板原理图明确集成ic AD0 的脉冲信号(一般为低)最后获得 7 位的 I2C 从设备详细地址为 1101 000(0xD0),在 mpu6050.h 文档中宏定义为 DEV_ADDR 。

注:文章内容偏重于解读传感器应用,I2C 涵数实际完成见文尾实例编码。但在开展下边的试验以前,请保证你的 I2C 通信是一切正常的、发送元器件详细地址能获得回复。

MPU6050 操作过程涵数

在完成了基本上时钟频率涵数后,应用 I2C 系统总线跟外围设备通讯事实上便是基本上时钟频率的拼凑。下边撰写 MPU6050 相关函数,在其中读写能力寄存器涵数是关键实际操作。

写寄存器

写寄存器步骤以下:

  1. 发送起始信号

  2. 发送机器设备详细地址(写方式)

  3. 发送內部寄存器详细地址

  4. 载入寄存器数据信息(8 位数据信息总宽)

  5. 发送完毕数据信号

编码完成:

// 【基本】基本上数据信息载入USERsrcmpu6050.c void MPU6050_Write_Reg(uint8_t regAddr, uint8_t regData){  I2C_Start();  I2C_Write_Byte(DEV_ADDR); if (I2C_Read_ACK()) goto stop;  I2C_Write_Byte(regAddr); if (I2C_Read_ACK()) goto stop;  I2C_Write_Byte(regData); if (I2C_Read_ACK()) goto stop; stop: I2C_Stop();}

读寄存器

读寄存器步骤以下:

  1. 发送起始信号

  2. 发送机器设备详细地址(写方式)

  3. 发送內部寄存器详细地址

  4. 发送反复起始信号

  5. 发送机器设备详细地址(读方式)

  6. 载入寄存器数据信息(8 位数据信息总宽)

  7. 发送完毕数据信号

编码完成:

// 【基本】基本上数据信息载入USERsrcmpu6050.c uint8_t MPU6050_Read_Reg(uint8_t regAddr){ uint8_t regData;  I2C_Start();  I2C_Write_Byte(DEV_ADDR); if (I2C_Read_ACK()) goto stop;  I2C_Write_Byte(regAddr); if (I2C_Read_ACK()) goto stop;  I2C_Start();  I2C_Write_Byte(DEV_ADDR | 0x01); if (I2C_Read_ACK()) goto stop;  regData = I2C_Read_Byte(); I2C_Write_ACK(1); // 非回复数据信号 stop: I2C_Stop(); return regData;}

寄存器详细地址宏定义

在 mpu6050.h 文档中对常见的寄存器详细地址开展宏定义,方便快捷并提升 程序流程易读性。

// 【基本】基本上数据信息载入USERsrcmpu6050.h #define DEV_ADDR 0xD0 // 6050 元器件详细地址 //----------------------------------------- // 界定MPU6050內部详细地址 //----------------------------------------- #define SMPLRT_DIV 0x19 //手机陀螺仪采样频率,典型值:0x07(125Hz) #define ConFIG 0x1A //低通滤波頻率,典型值:0x06(5Hz) #define GYRO_ConFIG 0x1B //手机陀螺仪自查及检测范围,典型值:0x18(不自查,2000deg/s) #define ACCEL_ConFIG 0x1C //加快计自查、检测范围及高通滤波頻率,典型值:0x01(不自查,2G,5Hz) #define ACCEL_XOUT_H 0x3B #define ACCEL_XOUT_L 0x3C #define ACCEL_YOUT_H 0x三维 #define ACCEL_YOUT_L 0x3E #define ACCEL_ZOUT_H 0x3F #define ACCEL_ZOUT_L 0x40 #define TEMP_OUT_H 0x41 #define TEMP_OUT_L 0x42 #define GYRO_XOUT_H 0x43#define GYRO_XOUT_L 0x44 #define GYRO_YOUT_H 0x45 #define GYRO_YOUT_L 0x46 #define GYRO_ZOUT_H 0x47 #define GYRO_ZOUT_L 0x48 #define PWR_MGMT_1 0x6B //电源管理,典型值:0x00(正常启用) #define WHO_AM_I 0x75 //IIC地址寄存器(默认数值0x68,只读) #define SlaveAddress 0xD0 //IIC写入时的地址字节数据,+1为读取

传感器初始化

在使用传感器测量数据之前,先要利用前面写好的读写寄存器函数,对传感器初始化,包括常用参数配置,如采样率、滤波频率等,如无特殊要求使用典型值即可(各参数具体含义请查阅芯片寄存器手册)。

代码实现:

// 【基础】基本数据读取USERsrcmpu6050.c void MPU6050_Init(void){ I2C_Init(); // I2C 初始化 MPU6050_Write_Reg(PWR_MGMT_1, 0x00); //解除休眠状态 MPU6050_Write_Reg(SMPLRT_DIV, 0x07); //陀螺仪采样率,典型值:0x07(125Hz) MPU6050_Write_Reg(CONFIG, 0x06); //低通滤波频率,典型值:0x06(5Hz) MPU6050_Write_Reg(GYRO_CONFIG, 0x18); //陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s) MPU6050_Write_Reg(ACCEL_CONFIG, 0x01); //加速计自检、测量范围及高通滤波频率,典型值:0x01(不自检,2G,5Hz) }

合成 16 位完整数据

传感器原始数据 AD 值为 16 位数字量,从寄存器地址定义宏名可知,一个数据需要两个字节(寄存器)来表示,如 ACCEL_XOUT_H 和 ACCEL_XOUT_L 两个寄存器分别存储 X 轴加速度高 8 位和低 8 位,共同组成 16 位数据,按照这个思路,编写一个连续读两个寄存器并合成 16 位数据的函数。

代码实现:

// 【基础】基本数据读取USERsrcmpu6050.c int16_t MPU6050_Get_Data(uint8_t regAddr){ uint8_t Data_H, Data_L; uint16_t data; Data_H = MPU6050_Read_Reg(regAddr); Data_L = MPU6050_Read_Reg(regAddr + 1); data = (Data_H << 8) | Data_L; // 合成数据 return data;}

基础功能 —— 读取原始数据

写好读 16 位数据的函数,获取加速度、陀螺仪数值自然便水到渠成。只需给出待读取数据高位寄存器地址(因为高位寄存器地址在前),调用 MPU6050_Get_Data() 函数即可得到合成后 16 位加速度、温度、陀螺仪数值。如 MPU6050_Get_Data(ACCEL_XOUT_H) 表示读取 16 位 X 轴加速度数据。

在 main.c 中编写 MPU6050_Display 函数,实现数据读取并在串口打印。

// 【基础】基本数据读取USERsrcmain.c void MPU6050_Display(void){  printf("ACCEL_X: %d ", MPU6050_Get_Data(ACCEL_XOUT_H)); printf("ACCEL_Y: %d ", MPU6050_Get_Data(ACCEL_YOUT_H)); printf("ACCEL_Z: %d ", MPU6050_Get_Data(ACCEL_ZOUT_H));  printf("TEMP: %0.2f ", MPU6050_Get_Data(TEMP_OUT_H) / 340.0 + 36.53);  printf("GYRO_X: %d ", MPU6050_Get_Data(GYRO_XOUT_H)); printf("GYRO_Y: %d ", MPU6050_Get_Data(GYRO_YOUT_H)); printf("GYRO_Z: %d ", MPU6050_Get_Data(GYRO_ZOUT_H)); printf("");}// 【基础】基本数据读取USERsrcmain.c int main(){ MPU6050_Init(); Usrat_1_Init(84,9600,0); while (1) { // Usart_Send_Byte(MPU6050_Read_Reg(GYRO_XOUT_L)); MPU6050_Display(); // 串口打印传感器数据 Delay(0xfffff); }}

编译下载程序、连接开发板与电脑并打开串口工具。抖动、倾斜开发板会看到数据随之变化。

实验效果:

单位换算

如需把原始的 16 位数据用重力加速度单位 g 表示,需要注意传感器的量程。默认测量范围是 ±2g,一共是 4g 的宽度,16 位数据的最大读数为除 4 就是即 1g 加速度对应的数值。打印前把数据除以即可得到对应单位为 g 的数值,角速度的单位转换也同理。

 printf("ACCEL_X: %lf ", MPU6050_Get_Data(ACCEL_XOUT_H) / <span 0); printf("ACCEL_Y: %lf ", MPU6050_Get_Data(ACCEL_YOUT_H) / <span 0); printf("ACCEL_Z: %lf ", MPU6050_Get_Data(ACCEL_ZOUT_H) / <span 0);

零偏校准

由于每个芯片在制作时都不一样,芯片固定在 PCB 上也会有位置误差,就会导致读到的数据有偏差,对传感器进行零偏校准可一定程度上减小这种误差的影响。对程序作最后修改,在读到的数据基础上加上一个补偿值 OFFSET 对数据校准。

代码实现

// 基本数据读取USERsrcmain.c #define X_ACCEL_OFFSET -600 #define Y_ACCEL_OFFSET -100 #define Z_ACCEL_OFFSET 2900 #define X_GYRO_OFFSET 32 #define Y_GYRO_OFFSET -11 #define Z_GYRO_OFFSET 1 void MPU6050_Display(void){  printf("ACCEL_X: %d ", MPU6050_Get_Data(ACCEL_XOUT_H) + X_ACCEL_OFFSET); printf("ACCEL_Y: %d ", MPU6050_Get_Data(ACCEL_YOUT_H) + Y_ACCEL_OFFSET); printf("ACCEL_Z: %d ", MPU6050_Get_Data(ACCEL_ZOUT_H) + Z_ACCEL_OFFSET);  printf("TEMP: %0.2f ", MPU6050_Get_Data(TEMP_OUT_H) / 340.0 + 36.53);  printf("GYRO_X: %d ", MPU6050_Get_Data(GYRO_XOUT_H) + X_GYRO_OFFSET); printf("GYRO_Y: %d ", MPU6050_Get_Data(GYRO_YOUT_H) + Y_GYRO_OFFSET); printf("GYRO_Z: %d ", MPU6050_Get_Data(GYRO_ZOUT_H) + Z_GYRO_OFFSET); printf("");}

把开发板/模块的放置在水平位置(或其他参考位置)保持静止,观察并记录输出的原始数据,不断调整各个补偿值 OFFSET 的大小,确保原始数据在 X、Y、Z 轴加速度为 0,0对应 1 g) 左右,且三轴角速度值为 0。

扩展操作 —— 坐标倾角计算

能读到原始数据来只是最基础、甚至是没什么卵用的,下面将进行简单的计算,将加速度数据转换为与重力(方向向上)的夹角,这才是重点好嘛!当然,涉及到的运算非常简单 —— 只涉及到三角函数(复杂的算法网上已经有用 Arduino 库、匿名、圆点博士、 DMP 库等实现)。

测量原理 —— 三角函数的妙用

只考虑 X 轴和 Z 轴(此时Y 轴垂直于屏幕),将重力加速度作在 X 轴上分解,传感器 X 坐标正方向与重力加速度 g 的夹角为 ∠b,如下图所示:

从图中可以看到,重力加速度 g(绿色箭头)分解为 X 轴上的 gx(黑色箭头)和 Z 轴上的 gz(紫色箭头), g 与 gx 的夹角 ∠b 的余弦值 cos(∠b) = gx / g,对 gx / g 求反余弦即可得到 ∠b 的值,即 ∠b = arccos(gx / g),这便是求倾角的原理。

同理,Z 轴与重力的夹角也可以通过 arccos(gz / g) 求出来,此时 gz 落在 Z 轴负半轴所以 gz / g 为负数,算出来的角度是大于 90° 的,大小等于图中的 180 - ∠c。Y 轴则始终垂直于重力方向,夹角为 90°。

将三个角度值用结构体封装,编写 MPU6050_Get_Angle() 函数获取夹角。利用 C 语言 math.h 头文件提供的 acos() 库函数可以很方便计算反余弦函数,不过返回的是弧度值,要转换为角度的话需乘上 57/code>。

代码实现:

// 【扩展】坐标倾角计算USERsrcmain.c typedef struct Angle{ double X_Angle; double Y_Angle; double Z_Angle; } MPU6050_Anglevoid MPU6050_Get_Angle(MPU6050_Angle *data){  data->X_Angle = acos((MPU6050_Get_Data(ACCEL_XOUT_H) + X_ACCEL_OFFSET) / <span 0); data->Y_Angle = acos((MPU6050_Get_Data(ACCEL_YOUT_H) + Y_ACCEL_OFFSET) / <span 0); data->Z_Angle = acos((MPU6050_Get_Data(ACCEL_ZOUT_H) + Z_ACCEL_OFFSET) / <span 0);  data->X_Angle = data->X_Angle * 57/span>; data->Y_Angle = data->Y_Angle * 57/span>; data->Z_Angle = data->Z_Angle * 57/span>;}

在主函数中不断读取实时倾角并打印:

// 【扩展】坐标倾角计算USERsrcmain.c int main(){ MPU6050_Angle data; MPU6050_Init(); Usrat_1_Init(84,9600,0); while (1) { MPU6050_Get_Angle(&data); // 计算三轴倾角 printf("X_Angle = %lf° ", data.X_Angle); printf("Y_Angle = %lf° ", data.Y_Angle); printf("Z_Angle = %lf° ", data.Z_Angle); printf(""); Delay(0xfffff); }}

需要再次强调,传感器实际测得的重力方向是向上的,计算出的夹角是传感器三个坐标轴与竖直向上重力向量的夹角,开发板水平放置时,传感器 X、Y 轴坐标垂直于重力,Z 轴与重力夹角为 0。

实验效果:

来点好玩的? —— 重力感应流水灯

智能手机和平板刚兴起那会儿,最吸引我的就是「重力感应」的功能 —— 通过倾斜手机,实现屏幕自动旋转、重力感应游戏,例如控制屏幕上的一个小球「走迷宫」的游戏:

是否能利用开发板上的四个 LED 实现类似的功能呢?通过倾斜开发板控制单个 LED 的流动方向、速度,不也实现了「小球滚动」的效果么?想想就有点小激动,废话不多说,理一下思路马上开干!

单步流水灯函数

先从简单的 LED 下手。将流水灯的「一次流动」封装为函数,接收一个参数 direction 表示流动方向(左/右),流动速度通过控制函数调用频率调节。

代码实现

// 【应用】重力感应流水灯USERsrcled.c void LED_flow(uint16_t direction){ static uint8_t state = 1; switch (state) { case 0: LED_ON(LED1_PIN); break; case 1: LED_ON(LED2_PIN); break; case 2: LED_ON(LED3_PIN); break; case 3: LED_ON(LED4_PIN); break; } state = (direction == DIR_LEFT) ? state - 1 : state + 1; state = state % 4; }

PS:其实这个函数写的并不好,使用静态变量会导致函数状态不确定。

开发板倾角分析

开发板四个 LED 沿着 X 轴方向横向排列,让开发板绕 Y 轴旋转,同时读取 Z 轴 和 X 轴与重力倾角。Z 轴倾角表示倾斜程度,倾角越大倾斜越厉害。X 轴倾角表示倾斜方向,小于 90°时向左倾,大于 90°时向右倾。

不同倾斜情况时 X 轴、Z 轴对应夹角示意图如下:

通过测量 ∠a 和 ∠b 两个夹角分别控制流水灯的速度和流向,留意图中第 2 和第 3 个开发板中 ∠a 的大小都是 30°,而 ∠b 一个小于 90°一个大于 90°,所以两个开发板上流水灯的速度相同、方向相反。在上一个测量倾角程序的基础上,根据 X 、Z 轴倾角调用流水灯函数,便可实现「重力感应流水灯」。

代码实现

// 【应用】重力感应流水灯USERsrcmain.c int main(){ MPU6050_Angle data; uint8_t Run_Direc = DIR_LEFT; uint32_t Run_Speed = 0x2ffffff; LED_Init(); MPU6050_Init(); Usrat_1_Init(84,9600,0); while (1) { MPU6050_Get_Angle(&data); // 计算三轴倾角 Run_Speed = 0/span> - 0x582D8 * data.Z_Angle; // 计算流水灯延时速度(参数自调) if (data.Z_Angle > 10) // 当 Z 轴倾斜大于 10° 时开启流水灯 { Run_Direc = (data.X_Angle < 90) ? DIR_LEFT : DIR_RIGHT; // 通过 X 轴倾角判断流动方向 LED_flow(Run_Direc); // 流水灯 Delay(Run_Speed); // 延时 } }}

实验效果


更多应用可能

经历了三个实验之后,文章也接近尾声,但强大的 MPU6050 绝不仅仅能用来控制流水灯、四轴、体感手柄……由于 MPU6050 高精度的特性,在「云 + 端」的大数据物联网时代,利用边缘的传感器采集海量数据,配合红到发紫的人工智能对数据进行分析、学习,更是具有无限可能。

工程示例代码下载:链接: 密码:i5us

关注公众号【DebugMind】,订阅最新文章。