结论:巡航设定速度(vCruise)是规划与显示的输入,不是车端已执行车速,也不代表系统已使能。源文档把它的产生拆成两条互斥路径:CP.pcmCruise=True 时直接读原车 cruiseState.speed/speedCluster 转 km/h,按键调速由原车系统负责;CP.pcmCruise=False 时由 VCruiseHelper 自己处理按键并步进。两条路径的单位(km/h)、哨兵(255/0/-1)与边界(8–145 km/h)都必须逐车型核对,不能按品牌名称猜测。
一、数据流与单位
Car.state_update() 在车型接口解析车况后,先用当前 sm['carControl'].enabled 调用 update_v_cruise(),在 carControl.enabled 从假变真时再用 CS_prev 调用 initialize_v_cruise()。源文档注释明确这是为了使用 selfdrived 使能那一步的按钮状态,避免按键边沿与 card 的下一次车况更新错位。
随后发布的 CS.vCruise、CS.vCruiseCluster 单位都是 km/h;原始 cruiseState.speed 是 m/s,在 PCM 路径中乘 MS_TO_KPH。LongitudinalPlanner.update() 对 carState.vCruise 上限裁剪到 V_CRUISE_MAX 再转 m/s,并区分 unset;Controls.state_control() 将 CS.vCruise 转 m/s 传给车型 PID 加速度限幅,而 HUD 的设定速度取 vCruiseCluster。两种速度值可能不同,不能把仪表显示值当作规划器原始目标。
二、PCM 与非 PCM 两条路径
| 条件 | update_v_cruise() 行为 | 影响 |
|---|---|---|
| 原车巡航不可用 | 设定值及仪表值均为 255;非 PCM 按键计时器不在该分支清零 |
规划器将其识别为未初始化;不代表车辆实际时速 255 km/h。恢复时旧计时器可能仍在。 |
CP.pcmCruise=True |
直接使用 CS.cruiseState.speed/speedCluster 转 km/h |
按键调速由原车系统负责,helper 不自行步进;speed==0 改为 unset,speed==-1 保留 -1 特殊值。 |
CP.pcmCruise=False 且原车巡航可用 |
从本地状态处理按钮,然后令仪表设定值等于内部设定值 | 自管设定速度;只有 enabled=True 时按键会改目标。 |
PCM 读取路径没有在本 helper 内对正常速度统一裁剪到 145 km/h;下游纵向规划器有自己的上限处理。量产适配需要分别核对原车报文中的 speed 和 speedCluster 含义、无效哨兵和单位,不能把任意 0 或 -1 当作普通速度。
三、非 PCM 按键算法与阈值
_update_v_cruise_non_pcm() 先找增/减速按钮的松开边沿;若找不到,再检查按住计时是否达到 50 帧倍数,将其视作长按重复。执行变更前有两个防重入门:增速键用于从静止巡航恢复时不调高设定值;若按钮按下时尚未 enabled,而松开前才完成使能,也不把同一次按键再次解释为调速。update_button_timers() 在当帧调速判断之后更新计时和按下时的 standstill/enabled 快照,这是边沿语义的一部分。
| 参数 / 规则 | 取值 | 说明 |
|---|---|---|
V_CRUISE_UNSET |
255 | 未设定哨兵。 |
V_CRUISE_MIN |
8 km/h | 结果下限。 |
V_CRUISE_MAX |
145 km/h | 结果上限;上限注释指出模型训练分布外的速度预测可能不可靠。 |
| 短按步长 | 公制 1 km/h;英制 IMPERIAL_INCREMENT=1.6 km/h |
英制由 1 mph 转换并取一位小数。 |
| 长按倍率 | ×5 | 长按时若当前值不在 5 倍步长网格,增速先向上取整、减速先向下取整,再继续步进。 |
| 长按判定 | 按住 50 帧倍数 | 最终结果取一位小数并限制在 8–145 km/h。 |
油门覆盖时按减速键,会把新目标至少抬到当前 vEgo,随后仍受 145 km/h 上限约束。源码条件还写有 setCruise,但该函数的按键选择集合仅含增速/减速,因此当前路径不会选择到 setCruise;不能据该条件宣称 SET 键在此处有同样效果。
四、使能初始化、上游门与测试
initialize_v_cruise() 对 PCM 直接返回;非 PCM 首次使能时默认从当前车速与初始下限中取较大者,普通模式初始下限 40 km/h,实验模式 105 km/h,再限制到 145 km/h。若是增速/恢复按键触发且已有旧设定值,则恢复 v_cruise_kph_last,不是重新用当前车速初始化。这里的 105 km/h 是设定速度的初始下限/上界提示语义,实验模式仍由模型和下游规划裁决实际加速,不能理解为车辆会立即开到该速度。
非 PCM 的按键使能来自 CarState.buttonEnable(基础接口由增/减速键松开边沿产生,车型可覆写),经 CarEvents 转成 buttonEnable 事件,再由状态机决定使能。VCruiseHelper 只管设定值,不负责判断驾驶员监测、车辆故障或 panda 的 controlsAllowed;取消键、刹车与安全事件仍可让系统退出。
源文档的输入清单里还列出 IsMetric 与 ExperimentalMode:前者参与决定短按步长取公制 1 km/h 还是英制 1.6 km/h,后者决定首次使能的初始下限取 40 还是 105 km/h。二者都只影响设定值的产生,不影响车端是否已执行,也不替代使能与安全门。
测试方面,test_cruise_speed.py 覆盖非 PCM 短按松开、使能中按键、静止 resume、油门覆盖和初始化,文件 TODO 明确 PCM 路径尚无对应测试;本工作区的八项原类隔离探针使用假 NumPy 裁剪和假车况,不依赖实际 capnp、CAN 或 UI,也不等于实车按键与执行器测试。
常见问题
设定速度和实际车速是一回事吗?
不是。设定速度是规划/显示输入,不是车端已执行车速,也不代表系统已使能。源文档还强调 vCruise 与 HUD 取的 vCruiseCluster 可能不同,不能把仪表显示值当作规划器原始目标。
PCM 车型的长按调速逻辑由谁负责?
由原车系统负责。CP.pcmCruise=True 时 helper 直接使用 CS.cruiseState.speed/speedCluster 转 km/h,本 helper 不自行步进;因此 50 帧长按、5 倍步进这类算法只存在于非 PCM 路径。
实验模式初始下限 105 km/h,是不是车会立刻加速到 105?
不是。源文档明确 105 km/h 是设定速度的初始下限/上界提示语义,实验模式仍由模型和下游规划裁决实际加速,不能理解为车辆会立即开到该速度。
按增速键从静止恢复会调高设定值吗?
不会。源文档给出的防重入门之一就是:增速键用于从静止巡航恢复时不调高设定值;另一个是若按钮按下时尚未 enabled,而松开前才完成使能,也不把同一次按键再次解释为调速。
延伸阅读
- 纵向规划 MPC:目标速度如何被裁剪与跟踪 —— 承接
V_CRUISE_MAX之后的上限处理。 - selfdrive 事件与状态告警 —— 解释取消键、刹车与安全事件为何能退出使能。
- 同一个「转向灯」在三家车厂是三种实现:车辆状态归一化的坑 —— 说明为何哨兵与单位必须逐车型核对。
- Honda CarState:车速、刹车与巡航输入 —— 一个巡航输入解析的具体样本。
- openpilot 的 100 Hz 控制回路:开源 ADAS 能给车载设备什么 —— 说明设定值到执行之间仍隔着一整条控制链。
参考资料
- 项目 02 ——
openpilot-research/CRUISE_SET_SPEED_DEEP_DIVE.md(取用了数据流与单位、PCM 与非 PCM 路径、非 PCM 按键算法、阈值常量与测试边界)。
说明:本文为对固定源码快照的静态结构分析,未运行完整测试套件、未连接硬件、未验证目标车型的真实参数组合。文中所有阈值、行号索引与配置均出自源文档,标注为未验证的结论保留其原有边界。
更新记录
首次发布 2026-10-11 最近核对:2026-10-11
发表回复