标签: process_replay

  • process_replay 的配置、触发器与同步:14 个进程怎么被回放

    结论:process_replay 的 ProcessConfig 里 pubs 是注入给进程的主题,subs 是采集进程输出的主题——与被测进程自己的发布 / 订阅视角相反。回放的「首帧」「双摄齐备」「输出主题周期」都是测试调度逻辑,给出的是功能回归证据,不等于实时车载时序:processing_time 只用于给输出合成 logMonoTime,不是测量到的运行时延。固定快照基线为 c8fb906,本文对原版三个回调做了 AST 隔离探针,不启动任何受管进程。

    配置字段与调用顺序

    ignore 和 tolerance 由测试比较器消费;processing_time 仅给输出合成 logMonoTime;timeout 控制单个 run_step() 周期。vision_pubs 把相机状态与额外 VisionIPC 帧发送关联起来。注意 ignore_alive_pubs 在两个模型配置中赋值,但该字段在本回放模块没有读路径,不能把它当作已实现的存活校验豁免。

    get_process_config() 从表中深拷贝配置;ProcessContainer.__init__() 再深拷贝配置及受管进程,故有状态触发器通常每次回放独立。start() 依次建立隔离前缀、Params / 环境、config_callback、fake 事件、Pub / Sub、可选 VisionIPC、进程,再执行 init_callback。ProcessConfig.__post_init__() 对普通 MessageBasedRcvCallback 自动把触发主题设为 main_pub,模型配置则显式指定 VisionIPC 端点——后者的事件同步点是图像流端点,不能简单等同相机状态消息自身。

    14 个进程配置速查

    下表是回放触发逻辑,不是车型支持或测试实际覆盖矩阵;默认测试会排除两个模型进程。各行 pubs / subs 的完整清单以 CONFIGS 定义为准。

    进程 回放触发 / 同步入口 主要输出
    selfdrived carState,并设置 first_frame=True selfdriveState、onroadEvents
    controlsd selfdriveState carControl、controlsState
    card can 输出频率与 sendcan 启动门,main_pub_drained=True sendcan、carState、carParams、carOutput、radarTracks
    radard modelV2 radarState
    plannerd modelV2 longitudinalPlan、driverAssistance
    calibrationd cameraOdometry,first_frame=True extrinsicsCalibration
    dmonitoringd driverStateV2 driverMonitoringState
    locationd cameraOdometry deviceMotion(合成处理时间 10 ms)
    paramsd deviceMotion vehicleParameters
    lagd deviceMotion lateralDelay
    ubloxd 没有回调:每条被注入消息都可结束一轮 ubloxGnss、gpsLocationExternal
    torqued deviceMotion,first_frame=True lateralTorqueParameters
    modeld 双路道路相机状态标志;主入口为窄路 VisionIPC 端点 modelV2、drivingModelData、cameraOdometry
    dmonitoringmodeld cabinCameraState;主入口为座舱 VisionIPC 端点 driverStateV2

    除 locationd 外,显式 processing_time 是 selfdrived / card / paramsd 的 4 ms 和两个模型的 20 ms,其余默认 1 ms。这些值用于回放中不同进程输出的排序,不能拿来证明对应进程在硬件上达到该延迟。

    ignore / tolerance 由测试比较器消费,属于输出比对边界,而不是进程行为的一部分;调整它们改变的是「通过」的定义,而非被测逻辑本身。ubloxd 没有回调,每条被注入消息都可结束一轮——这类「无触发门」的进程在回放里最容易产生与真实节拍不同的批次划分。模型配置的 fake 事件主入口是图像流端点(窄路或座舱 VisionIPC),而不是相机状态消息本身。

    触发器逐函数行为

    MessageBasedRcvCallback.__call__() 返回「当前主题是触发主题」或「first_frame=True 且 frame==1」。这里的 frame 实际是 run_step() 完成周期后递增的 self.cnt。对非触发主题做隔离探针,计数为 0、1、2 时结果依次为 False, True, False;因此「首帧」并不意味着第一个到达的输入无条件结束周期。

    触发器 探针结果与含义
    ModeldCameraSyncRcvCallback 双摄时两路出现标志齐备才返回真并复位;单摄时窄路出现即可。输入 wide, wide, narrow 结果为 False, False, True——只追踪出现标志,不匹配 frameId,也不验证宽 / 窄帧同一时刻。
    card_rcv_callback 只对 can 计算输出主题周期 frame % int(can频率 / 输出主题频率),且 sendcan 在 (frame - 1) < 2000 时从候选主题删掉。频率相同且仅 sendcan 为输出时,周期计数 0、1、2000、2001 返回 False, False, False, True。
    selfdrived_config_callback 把 cfg.pubs 由列表改为集合差值,删除 GPS 二选一的一侧。
    card_fingerprint_callback 初始化时喂入最多 300 条 CAN,等待 CarParams 出现;get_car_params_callback() 优先按指定指纹构造参数,否则用 CAN、固件查询跳过标志和缓存去识别车型。

    某一路相机缺失时消息会在 msg_queue 中继续累积,不能把「已等齐两路」解释为视觉同步已验证。card_rcv_callback 是测试初始化阶段的输出采集门,不是车端允许发送 CAN 的 safety 决策;配置的频率比还需保持可除且 int 后非零,源码没有在此函数显式校验非法配置。

    对插件化与自制硬件的含义

    新增插件不能只注册一个进程名:应同时声明输入 / 输出主题、schema、触发帧或时间门、初始 Params、相机帧关联键、输出完整性和故障策略。回放配置要能区分「收到任意消息」「收到触发主题」「同一 frameId 的双摄齐备」和真实 deadline;当前回放的标志同步与合成时间戳只给出功能回归证据。自制相机 / 计算板还须独立验证时间戳域、丢帧 / 重复帧、IPC 缓冲寿命、端到端帧年龄、超时回退,以及 panda / opendbc 的发送限制。本文未运行目标硬件或完整回放。

    常见问题

    pubs 和 subs 到底哪个是给被测进程的?

    pubs 是注入给进程的主题,subs 是采集进程输出的主题,与被测进程自己的发布 / 订阅视角相反。混淆两者会读错回放配置。

    回放的 processing_time 是真实运行时延吗?

    不是。processing_time 只给输出合成 logMonoTime,用于回放中不同进程输出的排序。显式值有 selfdrived / card / paramsd 的 4 ms 与两个模型的 20 ms,其余默认 1 ms;不能拿来证明硬件达到该延迟。

    双摄对齐在回放里有校验吗?

    没有校验同一时刻。ModeldCameraSyncRcvCallback 只追踪宽 / 窄两路「出现」标志,标志齐备即返回真,不匹配 frameId。探针输入 wide, wide, narrow 得到 False, False, True。

    「已等齐两路」能说明视觉同步已验证吗?

    不能。回放只检查出现标志;某一路缺失时消息会在 msg_queue 中继续累积。「首帧」也不意味着第一个到达的输入无条件结束周期——它依赖周期计数在 1 时被其他条件允许。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/PROCESS_REPLAY_CONFIG_TRIGGER_SYNC_CONTRACT_DEEP_DIVE.md(取用了 ProcessConfig 字段、14 个 CONFIGS、触发器探针行为与插件化含义分析)。

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

    更新记录

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

  • process_replay 容器:初始 Params、消息合流与合成时钟

    结论:回放器的关键是分清「测试初始条件」与「真实运行」,以及分清「合成时钟」与「真实时延」。replay_process()` 先用 migrate_all() 迁移历史消息,再交给多进程调度;return_all_logs=True 时一旦某主题产生至少一条新输出,该主题全部旧消息被替换。「完整日志」不是原始日志加增量。同时 get_output_msgs() 把输出时间改成 start_time + processing_time,这是调度模型的合成值,不是进程真实运行时间、相机曝光时间或 CAN 发送时间。

    一、入口和数据所有权

    入口/对象 行为 重要边界
    replay_process_with_name() 名称经 get_process_config() 转成独立配置,再委托 replay_process()。 名称必须存在于 CONFIGS,不是 manager 的 44 个进程均可回放。
    replay_process() migrate_all() 先做历史 schema/manager/panda/相机状态准备,再调用多进程调度;return_all_logs=True 时删除迁移日志中所有与新输出同主题的旧消息,合并后按 logMonoTime 排序。 迁移会改变输入和时间顺序;同主题旧消息被整体替换,而非增量叠加。
    ProcessContainer.__init__() 创建随机 OpenpilotPrefix,深拷贝回放配置和受管进程对象,维护输入队列/计数/最近输入时间。 前缀隔离 Params、msgq、部分日志路径,不隔离 CPU、设备或权限;同一解释器内环境变量仍是进程级共享状态。
    ProcessContainer.start() 显式创建前缀目录,写 Params/环境,运行配置回调,开启 fake 事件,创建 Pub/Sub 和可选 VisionIPC,然后启动进程并运行初始化回调。 先启动后回调意味着初始化回调必须按 fake 事件握手;启动中途异常可能留下部分资源。
    ProcessContainer.run_step() 先用触发器决定周期是否结束,当前消息始终入 msg_queue;周期结束才收上周期输出、依次发布队列消息与相机帧、解锁 fake 事件。 输出采集发生在本周期输入发送之前,不能按单次调用直接对齐输入输出。
    ProcessContainer.stop() 对进程发 SIGKILL、关闭 fake 事件、删除前缀目录及回放设置的环境变量。 不恢复回放前相同变量的旧值;失败清理可能再抛异常并掩盖最初故障。

    二、初始 Params、指纹与环境

    生成函数 关键写入 语义
    generate_params_config() 默认开启 OpenpilotEnabledToggle/DisengageOnAccelerator,默认不禁用日志;含 ubloxGnss 写 UbloxAvailable,含 driverMonitoringState 写 IsRhdDetected;固件来源保存 CarParamsCache,纵向权限与 notCar 决定 AlphaLongitudinalEnabled、JoystickDebugMode。 这些是测试初始条件,不是重新判断车辆安全许可。
    generate_environ_config() 设置 PARAMS_ROOT 和 REPLAY=1,可选 LOG_ROOT;强制指纹时设 SKIP_FW_QUERY=1。 原函数的 AST 隔离探针分别确认三种字典输出,未创建任何目录或执行指纹查询。
    _setup_env() / _clean_env() 对空字符串执行「从 os.environ 删除该键」,非空值覆盖;设置 PROC_NAME;SIMULATION=1。 _clean_env() 不会恢复之前的值,故回放应运行于独立子进程或自行保留环境快照。

    环境隔离还有一层容易被忽略:_setup_env() 对配置中的空字符串执行「从 os.environ 删除该键」,非空值直接覆盖,并设置 PROC_NAME;_clean_env() 删除配置中所有环境键及 PROC_NAME/SIMULATION,不会恢复之前的值。而 OpenpilotPrefix 自己仅保存和恢复 OPENPILOT_PREFIX。因此在已有环境变量的测试宿主中,回放应运行于独立子进程或自行保留环境快照,不能把容器当成可嵌套的完整环境事务。

    OpenpilotPrefix 自己仅保存和恢复 OPENPILOT_PREFIX;因此在已有环境变量的测试宿主中,容器不是可嵌套的完整环境事务。

    三、消息合流和时钟

    _replay_multi_process() 先创建所有容器,再计算 all_pubs(注入主题)与 all_subs(输出主题)。仅把原路段中属于 all_pubs - all_subs 的消息放入外部队列;与某个受测进程输出同主题的原日志不会进入外部队列,必须依靠新的内部输出驱动下游。每次被测进程产出的消息都加入最终 log_msgs;若其主题又属于 all_pubs,则加入内部队列和按 logMonoTime 排序的小根堆。外部和内部候选比较时,时间相等优先内部消息。

    时间上,get_output_msgs() 等进程下一次 recv_called,排空输出 socket,并把所有采到的输出时间改成 start_time + processing_time;start_time 是此前收到的输入最大 logMonoTime。run_step() 内的 Timeout 只覆盖已结束的周期;没有触发周期的积压输入,以及尾部 get_output_msgs() 冲刷,不共享这个超时上下文。

    两个收尾条件值得单独验证:主循环在外部队列耗尽、所有容器的 msg_queue 都为空时会停止,即使内部堆仍有已生成消息——这些消息可能已进入最终输出,却未再次送给下游容器;结束后逐容器冲刷输出也只追加到最终结果,不重新进入内部堆。这是源码控制流可见的覆盖边界,尚无实际多进程路段复现,不能把多进程回放等同完整闭环执行。

    四、故障与硬件插件验收

    失效路径也需注意:_replay_multi_process() 先将容器加入 containers 再调用 start(),若在 self.rc 或 capture 初始化之前抛错,finally 中的 stop() 及捕获读取可能因部分初始化而再次异常。尾部冲刷的 ReplayContext.wait_for_recv_called() 最终使用默认无限等待,需另设总 deadline 才能证明崩溃/失联路径有界。以上为静态失效路径,未执行故障注入。

    要把新的传感器、计算或车辆接口做成插件,需给回放器明确的输入/输出主题、触发条件、初始 Params/环境、时钟域、消息新鲜度、最大积压和异常清理契约。验收应分开记录功能输出、端到端时延、处理器资源、断流/进程退出和 panda 安全测试;processing_time 或对齐后的参考日志不构成实车时限证据。当前缺 capnp、参考路段和目标硬件,本专题没有运行完整回放。此外迁移本身会改变输入与时间顺序,因此比较两次回放前必须固定同一迁移版本与路段快照,否则结果不可比。

    常见问题

    所有 manager 进程都能被回放吗?

    不是。replay_process_with_name() 要求名称存在于 CONFIGS,源文档明确不是 manager 的 44 个进程均可回放。名称只是转成独立配置的入口,不代表该进程具备完整的回放输入/输出契约。

    return_all_logs=True 时日志是原日志加增量吗?

    不是。一旦某主题产生至少一条新输出,该主题全部旧消息被替换,而不是叠加。源文档强调「完整日志」不是原始日志加增量,且迁移会改变输入和时间顺序。

    processing_time 是进程真实运行时间吗?

    不是。get_output_msgs() 把输出时间改成 start_time + processing_time,start_time 是此前输入最大 logMonoTime,因此它是调度模型的合成值,不是进程真实运行时间、相机曝光时间或 CAN 发送时间。

    多进程回放等于完整闭环吗?

    不等于。主循环在外部队列耗尽且所有容器 msg_queue 为空时会停止,即使内部堆仍有已生成消息;尾部冲刷也不重新进入内部堆。源文档称这是控制流可见的覆盖边界,尚无实际多进程路段复现。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/PROCESS_REPLAY_CONTAINER_SCHEDULER_PARAM_LIFECYCLE_DEEP_DIVE.md(取用了容器入口与数据所有权、初始 Params/环境生成、消息合流与合成时钟、故障与插件验收)。

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

    更新记录

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