平衡车 -- MPU6050
🌈个人主页:羽晨同学
💫个人格言:“成为自己未来的主人~”
传感器原理
此外,用陀螺仪获取x,y,z轴的加速度。
初始化
我们现在对MPU6050进行初始化,MPU6050通过I2C总线与单片机进行通信,通过的是PB8和PB9,并且PB8和PB9需要通过重映射,才可以发挥出I2C总线的功能。
//
// @简介: 对MPU6050进行初始化
//
void App_Mpu6050_Init(void)
{//1. 初始化I2C总线,PB8、PB9 -I2C1//将I2C1的引脚重映射到PB8到PB9RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);GPIO_PinRemapConfig(GPIO_Remap_I2C1,ENABLE);//初始化PB8和PB9 - AF_ODRCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitTypeDef GPIO_InitStruct = {0};GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;GPIO_Init(GPIOB,&GPIO_InitStruct);}
这个是对I2C1的初始化
//2. 初始化I2C1// 开启I2C1的时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);//设置I2C的参数I2C_InitTypeDef I2C_InitStruct = {0};I2C_InitStruct.I2C_ClockSpeed = 400000;//I2C通信速度I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2;//占空比I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;I2C_Init(I2C1,&I2C_InitStruct);
我们对I2C1的速度设置为40000hz,也就是快速模式,然后选择的I2C的模式是标准I2C模式,占空比为2比1.我们选用快速模式的时候,一般选择的占空比就是2比1的。
寄存器部分
接下来,我们要配置寄存器部分。
所以,我们接下来封装一下读写寄存器的函数。
//
// @简介:写寄存器函数
// @参数 reg - 要写入的寄存器的地址
// @参数 value - 要写入的值
//
static void reg_write(uint8_t reg,uint8_t value)
{uint8_t bytesToSend[] = {reg,value};My_I2C_SendBytes(I2C1,0xd0,bytesToSend,2);
}
//
// @简介:读寄存器函数
// @参数 reg - 要读取的寄存器的地址
// @返回值:表示读取到的值
//
static uint8_t reg_read(uint8_t reg)
{My_I2C_SendBytes(I2C1,0xd0,®,1);uint8_t regValue;My_I2C_ReceiveBytes(I2C1,0xd0,®Value,1);return regValue;
}
然后,我们对寄存器的参数进行配置
// 2. 设置MPU6050的参数reg_write(0x6b,0x80);//复位Delay(100);reg_write(0x1b,0x18);//将陀螺仪的量程设置为+-2000°/sreg_write(0x1c,0x00);//将加速度计的量程设置为+-2g
接下来,我们对MPU6050的值进行更新
//
//@简介: MPU6050的数据更新函数
//
void App_MPU6050_Update(void)
{int16_t ax_raw = (int16_t)((reg_read(0x3b)<<8)+reg_read(0x3c));//ax的原始数据int16_t ay_raw = (int16_t)((reg_read(0x3d)<<8)+reg_read(0x3e));//ay的原始数据int16_t az_raw = (int16_t)((reg_read(0x3f)<<8)+reg_read(0x40));//az的原始数据int16_t temperature_raw = (int16_t)((reg_read(0x41)<<8)+reg_read(0x42));//温度的原始数据int16_t gx_raw = (int16_t)((reg_read(0x43)<<8)+reg_read(0x44));//gx的原始数据int16_t gy_raw = (int16_t)((reg_read(0x45)<<8)+reg_read(0x46));//gy的原始数据int16_t gz_raw = (int16_t)((reg_read(0x47)<<8)+reg_read(0x48));//gz的原始数据
}
然后我们对数值进行更新:
void App_MPU6050_Update(void)
{int16_t ax_raw = (int16_t)((reg_read(0x3b)<<8)+reg_read(0x3c));//ax的原始数据int16_t ay_raw = (int16_t)((reg_read(0x3d)<<8)+reg_read(0x3e));//ay的原始数据int16_t az_raw = (int16_t)((reg_read(0x3f)<<8)+reg_read(0x40));//az的原始数据ax = ax_raw * 6.1035e-5f;ay = ay_raw * 6.1035e-5f;az = az_raw * 6.1035e-5f;int16_t temperature_raw = (int16_t)((reg_read(0x41)<<8)+reg_read(0x42));//温度的原始数据//temperature = temperature_raw/340 +36.53;//MPU6050temperature = temperature_raw/333.87f+21.0f;//MPU6500int16_t gx_raw = (int16_t)((reg_read(0x43)<<8)+reg_read(0x44));//gx的原始数据int16_t gy_raw = (int16_t)((reg_read(0x45)<<8)+reg_read(0x46));//gy的原始数据int16_t gz_raw = (int16_t)((reg_read(0x47)<<8)+reg_read(0x48));//gz的原始数据 gx = gx_raw * 6.1035e-2f;gy = gy_raw * 6.1035e-2f;gz = gz_raw * 6.1035e-2f;
}
然后我们创建一个接口函数,将更新到的值进行导出
float App_MPU6050_GetAx(void)
{return ax;
}
float App_MPU6050_GetAy(void)
{return ay;
}
float App_MPU6050_GetAz(void)
{return az;
}
float App_MPU6050_Temperature(void)
{return temperature;
}
float App_MPU6050_GetGx(void)
{return gx;
}
float App_MPU6050_GetGy(void)
{return gy;
}
float App_MPU6050_GetGz(void)
{return gz;
}
解算欧拉角
陀螺仪解算欧拉角
static float yaw,pitch,roll;//单位是°
//
// @简介:MPU6050的进程函数,计算欧拉角
//
void App_MPU6050_Proc(void)
{static uint32_t nxt = 0;//下次程序运行的时间if(GetTick() < nxt) return;App_MPU6050_Update();//通过陀螺仪的测量结果计算欧拉角yaw = yaw + gz * 0.005;pitch = pitch + gx * 0.005;roll = roll - gy * 0.005;nxt += 5;
}
//
这样,我们就会每5ms来执行一次这个程序
为了简化这个步骤,我们也可以这样子写
//
// @简介:MPU6050的进程函数,计算欧拉角
//
void App_MPU6050_Proc(void)
{PERIODIC(5);App_MPU6050_Update();//通过陀螺仪的测量结果计算欧拉角yaw = yaw + gz * 0.005;pitch = pitch + gx * 0.005;roll = roll - gy * 0.005;}
接下来,我们对这个部分进行测试
//
// @简介: 用来测试欧拉角
//
void MPU6050_EularAngleTest(void)
{App_Usart_Init();App_Mpu6050_Init(); while(1){App_MPU6050_Proc();Uasrt2_Proc();}}
static void Uasrt2_Proc(void)
{PERIODIC(10)float ax = App_MPU6050_GetAx();float ay = App_MPU6050_GetAy();float az = App_MPU6050_GetAz();float temperature = App_MPU6050_Temperature();float gx = App_MPU6050_GetGx();float gy = App_MPU6050_GetGy();float gz = App_MPU6050_GetGz();float yaw = App_MPU6050_GetYaw();float pitch = App_MPU6050_GetPitch();float roll = App_MPU6050_GetRoll();My_USART_Printf(USART2,"%f,%f,%f,%f,%f,%f,%f,%f,%f,%f\n",ax,ay,az,temperature,gx,gy,gz,yaw,pitch,roll);
}
加速度计解算欧拉角
我们一般是会使用加速度计来计算俯仰角和翻滚角,一般不会使用加速度计来计算偏航角,因为重力对于平面上的影响是非常小的。重力向量在设备坐标系中的投影变化,直接反映了设备的Pitch和Roll角度。重力是这些角度的“绝对参考”。重力向量与设备的偏航角是完全解耦的,它不提供任何关于偏航方向的信息。所以我们不会用来计算偏航角。
//通过加速度计解算欧拉角yaw = 0;pitch = atan2(ay,az) / 3.1415927f * 180.0f;roll = atan2(ax,az) / 3.1415927f * 180.0f;
用陀螺仪是有漂移问题的,而加速度计有噪声,所以我们用互补滤波器来解决这种问题。
//
// @简介:MPU6050的进程函数,计算欧拉角
//
void App_MPU6050_Proc(void)
{PERIODIC(5);App_MPU6050_Update();//通过陀螺仪的测量结果计算欧拉角float yaw_g = yaw + gz * 0.005;float pitch_g = pitch + gx * 0.005;float roll_g = roll - gy * 0.005;//通过加速度计解算欧拉角float yaw_a = 0;float pitch_a = atan2(ay,az) / 3.1415927f * 180.0f;float roll_a = atan2(ax,az) / 3.1415927f * 180.0f;//使用互补滤波器yaw = yaw_g;pitch = 0.95238 * pitch_g + (1-0.95238) * pitch_a;roll = 0.95238 * roll_g + (1-0.95238) * roll_a;
}
好了,今天的内容就到这里,明天再见