驾驭BMI088:精密的陀螺仪数据处理

1.BMI088惯性传感器介绍

1.1传感器原理图

BMI088电路原理图
传感器采用3.3V供电,使用SPI/IIC通讯模式(本文采用SPI通讯协议)。

1.2传感器功能介绍

传感器主要参数
***注:***这里提到的数据读取频率 2000Hz是陀螺仪的数据最快读取频率,其加速度计的数据最快读取频率为1600Hz,加速度测量单位g为重力加速度,陀螺仪测量单位°/s为角度单位,这里注意角度制和弧度制的区别,三角函数使用的是弧度制。
陀螺仪传感器内部寄存器对照表:
BMI088陀螺仪传感器寄存器对照表
加速度传感器内部寄存器对照表:
BMI088加速度传感器寄存器对照表
***注意:***BMI088作为一款成熟的惯性器件,可以读取传感器当前工作温度,但是温度传感器挂载与加速度计部分,这也就是说如果准备做温度补偿,需要在读取加速度计数据的同时读取温度数据。
数据读取模式选择:读取频率、数据范围、以及滤波带宽的选择:
加速度计部分:
加速度计配置寄存器-1
加速度计配置寄存器-2
陀螺仪部分:
陀螺仪寄存器配置-1
陀螺仪寄存器配置-2
详细说明以及官方例程请见:
链接:https://pan.baidu.com/s/1OeIyNdDWo1UlggMcK_LXlg
提取码:6789
–来自百度网盘超级会员V4的分享

2.BMI088陀螺仪数据读取

2.1传感器初始化配置

BMI088.c文件:

/*!***************************************************
 * @file: BMI088.c
 * @brief: 用于配置BMI088陀螺仪 加速度计的各项参数并读取数据
 * @author:        
 * @date:2022/05/31
 * @note:	
 ****************************************************/
#include "BMI088.h"
#include "spi.h"
#define SPI1_ACC_Enable       {  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);}//PA4 PB0
#define SPI1_ACC_Disable       {  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);}//PA4 PB0
#define SPI1_GYRO_Enable     {  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);}//PA4 PB0
#define SPI1_GYRO_Disable     {  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);}//PA4 PB0



#define CS_GYRO  0
#define CS_ACC   1


#define BOARD_IMU 0
#define FLOAT_IMU 1

/* 用于读取BMI088温度数据 */
#define BMI088_accel_read_muli_reg(reg, data, len) \
    {                                              \
        BMI088_ACCEL_NS_L();                       \
        BMI088_read_write_byte((reg) | 0x80);      \
        BMI088_read_muli_reg(reg, data, len);      \
        BMI088_ACCEL_NS_H();                       \
    }

/*!***************************************************
 * @file: BMI088.c
 * @brief: 用于BMI088传感器中的软件延时
 * @author:        
 * @date:2022/05/31
 * @note:	
 ****************************************************/
static void BMI088_Delay(int times)
{
	delay_ms(times);
}

/*!***************************************************
 * @file: BMI088.c
 * @brief: 通过SPI通讯总线读取和发送传感器数据
 * @author:        
 * @date:2022/05/31
 * @note:	
 ****************************************************/
static uint8_t BMI088_SPIReadSend(uint8_t data)
{
    uint8_t ret = 0xff;
    HAL_SPI_TransmitReceive(&hspi1, &data, &ret, 1, 0xffff);
    return ret;
}

/*!***************************************************
 * @file: BMI088.c
 * @brief: 用于读取陀螺仪的数据
 * @author:        
 * @date:2022/05/31
 * @note:	已经知道读取陀螺仪数据的函数,如果想使用数据,需要知道滤波函数的用法
 ****************************************************/
static uint8_t BMI088_Read_GYRO(uint8_t Addr, int BOARD_OR_FLOAT)
{
    uint8_t val;
    SPI1_GYRO_Enable
    BMI088_SPIReadSend(Addr | 0x80);
    val = (uint8_t)(BMI088_SPIReadSend(0x00) & 0xFF);
    SPI1_GYRO_Disable;
    return val;
}

/*!***************************************************
 * @file: BMI088.c
 * @brief: 用于读取加速度计的数据
 * @author:        
 * @date:2022/05/31
 * @note:	已经知道读取加速度计数据的函数,如果想使用数据,需要知道滤波函数的用法
 ****************************************************/
static uint8_t BMI088_Read_ACC(uint8_t Addr, int BOARD_OR_FLOAT)
{
    uint8_t val;
    SPI1_ACC_Enable
    BMI088_SPIReadSend(Addr | 0x80);
    BMI088_SPIReadSend(0x00) ;
    val = (uint8_t)(BMI088_SPIReadSend(0x00) & 0xFF);
    SPI1_ACC_Disable
    return val;
}

/*!***************************************************
 * @file: BMI088.c
 * @brief: 用于写传感器配置,如果需要改变传感器参数使用该方法
 * @author:        
 * @date:2022/05/31
 * @note:	
 ****************************************************/
static void BMI088_Write_Reg(uint8_t Addr, uint8_t Val, uint8_t ACC_OR_GYRO)
{
    if (ACC_OR_GYRO == CS_ACC)
    {
        SPI1_ACC_Enable
    }
    else if (ACC_OR_GYRO == CS_GYRO)
    {
        SPI1_GYRO_Enable
    }
    BMI088_SPIReadSend(Addr & 0x7f);
    BMI088_SPIReadSend(Val);
    if (ACC_OR_GYRO == CS_ACC)
    {
        SPI1_ACC_Disable
    }
    else if (ACC_OR_GYRO == CS_GYRO)
    {
        SPI1_GYRO_Disable
    }
}

