Files
Simba-OmniInfantry/doc/已知问题与排障.md
T
LSMushui 7c25e06947 docs: 重写 README,新增排障手册,补板级说明
原 README.md 是上游框架模板的拷贝:两棵树内容完全相同,描述的是"计划要做的
模块"(一堆未勾选的 checkbox),且大量链接指向根本不存在的 ./doc/*.md
(framework/注意事项/API/LOG/TODO/reference/appendix/questions 全都没有)。
它没有描述本项目实际重构后的代码。

本次文档落地为三份:

1. README.md(根,详细)
   - 项目概览(双板架构、两板同构、QEKF、双 INS 跟随方案)
   - 硬件与工具链、快速开始(编译烧录 + 上电前置检查)
   - 代码地图(Application/Bsp/Core/modules/MDK-ARM 逐层说明)
   - 运行架构(9 个任务的优先级/栈/职责表、控制周期、IWDG 看门狗)
   - 数据流与对外接口(CAN 分配、串口分配、CToC 帧表、姿态数据流)
   - 模块清单、USB CDC 遥测字段详解、调参入口与三个台架开关
   - 上电时序与灯语/蜂鸣器语义
   - 已知陷阱(整合了本次排障的全部硬知识)
   - 版本管理约定、致谢与联系

2. doc/已知问题与排障.md(新增)
   排障总则(先看 ca/cg 而非数值;用算式交叉验证)+ 症状速查表 + 6 个典型
   案例详解 + 提交前检查清单。把"踩过的坑"沉淀成可复查的手册。

3. 云台/README.md、底盘/README.md(重写为简短的板级说明)
   只讲本板特有的职责、编译宏、独占模块、上电特有行为、蜂鸣器序号,并指回
   根 README;顺便去掉指向不存在文件的死链。

另修一处文档缺陷:modules/comms/c_comm.h 里 0x1A9 帧被写了两遍,且两处对
Data[4] 的描述互相矛盾(一处说只有 att_ready,一处说还有编码器增向与自检位),
已合并为一份准确描述。
2026-09-12 11:53:50 +08:00

161 lines
9.0 KiB
Markdown

# 已知问题与排障手册
本文记录本项目实际踩过的坑、判断依据和处理方式。**遇到姿态/通讯异常时先读这里。**
---
## 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` 的 `<FileNumber>` 断号 | 删除条目后把末尾编号改为连续值 |
| 移动目录后编译不过 | `<IncludePath>` 没跟着改 | `.h` 不登记在工程里,靠 IncludePath 定位;移动目录时同步改 |
| CubeMX 重新生成后行为变了 | 手改过的生成文件被覆盖 | 复查 `startup_stm32f407xx.s` 的 `Heap_Size` 与 `Core/Src/gpio.c` 的 INT1 触发沿 |
---
## 3. 改动本仓库时的检查清单
改完代码准备提交前,过一遍:
- [ ] 两棵树都改了?(同构文件需要同步)
- [ ] 增删了 `.c`?→ 同步改 `.uvprojx` 与 `.uvoptx`
- [ ] 移动了目录?→ 同步改 `.uvprojx`/`.uvoptx` 的文件路径与 `<IncludePath>`
- [ ] 工程引用的文件是否都真实存在?磁盘上的 `.c` 是否都被工程引用(没有掉出编译)?
- [ ] 动过 `modules/imu/qekf/`?→ 先读 `imu/attitude.c`、`imu/ins.c` 文件头约定
- [ ] 姿态相关改动?→ 在 `README §8` 的字段里想清楚**怎么验证**(哪个字段能证明它对了)
- [ ] 改了 `robot_param.h` 里带"比赛请改 0"的开关?→ 记得改回来