锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

毕业设计2- MPU6050传感器调试记录(STM32CubeMX+STM32F103C8T6)

时间:2022-10-08 16:30:00 电机振动传感器漂移8fu传感器传感器t6n温度传感器va传感器伟力通t6c内置传感器电池bca73sl072传感器

文章目录

  • 一、前情提要
  • 二、STM32CubeMX配置
  • 三、寄存器介绍
  • 四、Keil代码介绍
  • 总结

本次调试的传感器为正点原子MPU6050(三轴陀螺仪 三轴加速度)。因为欧拉角不需要在我的毕设中解算,只是用来反映风电机组(demo)振动可以。为了简化, 所以我只用三轴加速度计,但在本介绍中,使用了硬件I2C读取硬件传感器的原始数据,并进行这些数据 卡尔曼滤波

一、前情提要

这部分问题是我刚接触到这个传感器的几个问题。我通过查阅我自己的信息有了一个大致的了解。所以我想在这里分享。感谢您的分享,让我不断学习。

看了一个博客和一个小破站的视频,感觉博主和UP主讲的很好
小破站视频: https://www.bilibili.com/video/BV1sL411F7fu?spm_id_from=333.337.search-card.all.click
知乎博客: https://zhuanlan.zhihu.com/p/195683958
代码参考视频: https://www.bilibili.com/video/BV1qf4y1r73k?spm_id_from=333.337.search-card.all.click

这些问题也是我自己的理解,仅供参考。如有不正确之处,请指出我的邮箱:280243362@qq.com

1.你可以解释这个N轴IMU一些常见的概念吗?
(1)笛卡尔坐标系:
在这里插入图片描述
在下面的实际图片中,它似乎与上面的图片不同。事实上,我们只需要旋转一下。我说这个的目的是在解决姿势时经常旋转坐标系,注意XYZ不要混淆轴的位置关系,有时我自己旋转得更多,所以我可以帮助我们理解右手坐标系。

(2)欧拉角:我们常说的欧拉角指的是ROLL(横滚角),偏航角(Yaw)、俯仰角(Pitch)。具体的大家可以根据下面的模型来进行理解。知乎博主码农爱学习写得真好,简单易懂,非常适合我们这种小白。https://zhuanlan.zhihu.com/p/195683958,我敢再写一遍也是抄袭别人的东西,博主的总结真的很完整。

(3)四元素:他是另一种表示旋转的方式,可以和欧拉角一起转换,具体可以参考CSDN另一位博主的文章:https://blog.csdn.net/xiaoma_bk/article/details/79082629
(4)
陀螺仪传感器:这涉及到很多物理方面,我们可以简单地认为它可以X、Y、Z轴的角速。
(5)加速度计传感器:测量X、Y、Z的加速度。
(6)磁力计传感器:测量的3轴磁场强度(我目前不太熟悉,以后要慢慢用)

2. 你能解释一下我在网上查阅的信息中有3轴、6轴、9轴和10轴传感器的区别吗?
(1) 三轴传感器是指三轴陀螺仪。 它测量了三个轴(XYZ)事实上,三个欧拉角(俯仰角、横滚角、偏航角)也可以通过这三个数据进行姿态解算,因此他也可以独立工作,市场上有一个常见的三轴传感器。但传感器的问题是:
(a)因为它的计算方法角度=角速度*dt,大家学过高数和大学物理,当dt很小时,我们可以把物体视为匀速运动,但问题不符合实际情况。我们只是假设会有很大的积累误差;
(b)之前看博主说只用了3轴陀螺仪。相位差问题(这部分我不太懂,以后需要补充一些知识)。
伟大的前辈也知道这个问题,所以产生了一个神奇的想法。你能用两个传感器互相纠正,然后取一个平均值吗(当然,传感器集成并不是那么简单,这里只是一个栗子)应该更准确。然而,从两个角度来看,效果应该更好,因为没有必要添加一个3轴陀螺仪(我不知道在这里解释更好,也就是说,误差很大,最好从另一个角度测量,而不是叠罗汉。),于是产生了6轴加速度。
(2)6轴传感器是指3轴陀螺仪 三轴加速度计。 3轴加速度计测量的是XYZ轴上加速度g的一个重量。当坐标系非常正确时,三轴的值应该是aacx=0g、aacy=0g、aacz=1*g,9是重力加速度,9是重力加速度.8m/s2。当传感器有倾斜时,gx轴和y轴上会有一个重量,可能会变成aacx=0.3g、aacy=0.2g、aacz=0.7g,事实上,我们可以根据这些数据得到三个欧拉角,所有的加速度计也可以单独结算,但如果单独使用,我们就无法解决偏航角。我们可以参考公式,我们通过传感器得到的是ax、ay、az但是我们通过推导发现y消失了(p代表pitch俯仰角、r表示raw横滚角、y表示yaw偏航角),数学是如此神奇,哈哈,出乎意料,因此,三轴加速度计只能解决pitch角和raw角,所以6轴传感器yaw(偏航角会一直漂移)pitch(俯仰角)和raw(横滚角)通过数据融合算法获得相对稳定的值。,目前常用的数据融合算法是DMP卡尔曼滤波器。卡尔曼滤波器必须说是一件非常神奇的事情,我强烈感谢卡尔曼前辈们能够研究出如此优秀和通用的算法。