/*!***************************************************
 * @file: BMI088.c
 * @brief: 用于配置初始化传感器中的加速度计
 * @author:        
 * @date:2022/05/31
 * @note:	配置加速度计产生中断的IO口的输出方式,数据输出频率1600hz
				加速度量程是 ±24g 有点大,目前的车无需这么大的量程
 ****************************************************/
/**
  * @brief  
  * @retval  尝试降低采样率是否能提高数据稳定性
  * @date: 2021/10/17
  */
int ret = 0;
static uint8_t BMI088_ACC_Congfig(int BOARD_OR_FLOAT)
{
    SPI1_ACC_Disable;
    BMI088_Write_Reg(0x7e, 0xb6, CS_ACC);//Soft Reset
    while (BMI088.acc_id != ACC_CHIP_ID) //Rising edge ,turn to spi
    {

        BMI088_Delay(100);
        BMI088.acc_id = BMI088_Read_ACC(0x00, BOARD_OR_FLOAT); //id:1E
    }
    BMI088_Delay(50);//> 1 ms ;
    ret = 0;
    while (ret != ACC_ON)
    {
        ret = BMI088_Read_ACC(ACC_PWR_CTRL, BOARD_OR_FLOAT);
        BMI088_Write_Reg(ACC_PWR_CTRL, ACC_ON, CS_ACC);
        BMI088_Delay(5);
    }
	**/* 可根据实际使用和加速度寄存器表修改加速度计测量范围 */**
    while (BMI088_Read_ACC(ACC_RANG, BOARD_OR_FLOAT) != Plus_Minus_24G)
    {
        BMI088_Write_Reg(ACC_RANG, Plus_Minus_24G, CS_ACC); //ACC Rang +- 24g;//
        BMI088_Delay(5);
    }
    while (BMI088_Read_ACC(0x40, BOARD_OR_FLOAT) != 0xBC)
    {
		/* 可根据实际使用和加速度寄存器表修改加速度计数据读取频率 */
        BMI088_Write_Reg(0x40, 0xBC, CS_ACC); //
        BMI088_Delay(5);
    }
    while (BMI088_Read_ACC(0X53, BOARD_OR_FLOAT) != 0X08)
    {
        BMI088_Write_Reg(0X53, 0X08, CS_ACC); //0000 1000 INT1 OUTPUT PUSH-PULL  Active low
        BMI088_Delay(5);
    }
    while (BMI088_Read_ACC(0X54, BOARD_OR_FLOAT) != 0X08)
    {
        BMI088_Write_Reg(0X54, 0X08, CS_ACC); //0000 1000 INT2 OUTPUT PUSH-PULL  Active low
        BMI088_Delay(5);
    }
    while (BMI088_Read_ACC(0X58, BOARD_OR_FLOAT) != 0x44)
    {
        BMI088_Write_Reg(0X58, 0x44, CS_ACC); //0100 0100 data ready interrupt to INT1 and INT2
        BMI088_Delay(5);
    }

    BMI088_Delay(20);//>1ms
    while (BMI088_Read_ACC(ACC_PWR_CONF, BOARD_OR_FLOAT) != ACC_ACTIVE)
    {
        BMI088_Write_Reg(ACC_PWR_CONF, ACC_ACTIVE, CS_ACC); //ACtive mode
        BMI088_Delay(600);//>50ms
    }
    return 0;
}

/*!***************************************************
 * @file: BMI088.c
 * @brief: 用于配置初始化传感器中的陀螺仪
 * @author:        
 * @date:2022/05/31
 * @note:	配置陀螺仪产生中断的IO口的输出方式,数据输出频率2000hz
 ****************************************************/
static uint8_t BMI088_GYRO_Congfig(int BOARD_OR_FLOAT)
{
    SPI1_ACC_Disable;

    while (BMI088.gyro_id != GYRO_CHIP_ID)
    {
        BMI088.gyro_id = BMI088_Read_GYRO(0x00, BOARD_OR_FLOAT); //0x0f
        BMI088_Delay(5);
    }
    while (BMI088_Read_GYRO(GYRO_RANG, BOARD_OR_FLOAT) != Plus_Minus_2000)
    {
        BMI088_Write_Reg(GYRO_RANG, Plus_Minus_2000, CS_GYRO);// rang +-2000
        BMI088_Delay(5);
    }
    //bit #7 is Read Only
		
	/* 可根据陀螺仪寄存器对照表陀螺仪采样率是2000hz */
    BMI088_Write_Reg(GYRO_BANDWIDTH, ODR_1000_FD_116, CS_GYRO);

    while (BMI088_Read_GYRO(0X11, BOARD_OR_FLOAT) != 0x00)
    {
        BMI088_Write_Reg(0X11, 0x00, CS_GYRO);//normal
        BMI088_Delay(5);
    }
    while (BMI088_Read_GYRO(0X15, BOARD_OR_FLOAT) != 0X80)
    {
        BMI088_Write_Reg(0X15, 0X80, CS_GYRO);// //interrupt
        BMI088_Delay(5);
    }
    while (BMI088_Read_GYRO(0X16, BOARD_OR_FLOAT) != 0X00)
    {
        BMI088_Write_Reg(0X16, 0X00, CS_GYRO);//OUTPUT PUSH-PULL  Active low
        BMI088_Delay(5);
    }
    while (BMI088_Read_GYRO(0X18, BOARD_OR_FLOAT) != 0X81)
    {
        BMI088_Write_Reg(0X18, 0X81, CS_GYRO);//data ready interrupt to INT1 and INT2
        BMI088_Delay(5);
    }
    return 0;
}

