244 lines
8.4 KiB
Markdown
244 lines
8.4 KiB
Markdown
# 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`。
|
||
|
||
这是当前这两套代码下,改动最小、收益最大、工程风险也最低的方案。
|