selfdrived 动态告警:49 个槽位与软退出的真实边界

作者:

在

结论:在 EVENTS 主映射里共有 49 个回调槽位、22 种回调形式:两个工厂覆盖 27 槽位,20 个直接函数覆盖 22 槽位。回调在 creation_delay 延迟过滤之前执行,即使告警最终未显示也会读取输入、构造文本,且这里没有逐回调异常隔离。更关键的是,回调返回的 ImmediateDisableAlert 只改变文案、声音、视觉标记、优先级与持续时间,是否立刻退出仍由事件类型决定,而不是由告警子类名里有 Immediate 决定。本文为固定源码快照 c8fb906 的静态阅读,未实测墙上时间与声音输出延迟。

调用链:从输入到仲裁

SelfdriveD.update_alerts() 提供六个实参:CP、CS、sm、is_metric、soft_disable_timer 和驾驶风格枚举字符串。Events.create_alerts() 只处理当前状态允许的事件类型:值若是 Alert 实例则直接使用,否则调用回调创建新实例。随后按连续帧计数判断 creation_delay,写入 EventName/类型 键和事件类型。

环节 行为 边界
update_alerts() 传入 CP、CS、sm、公英制、软退出计时与驾驶风格 输入字段来自当前 SubMaster 消息,跨主题时序需回放确认
create_alerts() 只处理当前状态允许的事件类型;静态 Alert 直接用,否则调用回调 回调在 creation_delay 过滤前执行,无逐回调异常隔离
AlertManager.add_many() 以 EventName/类型 键保存告警及结束帧 「事件已出现」与「UI 当前展示」可能不同步
process_alerts() 按优先级、起始帧选当前项 最终仲裁仍在受控核心,不由插件决定

这里有一个容易被忽略的顺序细节:create_alerts() 在判断 creation_delay 之前就已经执行了回调,因此告警文案与动态数值是在过滤之前构造的。若某回调依赖尚未更新的输入,它可能读到上一帧的字段;反过来,一个最终因延迟未显示的告警,其回调副作用——例如读取 sm、构造字符串——仍已发生。由于没有逐回调异常隔离,单个回调抛错会影响同一容器内的处理顺序,这一点在插件化时必须由受控核心兜底。

两类软退出工厂:27 槽位

软退出相关的槽位由两个工厂覆盖,共 27 个。soft_disable_alert() 覆盖 24 个事件,包括 bigModelFailed、cameraMalfunction、cameraFrameRate、locationdTemporaryError、paramsdTemporaryError、steerTempUnavailable、steerTimeLimit、sensorDataInvalid、excessiveActuation、overheat、calibrationInvalid、calibrationIncomplete、calibrationRecalibrating、espDisabled、commIssue、commIssueAvgFreq、selfdrivedLagging、processNotRunning、radarFault、radarTempUnavailable、modeldLagging、posenetInvalid、lowMemory、espActive;user_soft_disable_alert() 覆盖 3 个:wrongGear、doorOpen、seatbeltNotLatched。

回调形式 槽位数与事件 运行结果
soft_disable_alert() 24 个(含 overheat、radarFault、modeldLagging、lowMemory 等) 软退出计数低于 int(0.5/DT_CTRL) 时构造 ImmediateDisableAlert 外观,否则构造 SoftDisableAlert
user_soft_disable_alert() 3 个:wrongGear、doorOpen、seatbeltNotLatched 同样在剩余约 0.5 秒改成紧急外观,之前使用文案较缓和的 UserSoftDisableAlert

关键区别在于:回调返回的 ImmediateDisableAlert 只改变告警的文案、声音、视觉标记、优先级与持续时间,Events.create_alerts() 仍将 event_type 标为映射键 SOFT_DISABLE。StateMachine.update() 是否立刻退出由事件类型决定,不会因为 Alert 子类名含 Immediate 就改走 IMMEDIATE_DISABLE。计数按 DT_CTRL=0.01 的名义周期换算,墙上时间和声音输出延迟尚未实测。

20 个直接回调:22 槽位