(3)9轴传感器是指3轴陀螺仪 3轴加速度计 3轴磁力计。您可以猜到添加3轴磁力计的目的。您还可以校准偏航角,以获得相对稳定的3个欧拉角(或稳定的4元组)。您可以参考上过程可以参考上面推荐的知乎博客。老板写得很好。当然,小破站上弟弟的视频也很清楚,再次感谢老板们。我这次没有选用类似MPU92509轴传感器,一个是因为我不需要姿势计算,另一个是因为我的整个系统都有电机和钢轴,这将对磁计的校准产生很大的影响,不能发挥相应的效果。因此,我们在使用时应该主要有磁场校准的影响。
9轴传感器不是很完美吗?10轴传感器是什么?
(4)10轴传感器是指3轴陀螺仪 3轴加速度计 3轴磁力计 1个气压计,这计算气压高度,我认为这是锦上添花。 ,当然,存在是合理的,自然有它的应用场景。

3. 网上说的DMP你能简单说一下算法和卡尔曼滤波器是什么吗?
(1)对于DMP算法:可参考知乎博主码农爱学习这篇文章:https://zhuanlan.zhihu.com/p/165156300
复制他的段落:DMP就是MPU6050内部运动引擎,全称Digital Motion Processor,直接输出接输出可以减轻外围微处理器的工作负担,避免繁琐的滤波和数据集成。 我们用的就像MPU6050、MPU9250等来说,DMP是硬件集成,也就是说,企业已经做了硬件嵌入,我们只需要调用相应的驱动库,这可以非常方便,一些具体的驱动程序可以参考点原子的驱动程序。
(2)卡尔曼滤波算法: 采用加速度计采集的三轴加速度计进行姿态融合pitch、roll作为观测值, 由陀螺仪采集的三轴角度值组成pitch,yaw,raw作为测量值。因此,可以使用卡尔曼滤波器现pitch(俯仰角)、roll(横滚角)的稳定输出。
(3)下面还是引用知乎博主“码农爱学习”中的内容:这部分其实反映的是卡尔曼滤波的思想,K其实指的是卡尔曼增益,这部分我们可以这样理解:每个传感器都可以进行姿态解算,得到自己的三个欧拉角,而我们希望把这些融合(直接取平均的方式显然太low)了。因为每个传感器都有自己的优缺点,我们应该把每个传感器的优点利用起来会使结果更加地准确,因此前辈们提取了每个传感器权重不同的方法来进行融合,至于这个权重到底是多少,卡尔曼老先生在自己的论文中有明确的推导(卡尔曼滤波最经典的那5个方程:2个预测方程,3个更新方程)。此处不再多做介绍,大家如果对卡尔曼滤波原理什么的想要有更深的了解,可以自行搜索,后面我也会自己出一期我自己对卡尔曼滤波的理解。

二、STM32CubeMX配置

有了上面的一些基础概念和了解,我们下面开始配置STM32CubeMX
(1) 在“SYS”设置为“SW”调试模式;

(2)我们在“RCC”中选择外部
晶振模式;

(3) 因为MPU6050采用的通信协议是I2C,而我们又想采用硬件I2C去开发,所以必须进行勾选。

(4)因为我们想把数据打印到电脑的“串口调试助手”,所以我们需要开一路串口,波特率为115200;

(5)我们下面配置时钟树;

(6)将这个工程存放在文件夹中,整个目录最好不要有中文,因为后面自动生成代码的时候并不能直接打开,不过问题也不大,需要自己去相关文件夹中的找到工程文件打开;

(7)我们勾选一下我们希望生成文件的配置。

到此为止,我们的配置就已经完成了。

三、寄存器介绍