/*****************************以下几个函数用于读取BMI088传感器的温度数据****************************/
/*!***************************************************
 * @file: BMI088.c
 * @brief: 用于读取陀螺仪加速度传感器高八位数据
 * @author:        
 * @date:2022/05/31
 * @note:	参考的大疆源码BMI088传感器的温度补偿控制
			温度传感器应该是挂载在加速度传感器上,所以读取温度
			需要使用加速度传感器
 ****************************************************/
static void BMI088_ACCEL_NS_H(void)
{
    HAL_GPIO_WritePin(CS1_ACCEL_GPIO_Port, CS1_ACCEL_Pin, GPIO_PIN_SET);
}

/*!***************************************************
 * @file: BMI088.c
 * @brief: 用于读取陀螺仪加速度传感器低八位数据
 * @author:        
 * @date:2022/05/31
 * @note:	参考的大疆源码BMI088传感器的温度补偿控制
			温度传感器应该是挂载在加速度传感器上,所以读取温度
			需要使用加速度传感器
 ****************************************************/
static void BMI088_ACCEL_NS_L(void)
{
    HAL_GPIO_WritePin(CS1_ACCEL_GPIO_Port, CS1_ACCEL_Pin, GPIO_PIN_RESET);
}

/*!***************************************************
 * @file: BMI088.c
 * @brief: 用于写传感器配置,如果需要改变传感器参数使用该方法
 * @author:        
 * @date:2022/05/31
 * @note:	该函数与 BMI088_Write_Reg 函数不同,使用的时候需要注意
 ****************************************************/
static uint8_t BMI088_read_write_byte(uint8_t txdata)
{
    uint8_t rx_data;
    HAL_SPI_TransmitReceive(&hspi1, &txdata, &rx_data, 1, 1000);
    return rx_data;
}

/*!***************************************************
 * @file: BMI088.c
 * @brief: 用于读取BMI088传感器中多个寄存器的数据
 * @author:        
 * @date:2022/05/31
 * @note:	参考的大疆源码BMI088传感器的温度补偿控制
 ****************************************************/
static void BMI088_read_muli_reg(uint8_t reg, uint8_t *buf, uint8_t len)
{
    BMI088_read_write_byte(reg | 0x80);
    while (len != 0)
    {
        *buf = BMI088_read_write_byte(0x55);
        buf++;
        len--;
    }
}
/***************************************************************************************************/

/*!***************************************************
 * @file: BMI088.c
 * @brief: 用于配置初始化传感器中的陀螺仪和加速度计
 * @author:        
 * @date:2022/05/31
 * @note:	
 ****************************************************/
uint8_t BMI088_FLOAT_ACC_GYRO_Init(void)
{
    BMI088_ACC_Congfig(FLOAT_IMU);
    BMI088_GYRO_Congfig(FLOAT_IMU);
    return 1;
}

/*!***************************************************
 * @file: BMI088.c
 * @brief: 用于读取传感器的温度
 * @author:        
 * @date: 2021/10/18
 * @note:	读取出来温度进行温度补偿
 ****************************************************/
void BMI088_Read_TMP(float *temperate)
{
	int16_t bmi088_raw_temp;
	bmi088_raw_temp = (int16_t)((BMI088.temp_originalbuff[0] << 3) | (BMI088.temp_originalbuff[1] >> 5));
	if (bmi088_raw_temp > 1023)
	{
			bmi088_raw_temp -= 2048;
	}
	*temperate = bmi088_raw_temp * BMI088_TEMP_FACTOR + BMI088_TEMP_OFFSET;
}

/* 按键外部中断定义变量 */
extern uint8_t exit_flag;
extern uint8_t rising_falling_flag;

/*!***************************************************
 * @file: BMI088.c
 * @brief: 用于配置初始化传感器中的陀螺仪
 * @author:        
 * @date:2022/05/31
 * @note:	I/O外部中断回调函数,用于接收陀螺仪和加速度数据
 ****************************************************/
