结论:「实验模式是否生效」在源码里其实是三个不同的状态,很容易被一个 UI 图标混淆。一是请求值 ExperimentalMode,是用户意图,多个 UI 入口都能写;二是界面值,大屏行车按钮的新图标最多保持约两秒就回到消息报告的模式;三是运行值 selfdriveState.experimentalMode,由 selfdrived 在运行时车型能力门后发布,plannerd 在收到下一次模型更新时才使用它。源码没有提供端到端「点击已被规划器接受」的事务回执。以下为固定快照 c8fb906 的静态分析,不代表模型行为或实车交互已验证。
请求入口:五个 UI 路径写同一个参数
请求值可以从多处写入。大屏行车按钮的 ExpButton._update_state()/_handle_mouse_release() 从 selfdriveState.experimentalMode 取当前模式,在确认且 UI 认为有纵向控制时翻转 ExperimentalMode 参数;其 _engageable 只影响绘制透明度,不参与切换允许判断。大屏驻车主页的按钮点击进入设置,展示取自 Params,不直接切换模式。
设置页的 TogglesLayout._handle_experimental_mode_toggle() 以 UI 能力门决定可用性,首次开启需确认,确认后阻塞写两个参数,不可用时清除请求参数。mici 首页的 HomeLayout._update_state() 在长按超过 0.5 秒且 UI 能力与确认均成立时,修改 UI 本地布尔值并阻塞写 ExperimentalMode;mici 设置页首次开启走确认页面,后续写请求参数,但这里的普通 put_bool 未指定 block=True。
| 阶段 | 代码入口 | 源码可确认的行为 |
|---|---|---|
| 大屏行车按钮 | ExpButton._update_state()、_handle_mouse_release() |
从 selfdriveState.experimentalMode 取当前模式;确认且 UI 认为有纵向控制时翻转参数。 |
| 大屏按钮反馈 | _held_or_actual_mode() |
新图标保持约两秒后回到消息报告的模式;保持期只是视觉反馈。 |
| 大屏驻车主页 | HomeLayout._setup_callbacks()、ExperimentalModeButton.show_event() |
点击进入设置;展示取自 Params,不直接切换模式。 |
| 大屏设置 | TogglesLayout._handle_experimental_mode_toggle() |
UI 能力门决定可用性;首次开启需确认;不可用时清除请求参数。 |
| mici 首页 | HomeLayout._update_state() |
长按超过 0.5 秒且能力与确认成立时,阻塞写 ExperimentalMode。 |
| mici 设置 | TogglesLayout._on_experimental_mode() |
首次开启走确认页;后续写请求参数,普通 put_bool 未指定 block=True。 |
UI 能力计算集中在 UIState.update_params():它从 CarParamsPersistent 读车型能力,若 alphaLongitudinalAvailable 则展示门取 AlphaLongitudinalEnabled,否则取 CP.openpilotLongitudinalControl,后台约每 0.2 秒刷新。也就是说「按钮能不能点」和「模式实际会不会生效」用的是两套判定依据。
参数持久化与两个布尔键
ExperimentalMode 与 ExperimentalModeConfirmed 都是持久化布尔键。要点在于写盘语义:Params.put_bool(..., block=True) 会等待写盘,而不带 block=True 的调用只提交写入;这两种调用都不等于下游已消费。mici 首页调用的是通用 Params.put(..., block=True),其布尔值经类型转换后写入,与 put_bool 的 API 形式不同。
| 键 / 调用 | 含义 | 边界 |
|---|---|---|
ExperimentalMode |
用户请求值 | 多入口可写;无纵向控制能力时可能被清除。 |
ExperimentalModeConfirmed |
首次确认记录 | 持久化布尔键,用于首次开启确认。 |
put_bool(block=True) |
等待写盘 | 写盘完成不等于下游已消费。 |
put_bool() |
仅提交写入 | mici 设置的普通调用属此类。 |
运行值由能力门决定
运行时一侧,UIState.update_params() 从 CarParamsPersistent 读车型能力:若 alphaLongitudinalAvailable,展示门取 AlphaLongitudinalEnabled,否则取 CP.openpilotLongitudinalControl,后台约每 0.2 秒刷新。而 SelfdriveD.__init__() 在无 openpilot 纵向能力时启动清除请求;params_thread() 约每 0.1 秒计算 ExperimentalMode AND CP.openpilotLongitudinalControl,主循环再发布 selfdriveState.experimentalMode。
这里有一个条件性差异值得注意:UI 的能力展示读 CarParamsPersistent 且在 alpha 可用时依赖 AlphaLongitudinalEnabled,实际模式却按运行时 CP.openpilotLongitudinalControl 判定。若持久车型参数、运行时参数或刷新时点不同,UI 允许/展示与实际激活可短暂不一致;静态阅读不能证明这种差异会在某辆车上发生。
规划消费与规划激活
plannerd.main() 与 LongitudinalPlanner.update() 随 modelV2 更新读取 selfdriveState;实验模式改变巡航加速度计算,并把模型 desiredAcceleration 加入候选,最终按候选的最小加速度选目标——不是「模型始终直接控制油门刹车」。下游 controlsd 从 longitudinalPlan.aTarget 经纵向控制器生成候选执行器输出,card 还读取参数作为启用时巡航初值的输入,二者各有各自的更新时序。
面向自制硬件与插件重构
把 UI 插件的操作定义为 ModeChangeRequest,携带请求 ID、来源、单调时间戳和车辆能力版本;只授权写请求,不授予其直接发布 selfdriveState、longitudinalPlan 或 CAN 的能力。由固定核心层发布 requested / rejected / accepted / active 等结构化状态,其中 active 必须来自规划器消费确认,不能用图标、Params 写盘或 selfdriveState 单独代替;当前源码尚无此回执协议。
能力门应以运行时 CarParams 与独立网关安全策略为权威,UI 可显示能力但其缓存不得成为执行许可。验证顺序上,先用回放验证参数切换、状态发布、规划候选选择及失联时序,再对目标车型验证 controlsd 活动门、card 输出与 panda 安全钩子。
常见问题
点了实验模式按钮,规划器就立刻用上了吗?
不一定。按钮翻转的是请求值 ExperimentalMode,运行值 selfdriveState.experimentalMode 由 selfdrived 在能力门后发布,plannerd 要等下一次模型更新才使用;源码没有端到端「点击已被规划器接受」的事务回执。
大屏上的新图标表示模式已切换成功吗?
不能这样理解。_held_or_actual_mode() 里的新图标最多保持约两秒,然后回到消息报告的模式;这段保持期只是视觉反馈,不是参数写盘或规划激活的回执。
put_bool(block=True) 写盘成功就说明下游已消费吗?
不等于。block=True 只是等待写盘完成,不带 block=True 的调用只提交写入;两种调用都不代表下游已消费,mici 设置页用的就是不带 block 的普通 put_bool。
为什么 UI 显示的可用性与实际激活会不一致?
因为判定依据不同:UI 能力展示读 CarParamsPersistent、在 alpha 可用时依赖 AlphaLongitudinalEnabled,实际模式按运行时 CP.openpilotLongitudinalControl 判定。持久参数、运行时参数或刷新时点不同就可能短暂不一致,但不能据此断定会在某辆车上发生。
延伸阅读
- Params 生命周期 —— 承接
ExperimentalMode等持久化键的读写与清除语义。 - 纵向规划器与 MPC —— 对照实验模式如何改变巡航加速度候选。
- UI 控件与确认流程 —— 同类首次开启确认与能力门交互。
- 软件与开发者设置 —— 承接模式开关的 UI 呈现与参数来源。
- selfdrive 状态与告警事件 —— 对照
selfdriveState的发布与消费。
参考资料
- 项目 02 ——
openpilot-research/EXPERIMENTAL_MODE_UI_PARAM_ACTIVATION_DEEP_DIVE.md(取用了五个 UI 请求入口、两个持久化布尔键、三层状态划分、运行值能力门与规划消费说明)。
说明:本文为对固定源码快照的静态结构分析,未运行完整测试套件、未连接硬件、未验证目标车型的真实参数组合。文中所有阈值、行号索引与配置均出自源文档,标注为未验证的结论保留其原有边界。
更新记录
首次发布 2026-10-11 最近核对:2026-10-11
发表回复