回调与主映射事件 消费字段和动态内容
joystick_alert():joystickDebug.WARNING 从 carControl.actuators.accel/torque 计算调试文案;不是实车执行反馈
longitudinal_maneuver_alert():longitudinalManeuver.WARNING、lateralManeuver.WARNING 两个事件都读 alertDebug 的两行文案;横向事件由 lateralManeuverPlan 触发但回调仍读 alertDebug
invalid_lkas_setting_alert():invalidLkasSetting.PERMANENT 根据 Tesla/Mazda/Nissan 品牌调整原车 LKAS 说明,其余用通用文案
paramsd_invalid_alert():paramsdTemporaryError.NO_ENTRY 按 angleOffsetValid、steerRatioValid、stiffnessFactorValid 顺序挑第一个错误并附数值
camera_malfunction_alert():cameraMalfunction.PERMANENT 对三路摄像头逐个调用 sm.all_checks([service]) 拼出失败名称
overheat_alert():overheat.PERMANENT 取 CPU、GPU、内存温度的最大值显示;空列表用 0 兜底
comm_issue_alert():commIssue.NO_ENTRY 对 sm.data 各主题重新执行 all_checks,只列前四个失败主题
personality_changed_alert():personalityChanged.WARNING 显示当前驾驶风格字符串,持续时间 1.5 秒

其余直接回调包括 startup_master_alert()、below_steer_speed_alert()、wrong_car_mode_alert()、out_of_space_alert()、below_engage_speed_alert()、too_distracted_alert()、calibration_invalid_alert()、calibration_incomplete_alert()、process_not_running_alert()、modeld_lagging_alert()、posenet_invalid_alert()、low_memory_alert()。需要强调「槽位」是 EVENTS 中事件名与事件类型的组合,不等于 22 个独立事件,也不等于 49 条屏幕上同时可见的告警。此外 NoEntryAlert 在 mici 上会调换两行文字,EVENTS.update() 还覆写 9 个条目中的告警定义,实际设备呈现必须按设备类型重核。

从这些实现里还能看出一个共性:动态文案普遍消费当前订阅到的消息字段,例如 overheat_alert() 取 CPU/GPU/内存温度最大值、modeld_lagging_alert() 读 modelV2.frameDropPerc、posenet_invalid_alert() 计算 CS.vEgo - modelV2.velocity.x[0]。这些字段来自不同进程、不同采样时刻,回调本身不做新鲜度校验,因此「文案里显示的数值」不宜直接当作同一时刻的联合状态。

仲裁、测试与插件边界

AlertManager.add_many() 以 EventName/类型 键保存告警及结束帧,process_alerts() 按优先级、起始帧选当前项。update_alerts() 在不活动时清理 WARNING,已使能时清理 NO_ENTRY,这可能让「事件已出现」和「UI 当前展示」不同步。selfdriveState 再发布当前文案、声音与 HUD 标记。

测试覆盖存在缺口:test_alerts.py 的静态告警属性检查明确跳过回调值,test_alertmanager.py 主要验证持续帧逻辑,不能证明 20 个直接回调的所有输入边界正确。隔离探针确认回调在 creation_delay 过滤前执行、异常不在容器中隔离。重构成插件时应把输入观测和告警策略分层:插件提供可信字段与新鲜度,受控核心保留事件类型、软退出计时、回调异常处理和最终仲裁;不得让插件仅靠改文案或改告警子类来更改安全状态。

常见问题

回调返回 ImmediateDisableAlert 会让车辆立刻退出吗?

不会。create_alerts() 仍把 event_type 标为 SOFT_DISABLE,是否立刻退出由 StateMachine.update() 按事件类型决定,与告警子类名是否含 Immediate 无关。

回调只在告警最终显示时才执行吗?

不是。回调在 creation_delay 延迟过滤之前执行,因此即使最终未显示也会读取输入并构造文本,且这里没有逐回调异常隔离。

49 个槽位等于 49 条同时可见的告警吗?

不等于。槽位是 EVENTS 中事件名与事件类型的组合,既不是 22 个独立事件,也不是屏幕上同时可见的告警条数;mici 覆写还会改变实际呈现。

「事件已出现」和「UI 当前展示」总是同步吗?

不一定。update_alerts() 在不活动时清理 WARNING、已使能时清理 NO_ENTRY,因此事件状态与 selfdriveState 发布给 UI 的展示可能不同步。

延伸阅读

参考资料

  • 项目 02 —— openpilot-research/DYNAMIC_ALERT_CALLBACKS_DEEP_DIVE.md(取用了回调槽位统计、两类软退出工厂、20 个直接回调、仲裁与测试缺口、插件边界及隔离探针结论)。
  • 项目 02 —— openpilot-research/CAR_MESSAGES_FIELD_CONTRACT_DEEP_DIVE.md(仅用于确认回调消费字段的消息来源)。

说明:本文为对固定源码快照的静态结构分析,未运行完整测试套件、未连接硬件、未验证目标车型的真实参数组合。文中所有阈值、行号索引与配置均出自源文档,标注为未验证的结论保留其原有边界。

更新记录

首次发布 2026-10-11 最近核对:2026-10-11

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注