对于传感器而言,寄存器是很重要的东西,所以我们想要使用一个传感器,一定要清楚他的寄存器怎样配置,数据存储在哪个寄存器中
(其实我在学习的时候也不喜欢看手册,但是咱们都是搞这个的,datasheet肯定要看的,后面我们能形成属于自己的一套看datasheet的流程和经验,会很舒服的)。我这里只是介绍一下我再这次调试中用到的寄存器,具体的每一个比特位到底表示什么大家自己查手册就好了。








四、Keil代码介绍

这部分是参考小破站上的视频讲解:https://www.bilibili.com/video/BV1qf4y1r73k?spm_id_from=333.337.search-card.all.click(俺是妥妥自己搬运工具人,同时加上自己的一点理解)。关于我们应该对I2C通信有个简单的了解吧(不了解的话,小破绽也有很多资源,我比较推荐的是创客学院的武老师的讲解)。对于I2C设备,都有一个自己的设备地址,我们首先要做的知道设备地址。

(1)首先我们进行多串口重映像,在“usart.h”文件中添加以下代码;

#include "stdarg.h"
#include "string.h"
#include "stdio.h"

(2)在“usart.c”文件中添加下面的代码;

#define TXBUF_SIZE_MAX 100
void usart2_printf(const char *format, ...)
{ 
        
    va_list args;
    uint32_t length;
    uint8_t txbuf[TXBUF_SIZE_MAX] = { 
        0};
 
    va_start(args, format);
    length = vsnprintf((char *)txbuf, sizeof(txbuf), (char *)format, args);
    va_end(args);
    HAL_UART_Transmit(&huart2, (uint8_t *)txbuf, length, HAL_MAX_DELAY);
    memset(txbuf, 0, TXBUF_SIZE_MAX);
}

(3)对于类似树莓派的Linux系统中我们可以通过I2C查询的指令即可实现,当然,STM32也有相应的程序可以查看:

for (uint8_t i=0; i<255;i++)
{ 
        
    if(HAL_I2C_IsDeviceReady(&hi2c1, i ,1, 1000) == HAL_OK)    //寻找设备地址,I2C原装函数 
    { 
        		
		usart2_printf("%d\r\n",i);
	    break;
	}.
}

我们在串口中可以看到返回值,我返回的是208,转换成16进制数据为0xD0,所以我们的I2C设备地址为0xD0。
(4)下面我们在“main.c”文件中 添加宏定义:

//***************************************MPU6050是地址***********************************//
#define MPU6050_ADDR 0xD0
//*******************************通过查看芯片手册相关传感器地址**************************//
#define SMPLRT_DIV_REG 0x19
#define GYRO_CONFIG_REG 0x1B
#define ACCEL_CONFIG_REG 0x1c
#define ACCEL_XOUT_H_REG 0x3B
#define TEMP_OUT_H_REG 0x41
#define GYRO_XOUT_H_REG 0x43
#define PWR_MGMT_1_REG 0x6B //电源管理1寄存器
#define WHO_AM_I_REG 0x75 
//************************加速度计*********************//
int16_t Accel_X_RAW = 0;
int16_t Accel_Y_RAW = 0;
int16_t Accel_Z_RAW = 0;
//***********************陀螺仪***********************//
int16_t Gyro_X_RAW = 0;
int16_t Gyro_Y_RAW = 0;
int16_t Gyro_Z_RAW = 0;
int16_t Temp_RAW = 0;
float Ax,Ay,Az,Gx,Gy,Gz,Temp;    //将最后读取的值放入这些变量中

(5)进行MPU6050初始化,配置一些寄存器;

void MPU6050_Init(void )
{ 
        
	/* HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout); 第1个参数为I2C操作句柄 第2个参数为从机设备地址 第3个参数为从机寄存器地址 第4个参数为从机寄存器地址长度 第5个参数为发送的数据的起始地址 第6个参数为传输数据的大小 第7个参数为操作超时时间    */
	uint8_t check,Data;
	// check device ID WHO_AM_I
	//检查设备ID 
	HAL_I2C_Mem_Read (&hi2c1 ,MPU6050_ADDR,WHO_AM_I_REG,1,&check ,1,1000);
	if(check == 104)		//if the device is present,寄存器地址默认0x68
	{ 
        
		//power management register 0x6B we should write all 0's to wake the sensor up
		//电源管理寄存器,我们应该在0x6B中写入所有0来唤醒传感器
		Data = 0;
		HAL_I2C_Mem_Write (&hi2c1 ,MPU6050_ADDR ,PWR_MGMT_1_REG ,1,&Data ,1,1000);
		//Set DATA RATE of 1KHz by writing SMPLRT_DIV register
		//通过写入SMPLRT_DIV寄存器设置1KHz的数据速率
		Data = 0x07;
		HAL_I2C_Mem_Write (&hi2c1 ,MPU6050_ADDR ,SMPLRT_DIV_REG ,1 ,&Data,1,1000);
		// Set accelerometer configuration in ACCEL_CONFIG Register
		// XA_ST=0,YA_ST=0,ZA_ST=0, FS_SEL=0 ->±2g
		//在加速度寄存器中修改配置
		Data = 0x00;
		HAL_I2C_Mem_Write (&hi2c1 ,MPU6050_ADDR, ACCEL_CONFIG_REG, 1, &Data, 1, 1000);
		// Set Gyroscopic configuration in GYRO_CONFIG Register
		// XG_ST=0,YG_ST=0, FS_SEL=0 ->± 250 °/s
		//修改重力加速计配置
		Data = 0x00;
		HAL_I2C_Mem_Write (&hi2c1 ,MPU6050_ADDR, GYRO_CONFIG_REG, 1, &Data, 1, 1000);
	}
}

