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

9.0 KiB

已知问题与排障手册

本文记录本项目实际踩过的坑、判断依据和处理方式。遇到姿态/通讯异常时先读这里。


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
角度恒定不动(有值但不变) 数据路停摆(数据冻结) ca/cg;§2.2
yaw 匀速自漂 z 零偏标定被运动污染 zo vs gz,漂移率 vs gn;§2.3
角度是垃圾值 / yaw≈180 矩阵运算静默失败 cq 字段、Heap_Size;§2.1
蜂鸣器连续滴滴响 CAN 设备掉线报警(正常行为) 响几下 = 第几号设备掉线;§2.4
红灯常亮 CAN 设备掉线 同上
绿灯连闪两下 BMI088 失败 imu
反复重启(约 3~5 s 一次) 初始化超时 / 任务卡死 IWDG 5 s 预算;§2.5
串口收不到整帧 空闲中断 / DMA 未重启 bsp_usart.c

1. 工具与判据

1.1 USB CDC 遥测字段

完整字段说明见根 README §8。排障最常用的几个:

字段 关键读法
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。蜂鸣器的"响几下"直接对应掉线设备序号 + 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"的开关?→ 记得改回来