/* 定义变量用于读取陀螺仪数据具体传输频率 */
int16_t Gyro_Cnt = 0;
void  HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if( mtime.Init_OK!=1)
        return;

    switch (GPIO_Pin)
    {
    case GPIO_PIN_4://ACC
        BMI088.ACC.buff[0] = BMI088_Read_ACC(0X12, FLOAT_IMU);
        BMI088.ACC.buff[1] = BMI088_Read_ACC(0X13, FLOAT_IMU);
        BMI088.ACC.buff[2] = BMI088_Read_ACC(0X14, FLOAT_IMU);
        BMI088.ACC.buff[3] = BMI088_Read_ACC(0X15, FLOAT_IMU);
        BMI088.ACC.buff[4] = BMI088_Read_ACC(0X16, FLOAT_IMU);
        BMI088.ACC.buff[5] = BMI088_Read_ACC(0X17, FLOAT_IMU);
				**/* 用于读取传感器的温度 */
				BMI088_accel_read_muli_reg(BMI088_TEMP_M, BMI088.temp_originalbuff, 2);**
        break;
    case GPIO_PIN_5://GYRO
        BMI088.GYRO.buff[0] = BMI088_Read_GYRO(0X02, FLOAT_IMU);
        BMI088.GYRO.buff[1] = BMI088_Read_GYRO(0X03, FLOAT_IMU);
        BMI088.GYRO.buff[2] = BMI088_Read_GYRO(0X04, FLOAT_IMU);
        BMI088.GYRO.buff[3] = BMI088_Read_GYRO(0X05, FLOAT_IMU);
        BMI088.GYRO.buff[4] = BMI088_Read_GYRO(0X06, FLOAT_IMU);
        BMI088.GYRO.buff[5] = BMI088_Read_GYRO(0X07, FLOAT_IMU);
		/* 陀螺仪的引脚触发中断一次,计数+1,用于确定陀螺仪传感器实际的传输频率
        break;
    default:
        break;
    }
}

BMI088.h文件:

/*!***************************************************
 * @file: BMI088.c
 * @brief: 用于配置BMI088陀螺仪 加速度计的各项参数并读取数据
 * @author:   
 * @date: 2022/05/31
 * @note:	
 ****************************************************/
#ifndef __BMI088_H__
#define __BMI088_H__

#define ACC_CHIP_ID 0X1E

#define	ACC_PWR_CTRL  0X7D
#define	ACC_OFF  0X00
#define	ACC_ON  0X04

#define	ACC_PWR_CONF  0X7C
#define	ACC_ACTIVE  0X00
#define	ACC_SUSPEND  0X03

/* 加速度量程范围设定 */
#define	ACC_RANG  0X41
#define	Plus_Minus_3G  0X00
#define	Plus_Minus_6G  0X01
#define	Plus_Minus_12G  0X02
#define	Plus_Minus_24G  0X03

/* 加速度信号输出频率设定 */
#define BMI088_ACC_ODR_SHFITS 0x0
#define BMI088_ACC_12_5_HZ (0x5 << BMI088_ACC_ODR_SHFITS)
#define BMI088_ACC_25_HZ (0x6 << BMI088_ACC_ODR_SHFITS)
#define BMI088_ACC_50_HZ (0x7 << BMI088_ACC_ODR_SHFITS)
#define BMI088_ACC_100_HZ (0x8 << BMI088_ACC_ODR_SHFITS)
#define BMI088_ACC_200_HZ (0x9 << BMI088_ACC_ODR_SHFITS)
#define BMI088_ACC_400_HZ (0xA << BMI088_ACC_ODR_SHFITS)
#define BMI088_ACC_800_HZ (0xB << BMI088_ACC_ODR_SHFITS)
#define BMI088_ACC_1600_HZ (0xC << BMI088_ACC_ODR_SHFITS)


#define GYRO_CHIP_ID 0X0F

#define	GYRO_RANG  0X0F
#define	Plus_Minus_2000  0X00
#define	Plus_Minus_1000  0X01
#define	Plus_Minus_500   0X02
#define	Plus_Minus_250   0X03
#define	Plus_Minus_125   0X04


#define	GYRO_BANDWIDTH  0X10
/* 2000代表采样频率,532代表数据宽度 */
#define	ODR_2000_FD_532  0X00
#define	ODR_2000_FD_230  0X01
#define	ODR_1000_FD_116  0X02
#define	ODR_400_FD_47  0X03
#define	ODR_200_FD_23  0X04
#define	ODR_100_FD_12 0X05
#define	ODR_200_FD_64 0X06
#define	ODR_100_FD_32 0X07

/* 温度读取 */
#define BMI088_TEMP_M 0x22

#define BMI088_TEMP_L 0x23
#define BMI088_TEMP_FACTOR 0.125f
#define BMI088_TEMP_OFFSET 23.0f

/*!***************************************************
 * @file: BMI088.c
 * @brief: 用于BMI088传感器中的软件延时
 * @author:   
 * @date: 2022/05/31
 * @note:	
 ****************************************************/
static void BMI088_Delay(int times);

/*!***************************************************
 * @file: BMI088.c
 * @brief: 通过SPI通讯总线读取和发送传感器数据
 * @author:   
 * @date: 2022/05/31
 * @note:	
 ****************************************************/
static uint8_t BMI088_SPIReadSend(uint8_t data);

/*!***************************************************
 * @file: BMI088.c
 * @brief: 用于读取加速度计的数据
 * @author:   
 * @date: 2022/05/31
 * @note:	已经知道读取加速度计数据的函数,如果想使用数据,需要知道滤波函数的用法
 ****************************************************/
static uint8_t BMI088_Read_ACC(uint8_t Addr, int BOARD_OR_FLOAT);

/*!***************************************************
 * @file: BMI088.c
 * @brief: 用于读取陀螺仪的数据
 * @author:   
 * @date: 2022/05/31
 * @note:	已经知道读取陀螺仪数据的函数,如果想使用数据,需要知道滤波函数的用法
 ****************************************************/
static uint8_t BMI088_Read_GYRO(uint8_t Addr, int BOARD_OR_FLOAT);

/*!***************************************************
 * @file: BMI088.c
 * @brief: 用于写传感器配置,如果需要改变传感器参数使用该方法
 * @author:   
 * @date: 2022/05/31
 * @note:	
 ****************************************************/
static void BMI088_Write_Reg(uint8_t Addr, uint8_t Val, uint8_t ACC_OR_GYRO);

/*!***************************************************
 * @file: BMI088.c
 * @brief: 用于配置初始化传感器中的加速度计
 * @author:   
 * @date: 2022/05/31
 * @note:
 ****************************************************/
static uint8_t BMI088_ACC_Congfig(int BOARD_OR_FLOAT);

/*!***************************************************
 * @file: BMI088.c
 * @brief: 用于配置初始化传感器中的陀螺仪
 * @author:   
 * @date: 2022/05/31
 * @note:
 ****************************************************/
static uint8_t BMI088_GYRO_Congfig(int BOARD_OR_FLOAT);

/*!***************************************************
 * @file: BMI088.c
 * @brief: 用于读取陀螺仪加速度传感器高八位数据
 * @author:   
 * @date: 2022/05/31
 * @note:
 ****************************************************/
static void BMI088_ACCEL_NS_H(void);

/*!***************************************************
 * @file: BMI088.c
 * @brief: 用于读取陀螺仪加速度传感器低八位数据
 * @author:   
 * @date: 2022/05/31
 * @note:
 ****************************************************/
static void BMI088_ACCEL_NS_L(void);

/*!***************************************************
 * @file: BMI088.c
 * @brief: 用于写传感器配置,如果需要改变传感器参数使用该方法
 * @author:   
 * @date: 2022/05/31
 * @note:
 ****************************************************/
static uint8_t BMI088_read_write_byte(uint8_t txdata);

/*!***************************************************
 * @file: BMI088.c
 * @brief: 用于读取BMI088传感器中多个寄存器的数据
 * @author:   
 * @date: 2022/05/31
 * @note:
 ****************************************************/
static void BMI088_read_muli_reg(uint8_t reg, uint8_t *buf, uint8_t len);

/*!***************************************************
 * @file: BMI088.c
 * @brief: 用于配置初始化传感器中的陀螺仪和加速度计
 * @author:   
 * @date: 2022/05/31
 * @note:	
 ****************************************************/
uint8_t BMI088_FLOAT_ACC_GYRO_Init(void);

/*!***************************************************
 * @file: BMI088.c
 * @brief: 用于配置初始化传感器中的陀螺仪和加速度计
 * @author:   
 * @date: 2022/05/31
 * @note:	
 ****************************************************/
uint8_t BMI088_FLOAT_ACC_GYRO_Init(void);

/*!***************************************************
 * @file: BMI088.c
 * @brief: 用于读取传感器的温度
 * @author:   
 * @date: 2021/10/18
 * @note:	读取出来温度进行温度补偿
 ****************************************************/
void BMI088_Read_TMP(float *temperate);
#endif

以上部分主要根据加速度计和陀螺仪的寄存器对照表为参照,可根据个人实际使用状况进行配置,毕竟并不是所有场合都会出现±24g的重力加速度情况的。
BMI088传感器的基础配置程序和通过I/O中断读取的程序全部展示出来了,这里BMI088与MCU之间通讯方式为SPI,并采用的MCU上的硬件SPI进行读取,所以以上代码如果想顺利运行起来,需要事先完成硬件SPI总线配置,这里简单展示一下SPI的配置代码以及传感器的初始化函数:

/* SPI1通讯配置
*/
/** SPI主从模式设定		主机模式
		SPI方向设定				双向SPI通讯模式
		数据长度设定			8位数据长度
		时钟优先级设定		低优先级
		时钟阶段设定			一个时钟阶段对应一个边沿
		SPI-NSS引脚设定		软件NSS
		波特率预分频设定	64分配
		SPI通讯首位				主机标志位
		定时器模式				不使能
		CRC校验模式				不使能
		CRCPoly正常模式		10
 * @brief: 使用CbueMX配置生成的SPI通讯配置代码
 * @date: 2022/05/31
 * @note:	
*/
void MX_SPI1_Init(void)
{

  hspi1.Instance = SPI1;
  hspi1.Init.Mode = SPI_MODE_MASTER;
  hspi1.Init.Direction = SPI_DIRECTION_2LINES;
  hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi1.Init.NSS = SPI_NSS_SOFT;
  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64;
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi1.Init.CRCPolynomial = 10;
  if (HAL_SPI_Init(&hspi1) != HAL_OK)
  {
    Error_Handler();
  }
}
/* 进一步对SPI进行配置,包括引脚映射初始化 */
void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(spiHandle->Instance==SPI1)
  {
    /* SPI1 clock enable */
    __HAL_RCC_SPI1_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**SPI1 GPIO Configuration
    PB4     ------> SPI1_MISO
    PB3     ------> SPI1_SCK
    PA7     ------> SPI1_MOSI
    */
    GPIO_InitStruct.Pin = GPIO_PIN_4|GPIO_PIN_3;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  }
}

