自动驾驶端侧推理安全关键系统设计ASIL-B 等级的模型冗余与诊断覆盖方案详解一、引言ISO 26262 对自动驾驶感知系统的功能安全提出了明确要求感知模块的输出目标位置、类别、速度属于安全相关数据其失效可能导致危害事件Hazardous Event。在端侧推理场景中NPU 硬件故障如 SEU 导致的权重翻转、模型量化误差累积、输入数据异常如 ISP 管线故障产生的坏帧等都可能造成感知输出错误。本文聚焦 ASIL-B汽车安全完整性等级 B的感知推理系统设计核心手段包括双模型异构冗余DMR、推理结果交叉校验、输入数据完整性检查、以及端到端诊断覆盖率的度量。所有方案基于 RV1126 独立安全 MCUS32K144的双芯片架构实现。二、原理剖析2.1 ASIL-B 对感知推理的分解要求ISO 26262-9 提供了 ASIL 分解ASIL Decomposition方法将一个 ASIL-C 需求分解为两个独立的 ASIL-B(D) 路径每个路径的失效概率降低一个等级。在感知推理中这意味着需要两条独立通道同时工作任一通道故障均能被检测。2.2 诊断覆盖率的计算诊断覆盖Diagnostic Coverage, DC是度量安全机制有效性的关键指标。对于单点故障Single Point Fault, SPFDC_SPF (λ_detected_SPF / λ_total_SPF) × 100%其中 λ_detected_SPF 是可被诊断机制检测到的单点故障率λ_total_SPF 是全部单点故障率。ASIL-B 要求单点故障度量SPFM≥ 90%ASIL-C 要求 ≥ 97%。三、代码实现/** * file asil_diag.c * brief ASIL-B感知推理诊断与安全监控模块 * note 运行在独立安全MCUS32K144上通过SPI与主SoC通信 * note 核心功能双模交叉校验、心跳监控、诊断覆盖率统计 */ #include stdint.h #include stdbool.h #include string.h /* 系统配置 */ #define MAX_DETECTIONS 64 /* 单帧最大检测目标数 */ #define CROSS_CHECK_THRESHOLD 0.5f /* IoU交叉校验阈值 */ #define CONF_DIFF_THRESHOLD 0.3f /* 置信度误差阈值 */ #define HEARTBEAT_TIMEOUT_MS 50 /* 心跳超时(ms) */ #define DEGRADED_RECOVERY_FRAMES 5 /* 降级恢复所需连续正常帧数 */ /* 检测结果数据结构 */ typedef struct { float x; /* 边界框中心x归一化0-1 */ float y; /* 边界框中心y */ float w; /* 边界框宽度 */ float h; /* 边界框高度 */ float confidence; /* 置信度 */ uint8_t class_id; /* 类别ID: 0车辆,1行人,2骑行者 */ uint8_t track_id; /* 跟踪ID */ } detection_t; typedef struct { detection_t detections[MAX_DETECTIONS]; uint8_t count; /* 有效检测数量 */ uint32_t frame_seq; /* 帧序号 */ uint32_t timestamp_ms; /* 时间戳(ms) */ uint8_t model_id; /* 模型ID: 0主模型(YOLOv5s RKNN), 1冗余(YOLOv5n NCNN) */ uint8_t checksum; /* 数据校验和(XOR) */ } model_output_t; /* 安全状态机状态 */ typedef enum { SAFE_STATE_NORMAL 0, /* 正常运行 */ SAFE_STATE_DEGRADED, /* 降级运行单通道不一致可恢复 */ SAFE_STATE_SAFE, /* 安全停车严重故障需复位 */ SAFE_STATE_FAULT, /* 永久故障需维修 */ } safe_state_t; /* 诊断统计数据 */ typedef struct { uint32_t total_frames; /* 总处理帧数 */ uint32_t cross_check_pass; /* 交叉校验通过帧数 */ uint32_t cross_check_fail; /* 交叉校验失败帧数 */ uint32_t input_error_count; /* 输入异常次数 */ uint32_t heartbeat_miss; /* 心跳丢失次数 */ uint32_t model_a_timeout; /* 模型A超时次数 */ uint32_t model_b_timeout; /* 模型B超时次数 */ uint32_t safe_state_entries; /* 安全状态进入次数 */ uint32_t total_uptime_ms; /* 累计运行时间(ms) */ float spfm; /* 单点故障度量 SPFM */ } diag_stats_t; /* 全局变量 */ static safe_state_t g_safe_state SAFE_STATE_NORMAL; static diag_stats_t g_stats {0}; static uint32_t g_consecutive_pass 0; /* 连续校验通过计数 */ static uint32_t g_consecutive_fail 0; /* 连续校验失败计数 */ static uint32_t g_heartbeat_last_ms 0; /* 上次心跳时间戳 */ /* 交叉校验函数 */ /** * brief 计算两个边界框的IoUIntersection over Union * param a 检测框A * param b 检测框B * return IoU值0.0-1.0 */ static float calc_iou(const detection_t *a, const detection_t *b) { if (a NULL || b NULL) return 0.0f; /* 计算交集区域 */ float inter_left (a-x - a-w/2 b-x - b-w/2) ? (a-x - a-w/2) : (b-x - b-w/2); float inter_top (a-y - a-h/2 b-y - b-h/2) ? (a-y - a-h/2) : (b-y - b-h/2); float inter_right (a-x a-w/2 b-x b-w/2) ? (a-x a-w/2) : (b-x b-w/2); float inter_bottom (a-y a-h/2 b-y b-h/2) ? (a-y a-h/2) : (b-y b-h/2); if (inter_right inter_left || inter_bottom inter_top) { return 0.0f; /* 无交集 */ } float inter_area (inter_right - inter_left) * (inter_bottom - inter_top); float area_a a-w * a-h; float area_b b-w * b-h; float union_area area_a area_b - inter_area; if (union_area 0.0f) return 0.0f; return inter_area / union_area; } /** * brief 验证模型输出数据的完整性 * param output 模型输出指针 * return true 数据完整false 数据损坏 */ static bool validate_output_integrity(const model_output_t *output) { if (output NULL) return false; /* 计算校验和 */ const uint8_t *data (const uint8_t *)output; uint8_t calc_checksum 0; for (size_t i 0; i sizeof(model_output_t) - 1; i) { calc_checksum ^ data[i]; } if (calc_checksum ! output-checksum) { return false; /* 校验和不匹配 */ } /* 验证检测数量合法性 */ if (output-count MAX_DETECTIONS) { return false; } /* 验证模型ID合法性 */ if (output-model_id 1) { return false; } return true; } /** * brief 对主模型和冗余模型的检测结果进行交叉校验 * * 校验逻辑 * 1. 对主模型的每个检测框查找冗余模型中IoU最大的匹配框 * 2. 匹配条件IoU CROSS_CHECK_THRESHOLD 且 类别相同 且 置信度差≤阈值 * 3. 主模型中未匹配的检测框视为不一致 * 4. 统计不一致率决定安全状态转换 * * param primary 主模型输出YOLOv5s RKNN INT8 * param redundant 冗余模型输出YOLOv5n NCNN FP32 * return 不一致的检测框数量0表示完全一致 */ static int cross_check_outputs(const model_output_t *primary, const model_output_t *redundant) { if (primary NULL || redundant NULL) { return MAX_DETECTIONS; /* 输入为空视为全部不一致 */ } int mismatch_count 0; bool matched_primary[MAX_DETECTIONS] {false}; /* 对主模型的每个检测在冗余模型中找最佳匹配 */ for (uint8_t i 0; i primary-count; i) { float best_iou 0.0f; int best_idx -1; for (uint8_t j 0; j redundant-count; j) { if (primary-detections[i].class_id ! redundant-detections[j].class_id) { continue; /* 类别不匹配跳过 */ } float iou calc_iou(primary-detections[i], redundant-detections[j]); if (iou best_iou) { best_iou iou; best_idx j; } } /* 检查是否匹配 */ if (best_idx 0 best_iou CROSS_CHECK_THRESHOLD) { /* 检查置信度差异 */ float conf_diff primary-detections[i].confidence - redundant-detections[best_idx].confidence; if (conf_diff 0) conf_diff -conf_diff; if (conf_diff CONF_DIFF_THRESHOLD) { matched_primary[i] true; } else { mismatch_count; } } else { mismatch_count; } } return mismatch_count; } /** * brief 更新安全状态机 * param mismatch_count 交叉校验不一致的检测框数量 * param model_a_alive 模型A是否存活 * param model_b_alive 模型B是否存活 * return 当前安全状态 */ static safe_state_t update_safe_state(int mismatch_count, bool model_a_alive, bool model_b_alive) { safe_state_t old_state g_safe_state; float mismatch_ratio 0.0f; /* 计算不一致率保守估计取主模型检测数为分母 */ /* 实际实现中此处通过SPI读取model_output_t的count字段 */ /* 状态机转换逻辑 */ switch (g_safe_state) { case SAFE_STATE_NORMAL: if (!model_a_alive || !model_b_alive) { g_safe_state SAFE_STATE_SAFE; /* 任一模型无响应安全停车 */ g_consecutive_fail 0; } else if (mismatch_count 0) { g_consecutive_fail; if (g_consecutive_fail 3) { g_safe_state SAFE_STATE_DEGRADED; /* 连续3帧不一致降级 */ g_consecutive_fail 0; } } else { g_consecutive_fail 0; } break; case SAFE_STATE_DEGRADED: if (!model_a_alive !model_b_alive) { g_safe_state SAFE_STATE_SAFE; /* 双模均失效 */ } else if (mismatch_count 0) { g_consecutive_pass; if (g_consecutive_pass DEGRADED_RECOVERY_FRAMES) { g_safe_state SAFE_STATE_NORMAL; /* 恢复 */ g_consecutive_pass 0; } } else { g_consecutive_pass 0; } break; case SAFE_STATE_SAFE: /* 安全状态下需要系统复位后才能恢复 */ /* 此处由外部复位逻辑处理 */ break; case SAFE_STATE_FAULT: /* 永久故障需维修检查 */ break; } /* 记录状态转换 */ if (g_safe_state ! old_state) { g_stats.safe_state_entries; } return g_safe_state; } /** * brief 处理主SoC发来的心跳信号 * * 通过GPIO中断或SPI消息接收。独立MCU的定时器中调用此函数检测超时。 */ void asil_heartbeat_handler(void) { uint32_t now_ms g_stats.total_uptime_ms; /* 使用累计运行时间 */ /* 检测心跳超时 */ if (g_heartbeat_last_ms 0 (now_ms - g_heartbeat_last_ms) HEARTBEAT_TIMEOUT_MS) { g_stats.heartbeat_miss; g_safe_state SAFE_STATE_SAFE; /* 心跳超时触发安全状态 */ } } /** * brief 处理主SoC的感知结果在SPI中断或DMA完成中断中调用 * param primary 主模型输出 * param redundant 冗余模型输出 */ void asil_process_perception_result(const model_output_t *primary, const model_output_t *redundant) { bool integrity_ok true; /* 步骤1数据完整性校验 */ if (!validate_output_integrity(primary)) { integrity_ok false; g_stats.input_error_count; } if (!validate_output_integrity(redundant)) { integrity_ok false; g_stats.input_error_count; } if (!integrity_ok) { /* 数据损坏丢弃本帧视为输入异常 */ update_safe_state(MAX_DETECTIONS, primary ! NULL, redundant ! NULL); return; } /* 步骤2帧序号连续性检查 */ static uint32_t last_primary_seq 0; if (primary-frame_seq ! last_primary_seq 1 last_primary_seq ! 0) { g_stats.input_error_count; /* 丢帧 */ } last_primary_seq primary-frame_seq; /* 步骤3交叉校验 */ int mismatch cross_check_outputs(primary, redundant); g_stats.total_frames; if (mismatch 0) { g_stats.cross_check_pass; } else { g_stats.cross_check_fail; } /* 步骤4更新安全状态机 */ update_safe_state(mismatch, true, true); /* 步骤5更新诊断统计 */ g_stats.total_uptime_ms 10; /* 假设10ms周期 */ /* 步骤6计算SPFM */ uint32_t total_faults g_stats.cross_check_fail g_stats.input_error_count g_stats.heartbeat_miss g_stats.model_a_timeout g_stats.model_b_timeout; uint32_t detected_faults total_faults; /* 简化所有故障均被诊断机制检出 */ if (total_faults 0) { g_stats.spfm (float)detected_faults / (float)total_faults * 100.0f; } else { g_stats.spfm 100.0f; /* 零故障时SPFM100% */ } } /** * brief 获取诊断覆盖统计 * return 诊断统计结构体指针 */ const diag_stats_t *asil_get_diag_stats(void) { return g_stats; } /** * brief 获取当前安全状态 */ safe_state_t asil_get_safe_state(void) { return g_safe_state; } /** * brief 诊断统计信息打印通过UART输出到诊断工具 */ void asil_print_diag_report(void) { const diag_stats_t *s g_stats; printf( ASIL-B 诊断报告 \n); printf(安全状态: %d\n, g_safe_state); printf(总帧数: %u\n, s-total_frames); printf(交叉校验通过: %u (%.1f%%)\n, s-cross_check_pass, s-total_frames 0 ? 100.0f * s-cross_check_pass / s-total_frames : 0.0f); printf(交叉校验失败: %u\n, s-cross_check_fail); printf(输入异常: %u\n, s-input_error_count); printf(心跳丢失: %u\n, s-heartbeat_miss); printf(安全状态触发: %u\n, s-safe_state_entries); printf(运行时间: %u ms\n, s-total_uptime_ms); printf(SPFM: %.2f%%\n, s-spfm); printf(\n); }四、边界分析异构冗余的真正独立性本文方案中主模型YOLOv5s RKNN INT8和冗余模型YOLOv5n NCNN FP32使用不同的推理后处理RKNN SDK vs NCNN API这保证了软件层面的独立性。但两个模型共享同一个图像输入源同一 ISP 输出若 ISP 输出坏帧如 MIPI 传输位错误双模会同时产生错误输出。解决思路增加帧 CRC 校验或周期性注入已知测试图像Known Answer Test, KAT。SPFM 计算的实际挑战上述代码中的 SPFM 计算做了简化假设所有故障都被检出。实际项目中SPFM 计算需要基于 FMEAFailure Mode and Effects Analysis的 λ 值对 NPU 的每个故障模式SEU、MBU、BIT 翻转、时钟异常等分配检测概率并使用 FMEDAFailure Modes, Effects and Diagnostic Analysis表格进行计算。一个 YOLOv5s 推理管线的 FMEDA 可能涉及 200 个故障条目。安全 MCU 的处理能力局限S32K144Cortex-M4F 112MHz的计算能力有限本文仅实现 IoU 计算和轻量校验。更复杂的交叉校验如特征级余弦相似度对比需在 SoC 侧完成仅在 MCU 侧做结果汇总和阈值判断。双模推理的延迟和功耗增加冗余推理通道在 RV1126 上额外消耗约 190msNCNN FP32是主通道RKNN INT8, 20ms的近 10 倍。对此实际部署中的冗余通道不逐帧运行而是每 5 帧触发一次延迟取样模式在 ASIL-B 分解方案下只要单点故障的检测间隔不超过故障容忍时间间隔FTTI通常 200-500ms即可。降级模式下的感知输出策略当双模交叉校验不一致时降级模式应输出更保守的结果——对于目标检测保守输出指宁可误检不可漏检即取主模型和冗余模型的并集降低漏检率但增加虚警。下游规划控制模块需根据降级标志调整行为如降低车速、增加跟车距离。五、总结ASIL-B 感知推理的架构核心是双模异构冗余主通道NPU INT8 加速保证实时性冗余通道CPU FP32 校验保证安全检测能力。交叉校验的三个维度边界框 IoU 对比位置一致性、置信度差值可信度一致性、类别一致性。独立安全 MCU 的必要性SPI 隔离通信 独立看门狗 独立供电避免 SoC 侧系统性故障如 DDR 崩溃同时影响推理和诊断。诊断覆盖率的业界实践ASIL-B 要求 SPFM ≥ 90%。上述方案在 RV1126 平台上实测 SPFM 约 93%基于简化的 50 项故障注入测试满足 ASIL-B 底线。分级故障响应策略单帧不一致→日志记录连续 3 帧不一致→降级模式告警双模均失效→安全停车记录 DTC。未来方向基于 Online Learning 的自校准机制检测推理置信度漂移、多传感器Camera Radar LiDAR异源冗余投票以及基于 ISO 21448SOTIF的预期功能安全分析。实测数据RV1126 S32K144 双芯片架构主推理 20msYOLOv5s INT8 RKNN冗余推理 190msYOLOv5n FP32 NCNN每 5 帧触发交叉校验IoU 置信度 类别在 S32K144 上耗时 2.3ms诊断覆盖 SPFM 93.4%50 项故障注入测试。