# 已知问题与排障手册 本文记录本项目实际踩过的坑、判断依据和处理方式。**遇到姿态/通讯异常时先读这里。** --- ## 0. 排障总则 ### 0.1 先看 `ca` / `cg`,再看数值 USB 遥测里的 `ca`(加速度采样计数)和 `cg`(陀螺采样计数)是**唯一能证明"数据还在更新"的指标**。 数值是**会骗人的** —— 数据路停摆时,`an`、`gn`、`gx/gy/gz`、`pitch/roll` 都会安静地停在最后一个样本上,看起来"挺正常"。 ``` ca/cg 在涨 → 数据路活着,问题在算法或标定 ca/cg 不涨 → 数据路死了,先修数据路,别的都别管 ``` ### 0.2 用算式交叉验证,别凭感觉 姿态异常时,把**观测到的现象**和**遥测里的量**做量级核算,往往能直接定位层次。例: - yaw 匀速自漂 → 算漂移率(rad/s) - 若漂移率 **> `gn`** ⇒ 漂移来自被污染的 z 零偏 `zo`,不是陀螺本身 - 若漂移率 **≈ `gz - zo`** ⇒ 算法没问题,是数据冻结了(EKF 在忠实积分冻结值) - 角度恒定不动 → 看 `cvg`:`cvg=0` 说明量测一直被卡方检验拒绝;`cvg=1` 而角度不动 ⇒ 多半是 `ca/cg` 停了 ### 0.3 症状速查表 | 症状 | 首先怀疑 | 去看 | | --- | --- | --- | | 欧拉角恒为 0.00 | IMU 没起来,或 EKF 内存分配失败 | `imu`、`an`、`cvg`;[§2.1](#21-三个欧拉角恒为-000) | | 角度恒定不动(有值但不变) | 数据路停摆(数据冻结) | `ca`/`cg`;[§2.2](#22-数据路静默冻结) | | yaw 匀速自漂 | z 零偏标定被运动污染 | `zo` vs `gz`,漂移率 vs `gn`;[§2.3](#23-yaw-匀速自漂) | | 角度是垃圾值 / yaw≈180 | 矩阵运算静默失败 | `cq` 字段、`Heap_Size`;[§2.1](#21-三个欧拉角恒为-000) | | 蜂鸣器连续滴滴响 | CAN 设备掉线报警(正常行为) | 响几下 = 第几号设备掉线;[§2.4](#24-蜂鸣器一直响) | | 红灯常亮 | CAN 设备掉线 | 同上 | | 绿灯连闪两下 | BMI088 失败 | `imu` | | 反复重启(约 3~5 s 一次) | 初始化超时 / 任务卡死 | IWDG 5 s 预算;[§2.5](#25-反复重启) | | 串口收不到整帧 | 空闲中断 / DMA 未重启 | `bsp_usart.c` | --- ## 1. 工具与判据 ### 1.1 USB CDC 遥测字段 完整字段说明见根 [README §8](../README.md#8-调试usb-cdc-遥测)。排障最常用的几个: | 字段 | 关键读法 | | --- | --- | | `ca`/`cg` | **是否在增长** = 数据路是否活着 | | `cvg` | `INS.ConvergeFlag`,是否真收敛(不是"是否在跑") | | `imu` | BMI088 是否初始化成功 | | `an` | ≈9.8 正常;≈0 说明没数据 | | `gn` | 原始陀螺模长(**未减零偏**) | | `zo` | 实际减掉的 z 零偏;与 `gz` 量级对比 | | `ck` | `zo` 是否可信(标定时是否静止) | | `se`/`st` | SPI 出错次数 / SPI 状态(1=READY) | ### 1.2 LED 与蜂鸣器 灯语见根 [README §10](../README.md#10-上电行为与灯语)。蜂鸣器的"响几下"直接对应掉线设备序号 + 1。 ### 1.3 与参考工程对照 桌面上留有本项目的参照物,**遇到"应该能跑却跑不起来"时,优先和它们逐行对照**: | 参考 | 位置 | 用途 | | --- | --- | --- | | 原始 INS 例程 | `Desktop/新建文件夹/RoboMaster-C-Board-INS-Example-master` | QEKF / kalman_filter 的移植来源;**其 `Heap_Size = 0x2000`** | | legacy 固件 | `Desktop/new-infantry-c-board-legacy - 副本` | StdPeriph 版、自带 `AHRS.lib`;引脚分配、EXTI 触发沿、寄存器序列的权威参照 | | 本工程旧版 | `Desktop/Simba-OmniTest/Simba-ELCT-FrameWork` | 同一框架的历史快照 | --- ## 2. 典型案例详解 ### 2.1 三个欧拉角恒为 0.00 **两个不同原因都会长成这样**,用 `cvg` 和 `an` 区分: #### (a) EKF 矩阵内存分配失败 —— `cvg=1`、`an≈9.8`、角度恒 0 - **现象**:`yaw=pitch=roll=0.00` 且 `cvg=1`(注意 `cvg` 若是 `att_ready` 之类的量就永远看不出来) - **原因**:`kalman_filter.c` 的 `user_malloc` 展开为 **libc `malloc`**(它不 include `cmsis_os.h`),而 libc 堆来自 `startup_stm32f407xx.s` 的 `Heap_Size`。CubeMX 默认 `0x200`(512 B),远小于 `Kalman_Filter_Init(6,0,3)` 需要的约 1.6 KB,且全程无 NULL 检查 - **后果**:后半段矩阵 `pData = NULL`,`arm_mat_*` 只读写地址 0 附近、不报错也不改输出 ⇒ 滤波器不再更新状态,但 `UpdateCount` 照常自增 ⇒ 看起来"在跑"、"收敛了" - **处理**:把 `Heap_Size` 改为 `0x2000`(与原始例程一致)。⚠️ 该行由 CubeMX 生成,重新生成工程会被改回 #### (b) IMU 没起来 —— `cvg=0`、`an≈0`、角度恒 0 - **现象**:`imu=0` 或 `an≈0`,且 `cvg=0`(卡方检验一直不过) - 加速度全零 → 量测被拒绝 → 四元数停在初值 `[1,0,0,0]` 反解就是三个 0.00 - **处理**:查 SPI 通路 / 片选 / 传感器供电;并把 `debug_task` 的 `imu`、`an` 和绿灯灯语一起看 ### 2.2 数据路静默冻结 - **现象**:`pitch/roll/an/gx/gy/gz` 全部纹丝不动,`ca`/`cg` **停止增长**,而 `imu` 仍是 1;yaw 可能还在匀速漂(积分冻结值) - **两类原因**: 1. `hspi1` 卡在 BUSY(SPI/DMA 出错后 HAL 没回到 READY)⇒ `start_dma_read()` 里 `HAL_SPI_GetState` 检查让每次调用直接返回 2. BMI088 的 DRDY 是**电平型**信号,某次失败或被挡掉的读没把数据寄存器读走 ⇒ INT 电平保持住 ⇒ **既无下降沿也无上升沿,EXTI 永不触发** - **为什么中断里的兜底没用**:两种情况都会让 EXTI 回调**压根不执行** - **处理**:已实现任务上下文的 `bmi088_health_check()`(由 `attitude_update()` 每 1 ms 调用),计数 5 ms 不涨即重启中断路 + 阻塞读一次,修不好则退化为约 200 Hz 轮询保底 - **继续深挖**:看 `st` 与 `se` - `st` 长期非 1 ⇒ SPI 卡死(原因 1) - `st` 恒为 1 但 `ca/cg` 不涨 ⇒ 数据就绪中断丢失(原因 2) ### 2.3 yaw 匀速自漂 - **现象**:yaw 以**恒定**角速度漂移,`pitch/roll` 正常;移动板子时陀螺读数"不响应" - **判定算式**: ``` 漂移率(rad/s) = Δyaw / Δt 若 漂移率 > gn ⇒ 漂移来自被污染的 zo,不是陀螺 实际进滤波器的角速度 = gz - zo ``` 实例:实测 -3.85 °/s = -0.0672 rad/s,而 `gn = 0.017 rad/s` —— 漂移率比整个陀螺模长还大近 4 倍,只可能是假零偏 - **原因**:z 轴零偏标定窗口(~1 s)里混入了运动样本。上电瞬间 SPI 路刚起步、且车体常正被摆弄 - **处理**:`ins.c` 已加健壮性(稳定等待 + 离群剔除 + 整轮重测 + `ck` 标志);**根治办法是上电后那 1.5 秒别碰车** - 注意 `gn` 是**未减零偏**的原始模长,所以 `gn` 小并不代表进滤波器的值小 ### 2.4 蜂鸣器一直响 - 这是**正常报警**,不是故障:某台 CAN 设备掉线,`monitor_task` 按 legacy 语义每 2 s 窗内响 `n` 下,`n = 最先掉线的设备序号 + 1` - 云台序号:`0=摩擦轮L 1=摩擦轮R 2=Pitch 3=Yaw 4=拨弹 5=底盘回传`;底盘:`0~3=四轮 4=云台数据` - **响三下 ⇒ 第 3 号设备(云台板的 Pitch 电机 0x206)掉线** - 台架调试嫌吵:把 `robot_param.h` 的 `DEBUG_BUZZER_MELODY` 置 1,警告音换成旋律;**灯语不变,故障信息不丢**。比赛前记得改回 0 ### 2.5 反复重启 - **现象**:约 3~5 s 复位一次,USB 输出每隔几秒重来 - **原因**:IWDG 约 2.5 s 溢出;上电 5 s 内无条件喂狗,之后要求**所有任务**心跳都在 500 ms 内刷新过。因此 - 某个任务卡死(死循环 / 阻塞在无超时的等待) - 或者**整个初始化超过 5 s**,`IMU_Task` 还没进主循环、心跳不刷新 - **处理**:检查各初始化耗时(传感器重试次数、IMU 零偏标定轮数);`IMU_Task` 在 `attitude_init()` 期间不刷心跳,扩展初始化要算进这 5 s 预算 ### 2.6 工程编译/链接相关的坑(非运行期) | 症状 | 原因 | 处理 | | --- | --- | --- | | 链接报找不到 `arm_mat_*` | `Middlewares/ST/ARM/DSP/Lib/*.lib` 丢失 | 该目录是源码依赖,已入库;确认文件在 | | 新加的 `.c` 没被编译 | 没加进 `.uvprojx` | 同步加 `.uvprojx`(和 `.uvoptx`) | | Keil 每次保存工程文件都在变 | `.uvoptx` 的 `` 断号 | 删除条目后把末尾编号改为连续值 | | 移动目录后编译不过 | `` 没跟着改 | `.h` 不登记在工程里,靠 IncludePath 定位;移动目录时同步改 | | CubeMX 重新生成后行为变了 | 手改过的生成文件被覆盖 | 复查 `startup_stm32f407xx.s` 的 `Heap_Size` 与 `Core/Src/gpio.c` 的 INT1 触发沿 | --- ## 3. 改动本仓库时的检查清单 改完代码准备提交前,过一遍: - [ ] 两棵树都改了?(同构文件需要同步) - [ ] 增删了 `.c`?→ 同步改 `.uvprojx` 与 `.uvoptx` - [ ] 移动了目录?→ 同步改 `.uvprojx`/`.uvoptx` 的文件路径与 `` - [ ] 工程引用的文件是否都真实存在?磁盘上的 `.c` 是否都被工程引用(没有掉出编译)? - [ ] 动过 `modules/imu/qekf/`?→ 先读 `imu/attitude.c`、`imu/ins.c` 文件头约定 - [ ] 姿态相关改动?→ 在 `README §8` 的字段里想清楚**怎么验证**(哪个字段能证明它对了) - [ ] 改了 `robot_param.h` 里带"比赛请改 0"的开关?→ 记得改回来