/* 本函数具体作用是取消SPI对以上功能的配置 */
void HAL_SPI_MspDeInit(SPI_HandleTypeDef* spiHandle)
{
  if(spiHandle->Instance==SPI1)
  {
    /* Peripheral clock disable */
    __HAL_RCC_SPI1_CLK_DISABLE();
    /**SPI1 GPIO Configuration
    PB4     ------> SPI1_MISO
    PB3     ------> SPI1_SCK
    PA7     ------> SPI1_MOSI
    */
    HAL_GPIO_DeInit(GPIOB, GPIO_PIN_4|GPIO_PIN_3);
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_7);
  }
}

在根据以上代码配置完SPI通讯之后,只需要调用如下函数,即可完成对BMI088传感器的所有初始化:

	BMI088_FLOAT_ACC_GYRO_Init();

陀螺仪数据读取.c文件

/*!***************************************************
 * @brief: 用于配置对陀螺仪加速度计进行单位换算
 * @note:	
 ****************************************************/

#include "BMI088.h"
//陀螺仪零偏矫正
mpu BMI088;

/**
  * @brief 	读取陀螺仪的数据        
  * @retval  将读取出来的数据赋值给原始数
	*	@note	 data数据长度是3  原始数据长度是3  采集出来的数据就是原始数据
	* @attention 从这开始实际的频率编程 2000hz
  */
void BMI088_Read_Gyro_Data(void)
{
    BMI088.gyro.origin [xx] = BMI088.GYRO.data[xx];
    BMI088.gyro.origin [yy] = BMI088.GYRO.data[yy];
    BMI088.gyro.origin [zz] = BMI088.GYRO.data[zz];
}

/**
  * @brief 	读取加速度计的数据        
  * @retval  将读取出来的数据赋值给原始数
	*	@note	 data数据长度是3  原始数据长度是3  采集出来的数据就是原始数据
	* @attention 从这开始实际的频率编程 1600hz
  */
