STM32F10x驱动WT931姿态传感器:从串口协议解析到欧拉角解算实战

📅 2026/7/16 1:47:25
STM32F10x驱动WT931姿态传感器:从串口协议解析到欧拉角解算实战
1. WT931姿态传感器与STM32F10x开发基础WT931是一款高精度九轴姿态传感器模块集成了三轴加速度计、三轴陀螺仪和三轴磁力计。通过融合多传感器数据它可以实时输出载体的俯仰角(Pitch)、横滚角(Roll)和偏航角(Yaw)。在实际项目中我经常用它来做无人机飞控、机器人导航等需要姿态检测的场景。STM32F10x系列是ST公司经典的Cortex-M3内核微控制器性价比极高。它的USART外设支持多种串口通信模式正好可以对接WT931的TTL串口输出。这里我用的是STM32F103C8T6这款64引脚的中容量型号有3个USART接口完全够用。1.1 硬件连接要点WT931的串口引脚是3.3V电平直接与STM32的USART引脚相连即可。我的接线方案如下WT931的TX → STM32的PB11(USART3_RX)WT931的RX → STM32的PB10(USART3_TX)WT931的GND → STM32的GNDWT931的VCC → 3.3V电源注意一定要共地曾经有个项目因为没共地导致数据乱跳排查了半天才发现这个问题。如果传输距离超过1米建议加上MAX3485这类RS485芯片增强抗干扰能力。2. STM32串口初始化实战2.1 USART3配置步骤在STM32CubeMX里配置USART3很简单但手动初始化更有助于理解底层原理。下面是我的初始化代码void USART3_Init(u32 baudrate) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; // 使能时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 配置PB10为复用推挽输出(TX) GPIO_InitStructure.GPIO_Pin GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOB, GPIO_InitStructure); // 配置PB11为浮空输入(RX) GPIO_InitStructure.GPIO_Pin GPIO_Pin_11; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOB, GPIO_InitStructure); // USART参数配置 USART_InitStructure.USART_BaudRate baudrate; USART_InitStructure.USART_WordLength USART_WordLength_8b; USART_InitStructure.USART_StopBits USART_StopBits_1; USART_InitStructure.USART_Parity USART_Parity_No; USART_InitStructure.USART_Mode USART_Mode_Rx | USART_Mode_Tx; USART_InitStructure.USART_HardwareFlowControl USART_HardwareFlowControl_None; USART_Init(USART3, USART_InitStructure); // 使能接收中断 USART_ITConfig(USART3, USART_IT_RXNE, ENABLE); // 配置NVIC NVIC_InitStructure.NVIC_IRQChannel USART3_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority 1; NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; NVIC_Init(NVIC_InitStructure); USART_Cmd(USART3, ENABLE); }2.2 波特率选择经验WT931支持多种波特率从9600到115200都可以。我推荐用115200这个速率既能保证数据实时性又不会给STM32带来太大处理压力。如果发现数据丢包可以尝试以下方法降低波特率到57600检查时钟配置是否正确优化中断服务函数处理逻辑曾经有个项目用230400的波特率结果发现Yaw角偶尔会跳变后来发现是串口缓冲区溢出导致的。降到115200后就稳定了。3. WT931数据帧解析技巧3.1 数据帧格式详解WT931的输出帧格式如下以角度帧为例字节位置内容说明00x55帧头10x53标识为角度帧2-3Roll横滚角(低字节在前)4-5Pitch俯仰角(低字节在前)6-7Yaw偏航角(低字节在前)8-9保留通常为010SUM校验和(前面字节累加)实际测试发现原始数据是int16类型需要除以32768再乘以180才能得到实际角度值。这个转换公式在手册里其实没写清楚是我通过实验得出的。3.2 中断服务程序实现下面是我优化过的中断服务程序加入了超时处理和错误重试机制#define FRAME_LENGTH 11 u8 USART3_RX_BUF[FRAME_LENGTH]; u8 USART3_RX_CNT 0; u8 USART3_RX_FLAG 0; void USART3_IRQHandler(void) { u8 res; static u8 timeout 0; if(USART_GetITStatus(USART3, USART_IT_RXNE) ! RESET) { res USART_ReceiveData(USART3); // 超时处理 if(timeout 20) USART3_RX_CNT 0; // 帧头检测 if(USART3_RX_CNT 0 res ! 0x55) return; USART3_RX_BUF[USART3_RX_CNT] res; // 完整帧接收 if(USART3_RX_CNT FRAME_LENGTH) { USART3_RX_CNT 0; if(CheckSum(USART3_RX_BUF)) // 校验通过 USART3_RX_FLAG 1; } } } u8 CheckSum(u8 *buf) { u8 sum 0; for(int i0; i10; i) sum buf[i]; return (sum buf[10]); }这段代码有几个优化点加入超时计数器防止半帧数据卡死校验单独封装成函数便于维护使用标志位通知主程序减少中断耗时4. 欧拉角解算与校准4.1 原始数据转换从传感器获取的是16位有符号原始数据需要转换为实际角度void ParseAngleData(u8 *buf, float *roll, float *pitch, float *yaw) { short raw_roll (buf[3]8) | buf[2]; short raw_pitch (buf[5]8) | buf[4]; short raw_yaw (buf[7]8) | buf[6]; *roll raw_roll / 32768.0f * 180.0f; *pitch raw_pitch / 32768.0f * 180.0f; *yaw raw_yaw / 32768.0f * 180.0f; }注意这里的坐标系定义X轴传感器左侧Y轴传感器前方Z轴传感器上方4.2 九轴模式校准要点WT931在九轴模式下精度更高但必须校准校准流程如下水平放置传感器静止5秒绕Z轴缓慢旋转360度绕X轴和Y轴各倾斜45度多次通过上位机发送校准命令校准不充分会导致Yaw角漂移我做过测试未校准时Yaw漂移可达5°/分钟校准后能控制在1°/小时以内。5. 实际项目中的坑与解决方案5.1 磁场干扰问题在钢铁结构附近磁力计会受到严重干扰。解决方案尽量远离金属物体使用软件滤波滑动平均或卡尔曼滤波在关键位置设置磁力计补偿参数5.2 数据抖动处理原始数据会有高频噪声我的滤波方案#define FILTER_NUM 5 float filter_buf[FILTER_NUM]; float MovingAverageFilter(float new_val) { static u8 index 0; float sum 0; filter_buf[index] new_val; if(index FILTER_NUM) index 0; for(int i0; iFILTER_NUM; i) sum filter_buf[i]; return sum / FILTER_NUM; }对于动态场景可以改用互补滤波平衡响应速度和稳定性。