标签: 插件边界

  • tinygrad 数据集与 Torch 前端:训练入口不该进车端

    结论:tinygrad 的 nn/datasets.py 与 nn/torch.py 都是开发/训练侧入口,不是 openpilot 车端实时模型加载主链。前者下载并解包第三方数据、按固定字节偏移切片;后者在导入时编译并载入 C++ 扩展、注册 PrivateUse1 后端,并修改全局进程状态。结论是明确的:数据集下载、PyTorch 兼容层与离线训练必须留在构建/研发域,网络、文件系统和原生扩展编译都不得通过运行期插件装载流入车辆控制进程。

    数据集组件与 I/O

    两条数据入口都返回原始字节张量,且都未传入固定哈希。mnist() 按 fashion 选择 MNIST 或 Fashion-MNIST 基址,分别读取训练/测试图像和标签四个 gzip IDX 文件;图像跳过 16 字节头并 reshape 为 (-1,1,28,28),标签跳过 8 字节头,返回原始字节值张量,device 只用于结果转移。cifar() 下载 CIFAR-10 二进制 tar.gz,每条 3073 字节记录的第 0 字节为类别,其余 reshape 为 (N,3,32,32)。

    入口 实际行为与限制
    mnist() 读四个 gzip IDX 文件,跳过 16/8 字节头并 reshape;未在此归一化、核验 IDX magic/声明样本数或固定哈希。
    cifar() 拼接五个训练 batch、取一个测试 batch;未对五批样本数、标签范围或归档哈希做独立校验。
    Tensor.from_url()、fetch() fetch 默认按 URL/请求头生成下载缓存路径,gzip 边读边解压,临时文件完成后重命名;只有调用方传入 sha256 才验证内容。两种 nn.datasets 入口均未传入固定哈希。
    tar_extract() 经 tarfile 读取归档目录,按 offset_data/size 返回常规文件 Tensor 切片;不是通用安全解包器,成员名和内容应在训练数据入口做白名单、大小及哈希校验。

    需要特别区分的是,extra.datasets.fetch_mnist() 是另一条训练示例路径:用 NumPy/gzip 解码后输出 float32 扁平图像和 int8 标签,不能与 nn.datasets.mnist() 的原始字节 NCHW 输出混为一谈。数据输入插件须声明像素范围、通道布局、标签 dtype 和样本数量,不能仅声明「MNIST/CIFAR」。

    下载侧还有一处顺序细节:from_url 用 fetch 得到本地文件后构造 Tensor,fetch 对 gzip 下载边读边解压、临时文件完成后重命名,并默认按 URL/请求头生成缓存路径。这种「先缓存后校验」的顺序意味着,即使同一 URL 已被下载过,只要调用方没传哈希,缓存命中就不能作为来源未被篡改的证据。

    PyTorch 前端组件与副作用

    nn/torch.py 在导入时把 tinygrad 根目录加到 sys.path 并导入 extra.torch_backend.backend;若发行包无 extra 前端则抛 ImportError——它不是纯类型别名或无需依赖的适配器。backend.py 导入段随后通过 torch.utils.cpp_extension.load 编译并载入 C++ 扩展,注册 tiny 的 PrivateUse1 后端,同时关闭 PyTorch autograd 多线程。这会触发构建、动态载入和全局进程状态修改,不适合在车端硬件插件发现阶段自动导入。

    入口 实际行为与限制
    nn/torch.py 导入时改 sys.path 并导入 extra 前端;无 extra 则 ImportError。
    backend.py 导入段、wrapped_tensor.cpp 编译并载入 C++ 扩展,注册 PrivateUse1,关闭 autograd 多线程;触发构建与全局状态修改。
    wrap()、unwrap()、wrap_tensor() Python Tensor 存入 C++ OpaqueTensorImpl;源码自标 calculate_storage_offset 对 PERMUTE/EXPAND/PAD/FLIP 假设不完整,不能把一般视图元数据视为已证明正确。
    _copy_between_devices()、_index_put_impl_() CPU 与 tiny 经 NumPy/copy_ 转换;部分索引、特征分解及上采样反传显式退回 CPU,不能由 device='tiny' 推断所有算子都留在目标加速器或满足实时延迟。
    CustomNoOpDeviceGuardImpl 设备切换、stream 同步及 event 记录/等待基本按 no-op 实现,deviceCount() 固定 1;不能用它作为车端多设备同步或完成回执。

    此外,tiny_backend 注册循环按 ATen schema 的写参数和 inplace-view 标签选择包装器,native_batch_norm() 有单独正反向实现以更新运行统计量,但算子表不是完整 PyTorch 兼容性保证。_apply_inplace()/wrap_view_op() 用弱引用追踪派生视图、对视图写入重放索引并刷新别名,涉及存储别名与 autograd 约束,并非普通零拷贝 wrapper。Python TinyBackend.device_count() 另读 GPUS,与 C++ 层的设备数量口径不一致。

    存储与视图元数据方面,wrap() 把 Python tinygrad Tensor 存入 C++ OpaqueTensorImpl,Torch 侧 shape/stride/offset 必须与底层张量保持一致;源码自己标明 calculate_storage_offset 对 PERMUTE/EXPAND/PAD/FLIP 的假设并不完整。因此一般视图的元数据不能直接当作已证明正确,涉及视图写入时还需要处理存储别名与原位更新约束。

    与 openpilot 和插件重构的关系

    compile_model() 调用 tinygrad ONNX 编译脚本,ModelState.run() 运行已捕获的模型。对 openpilot 与 examples 目录的直接调用搜索,未找到 nn.datasets 或 tinygrad.nn.torch 入口——这只能界定当前可见源码,不代表所有外部开发脚本。

    面向自制车载硬件,建议把数据集下载、PyTorch 兼容层和离线训练放在构建/研发插件域:网络、文件系统、原生扩展编译均不得通过运行期插件装载流入车辆控制进程。车端推理插件只消费签名/哈希/版本绑定的模型工件与明确的张量 ABI;相机输入需约定像素范围、色彩格式、行步长、时间戳和缓冲所有权。PyTorch 的 tiny 设备是研发兼容性工具,不可代替目标后端的执行完成、时限或安全认证。

    常见问题

    tinygrad 的数据集和 PyTorch 前端能在车端直接用吗?

    不应直接使用。两条路径都是开发/训练侧入口:nn/datasets.py 会联网下载并解包第三方归档,nn/torch.py 在导入时就编译并载入 C++ 扩展、修改全局进程状态。源码建议把它们放在构建/研发插件域,网络、文件系统与原生扩展编译不得通过运行期插件装载流入车辆控制进程。

    为什么说下载缓存命中不代表数据可信?

    因为 fetch() 默认只按 URL/请求头生成下载缓存路径、边读边解压、临时文件完成后重命名,只有调用方传入 sha256 才验证内容;而 mnist() 与 cifar() 两个入口都没有传入固定哈希。因此缓存命中不能作为来源或版本已被独立证明的依据。

    用 device=’tiny’ 是否等于所有算子都跑在加速器上?

    不等于。_copy_between_devices() 和 _index_put_impl_() 显示 CPU 与 tiny 之间经 NumPy/copy_ 转换,部分索引、特征分解及上采样反传显式退回 CPU;源码明确不能由 device='tiny' 推断所有算子都留在目标加速器或满足实时延迟。

    PyTorch 的 tiny 设备能当车端多设备同步用吗?

    不能。CustomNoOpDeviceGuardImpl 把设备切换、stream 同步及 event 记录/等待基本按 no-op 实现,deviceCount() 固定 1;Python TinyBackend.device_count() 又另读 GPUS,两层口径不一致。它不能作为车端多设备同步或完成回执。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/TINYGRAD_DATASET_TORCH_FRONTEND_INGRESS_BOUNDARY_DEEP_DIVE.md(取用了数据集 I/O 与哈希缺口、PyTorch 前端副作用、与 openpilot 的边界关系与验证范围)。
    • 项目 02 —— openpilot-research/TINYGRAD_TORCH_PRIVATEUSE1_ATEN_ALIAS_DEVICE_DEEP_DIVE.md(仅用于确认 PrivateUse1/ATen 别名专题的接续关系,未取用其未公开内容)。

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

    更新记录

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

  • body、BYD、MG、PSA 与 mock:五种平台的执行语义与资格标记

    结论:在固定快照 c8fb906 中,body、BYD、MG、PSA 与 mock 这五个平台各有完全不同的执行语义:body 是双轮开发平台而非乘用车,只做左右轮速度与差速;BYD 与 PSA 走转角链,MG 走扭矩链;mock 返回空状态与空发送,只在无法识别车型时回退。BYD、MG、PSA 在快照中均被标记 dashcamOnly,其 panda 安全模式仅在 ALLOW_DEBUG 注册,因此不能把代码路径解读为发布固件可用功能。本文为静态代码分析,未运行测试、未做实车验证。

    平台资格:notCar、dashcamOnly 与调试注册

    同一套接口在不同平台上可能只是「能加载」而不是「允许执行」。body 通过 CarInterface._get_params() 设置 notCar=True,它并不是乘用车;BYD、MG、PSA 与 mock 都设置了 dashcamOnly=True,且它们的安全模式只出现在调试注册表里。插件清单必须把这几类资格分开标注。

    平台 控制语义 资格标记
    body 双轮差速、速度控制 notCar=True,非乘用车
    BYD 角度转向、原车纵向 dashcamOnly=True,调试注册
    MG 扭矩转向、原车巡航 dashcamOnly=True,调试注册
    PSA 角度转向、EPS 重使能 dashcamOnly=True,调试注册
    mock 空状态、空发送 dashcamOnly=True,未知车型回退

    body:开发用双轮平台

    CarState.update() 从电机数据读取左右电机速度并估算速度,电机错误会形成永久转向故障,同时读取充电与电量,且巡航状态被设为可用且已启用。CarController.update() 与构帧函数把 actuators.accel 与 torque 转成期望前进速度与左右差速,使用两个 PID,经过死区、变化率及幅度限制,最终编码双轮扭矩;代码明确注明加速度字段并非真实加速度语义。

    body_rx_hook() 在 RX 有效后允许控制,body_tx_hook() 只开放指定 TX 地址与长度并关闭转发。需要注意两处例外:未允许控制时仍可发送 0x1,且 0x250 的特定魔数刷写帧也被放行。固件没有乘用车式的双轮扭矩逐字段限制,因此它的安全策略不能移植到车辆。

    层 关键函数 实现事实
    参数 _get_params() notCar=True、body 安全模式、纵向开启、角度控制类型
    输入 CarState.update() 左右电机速度估算速度;电机错误形成永久转向故障
    输出 CarController.update() / create_control() accel 与 torque 转差速,经死区与限幅编码双轮扭矩
    固件 body_rx_hook() / body_tx_hook() 仅白名单与长度;未允许时仍可发 0x1,0x250 刷写帧为例外

    BYD 与 MG:转角链与扭矩链

    BYD 走角度转向:CarController.update() 用车辆模型限制目标转角,每两帧发转角与 HUD,取消时每十帧发按钮候选帧;byd_tx_hook() 检查转角与速率,按钮只接受取消或释放,且取消需有先前巡航接合状态。其部分 RX 项显式忽略校验和与计数器,不能泛称全链路完整性校验。驾驶员 EPS 扭矩分别用于软覆盖与硬退出。

    MG 走扭矩转向:CarController.update() 每两帧按驾驶员施力与变化率对期望扭矩限幅,未激活时置零,只构造 LKAS 转向帧;mg_tx_hook() 的 TX 白名单仅一条转向帧,固件再次检查最大扭矩、速率、实时变化与驾驶员扭矩。其安全注册只在调试编译中。

    平台 输入来源 输出与固件门
    BYD 动力总线取速度、转角、扭矩、踏板、车门;相机总线取 ACC 与原 LKAS HUD 车辆模型限幅转角;TX 检查转角与速率,按钮只接受取消/释放
    MG 状态来自 PT 与相机两路,含巡航故障与临时转向故障 每两帧限幅扭矩、未激活置零;TX 仅一条转向帧并复检扭矩/速率

    PSA 与 mock:EPS 重使能与未知车型回退

    PSA 设转角控制与 dashcamOnly=True,主、ADAS、相机三总线共同提供速度、驾驶员与 EPS 状态、巡航、制动与安全带。CarController.update() 每五帧做转角限幅并发 LKAS;在 EPS 覆盖后按 READY 到 AUTHORIZED 再到 ACTIVE 的状态序列尝试重使能,实际策略必须在目标平台验证。psa_tx_hook() 的白名单只含 LKAS 转角帧,并限制目标角度与速度相关变化率;部分 RX 项忽略校验与计数。

    mock 是明确的隔离边界:_get_params() 设 dashcamOnly=True,CarState.update() 返回空状态,CarController.update() 返回空 CAN 列表。get_car() 在无法识别车型时回退到它,被动路径还会把安全配置设为 noOutput。因此 mock 可用于只读、仿真与未知车型隔离,不能用来推定车辆控制可用。

    对硬件与插件化的含义

    插件清单必须区分 notCar、dashcamOnly、发布与调试固件注册状态、控制语义和总线拓扑;加载实现并不等于允许执行。车型控制插件只能产出候选帧,独立安全 MCU 仍需按车型验证 RX 有效性、TX 白名单与转角或扭矩限制。body 的刷写例外不能进入乘用车固件。

    BYD 的相机 HUD 保留、PSA 的 EPS 重使能状态机及各平台的巡航接合语义,都应放入明确的能力与风险契约,不能抽成无条件的通用按钮或转向插件。相关测试入口包括 body、BYD、MG、PSA 的专项测试与通用车型接口测试,本次只确认文件存在,未运行也未验证实车行为。

    常见问题

    body 是乘用车平台吗?

    不是。body 是开发平台,其接口设置 notCar=True,执行方式是双轮差速与速度控制,代码还明确注明加速度字段并非真实加速度语义。它的固件安全策略不能移植到乘用车。

    dashcamOnly 与调试注册意味着什么?

    意味着这些路径在固定快照中不作为发布可用功能。BYD、MG、PSA 都设了 dashcamOnly=True,其 panda 安全模式只在 ALLOW_DEBUG 下注册,因此不能把代码路径解读为发布固件可用功能。

    mock 会不会真的发控制帧?

    不会。mock 的 CarState.update() 返回空状态,CarController.update() 返回空 CAN 列表,被动路径还把安全配置设为 noOutput。它用于只读、仿真与未知车型隔离,不能用来推定车辆控制可用。

    插件加载了某平台实现就代表可以执行控制吗?

    不代表。插件清单要区分 notCar、dashcamOnly、发布或调试固件注册状态与控制语义;加载实现不等于允许执行,最终 TX 仍由独立安全 MCU 按车型校验。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/OTHER_PLATFORMS_DEEP_DIVE.md(取用了 body/BYD/MG/PSA/mock 五平台的参数、输入、输出与固件事实,以及插件化含义)。
    • 项目 02 —— openpilot-research/BYD_PANDA_SAFETY_MODE_DEEP_DIVE.md(关联的 BYD Atto 3 safety 专题,用于补充转角链与按钮门细节,本文未展开其探针)。

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

    更新记录

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