void BMI088_Read_Acc_Data(void)
{
    BMI088.acc.origin [xx] = BMI088.ACC.data[xx];
    BMI088.acc.origin [yy] = BMI088.ACC.data[yy];
    BMI088.acc.origin [zz] = BMI088.ACC.data[zz];
}

/**
  * @brief 	读取温度     
  * @retval  将读取出来的数据赋值给原始数
	*	@note	 data数据长度是3  原始数据长度是3  采集出来的数据就是原始数据
	* @attention 从这开始实际的频率编程 1600hz
  */
void BMI088_Read_Tmp_Data(void)
{
	BMI088_Read_TMP(&BMI088.Temperature);
}

/*!***************************************************
 * @file: MYGYROData.c
 * @brief:读取加速度,并转换为 m/(s^2)
 * @note:	
 ****************************************************/
void IMU_Read(void)
{
	mpu *mympu=&BMI088;
	BMI088_Read_Gyro_Data();	/* 读取陀螺仪数据 */
	BMI088_Read_Acc_Data();		/* 读取加速度数据 */
	
	mympu->gyro.dps[xx]  = mympu->gyro.origin[xx] * MPU_GYRO_TO_DPS;
	mympu->gyro.dps[yy]  = mympu->gyro.origin[yy] * MPU_GYRO_TO_DPS;
	mympu->gyro.dps[zz]  = mympu->gyro.origin[zz] * MPU_GYRO_TO_DPS;
	
	mympu->acc.m_s_2 [xx] = mympu->acc.origin[xx] * MPU_ACCE_M_S_2;
	mympu->acc.m_s_2 [yy] = mympu->acc.origin[yy] * MPU_ACCE_M_S_2;
	mympu->acc.m_s_2 [zz] = mympu->acc.origin[zz] * MPU_ACCE_M_S_2;
}

陀螺仪数据读取.h文件

/*!***************************************************
 * @file: MYGYROData.c
 * @brief: 用于配置对陀螺仪加速度计进行数据单位换算
 * @note:	
 ****************************************************/
#define BMI088 BMI088
#define xx 0
#define yy 1
#define zz 2

#define G_Z 0

/*陀螺仪加速度计去零漂方式选择*/

#define MPU_ACC_CALOFFSET_AUTO
#define  MPU_GROY_CALOFFSET_AUTO
/*---------------------------------------------*/

#define XAISN   (3)  //3个坐标
#define ITEMS  6
#define MPU9250_FILTER_NUM   4
#define MPU_CORRECTION_FLASH     0x0800F000        //存储校正数据的FLASH地址,SIZE=6*3*4字节
#define GG 9.8															// 重力加速?
/* 现在量程设定为±6g */
#define MPU_ACCE_M_S_2 (24.0 * GG / 32768.0) //单位换算,将LSB化为m/(s^2),GG前面的"X"需根据量程修改
#define MPU_GYRO_TO_DPS (2000 / 32768.0)    //单位换算,将LSB化为 gegree/s
#define MPU_MAGN_TO_GS (4800 / 16384.0 / 100.0)            //单位换算,将LSB化为Gs
#define MPU_MAGN_TO_UT (4800 / 16384.0)            //单位换算,将GS化为UT
#define MPU_TEMP_K (0.002995177763f)            //degC/LSB

typedef struct _accdata
{
    short origin[XAISN];  //原始值
    float offset[XAISN];      //零偏值
    float offset_max[XAISN];  //零偏值最大值
    float offset_min[XAISN];  //零偏值最小值
    float calibration[XAISN]; //校准值
    float filter[XAISN];      //滑动平均滤波值
    float m_s_2[XAISN];      //米每二次方秒
} accdata;
typedef struct _gyrodata
{
    short origin[XAISN];  //原始值
    float offset_max[XAISN];  //零偏值最大值
    float offset_min[XAISN];  //零偏值最小值
    float offset[XAISN];      //零偏值
    float calibration[XAISN]; //校准值
    float filter[XAISN];      //滑动平均滤波值
    float dps[XAISN];         //度每秒
    float radps[XAISN];       //弧度每秒
		/* 2022-01-11 加入滑动滤波 */
		float last_filter[XAISN];
} gyrodata;

struct _mpu
{
    accdata acc;
    gyrodata gyro;

    float Temperature;
    uint8_t acc_id, gyro_id;
    union
    {
        int16_t data[3];
        uint8_t buff[6];
    } ACC, GYRO;
    uint8_t temp_originalbuff[2];
    uint8_t gyro_times;
    uint8_t acc_times;
    enum
    {
        ReadingACC,
        ReadingGYRO,
        IDLE,
    } state;
		
    float pitch;
    float roll;
    float yaw;
    float lastyaw;
		/* 记录陀螺仪累计旋转多少度 */
		float yawsum;
		/* 陀螺仪动态数据矫正 */
		uint8_t DynamicOffsetEnable;	// 是否进行动态校准标志位
		int32_t DynamicTmp[3];				// 动态数据累加
		uint32_t DynamicTimes;				// 校准时间计数
		uint16_t OffsetCnt;						// 校准周期
		uint16_t OffsetErrorRange;		// 校准误差范围
		float gyro_z;
		int16_t yaw_turns;
};

typedef struct _mpu mpu;
extern mpu BMI088;

/**
  * @brief 	读取陀螺仪的数据        
  * @retval  将读取出来的数据赋值给原始数
  *	@note	 data数据长度是3  原始数据长度是3  采集出来的数据就是原始数据
  * @attention 从这开始实际的频率编程 2000hz
  */
void BMI088_Read_Gyro_Data(void);

