首页 / 资讯中心 / 文章详情

STM32F103C8T6+MPU6050姿态解算工程:I2C到滤波实战

STM32F103C8T6+MPU6050姿态解算工程:I2C到滤波实战 ★ FEATURED ARTICLE
简介面向STM32初学者与嵌入式开发者的MPU6050姿态解算完整工程基于STM32F103C8T6平台已配置好可直接编译下载的Keil工程环境可用于飞行器、机器人、智能穿戴与健康监测等场景的姿态数据采集和控制验证省去从零搭建外设与工程模板的时间。压缩包共188个文件约6.16MB以32个.c源文件、35个.h头文件为核心配有.s汇编启动文件、Keil工程与目标配置文件以及编译生成的.o、.d、.crf等中间文件和.hex可执行固件目录涵盖STM32标准外设库、I2C与MPU6050驱动及主程序模块。已有2619人学习下载适合用来对照工程结构理解外设初始化、传感器读取和姿态解算流程也可作为课程设计、毕业设计或项目原型的起步模板减少环境配置与底层联调成本。1. 先别急着烧录这套 STM32F103C8T6MPU6050 工程真正省掉的是哪几步STM32F103C8T6 加 MPU6050 的组合最难的不是写一行姿态解算源码而是把时钟、I2C、串口、寄存器配置、零偏校准和主循环节拍一次性串顺。这套完整工程的价值就在这里它把“模块能读到数”到“串口能输出角度”之间的脏活累活先做成了可编译的底稿。你拿到手可以直接编译下载先看原始数据再改解算参数。适合正在做课程设计、平衡车或云台入门的开发者也适合手里只有模块和杜邦线、不想从新建工程开始踩坑的人。它不是算法教科书但能让你少走几天弯路。2. 工程结构与数据链路从 I2C 寄存器到串口角度值2.1 目录分层先认清哪一层在动硬件拿到一套能直接编译的 STM32F103C8T6MPU6050 工程第一件事不是打开 main.c 找姿态解算而是看目录分层。常见做法是把工程分成启动与时钟、外设初始化、传感器驱动、姿态解算、应用输出几块。启动文件负责中断向量和复位入口时钟配置决定 I2C 和串口能不能跑在预期频率外设初始化把 GPIO 复用成 I2C1 的 SCL/SDA 和 USART1 的 TX/RX传感器驱动只做寄存器和原始数据姿态解算单独放一个文件主循环负责定时采样和打印。这样分的好处是排错时能快速定位WHO_AM_I 读不到就查 I2C 和供电角度漂就查零偏和解算周期串口乱码就查波特率和时钟。我一般会先看工程里有没有独立的 MPU6050 驱动文件。如果寄存器读写、量程换算、校准全挤在 main.c 里后面改量程容易翻车。分层清楚的工程驱动层只暴露MPU6050_Init、MPU6050_ReadRaw、MPU6050_CalibrateGyro这类接口解算层只吃原始数据和 dt输出层只负责格式化。这样你把互补滤波换成四元数时不用动 I2C 时序把串口输出改成 CSV 时也不用碰寄存器配置。C8T6 只有 64KB Flash 和 20KB RAM分层不会明显增加开销反而让编译产物更好查。还有一类工程会把 MPU6050 的 DMP 固件也塞进来。DMP 能直接输出四元数省掉主控解算但移植时对 I2C 时序和固件加载顺序要求更高第一次上手不建议一上来就调 DMP。先把原始数据链路跑通确认加速度计和陀螺仪三轴方向、量程、零偏都对再决定要不要上 DMP。这套工程如果默认走的是主控解算那它的教学价值大于直接产品价值改参数和换滤波都更方便。2.2 上电后的数据流谁先初始化、谁在定时采样上电顺序是有讲究的。先复位外设和 SysTick再配 72MHz 主频然后初始化 GPIO、I2C、串口接着复位 MPU6050、唤醒、写量程和 DLPF最后做静止零偏校准。校准没做完就进主循环陀螺仪零偏会被直接积分进角度表现就是静止时角度缓慢往一个方向跑。很多“姿态解算不准”的问题根子不在滤波系数而在上电后没有给模块足够的静止时间。下面这段初始化顺序是这类工程里最常见的骨架函数名按你手里的工程实际替换即可/* main.c 里的初始化顺序函数名按工程实际替换 */ int main(void) { HAL_Init(); // 复位外设、初始化 SysTick SystemClock_Config(); // 72 MHz 主频I2C 和串口都依赖它 MX_GPIO_Init(); // 指示灯、I2C 引脚复用 MX_I2C1_Init(); // 先 100 kHz稳定后再试 400 kHz MX_USART1_UART_Init(); // 115200 8N1方便看原始数据 MPU6050_Init(); // 复位、唤醒、配置量程和 DLPF MPU6050_CalibrateGyro(800); // 静止采样 800 次求陀螺零偏 while (1) { MPU6050_ReadRaw(raw); // 一次突发读 14 字节 MPU6050_UpdateAttitude(raw, dt); printf(roll%.2f pitch%.2f yaw%.2f\r\n, roll, pitch, yaw); HAL_Delay(10); // 实际工程建议用定时器节拍 } }这段代码里HAL_Init和SystemClock_Config决定后续延时和 I2C 时钟是否准确MX_I2C1_Init里的 I2C 速率建议先设 100kHz杜邦线长、上拉弱的时候 400kHz 容易通信失败MPU6050_CalibrateGyro(800)的 800 是采样次数按 5ms 一次算大约 4 秒按 10ms 一次算大约 8 秒时间不够零偏均值会抖HAL_Delay(10)只适合验证真正做姿态解算要用定时器中断或 SysTick 固定节拍否则 dt 不准互补滤波和四元数都会跟着漂。主循环里一次突发读 14 字节顺序通常是加速度 X/Y/Z、温度、陀螺仪 X/Y/Z每个轴高字节在前。读完后先做原始数据到物理单位的换算再进姿态解算最后输出。不要一边读一边打印串口打印本身会拖慢采样周期调试阶段可以每 10 次采样打印一次或者用定时器打时间戳。工程里如果已经带了dt计算优先用它不要自己再估一个固定值。2.3 编译环境与下载配置能编译不等于能跑工程能编译通过只说明语法和链接没问题不代表下载后能跑。C8T6 的 BOOT0 要拉低复位后从主 Flash 启动下载方式常见是 SWDSWDIO 和 SWCLK 两根线要接对目标板共地。串口能出数据但全是乱码先查波特率是不是 115200、时钟是不是真跑到 72MHz、串口引脚有没有被别的外设占用。I2C 能读但偶尔失败先降速到 100kHz再看 SDA/SCL 上拉电阻很多模块自带上拉但杜邦线一长波形边沿会变差。编译选项里还要注意优化等级。调试阶段建议先关高等级优化或者至少保证volatile用在中断和主循环共享的变量上比如定时采样标志、原始数据缓存。优化开太高时编译器可能把循环里的寄存器读操作优化掉表现就是串口一直输出同一个值。堆栈也要留够姿态解算里如果用了浮点 printf栈别设得太小。C8T6 资源不宽裕但把串口打印和浮点运算同时开起来20KB RAM 仍然够用前提是别在中断里做大面积浮点格式化。提示第一次下载后先只打印 WHO_AM_I 和原始六轴数据确认 I2C 通信和方向映射再打开姿态解算输出。这样出问题时能快速判断是驱动层还是解算层。3. MPU6050 寄存器配置量程、DLPF 与零偏校准的实操顺序3.1 上电默认值与量程选择±2g 还是 ±8gMPU6050 上电后默认处于睡眠状态PWR_MGMT_1 的 SLEEP 位是 1直接读传感器数据可能全是 0。初始化第一步通常是写 0x6B 寄存器先给 0x80 复位延时后写 0x00 唤醒再写 0x01 选择陀螺仪 X 轴 PLL 作为时钟源。时钟源稳定的情况下陀螺仪零偏会比内部 8MHz 振荡器小一些。复位后不要立刻连续写配置给器件一点启动时间常见做法是延时 50ms 到 100ms。量程选择要看应用。做静态倾角、慢速云台加速度计量程用 ±2g 足够灵敏度 16384 LSB/g分辨率高做剧烈运动或冲击检测±4g 或 ±8g 更安全但分辨率下降。陀螺仪做姿态解算常用 ±2000dps灵敏度 16.4 LSB/(°/s)量程大不容易饱和代价是小角速度分辨率变粗慢速应用可以用 ±500dps 或 ±250dps零偏和噪声表现会好一些。工程里如果默认是 ±2g 加 ±2000dps这是比较通用的入门组合。配置寄存器写入顺序建议是PWR_MGMT_1 复位并唤醒SMPLRT_DIV 设采样分频CONFIG 设 DLPFGYRO_CONFIG 设陀螺量程ACCEL_CONFIG 设加速度量程。顺序反了不一定报错但 DLPF 改变后采样率会变校准和 dt 要跟着改。下面这张表是常用配置的对照改量程时直接查灵敏度配置项寄存器常见值含义电源管理0x6B0x80 复位0x00 唤醒0x01 时钟源睡眠位必须清掉采样分频0x190x09采样率 陀螺输出率 / (1 值)低通滤波0x1A0x03加速度 44Hz、陀螺 42Hz 带宽陀螺量程0x1B0x18±2000dps灵敏度 16.4加速度量程0x1C0x00±2g灵敏度 16384采样分频不是越小越好。DLPF 打开后陀螺输出率会降到 1kHzSMPLRT_DIV9对应 100Hz 采样主循环 10ms 读一次刚好匹配。如果主循环 5ms 读一次就把分频改成 4对应 200Hz。读得比配置快会重复读到旧数据读得比配置慢会丢样本姿态解算的 dt 就不均匀。很多“角度抖动”不是滤波没调好而是采样率和主循环节拍对不上。3.2 寄存器写入与突发读取I2C 地址别左移错MPU6050 的 7 位地址由 AD0 决定AD0 接地是 0x68接高是 0x69。用 HAL 的HAL_I2C_Mem_Write时传入的是 8 位地址也就是 0x68 左移一位后的 0xD0读的时候是 0xD1。有些工程宏定义直接写0xD0有些写0x68再在函数里左移改代码前先看驱动层怎么处理。地址错了WHO_AM_I 读回 0x00 或 0xFF很多人会误判为模块坏了。下面这段是寄存器写和突发读的常见写法MPU6050_ADDR按工程实际填 0xD0 或 0x68 左移后的值/* 寄存器写HAL 的 Mem_Write 内部会处理寄存器地址 */ static void MPU6050_WriteReg(uint8_t reg, uint8_t val) { HAL_I2C_Mem_Write(hi2c1, MPU6050_ADDR, reg, I2C_MEMADD_SIZE_8BIT, val, 1, 50); } /* 一次读 14 字节加速度 6 温度 2 陀螺 6 */ void MPU6050_ReadRaw(MPU6050_Raw *raw) { uint8_t buf[14]; HAL_I2C_Mem_Read(hi2c1, MPU6050_ADDR, 0x3B, I2C_MEMADD_SIZE_8BIT, buf, 14, 50); raw-ax (int16_t)((buf[0] 8) | buf[1]); raw-ay (int16_t)((buf[2] 8) | buf[3]); raw-az (int16_t)((buf[4] 8) | buf[5]); raw-gx (int16_t)((buf[8] 8) | buf[9]); raw-gy (int16_t)((buf[10] 8) | buf[11]); raw-gz (int16_t)((buf[12] 8) | buf[13]); }0x3B是加速度 X 轴高字节连续读 14 字节刚好覆盖到陀螺仪 Z 轴低字节。温度在buf[6]和buf[7]做温度补偿时用得上。int16_t强转不能省高字节左移后先拼成 16 位再转有符号数直接按无符号打印会出现几万的大数。I2C 超时时间 50ms 对 100kHz 来说够用如果总线上有多个从机读失败时先看 HAL 返回状态不要只判断数据。3.3 零偏校准静止采样取均值别把桌子震动算进去陀螺仪零偏是姿态漂移的头号来源。校准方法不复杂模块静止放在水平桌面上连续读 N 次原始数据每个轴求平均把这个平均值存成gyro_bias_x/y/z后续解算时先减掉再积分。采样次数常见 500 到 1000 次间隔 5ms 到 10ms。次数太少均值受噪声影响次数太多校准时间太长温度已经变了。校准期间不要碰桌子风扇吹着、电脑震动都会进均值。加速度计也可以做零偏校准但更推荐六面校准因为加速度计不仅存在零偏还有比例误差和轴间误差。入门阶段先把陀螺零偏扣掉加速度计只用来看倾角效果已经能接受。校准代码里要判断数据是否稳定比如连续几次采样的最大值和最小值差超过阈值就重新开始或者直接提示“请保持静止”。工程里如果只做一次上电校准温度变化大的场景会慢慢漂后面可以加温度补偿或运行时零偏估计。/* 静止采样求陀螺零偏采样间隔由调用者控制 */ void MPU6050_CalibrateGyro(uint16_t samples) { int32_t sum_gx 0, sum_gy 0, sum_gz 0; MPU6050_Raw raw; for (uint16_t i 0; i samples; i) { MPU6050_ReadRaw(raw); sum_gx raw.gx; sum_gy raw.gy; sum_gz raw.gz; HAL_Delay(5); } gyro_bias_x (float)sum_gx / samples; gyro_bias_y (float)sum_gy / samples; gyro_bias_z (float)sum_gz / samples; }这段代码把零偏存成原始 LSB 单位后面换算成 dps 时再统一减。也可以先换算再求平均结果一样但要注意量程灵敏度。samples取 800 时5ms 间隔大约 4 秒校准期间串口不要频繁打印打印会拖慢采样。校准完成后建议把零偏值打印一次换模块或换量程时对比一下零偏大得离谱通常意味着模块焊接、供电或 I2C 时序有问题。4. 姿态解算源码拆解互补滤波的 alpha 和四元数的 dt 怎么定4.1 原始数据换算LSB 灵敏度与单位统一姿态解算的第一步不是滤波而是把原始整数换成物理单位。加速度计按量程除以灵敏度±2g 是 16384 LSB/g±4g 是 8192±8g 是 4096±16g 是 2048。陀螺仪 ±250dps 是 131 LSB/(°/s)±500 是 65.5±1000 是 32.8±2000 是 16.4。换算后的加速度单位是 g陀螺仪单位是 °/s。后面如果要用四元数陀螺仪还要转成 rad/s乘 0.0174533。单位不统一是新手最容易翻车的地方。有人把陀螺仪原始值直接当 °/s 用积分出来角度大得离谱有人把加速度计原始值直接送进 atan2虽然角度比例对但阈值判断和滤波系数会完全错位。我的习惯是驱动层只输出ax_g、ay_g、az_g、gx_dps、gy_dps、gz_dps解算层不再碰 LSB。这样换量程时只改一个宏不用在滤波函数里到处找除数。/* 原始值转物理单位量程变了只改这两个宏 */ #define ACC_LSB_PER_G 16384.0f // ±2g #define GYRO_LSB_PER_DPS 16.4f // ±2000dps void MPU6050_RawToUnit(const MPU6050_Raw *raw, float *ax, float *ay, float *az, float *gx, float *gy, float *gz) { *ax raw-ax / ACC_LSB_PER_G; *ay raw-ay / ACC_LSB_PER_G; *az raw-az / ACC_LSB_PER_G; *gx (raw-gx - gyro_bias_x) / GYRO_LSB_PER_DPS; *gy (raw-gy - gyro_bias_y) / GYRO_LSB_PER_DPS; *gz (raw-gz - gyro_bias_z) / GYRO_LSB_PER_DPS; }这里的gyro_bias_x是原始 LSB 单位所以先减再除。如果校准函数存的是 dps 单位顺序就要反过来。换算完的加速度模值应该在 1g 附近静止时sqrt(ax*axay*ayaz*az)在 0.95 到 1.05 之间算正常偏差太大先查量程配置和模块是否倾斜。陀螺仪静止时三轴应在 0 附近偶尔有几个 LSB 跳动是噪声不用追求绝对零。4.2 互补滤波陀螺仪积分会漂加速度计负责拉回来互补滤波的思路很直白陀螺仪动态响应好但积分会漂加速度计静态准但振动噪声大。用高通滤陀螺仪、低通滤加速度计合起来就是angle alpha * (angle gyro * dt) (1 - alpha) * acc_angle。alpha常见 0.95 到 0.98越大越信陀螺仪动态跟随快但漂移修正慢越小越信加速度计静态稳但振动时角度会抖。dt必须和采样周期一致10ms 采样就传 0.01不能随手写 1。加速度计算倾角时roll 用atan2(ay, az)pitch 用atan2(-ax, sqrt(ay*ayaz*az))结果转成角度。符号取决于模块安装方向换板子后角度反了先调符号不要急着改滤波。yaw 没有磁力计参考只能靠陀螺仪 Z 轴积分时间一长必然漂这是原理限制不是代码写错。做平衡车只用 roll 和 pitchyaw 可以不用做云台航向要么加磁力计要么接受缓慢漂移并定期回正。/* 互补滤波更新 roll 和 pitchdt 单位秒 */ #define ALPHA 0.96f #define RAD_TO_DEG 57.29578f void Attitude_Update(const MPU6050_Raw *raw, float dt) { float ax, ay, az, gx, gy, gz; MPU6050_RawToUnit(raw, ax, ay, az, gx, gy, gz); float acc_roll atan2f(ay, az) * RAD_TO_DEG; float acc_pitch atan2f(-ax, sqrtf(ay * ay az * az)) * RAD_TO_DEG; roll ALPHA * (roll gx * dt) (1.0f - ALPHA) * acc_roll; pitch ALPHA * (pitch gy * dt) (1.0f - ALPHA) * acc_pitch; /* yaw 只能积分没有磁力计会漂 */ yaw gz * dt; }ALPHA取 0.96 时时间常数大约几十毫秒到几百毫秒具体看 dt。10ms 采样下0.96 对应修正比较慢静态稳但上电对齐要等一会儿0.98 跟随快振动时更容易把噪声带进角度。atan2f和sqrtf用单精度浮点C8T6 没有硬件 FPU计算开销要靠采样周期摊掉10ms 一次完全够用。如果主循环里还有串口 printf建议把打印降到 100ms 一次避免解算周期被拖长。4.3 四元数更新一阶积分够用但归一化不能省当姿态要参与三维旋转或避免万向锁时四元数比欧拉角更合适。MPU6050 没有磁力计四元数更新通常只用陀螺仪角速度加速度计用来修正 roll 和 pitchyaw 仍然没有绝对参考。一阶龙格库塔积分在 100Hz 到 200Hz 采样下够用代码量也不大。关键是每步更新后做归一化否则数值误差累积四元数模长慢慢偏离 1角度就会越算越歪。四元数微分方程里q0是标量部分q1/q2/q3是向量部分陀螺仪角速度要先转成 rad/s。更新时用临时变量保存旧四元数避免边算边覆盖。下面这段只给更新骨架加速度计修正可以后加/* 四元数一阶积分gx/gy/gz 单位 rad/sdt 单位秒 */ void Quat_Update(float gx, float gy, float gz, float dt) { float q0 quat[0], q1 quat[1], q2 quat[2], q3 quat[3]; quat[0] (-q1 * gx - q2 * gy - q3 * gz) * 0.5f * dt; quat[1] ( q0 * gx q2 * gz - q3 * gy) * 0.5f * dt; quat[2] ( q0 * gy - q1 * gz q3 * gx) * 0.5f * dt; quat[3] ( q0 * gz q1 * gy - q2 * gx) * 0.5f * dt; float norm sqrtf(quat[0]*quat[0] quat[1]*quat[1] quat[2]*quat[2] quat[3]*quat[3]); quat[0] / norm; quat[1] / norm; quat[2] / norm; quat[3] / norm; }dt不稳定时四元数积分误差比互补滤波更明显。主循环用HAL_Delay(10)只能算近似 10ms串口打印一多就变成 12ms、15ms角度会跟着抖。常见做法是用定时器中断置标志位主循环检测到标志再读传感器和解算dt用定时器周期。四元数转欧拉角时注意公式里的符号和奇异点pitch 接近 ±90° 时 roll 和 yaw 会耦合这是欧拉角本身的局限不是解算错了。5. 避坑与排查读不到 ID、角度漂移、数据乱跳的 5 个现场5.1 现象WHO_AM_I 读回 0x00 或 0xFF现象很直接串口打印 WHO_AM_I预期是 0x68实际一直是 0x00 或 0xFF后面所有原始数据也是死的。原因通常分三类供电和地没接好I2C 从机地址不对总线被拉死。MPU6050 模块有的带稳压有的直接 3.3V 供电先量 VCC 和 GND 之间电压再看 SDA/SCL 静态电平是不是被上拉到 3.3V。地址方面AD0 接地是 0x68接高是 0x69HAL 里传 8 位地址时是 0xD0 或 0xD1传 7 位地址的驱动则写 0x68。地址左移错一位读回值就全是 0xFF。解决办法是先写一个 I2C 扫描小程序从 0x08 到 0x77 逐个地址发空写看哪个地址有应答。扫描不到任何设备查接线和上拉扫描到 0x68 但 WHO_AM_I 不对查寄存器地址是不是写成了 0x75 以外的值。SDA 被从机拉死时可以断电重启或者在 SCL 上手动发 9 个时钟脉冲解锁。血泪经验是杜邦线接触不良比代码错误更常见尤其是排针氧化后万用表量通断都正常一上电就时好时坏。5.2 现象角度静止时缓慢漂移静止放在桌上roll 和 pitch 每隔几秒漂零点几度yaw 漂得更快。原因首先是陀螺仪零偏没扣干净其次是校准期间桌子有震动最后是温度变化。MPU6050 的陀螺仪零偏随温度会有小幅变化上电校准一次只能管一段时间。如果校准采样时电脑风扇对着吹或者手还放在桌上均值会把震动吃进去解算后就会往一个方向积分。解决顺序是校准前静置 30 秒确认串口原始陀螺仪三轴在零附近跳动校准采样 800 次以上采样间隔固定校准后打印零偏值换模块对比。静止漂移仍然明显可以在主循环里做运行时零偏估计当加速度计模值接近 1g 且陀螺仪角速度很小时缓慢更新零偏。这个做法不能太激进否则真实缓慢转动会被当成零偏吃掉。yaw 漂移没有磁力计就无解只能接受或加磁力计。5.3 现象串口有数据但姿态抖动大角度能出来但数值上下跳手不碰模块也在抖。常见原因有三个DLPF 带宽太高加速度计把桌面高频振动吃进来采样周期不稳定dt忽大忽小互补滤波的 alpha 太小过度相信加速度计。先看原始数据如果加速度计三轴静止时跳动几十个 LSB先查供电和模块固定软胶垫或海绵能滤掉一部分机械振动。DLPF 设到 3 档加速度 44Hz、陀螺 42Hz对入门场景够用。采样周期方面把HAL_Delay(10)换成定时器节拍串口打印改成 100ms 一次观察角度是否立刻变稳。alpha 从 0.96 往 0.98 调动态跟随变快但静态修正变慢如果抖动是加速度计噪声引起的调大 alpha 会明显改善。还要注意浮点 printf 在小容量芯片上的开销打印本身可能拖慢主循环形成“越打印越抖”的循环。先把打印关掉看原始波形再决定要不要改滤波参数。5.4 现象编译通过但下载后不运行编译零错误下载也提示成功但串口没输出指示灯不亮。先查 BOOT0 是不是拉低复位引脚是不是被外部电路拉住SWD 下载后有没有勾选复位运行。C8T6 的启动模式由 BOOT0 和 BOOT1 决定BOOT0 高电平会进系统存储器程序不跑。电源方面有些下载器只给 3.3V 小电流模块一加上去电压就掉表现就是下载成功但运行复位。还有一类是工程配置问题启动文件选错、堆栈太小、中断向量表偏移不对。串口 printf 重定向没开也会看起来“没输出”。排查时先点灯确认主循环在跑再打印一个固定字符串确认串口和时钟最后读 WHO_AM_I。顺序不要反否则会在 I2C 和下载配置之间来回怀疑。黑匣子思路在这里很有用把每个阶段的状态码打到串口哪一步停住一目了然。5.5 现象换一块板子数据就反了同一套代码换一块 MPU6050 模块roll 和 pitch 方向反了或者 yaw 转向相反。原因通常是模块安装方向不同、AD0 电平不同、量程配置没跟着改。MPU6050 的 X/Y/Z 轴定义固定在芯片上模块焊在板子哪一面、怎么摆决定了你看到的正方向。换模块后先看原始数据倾斜模块哪个轴的加速度变化符合预期再决定要不要在解算前乘 -1。解决方法是固定一套机体坐标系在驱动层做轴映射不要在滤波函数里到处改符号。比如规定 X 向前、Y 向右、Z 向下模块装反了就交换轴或取反映射表写清楚。量程换了灵敏度宏也要换否则角度比例会错。校准零偏也要重新做不同模块的零偏不一样。踩坑最多的情况是只看欧拉角输出不看原始数据结果符号改来改去最后自己也说不清哪一轴反了。6. 进阶验证六面静态校准与串口 CSV 波形自查调姿态解算不能只看串口数字“像不像”要有可重复的验证方法。我常用六面静态校准把模块分别按六个面朝下放在水平桌面上每个面静止 5 秒记录加速度计三轴平均值和解算出的 roll/pitch。如果某个面角度偏差超过 2°先查模块是否真的水平、量程是否配置正确、轴映射有没有错。六面数据还能用来估加速度计零偏和比例误差入门阶段至少能确认方向映射是对的。摆放方式预期主加速度轴预期 roll/pitch主要检查点正面朝上Z 约 1groll≈0pitch≈0水平基准正面朝下Z 约 -1groll≈180 或 pitch≈180符号与奇异点左侧朝下X 约 1g 或 -1groll≈±90轴映射右侧朝下X 反向roll≈∓90轴映射前侧朝下Y 约 1g 或 -1gpitch≈±90轴映射后侧朝下Y 反向pitch≈∓90轴映射串口输出改成 CSV用表格软件或串口波形工具看曲线比盯着一行行文字有效。下面这段每 100ms 打一行包含时间戳、原始加速度、原始陀螺仪和角度方便回放/* CSV 输出时间戳, ax, ay, az, gx, gy, gz, roll, pitch, yaw */ printf(%lu,%.3f,%.3f,%.3f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f\r\n, HAL_GetTick(), ax, ay, az, gx, gy, gz, roll, pitch, yaw);看波形时重点看三件事静止时角度是否收敛到一条水平线快速倾斜后角度是否跟随且不过冲回到静止后是否在几秒内稳住。如果快速倾斜时角度滞后严重alpha 往大调如果静止时来回摆alpha 往小调或加大 DLPF。每次只改一个参数改完重新做六面静态别一次动三个地方。从那以后我每次拿到新的 MPU6050 模块都先做一遍静态六面校验再上整机不然调姿态解算就是纯玄学。需要对照原工程的话按资源页的下载入口取一份即可拿到后先跑通串口原始数据再动解算参数。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站