本文统一使用 MCU(Microcontroller Unit) 表示雷达 CAN FD 报文的接收与处理节点。
在系统架构语境中,该 MCU 可能承担域控制器(DCU)的角色,但在本文讨论的 CAN 接收、AUTOSAR 链路与 Object List 拼装问题上,两者指代同一执行实体。
1. 从 CAN 报文进入 MCU 开始,问题才真正出现
在系统设计评审中,经常会看到一种"错觉":只要 CAN FD 带宽算够、报文能收到,雷达数据就算接进来了。
但在真实工程中,CAN 报文进入 MCU 只是物理层和驱动层的完成,离"可被感知算法使用"还隔着一整套软件语义重建过程。
一次完整的雷达 Object 更新周期,在 MCU 内部通常会经历如下路径:
CAN FD Rx ISR
CanIf
PduR
COM / Decode
Object List Assembler
Consistency Check
Validated Object Buffer
Perception / Fusion
这里存在一个非常重要但容易被忽略的事实:
CAN 报文是"帧级事件",
Radar Object List 是"周期级结果"。
MCU 的职责,不是"尽快处理每一帧 CAN 报文",
而是在正确的时间点,向上层提供一个完整、一致、可信的 Object List 快照。
2. MCU 为什么必须先知道"这一周期应该有多少 Object"
在 Object List 拼装逻辑中,expected_obj_num 是一个绕不开的变量。它的存在,解决的是一个问题:
MCU 如何判断"这一轮雷达数据是否已经收全"?
2.1 expected_obj_num 的来源
工程上的共识:
Object 数量只能由雷达声明,不能由 MCU 推测。
在主流量产设计中,雷达会在 Object List Header 报文中明确给出:
| Header 字段 | 含义 |
|---|---|
| Measurement Cycle Counter | 本次感知周期编号 |
| Object Count | 本周期有效 Object 数 |
| Timestamp | 雷达时间基准 |
| Alive Counter | 功能安全 |
| CRC | 报文完整性 |
当 MCU 接收到 Header 后,会立即建立本周期的"期望模型":
c
current_cycle_id = header.cycle_counter;
expected_obj_num = header.object_count;
received_obj_num = 0;
从这一刻开始,MCU 不再是"被动接收 CAN 帧",
而是进入一种验证雷达声明是否成立的状态。
3. Object List 在 MCU 内部是如何被逐步拼装的
3.1 拼装的基本原则
MCU 内部的 Object List 拼装遵循三个非常工程化的原则:
- 以 Header 为周期起点
- 以 Object Count 作为完成条件
- 以周期一致性作为第一优先级
一个典型的拼装缓冲区如下:
c
struct ObjectListBuffer {
uint16_t cycle_id;
uint8_t expected_obj_num;
uint8_t received_obj_num;
Object objects[MAX_OBJ];
};
3.2 Object 数据如何进入缓冲区
每当 MCU 收到一帧 Object 数据 CAN FD 报文时,并不会立刻"处理 Object",而是:
- 校验该帧的 Cycle Counter 是否与当前周期一致
- 解析 Object Index / Slot
- 将 Object 数据写入对应位置
- 累加
received_obj_num
这一阶段的代码特征通常是:
- 不做融合
- 不做决策
- 不触发算法
它的唯一目标是: 在 MCU 内部还原出雷达"本周期完整结果"的原始形态。
4. 一致性校验:为什么 MCU 宁可丢一个周期
当 received_obj_num == expected_obj_num 时,
很多新手会认为"Object List 已经完成了"。
但在工程上,这只是进入一致性校验的起点。
4.1 常见校验项
| 校验项 | 工程意义 |
|---|---|
| Cycle Counter 连续 | 防止周期交叉 |
| Object Index 唯一 | 防止重复或覆盖 |
| 数量匹配 | 防止丢帧 |
| 周期超时 | 防止雷达或总线异常 |
4.2 为什么不能"部分使用"
假设雷达声明本周期有 20 个 Object,但 MCU 实际只收到了 18 个:
- 这 2 个缺失目标,可能正好是前车或行人
- 对 Fusion / Tracking 来说,这是语义错误,而不是精度下降
因此,量产系统中普遍采用的策略是:
只要 Object List 不完整,整个周期直接作废。
这是系统稳定性的底线,而不是保守设计。
5. Object List 是如何被 MCU 提供给上层消费的
当 Object List 通过一致性校验后,MCU 才会将其提交到"可消费区域"。
Fusion / Tracking Validated Buffer Assembler Fusion / Tracking Validated Buffer Assembler Commit Object List Read (周期触发) Stable Snapshot
这里有两个非常重要的工程设计选择:
5.1 消费是"周期驱动",而不是"CAN 驱动"
- CAN 接收中断:高频、短路径
- Object List 提交:低频、强语义
因此,MCU 通常会通过:
- OS Task
- Runnable
- Event Flag
来触发上层算法,而不是在 CAN ISR 或 Rx 回调中直接调用。
5.2 双缓冲几乎是标配
| 机制 | 目的 |
|---|---|
| Double Buffer | 避免读写冲突 |
| Ring Buffer | 支持延迟消费 |
这一步,决定了系统在高 CAN 负载和算法高耗时场景下是否还能稳定运行。
总结(工程视角)
可以用一句话总结整个流程:
CAN 帧只是进入 MCU 的数据碎片,
Object List 才是 MCU 向上层交付的"感知事实"。
雷达负责"声明世界",
MCU 负责"验证声明是否成立",
算法只面对稳定、完整、同一时刻的世界快照。