/**
  * @brief 	读取加速度计的数据        
  * @retval  将读取出来的数据赋值给原始数
  *	@note	 data数据长度是3  原始数据长度是3  采集出来的数据就是原始数据
  * @attention 从这开始实际的频率编程 1600hz
  */
void BMI088_Read_Acc_Data(void);

/**
  * @brief 	读取温度     
  * @retval  将读取出来的数据赋值给原始数
  *	@note	 data数据长度是3  原始数据长度是3  采集出来的数据就是原始数据
  * @attention 从这开始实际的频率编程 1600hz
  */
void BMI088_Read_Tmp_Data(void);

/*!***************************************************
 * @file: MYGYROData.c
 * @brief:读取加速度,并转换为 m/(s^2)
 * @note:	
 ****************************************************/
void IMU_Read(void);

以上代码如何使用将在第三章进行展现。

3.BMI088陀螺仪数据处理:

通过碰撞测试发现,180s时间内BMI088会产生15度的0点漂移,碰撞时的振动以及底盘加减速带来的冲击确实会使得陀螺仪0点发生漂移,但是加减速带来的冲击对陀螺仪的数据影响最大,也就是说在车运动过程中突然换因为惯性打滑导致车体的振动对传感器数据的影响更大。
决定使用MATLAB调用FFT函数,观测信号频谱图,根据信号频谱确定基波频率和主要谐波频率,然后使用二阶低通滤波算法,确定采样频率和截止频率,将滤波效果最大化,提高代码的运行效率。
4096点FFT的MATLAB代码如下:

clear  all
clc

%测试代码
%t = 0:0.005:1;  %时间间隔为0.005 采样频率为200hz
%x= sin(2*pi*30*t)+sin(2*pi*500*t);	%

N = 4096; %使用1024点快速傅里叶变换
filename = '33.xlsx';    %读取数据
x = xlsread(filename);
f=x;			
subplot(121);
plot(f);							%画出原始信号的波形图
ylabel('幅值');
xlabel('时间');
title('原始信号');
y=abs(fft(f,N));	%对原始信号进行离散傅里叶变换,参加DFT的采样点个数为N

ff=200*(0:(N/2-1))/N;				%计算变换后不同点所对应的频率值    N点只需打印一半即可,另一半是对称的

subplot(122);
plot(ff,y(1:(N/2)));					%画出信号的频谱图
ylabel('功率谱密度');
xlabel('频率');
title('信号功率谱图')

得出部分数据频谱图如下所示(左侧为原始数据,右侧为数据频谱图):





这里就不公开数据集了,因为采用不同的测试平台,采集到的数据波形可能是不同的,通过观察以上频谱图可以确定基波频率在0-5Hz,10Hz附近的信号频率即可认为是高频谐波,于是采用二阶低通滤波的方式,采样频率设定为50hz,截止频率设置为10hz,传感器中断读取频率为1000hz,滤波算法执行频率为500hz,采用积分的方法,以0.002s为单位时间进行积分,最终得到当前效果,在运行过程中,启动停止,突然换向以及撞击所造成的陀螺仪数据抖动,基本上可以说完全消除,不旋转只撞击的情况下,240s内yaw轴数据可以稳定在1.5°以内。
二阶低通滤波代码如下:

/**
  * @brief  二阶低通滤波函数       
  * @param[in]   对应传感器   
  * @param[in]   样本数据频率
  * @param[in]   截止频率
  * @retval         none
	* @note
  */
void LPF2pSetCutoffFreq(int index, float sample_freq, float cutoff_freq)
{
    LPF *lpf;
    lpf = &lpf4[index];
    float fr = 0;
    float ohm = 0;
    float c = 0;

    fr = sample_freq / cutoff_freq;
    ohm = tanf(M_PI_F / fr);
    c = 1.0f + 2.0f * cosf(M_PI_F / 4.0f) * ohm + ohm * ohm;

		/* 直接给出截止频率减少运算步骤 */
    lpf->_cutoff_freq1 = cutoff_freq;
    if (lpf->_cutoff_freq1 > 0.0f)
    {
        lpf->_b01 = ohm * ohm / c;
        lpf->_b11 = 2.0f * lpf->_b01;
        lpf->_b21 = lpf->_b01;
        lpf->_a11 = 2.0f * (ohm * ohm - 1.0f) / c;
        lpf->_a21 = (1.0f - 2.0f * cosf(M_PI_F / 4.0f) * ohm + ohm * ohm) / c;
    }
}

/**
  * @brief  二阶低通滤波函数   
  * @param[in]   对应传感器   
  * @param[in]   样本数据
  * @retval 二阶低通滤波函数
  */
float LPF2pApply(int index, float sample)
{
    LPF *lpf;
    lpf = &lpf4[index];
    float delay_element_0 = 0, output = 0;
    if (lpf->_cutoff_freq1 <= 0.0f)
    {
        // no filtering
        return sample;
    }
    else
    {
        delay_element_0 = sample - lpf->_delay_element_11 * lpf->_a11 - lpf->_delay_element_21 * lpf->_a21;
        // do the filtering
        if (isnan(delay_element_0) || isinf(delay_element_0))
        {
            // don't allow bad values to propogate via the filter
            delay_element_0 = sample;
        }
        output = delay_element_0 * lpf->_b01 + lpf->_delay_element_11 * lpf->_b11 + lpf->_delay_element_21 * lpf->_b21;

        lpf->_delay_element_21 = lpf->_delay_element_11;
        lpf->_delay_element_11 = delay_element_0;

        // return the value.  Should be no need to check limits
        return output;
    }
}

在采用二阶低通滤波之后,发现如果因为其它原因带来的尖峰数据会对陀螺仪Yaw轴积分结果造成影响,这里先对原始数据进行平滑滤波处理,平滑滤波之后再进行二次低通滤波,最终对数据积分得到Yaw轴角度:
平滑滤波处理:

