This commit is contained in:
2026-04-08 07:38:46 +08:00
parent a9ec02dc93
commit fda081c1f0
51 changed files with 51337 additions and 0 deletions

243
修改建议报告.md Normal file
View File

@@ -0,0 +1,243 @@
# CAN/里程计频率修改建议报告
## 1. 结论
建议采用以下组合修改,而不是单独只改一边:
1. 下位机 `FDR-Core``0x200 Odom Delta` 从当前约 `60ms` 提升到 `20ms` 固定发送。
2. 上位机 `ARES``0x200` 的消费与 `Odom_Update()``monitorTask(100ms)` 挪到 `navTask(20ms)`
3. `0x182/0x183` 保持低频轮询即可,不需要跟着一起提到 `20ms`
4. 当前阶段不建议把 `0x200` 进一步提到 `10ms`,先做到 `20ms` 即可。
一句话概括:当前 odom 不够实时,不是总线带宽不够,而是“下位机发送慢 + 上位机消费慢”叠加导致的;最合适的修法是把两端都对齐到 `20ms`
## 2. 本次核对到的事实
### 2.1 下位机当前真实行为
下位机工程:`C:\Users\Falling_jasmine\CLionProjects\FDR-Core`
实际实现文件:`Core/Src/f4_can_app.c`
关键实现如下:
1. `CAN_Send_Telemetry_20ms()``20ms` 调一次。
2. `0x181` 状态帧固定每次都发。
3. `0x182 / 0x183 / 0x200` 通过 `s_telem_slot` 三选一轮询发送。
4. 所以 `0x200` 实际发送周期约为 `60ms`,不是 `20ms`
5. `0x184` 通过 `s_comm_diag_divider``100ms` 发送一次。
对应代码位置:
1. `C:\Users\Falling_jasmine\CLionProjects\FDR-Core\Core\Src\f4_can_app.c:925-966`
2. `C:\Users\Falling_jasmine\CLionProjects\FDR-Core\Core\Src\f4_can_app.c:907-923`
另外,下位机头文件注释和实际实现已经出现漂移:
1. `f4_can_app.h` 注释写的是 `0x181/0x182/0x183/0x200` 都在 `20ms` 周期发送。
2. 但真实代码并不是这样,而是只有 `0x181` 固定 `20ms`
对应位置:
1. `C:\Users\Falling_jasmine\CLionProjects\FDR-Core\Core\Inc\f4_can_app.h:165-173`
2. `C:\Users\Falling_jasmine\CLionProjects\FDR-Core\Core\Src\f4_can_app.c:925-966`
### 2.2 下位机 odom 数据源没有问题
下位机 `motor.c` 中,编码器增量在控制周期中持续累加到 `s_odom_acc_ticks[]`
1. 每次电机更新时都会把四轮增量累加到 odom 累加器。
2. `CAN_Send_OdomFrame()` 发送 `0x200` 时,通过 `Motor_Get_And_Clear_Delta_Ticks()` 原子取走并清零。
这说明:
1. `0x200` 本质是“自上次发送以来的积分增量”。
2. 如果把 `0x200` 发送频率从 `60ms` 提高到 `20ms`,语义仍然正确,只是每帧覆盖的时间窗变短、实时性更高。
对应位置:
1. `C:\Users\Falling_jasmine\CLionProjects\FDR-Core\Core\Src\motor.c:225-245`
2. `C:\Users\Falling_jasmine\CLionProjects\FDR-Core\Core\Src\motor.c:263-295`
3. `C:\Users\Falling_jasmine\CLionProjects\FDR-Core\Core\Src\f4_can_app.c:907-923`
### 2.3 上位机当前真实行为
上位机工程:`D:\ARES`
当前 `0x200` 的消费模型是:
1. CAN ISR 收到每帧 `0x200` 后,不直接覆盖,而是累加到 `odom_accum`
2. `monitorTask``100ms` 调用 `SNC_CAN_ConsumeOdomDelta()` 原子取走累计值。
3. 然后再调用 `Odom_Update()` 推送黑板。
4. `navTask` 本身跑在 `20ms`,但它读到的 `board.odom_vx` 很多拍都是旧值。
对应位置:
1. `D:\ARES\App\Can\snc_can_app.c:185-225`
2. `D:\ARES\App\app_tasks.c:145-167`
3. `D:\ARES\App\app_tasks.c:295-340`
## 3. 问题本质
当前 odom 实时性差,来自两个串联环节:
1. 下位机发送 `0x200` 只有约 `60ms` 一次。
2. 上位机又只在 `100ms``monitorTask` 中消费一次。
所以当前 `odom_vx` 的有效刷新节拍,不是 `navTask``20ms`,而是被这两个环节一起拖慢了。
这会直接影响:
1. `CorridorEKF_Predict()``odom_vx` 的实时性。
2. 沿程 `s` 的连续性。
3. 车体加减速或转向阶段时,状态传播的平滑度。
## 4. 是否需要把 CAN 提得更高频
当前不建议先追求高于 `20ms` 的 CAN 频率。
原因:
1. 上位机 `navTask` / EKF 当前就是 `20ms / 50Hz`
2. `0x100` 速度命令协议推荐节拍也是 `20ms`
3. `0x200` 先做到 `20ms`,就已经与主导航闭环对齐。
4. 再往 `10ms` 提,边际收益明显变小,且会引入更多调度与验证成本。
在你当前系统中,更合理的目标是:
1. `0x100` 保持 `20ms`
2. `0x181` 保持 `20ms`
3. `0x200` 提到 `20ms`
4. `0x182/0x183/0x184` 维持低频
## 5. 总线负载是否会成为问题
基于你补充的信息:总线上只有两个单片机,且 CAN 速率为 `1 Mbps`
在这个前提下,把 `0x200` 提到 `20ms` 是合理的,风险较低。
主要原因:
1. 当前实时关键帧就只有 `0x100``0x181``0x200`
2. 即便这三类帧都在 `20ms` 发送,总线占用通常仍远低于饱和。
3. 下位机当前发送代码本身也把遥测帧视为“允许丢”的低优先级消息,不会因短时发不出去而阻塞主循环。
对应代码:
1. `C:\Users\Falling_jasmine\CLionProjects\FDR-Core\Core\Src\f4_can_app.c:808-821`
## 6. 推荐修改方案
### 6.1 下位机修改建议
修改文件:
1. `C:\Users\Falling_jasmine\CLionProjects\FDR-Core\Core\Src\f4_can_app.c`
2. `C:\Users\Falling_jasmine\CLionProjects\FDR-Core\Core\Inc\f4_can_app.h`
修改目标:
1. `0x181``20ms` 固定发送。
2. `0x200``20ms` 固定发送。
3. `0x182/0x183` 改成低频轮询发送。
4. `0x184` 继续 `100ms`
最小改法建议如下:
1. 保留 `CAN_Send_StatusFrame()` 每拍都发。
2.`CAN_Send_Telemetry_20ms()` 中,每拍额外固定调用 `CAN_Send_OdomFrame()`
3. 把原来的 `s_telem_slot` 轮询从 `0x182/0x183/0x200` 三选一,改成只在 `0x182/0x183` 两者之间轮询。
4. 更新头文件中的注释,避免文档/实现再次漂移。
推荐把 `CAN_Send_Telemetry_20ms()` 的逻辑改成:
1. 先发 `0x181`
2. 再发 `0x200`
3. 再按 slot 发 `0x182``0x183`
4. 每 5 拍发一次 `0x184`
这样改的优点:
1. `0x200` 得到稳定 `20ms` 周期。
2. `0x182/0x183` 仍然保留,但不抢实时关键链路。
3. 代码改动很小,不需要碰控制内环和接收协议。
### 6.2 上位机修改建议
修改文件:
1. `D:\ARES\App\app_tasks.c`
修改目标:
1. `SNC_CAN_ConsumeOdomDelta()` 不再由 `monitorTask(100ms)` 调用。
2. odom 消费与 `Odom_Update()` 挪到 `navTask(20ms)`
具体建议:
1. 删除 `AppTasks_RunMonitorTask()``145-167` 这段 odom 消费代码。
2.`AppTasks_RunNavTask_Impl()` 中、`Blackboard_GetSnapshot(&board)` 之前插入同样的 odom 消费逻辑。
3. 保持 `SNC_CAN_ConsumeOdomDelta()` 全系统只有一个消费者。
推荐新顺序:
1. `now_ms = HAL_GetTick()`
2. `SNC_CAN_ConsumeOdomDelta(...)`
3. `Odom_Update(...)``Odom_HandleTimeout(...)`
4. `Blackboard_GetSnapshot(&board)`
5. `CorridorPreproc_ExtractObs(...)`
6. `CorridorFilter_Update(...)`
这样可以保证:
1. `navTask` 每一拍都能第一时间消费已到达的 `0x200`
2. 当下位机把 `0x200` 提到 `20ms` 后,上位机不会再被 `100ms monitorTask` 卡住。
## 7. 不建议的修改方式
### 7.1 只改上位机,不改下位机
这样只能把 odom 的消费延迟从 `100ms` 降下来,但 `0x200` 源头仍是约 `60ms`,收益有限。
### 7.2 只改下位机,不改上位机
这样虽然 `0x200` 更快到达了,但上位机仍在 `100ms` 才取走一次,实时性改善不完整。
### 7.3 直接把 `0x200` 提到 `10ms`
现阶段不建议。原因:
1. 上位机 `navTask` 仍是 `20ms`
2. EKF 和控制链路当前主频也是 `20ms`
3. 先把 `20ms` 跑顺、验证完,再决定是否需要更高频更合理。
## 8. 推荐实施顺序
建议按下面顺序改:
1. 先改下位机 `f4_can_app.c`,让 `0x200` 固定 `20ms` 发送。
2. 同步修正下位机 `f4_can_app.h` 注释。
3. 再改上位机 `app_tasks.c`,把 odom 消费搬进 `navTask`
4. 实车或日志确认 `0x200` 到达间隔、`odom_vx` 刷新节拍和 EKF 状态表现。
## 9. 验收建议
改完后至少验证以下几点:
1. 上位机收到的 `0x200` 时间戳间隔是否接近 `20ms`
2. `SNC_CAN_ConsumeOdomDelta()` 是否只在 `navTask` 被调用一次。
3. `board.odom_vx` 是否不再长时间停留旧值。
4. `EKF``s``e_y` 传播是否更连续。
5. `s_can_tx_drop_total` 是否没有明显上升。
6. `0x181``0x184``0x100` 协议行为是否保持兼容。
## 10. 最终建议
最终建议如下:
1. 下位机把 `0x200` 改成 `20ms` 固定发送。
2. 上位机把 odom 消费改到 `navTask(20ms)`
3. `0x182/0x183` 保持低频,不必提频。
4. 先不要把 `0x200` 提到 `10ms`
这是当前这两套代码下,改动最小、收益最大、工程风险也最低的方案。