文章目录
- 一、前情提要
- 二、STM32CubeMX配置
- 三、寄存器介绍
- 四、Keil代码介绍
- 总结
一、前情提要
这部分问题是我刚接触到这个传感器的几个问题。我通过查阅我自己的信息有了一个大致的了解。所以我想在这里分享。感谢您的分享,让我不断学习。
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)笛卡尔坐标系:
在下面的实际图片中,它似乎与上面的图片不同。事实上,我们只需要旋转一下。我说这个的目的是在解决姿势时经常旋转坐标系,,有时我自己旋转得更多,所以我可以帮助我们理解右手坐标系。 (2)欧拉角:我们常说的欧拉角指的是ROLL(横滚角),偏航角(Yaw)、俯仰角(Pitch)。具体的大家可以根据下面的模型来进行理解。知乎博主码农爱学习写得真好,简单易懂,非常适合我们这种小白。https://zhuanlan.zhihu.com/p/195683958,我敢再写一遍也是抄袭别人的东西,博主的总结真的很完整。 (3)四元素:他是另一种表示旋转的方式,可以和欧拉角一起转换,具体可以参考CSDN另一位博主的文章: (4)陀螺仪传感器:这涉及到很多物理方面,我们可以简单地认为它可以X、Y、Z轴的角速。 (5)加速度计传感器:测量X、Y、Z的加速度。 (6)磁力计传感器:测量的3轴磁场强度(我目前不太熟悉,以后要慢慢用)
它测量了三个轴(XYZ)事实上,三个欧拉角(俯仰角、横滚角、偏航角)也可以通过这三个数据进行姿态解算,因此他也可以独立工作,市场上有一个常见的三轴传感器。但传感器的问题是: (a)因为它的计算方法,大家学过高数和大学物理,当dt很小时,我们可以把物体视为匀速运动,但问题不符合实际情况。我们只是假设会有很大的积累误差; (b)之前看博主说只用了3轴陀螺仪。问题(这部分我不太懂,以后需要补充一些知识)。 伟大的前辈也知道这个问题,所以产生了一个神奇的想法。你能用两个传感器互相纠正,然后取一个平均值吗(当然,传感器集成并不是那么简单,这里只是一个栗子)应该更准确。然而,从两个角度来看,效果应该更好,因为没有必要添加一个3轴陀螺仪(我不知道在这里解释更好,也就是说,误差很大,最好从另一个角度测量,而不是叠罗汉。),于是产生了6轴加速度。 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轴磁力计的目的。您还可以校准偏航角,以获得相对稳定的3个欧拉角(或稳定的4元组)。您可以参考上过程可以参考上面推荐的知乎博客。老板写得很好。当然,小破站上弟弟的视频也很清楚,再次感谢老板们。 9轴传感器不是很完美吗?10轴传感器是什么? ,这计算气压高度,
我认为这是锦上添花。,当然,存在是合理的,自然有它的应用场景。
:可参考知乎博主码农爱学习这篇文章:https://zhuanlan.zhihu.com/p/165156300 复制他的段落: 我们用的就像MPU6050、MPU9250等来说,DMP是硬件集成,也就是说,企业已经做了硬件嵌入,我们只需要调用相应的驱动库,这可以非常方便,一些具体的驱动程序可以参考点原子的驱动程序。 采用加速度计采集的三轴加速度计进行姿态融合pitch、roll作为观测值, 由陀螺仪采集的三轴角度值组成pitch,yaw,raw作为测量值。因此,可以使用卡尔曼滤波器现pitch(俯仰角)、roll(横滚角)的稳定输出。 :这部分其实反映的是卡尔曼滤波的思想,K其实指的是卡尔曼增益,这部分我们可以这样理解:每个传感器都可以进行姿态解算,得到自己的三个欧拉角,而我们希望把这些融合(直接取平均的方式显然太low)了。,因此前辈们提取了每个传感器权重不同的方法来进行融合,至于这个权重到底是多少,卡尔曼老先生在自己的论文中有明确的推导(卡尔曼滤波最经典的那5个方程:2个预测方程,3个更新方程)。此处不再多做介绍,大家如果对卡尔曼滤波原理什么的想要有更深的了解,可以自行搜索,后面我也会自己出一期我自己对卡尔曼滤波的理解。
二、STM32CubeMX配置
有了上面的一些基础概念和了解,我们下面开始配置STM32CubeMX (1) 在“SYS”设置为“SW”调试模式; (2)我们在“RCC”中选择外部晶振模式; (3) 因为MPU6050采用的通信协议是I2C,而我们又想采用硬件I2C去开发,所以必须进行勾选。 (4)因为我们想把数据打印到电脑的“串口调试助手”,所以我们需要开一路串口,波特率为115200; (5)我们下面配置时钟树; (6)将这个工程存放在文件夹中,整个目录最好不要有中文,因为后面自动生成代码的时候并不能直接打开,不过问题也不大,需要自己去相关文件夹中的找到工程文件打开; (7)我们勾选一下我们希望生成文件的配置。 到此为止,我们的配置就已经完成了。
三、寄存器介绍
对于传感器而言,寄存器是很重要的东西,所以我们想要使用一个传感器,一定要清楚他的寄存器怎样配置,数据存储在哪个寄存器中 。我这里只是介绍一下我再这次调试中用到的寄存器,具体的每一个比特位到底表示什么大家自己查手册就好了。
四、Keil代码介绍
这部分是参考小破站上的视频讲解:(俺是妥妥自己搬运工具人,同时加上自己的一点理解)。关于我们应该对I2C通信有个简单的了解吧(不了解的话,小破绽也有很多资源,我比较推荐的是创客学院的武老师的讲解)。对于I2C设备,都有一个自己的设备地址,我们首先要做的知道设备地址。
#include "stdarg.h"
#include "string.h"
#include "stdio.h"
#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);
}
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。 添加宏定义:
//***************************************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; //将最后读取的值放入这些变量中
,配置一些寄存器;
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;
}
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)在草稿本上认真推导姿态解算的过程,并把它记录下来。