最近在开发一套麦轮小车,需要用到 IMU 获取 Yaw 轴的值,保证车头的朝向不变。使用的是 维特科技JY901s 模块。

该模块包含三轴陀螺仪、三轴加速度计和三轴电子罗盘,还能为客户提供高精度的三轴姿态角度(Pitch,Roll,Yaw),通过 IIC 或串口进行通信。

浅使用后,确实是一款不错的产品,从硬件到软件资料比较完备,最主要的是官方较有条理的例程库给我们今后自己写库提供了思路。

1. 上位机

上位机下载

直接通过串口连接模块,即可通过上位机读取姿态角数据,为了更适配个人需求,还需要在配置中进行修改。

image-20230516231037725

算法可以选择九轴或者六轴:

  • 六轴即用三轴陀螺仪、三轴加速度计计算姿态角,但是由于 Yaw 轴的积分误差,时间长了会存在零漂。
  • 九轴则加上了三轴电子罗盘,可以对 Yaw 轴进行校准。但是在使用九轴前需要进行磁场校准,否则会导致 Yaw 漂移得更离谱!

同时可以设置数据回传内容,速率等等。

2. 移植官方例程

官方例程下载

本人选择的是 STM32Core_SDK_Normal,即串口通信。

在 main 函数中

1
2
3
4
5
6
7
Usart1Init(115200);
Usart2Init(9600);
WitInit(WIT_PROTOCOL_NORMAL, 0x50);
WitSerialWriteRegister(SensorUartSend);
WitRegisterCallBack(SensorDataUpdata);
WitDelayMsRegister(Delayms);
AutoScanSensor();

开头是初始化两个串口,一个用于与 IMU 通信,另一个和电脑通信。

接下来一行是初始化,不用管内部实现。

接下来三个是注册相关函数,通过传入函数指针,来实现与我们自己的库适配的函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
static void SensorUartSend(uint8_t *p_data, uint32_t uiSize)
{
Uart2Send(p_data, uiSize);
}

static void Delayms(uint16_t ucMs)
{
delay_ms(ucMs);
}
static void SensorDataUpdata(uint32_t uiReg, uint32_t uiRegNum)
{
...
}

这是原来注册函数的内容,对于串口传输数据以及毫秒级延时函数,我们都可以修改为 HAL 库中相同功能的函数。

接下来看 USART2 中断函数:

1
2
3
4
5
6
7
8
9
10
void USART2_IRQHandler(void)
{
unsigned char ucTemp;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
{
ucTemp = USART_ReceiveData(USART2);
WitSerialDataIn(ucTemp);
USART_ClearITPendingBit(USART2, USART_IT_RXNE);
}
}

可以看到,每次接受一个数据,将数据通过 WitSerialDateIn 传入到“黑盒”中,我们在自己的中断函数也这样做就可以,之后的数据处理等一系列的东西,我们不需要了解。

最后在 while 死循环中判断是否更新数据并将数据读取。

总的工作框图如下:

image-20230517104320091

3. 总结与反思

通过移植维特的例程库,我们可以从中学习到如何实现一套高内聚,低耦合历程库的方法:

  • 对于与硬件相关的函数,通过函数指针进行注册相关的函数
  • 将用户与库之间数据的输入输出封装好,尽可能做到数据的处理是个黑盒。

image-20230517104834204