大疆C板利用BMI088传感器进行姿态解算
实物教程——大疆C板读取BMI088传感器数据_操气的小虫儿的博客-CSDN博客_bmi088代码这是我写的上一篇文章,用SPI实现了读取BMI088传感器数据。现在要做的就是在读取BMI088传感器数据的基础上,对得到的数据进行计算得到四元数,再对四元数计算得到欧拉角。这两个计算已经有轮子可用了,分别是MahonyHARSupdateIMU()函数和get_angle()函数,详细情况请参考《R
实物教程——大疆C板读取BMI088传感器数据_操气的小虫儿的博客-CSDN博客_bmi088代码
这是我写的上一篇文章,用SPI实现了读取BMI088传感器数据。
现在要做的就是在读取BMI088传感器数据的基础上,对得到的数据进行计算得到四元数,再对四元数计算得到欧拉角。这两个计算已经有轮子可用了,分别是MahonyHARSupdateIMU()函数和get_angle()函数,详细情况请参考《RoboMaster开发板C型嵌入式软件教程文档》中18.4.2。
《RoboMaster开发板C型嵌入式软件教程文档》中第18章用到了SPI+DMA来读取传感器数据,但是我不会DMA,所以只用了SPI。官方教程里还用了Freertos来实现1ms为周期读取传感器数据,但是我不会Freertos,但我会用TIM,所以我用了TIM1来实现1ms为周期。
硬件环境
Robomaster 开发板C型
STLink V2
XT30电源
CubeMX配置
请参考实物教程——大疆C板读取BMI088传感器数据里的这个配置,除此之外,我们还要配置TIM1,请参考《RoboMaster开发板C型嵌入式软件教程文档》中3.4.1,官方文档里边分频值16799重载值4999实现了2Hz,我们需要1000Hz,所以分频值167重载值999。
程序代码
代码看起来比较长,不要害怕,大部分代码都是实物教程——大疆C板读取BMI088传感器数据里的,这里就是把加速度计的程序代码和陀螺仪的程序代码写在了一起,把while(1)循环里的代码放到了定时器回调函数HAL_TIM_PeriodElapsedCallback()里,最后的MahonyAHRSupdateIMU()函数和get_angle()函数官方文档里讲的有。
#define BMI088_ACCEL_3G_SEN 0.0008974358974f //这个数字我也不知道哪来的
#define BMI088_GYRO_2000_SEN 0.00106526443603169529841533860381f //这个数字我也不知道哪来的
float BMI088_ACCEL_SEN = BMI088_ACCEL_3G_SEN;
float BMI088_GYRO_SEN = BMI088_GYRO_2000_SEN;
float accelerometer[3];
float gyro[3];
float quat[4] = {1.0f, 0.0f, 0.0f, 0.0f};
float INS_angle[3] = {0.0f, 0.0f, 0.0f};
uint8_t i = 0;
uint8_t buf[8]={0,0,0,0,0,0,0,0};
uint8_t pTxData;
uint8_t pRxData;
int main(void)
{
//这9行代码,向地址0x7E处写入0xB6值,加速度计软件复位,使加速度计各个寄存器恢复为默认值
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); //PA4置0,片选加速度计
pTxData = (0x7E & 0x7F); //Bit #0和Bit #1-7,Bit #0是0,表示写
HAL_SPI_Transmit(&hspi1, &pTxData, 1, 1000);
while(HAL_SPI_GetState(&hspi1)==HAL_SPI_STATE_BUSY_TX); //等待SPI发送完成
pTxData = 0xB6; //Bit #8-15
HAL_SPI_Transmit(&hspi1, &pTxData, 1, 1000);
while(HAL_SPI_GetState(&hspi1)==HAL_SPI_STATE_BUSY_TX); //等待SPI发送完成
HAL_Delay(1); //延时1ms
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); //PA4置1,取消片选加速度计
//加速度计复位后默认是暂停模式,这9行代码,向地址0x7D处写入0x04值,使加速度计进入正常模式
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); //PA4置0,片选加速度计
pTxData = (0x7D & 0x7F); //Bit #0和Bit #1-7,Bit #0是0,表示写
HAL_SPI_Transmit(&hspi1, &pTxData, 1, 1000);
while(HAL_SPI_GetState(&hspi1)==HAL_SPI_STATE_BUSY_TX); //等待SPI发送完成
pTxData = 0x04; //Bit #8-15
HAL_SPI_Transmit(&hspi1, &pTxData, 1, 1000);
while(HAL_SPI_GetState(&hspi1)==HAL_SPI_STATE_BUSY_TX); //等待SPI发送完成
HAL_Delay(1); //延时1ms
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); //PA4置1,取消片选加速度计
//这9行代码,向地址0x14处写入0xB6值,陀螺仪软件复位,使陀螺仪各个寄存器恢复为默认值
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); //PB0置0,片选陀螺仪
pTxData = (0x14 & 0x7F); //Bit #0和Bit #1-7,Bit #0是0,表示写
HAL_SPI_Transmit(&hspi1, &pTxData, 1, 1000);
while(HAL_SPI_GetState(&hspi1)==HAL_SPI_STATE_BUSY_TX); //等待SPI发送完成
pTxData = 0xB6; //Bit #8-15
HAL_SPI_Transmit(&hspi1, &pTxData, 1, 1000);
while(HAL_SPI_GetState(&hspi1)==HAL_SPI_STATE_BUSY_TX); //等待SPI发送完成
HAL_Delay(30); //延时30ms
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); //PB0置1,取消片选陀螺仪
HAL_TIM_Base_Start_IT(&htim1); //使TIM1定时器开始工作,并使能其定时中断
while(1)
{
}
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); //PA4置0,片选加速度计
pTxData = (0x12 | 0x80); //Bit #0和Bit #1-7,Bit #0是1,表示读
HAL_SPI_Transmit(&hspi1, &pTxData, 1, 1000);
while(HAL_SPI_GetState(&hspi1)==HAL_SPI_STATE_BUSY_TX); //等待SPI发送完成
HAL_SPI_Receive(&hspi1, &pRxData, 1, 1000); //Bit #8-15,无效值
while(HAL_SPI_GetState(&hspi1)==HAL_SPI_STATE_BUSY_RX); //等待SPI接收完成
i = 0;
while (i < 6)
{
HAL_SPI_Receive(&hspi1, &pRxData, 1, 1000); //Bit #16-23,寄存器0x12的值,然后是寄存器0x13、0x14、0x15、0x16、0x17的值
while(HAL_SPI_GetState(&hspi1)==HAL_SPI_STATE_BUSY_RX); //等待SPI接收完成
buf[i] = pRxData;
i++;
}
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); //PA4置1,取消片选加速度计
accelerometer[0] = ((int16_t)((buf[1]) << 8) | buf[0]) * BMI088_ACCEL_SEN;
accelerometer[1] = ((int16_t)((buf[3]) << 8) | buf[2]) * BMI088_ACCEL_SEN;
accelerometer[2] = ((int16_t)((buf[5]) << 8) | buf[4]) * BMI088_ACCEL_SEN;
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); //PB0置0,片选陀螺仪
pTxData = (0x00 | 0x80); //Bit #0和Bit #1-7,Bit #0是1,表示读
HAL_SPI_Transmit(&hspi1, &pTxData, 1, 1000);
while(HAL_SPI_GetState(&hspi1)==HAL_SPI_STATE_BUSY_TX); //等待SPI发送完成
i = 0;
while (i < 8)
{
HAL_SPI_Receive(&hspi1, &pRxData, 1, 1000); //Bit #8-15,寄存器0x00的值,然后是寄存器0x01、0x02、0x03、0x04、0x05、0x06、0x07的值
while(HAL_SPI_GetState(&hspi1)==HAL_SPI_STATE_BUSY_RX); //等待SPI接收完成
buf[i] = pRxData;
i++;
}
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); //PB0置1,取消片选陀螺仪
if(buf[0] == 0x0F) //buf[0]储存GYRO_CHIP_ID,应该为0x0F,判断我们读取到的是不是陀螺仪的值。
{
gyro[0] = ((int16_t)((buf[3]) << 8) | buf[2]) * BMI088_GYRO_SEN;
gyro[1] = ((int16_t)((buf[5]) << 8) | buf[4]) * BMI088_GYRO_SEN;
gyro[2] = ((int16_t)((buf[7]) << 8) | buf[6]) * BMI088_GYRO_SEN;
}
MahonyAHRSupdateIMU(quat, gyro[0], gyro[1], gyro[2], accelerometer[0], accelerometer[1], accelerometer[2]); //对得到的加速度计、陀螺仪数据进行计算得到四元数
get_angle(quat, INS_angle, INS_angle+1, INS_angle+2); //对四元数计算得到欧拉角
}
有两个点容易遗漏,一是我们要在"MahonyAHRS.c"文件里把#define sampleFreq 521.0f中的521.0f改为1000.0f,sampleFreq代表采样频率。二是上边的第49行代码HAL_TIM_Base_Start_IT(&htim1);,这行代码让TIM1开始工作,我最初也是奇怪为什么我觉得代码没问题,但debug却没有数据,后来看了官方文档才发现自己没有写这个。
效果演示
我主要用到了INS_angle[1]这个数据。官方的程序效果是小数点后第3位稳定,第4位跳动;我这个程序效果是小数点后第2位稳定,第3位跳动。不知道为什么,还在查找原因。0.01rad≈0.573°,0.001rad≈0.0573°。
20221020:优化了代码之后,我已经可以达到接近官方的效果了,但是最近忙于考研,考完研之后再把代码整理一下发出来。
杂谈
越来越觉得官方的一些资料非常重要,很多问题都可以在官方资料里找到答案或者解决思路。
我用的Robomaster 开发板C型,我刚开始学的时候就是按照官方教程《RoboMaster开发板C型嵌入式软件教程文档》来学的,代码什么的也是首先来看官方给的代码,之后写代码遇到一些问题的时候也会来里边找思路。
姿态解算用到的传感器数据是BMI088提供的,刚开始写这部分代码的时候,对SPI的一些操作很不熟悉,不知道加速度计的Bit #8-15是无效值,不知道将加速度计、陀螺仪软件复位,不知道将加速度计从暂停模式变为正常模式,不知道复位、转换模式之后要给延时。导致写出来的代码用不了,之后看了BMI088的官方文档,慢慢地把这些理清了。
传感器是一门学问,写点代码凑合着读出来比较简单,但是想发挥出硬件的极致比较难,我这个代码说实话没有发挥出传感器的极致,精度还是比较低。
参考:
[1] 《RoboMaster开发板C型嵌入式软件教程文档》
[2] 《BMI088 6-axis Motion Tracking for High-performance Applications》
ChatGPT4
用ChatGPT4理解上述代码:
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)