(6)进行数据读取函数:加速度数据读取、陀螺仪数据读取、温度数据读取;

//************************************加速度传感器读取*************************************//
void MPU6050_Read_Accel(void)
{ 
        
	uint8_t Rec_Data[6];
	//Read 6 BYTES of data starting from ACCEL_XOUT_H register
	HAL_I2C_Mem_Read (&hi2c1 ,MPU6050_ADDR ,ACCEL_XOUT_H_REG ,1,Rec_Data ,6,1000);
	Accel_X_RAW = (int16_t )(Rec_Data [0] <<8 | Rec_Data [1]);
	Accel_Y_RAW = (int16_t )(Rec_Data [2] <<8 | Rec_Data [3]);
	Accel_Z_RAW = (int16_t )(Rec_Data [4] <<8 | Rec_Data [5]);
	Ax = Accel_X_RAW/16384.0;
	Ay = Accel_Y_RAW/16384.0;
	Az = Accel_Z_RAW/16384.0;
}
//*********************************陀螺仪数据读取*****************************************//
void MPU6050_Read_Gyro(void )
{ 
        
	uint8_t Rec_Data[6];
	// Read 6 BYTES of data staring from GYRO_XOUT_H register
	HAL_I2C_Mem_Read (&hi2c1, MPU6050_ADDR ,GYRO_XOUT_H_REG ,1,Rec_Data ,6 ,1000);
	Gyro_X_RAW = (int16_t )(Rec_Data [0] << 8 | Rec_Data [1]);
	Gyro_Y_RAW = (int16_t )(Rec_Data [2] << 8 | Rec_Data [3]);
	Gyro_Z_RAW = (int16_t )(Rec_Data [4] << 8 | Rec_Data [5]);
	Gx = Gyro_X_RAW/131.0;
	Gy = Gyro_Y_RAW/131.0;
	Gz = Gyro_Z_RAW/131.0;
}
//***********************************芯片温度读取************************************//
void MPU6050_Read_Temp(void )
{ 
        
	uint8_t Rec_Data[2];
	HAL_I2C_Mem_Read (&hi2c1 ,MPU6050_ADDR ,TEMP_OUT_H_REG ,1 ,Rec_Data  ,2 ,1000);
	Temp_RAW = (int16_t )(Rec_Data [0]<<8)|Rec_Data [1];
	Temp = 36.53 + (Temp_RAW )/340;
}

(7) 在主函数中调用这些函数即可;

MPU6050_Init ();
MPU6050_Read_Accel();
MPU6050_Read_Gyro();
MPU6050_Read_Temp();
usart2_printf("Ax=%.2f,Ay=%.2f,Az=%.2f\r\n",Ax,Ay,Az);
usart2_printf("Gx=%.2f,Gy=%.2f,Gz=%.2f\r\n",Gx,Gy,Gz);
usart2_printf("Temperature=%.2f,\r\n",Temp );
HAL_Delay (500);

(8)我们将这些代码编译,烧录到我们单片机中即可。


总结

通过本次实验,我学会了:
(1)了解了陀螺仪进行姿态计算的原理;
(2)大致了解卡尔曼滤波的基本过程,同时怎么样将卡尔曼应用到卡尔曼滤波;
(3)通过单片机实现了MPU6050原始数据的读取。
后面还需要学习的:
(1)陀螺仪、加速度计、磁力计他们的工作原理是什么,误差到底来自于那些方面;
(2)利用这些原始数据进行姿态解算的具体怎么操作;
(3)在草稿本上认真推导姿态解算的过程,并把它记录下来。

锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章