/*!***************************************************
 * @file: IMU.c
 * @brief: 对陀螺仪的数据进行处理
 * @date: 2021/12/25
 ****************************************************/
#define	BMI088_FILTER_NUM   10
#define BMI088_ITEMS  3
void IMU_Filter(void)
{
	u8 i;
	/* 平滑滤波数组参数定义 */
	int32_t FILT_TMP[BMI088_ITEMS] = {0};
	static int16_t FILT_BUF[BMI088_ITEMS][BMI088_FILTER_NUM] = {0};
	BMI088_Read_Gyro_Data();	/* 读取陀螺仪数据 */
	for(i=BMI088_FILTER_NUM - 1;i>=1;i--)
	{
		FILT_BUF[zz][i] = FILT_BUF[zz][i-1];
	}
	FILT_BUF[zz][0] = BMI088.gyro.origin[zz];
	for(i=0; i<BMI088_FILTER_NUM; i++)
	{
		FILT_TMP[zz] += FILT_BUF[zz][i];
	}
	
	BMI088.gyro.last_filter[zz] = (float)(FILT_TMP[zz])/(float)BMI088_FILTER_NUM;	/* 滑动滤波 */
	BMI088.gyro.filter[zz] = LPF2pApply(GYRO_Z_LPF, BMI088.gyro.last_filter[zz]);	/* 二阶低通滤波 */
	BMI088.gyro.dps[zz] = BMI088.gyro.filter[zz] * MPU_GYRO_TO_DPS;	/* 得到角速度,单位:度/s */
}

经过处理后对数据进行积分,解算Yaw轴角度:

/*!***************************************************
 * @file: IMU.c
 * @brief:先处理零漂,然后进行陀螺仪数据处理使用积分方法对角速度数据进行积分计算yaw轴变化
 * @note:	
 ****************************************************/
#define d_t 0.002f  //间隔2ms进行积分
#define Zero_Offset_Coun  (1 / d_t)   //去零漂
int16_t g_GetZero_Offset = 0;
float Gyroz_offset = 0.0f;
void IMU_Normalization(void)
{
	/* 计算零漂 */
	if( g_GetZero_Offset < Zero_Offset_Coun)
	{
		Gyroz_offset += BMI088.gyro.dps[zz] * d_t;		/* 进行积分 */
		g_GetZero_Offset++;
	}
	BMI088.gyro.dps[zz] -= Gyroz_offset;	/* 除去零漂 */
	
	/* 零偏去除之后通过积分计算角度 */
	if(g_GetZero_Offset > Zero_Offset_Coun)
	{
		BMI088.lastyaw += BMI088.gyro.dps[zz] * d_t;		/* 积分结算Yaw轴角度 */
		BMI088.gyro_z = BMI088.gyro.dps[zz];		/* 传递当前角速度数值 */
	}
	/* 将角度限制在±180° */
	if(BMI088.lastyaw > 180.0f)
	{
		BMI088.lastyaw = BMI088.lastyaw - 360;
		BMI088.yaw_turns ++;
	}
	else if(BMI088.lastyaw < -180.0f)
	{
		BMI088.lastyaw = BMI088.lastyaw + 360;
		BMI088.yaw_turns --;		
	}
	else
		BMI088.lastyaw = BMI088.lastyaw;
}

截止到当前部分,针对于BMI088传感器的传感器配置、陀螺仪数据读取以及陀螺仪数据处理三方面内容基本上完整了。

4.BMI088陀螺仪温度补偿

这里的温度补偿不做过多赘述,采用普通的PID算法,在之前第二章提到的数据读取中,通过引脚中断的方式与加速度计一同读取出来当前温度,与设定温度进行PID控制即可,注意这里的温度控制频率尽量要高于数据处理的频率或者相等,但是最好不要比数据处理的频率低:

/**
  * @brief  温控任务    
  * @param[in] 
  * @note:
  */
void imu_temp_control_task(void)
{
	uint16_t tempPWM;
	/* 粗略计算5s,系统开始运行 */
	static uint32_t time = 0;
	//pid calculate. PID计算
	/* 温度读取 */
	BMI088_Read_Tmp_Data();
	PID_calc(&imu_temp_pid,BMI088.Temperature,40.0f);
	if (imu_temp_pid.out < 0.0f)
	{
			imu_temp_pid.out = 0.0f;
	}
	tempPWM = (uint16_t)imu_temp_pid.out;	
	WenDuBuChang_PWM(tempPWM);
	if(BMI088.Temperature >= 40)
	{
		/* 当读取的温度数据第一次大于40之后陀螺仪开始正常工作 */
		mtime.Temp_OK = 1;
	}
}

5.BMI088陀螺仪总结

总的来说改款陀螺仪的稳定性还是非常高的,比传统的MPU6050拥有更高的精度和更强的稳定性,但是存在的唯一的缺陷就是,该陀螺仪的陀螺仪数据读取频率和加速度计的读取频率,只有在400hz及以下时达成一致,这显然是非常难受的一件事,在高频段,陀螺仪采样频率是2000、1000、400hz,加速度计的采样率是1600、800、400hz,如果使用加速度计进行数据融合采用互补滤波的算法校准结算欧拉角,显然这种不同频的数据读取影响是非常大的,本人也是在尝试了多个融合算法之后发现,两个传感器数据不同步对结算的影响真的不小,所以根据实际应用场景,选择了对Yaw轴角速度进行积分结算出Yaw轴角度进行使用。

物联沃分享整理
物联沃-IOTWORD物联网 » 驾驭BMI088:精密的陀螺仪数据处理

发表评论