JotPluggler 绘图:状态块、分桶抽样与时间交互边界

作者:

在

结论:JotPluggler 的绘图是一条显示语义链,不是控制语义链。曲线进入绘图前会经过时间窗裁剪、导数/缩放变换和分桶抽样,跟踪时间的图例值还会按曲线类型做阶梯取值或线性插值;这些都属于屏幕显示行为。抽样结果只用于显示,导出与控制决策必须回到原始数据;车端部署应把诊断绘图插件限定为只读,不能因其消费 CAN 曲线就获得 CAN TX 或安全策略写权限。

曲线进入绘图的路径

基线是主仓库 c8fb906 本地源码归档,仅静态阅读;未编译、运行 GUI 或测量大路段绘制性能。draw_pane_windows() 按 pane 类型分派地图、缩略图、相机或 draw_plot()。图表仅处理 visible 且有本地样本或有效路段样本的曲线;RouteSeries 至少需要两个等长的时间和值样本。

build_curve_series() 将曲线转换成 PreparedCurve:普通路段曲线先按共享时间窗二分查找,保留窗边相邻样本;本地曲线直接复制已有 xs/ys,不走该时间窗裁剪。路段样本在收集阶段经时间排序,但这里未重新排序本地样本;插件提供本地序列时应明确单调时间契约。

导数模式逐相邻点计算差分,derivative_dt > 0 时使用指定间隔,否则使用实际时间差;dt <= 0 的样本对被跳过。随后统一施加 value_scale 和 value_offset。枚举元数据只在未变换的原始路段曲线上保留。跟踪时间的图例值由采样函数获得:阶梯曲线取前一值,普通曲线线性插值,越界时间取端点值——这是显示语义,不应直接作为控制回路的时序/插值语义。

渲染与抽样语义

函数/组件 关键行为 边界
app_decimate_samples_impl() 将样本分桶,每桶保留首点、最小、最大和末点,按原索引去重;目标点数从绘图区宽度的约两倍计算。 显示抽样,不是无损数据导出;保留极值不保证每个瞬态的形状与准确持续时间。
build_curve_series() integer_like 曲线改用阶梯模式,直接保留全部变换后样本;非阶梯曲线才按桶抽样。 大量整数/CAN 状态样本仍可能全量进入每帧绘制;格式判断最多约每 256 个样本取一步,稀疏小数可能被漏判。
compute_plot_bounds() 依据已准备曲线求 Y 极值,布尔型至少覆盖 0/1,补边距,再应用 pane 的显式 Y 限值。 Y 范围来源于准备后的显示样本;非有限值、极端值和混合单位需单独测试。
draw_state_blocks_pane() 当可见曲线全部具有枚举元数据时,按连续相同状态聚合为彩色块,悬停显示标签、时间区间和持续时间。 块按 llround 后的整数状态划分;不是原始事件边沿的独立校验。
draw_plot() 普通曲线用 ImPlot PlotLine,整数型用 PlotStairs;共享 X 轴、图例当前值、tracker 竖线和点击选时。 状态块、阶梯与线图有不同采样和显示语义,不应合并成一个无差别插件接口。

时间窗、播放与持久化

ensure_shared_range() 优先按布局中的 tab 范围初始化离线路段,实时模式按缓冲时长从最新时间回看;clamp_shared_range() 限制最小水平跨度为 2 秒,并分别处理离线边界和流式右边界;update_follow_range() 将流式窗口保持在最新样本处,infer_stream_follow_state() 根据窗口右缘和目标跨度判断是否恢复跟随。绘图时 ImPlot::SetupAxisLinks 直接绑定 UI 中的 x_view_min/max,所以 pane 之间共用水平视窗。

函数 行为 边界
ensure_shared_range() 离线路段按布局 tab 范围初始化,实时模式按缓冲时长从最新时间回看。 实时窗口依赖缓冲时长,不等于数据源已完整。
clamp_shared_range() 限制最小水平跨度为 2 秒,分别处理离线边界与流式右边界。 跨度限制是显示约束,不改动原始样本。
advance_playback() 按 ImGui 帧间隔与播放倍率推进 tracker,支持视窗循环,非循环时光标越过 70% 视窗后滚动。 播放是 UI 时间光标行为,不控制车辆。
apply_axis_limits_editor() 检查有限数与上下界次序,把共享 X 范围写到 tab 所有 pane、Y 限值写回布局,入撤销栈并自动保存草稿。 继承撤销栈「修改前快照」语义问题。

render_layout() 每帧先维护时间窗,再处理快捷键、播放器和工作区。advance_playback() 按 ImGui 帧间隔与播放倍率推进 tracker,支持选定视窗循环,非循环时在光标越过 70% 视窗后滚动;step_tracker() 以配置步长单步移动。绘图区点击更新 tracker 并画竖线;播放/点击都是 UI 时间光标行为,不会直接控制车辆或改变日志源。

apply_axis_limits_editor() 检查有限数和上下界次序,把共享 X 范围写到 tab 的所有 pane,并把该 pane 的 Y 限值写回布局,随后入撤销栈、自动保存草稿;它继承布局专题指出的撤销栈「修改前快照」语义问题。draw_pane_context_menu() 提供轴编辑、分割、重置视窗与清空曲线;「曲线样式」、复制图片、保存图表和统计项在本版本被显式禁用,不能列为已实现功能。

对「万物即插件」的约束

建议将 SeriesProvider(时间和值及来源)、SeriesTransform(导数/缩放/自定义计算)、PlotRenderer(线/阶梯/状态块)和 TimeCursor(共享窗口/播放)分别定义契约。输入需带时间基准、单调性、单位、枚举表、无值/非有限值规则及最大点数;渲染端有可取消任务与逐帧预算。抽样结果只用于屏幕显示,导出与控制决策须回到原始数据。车端部署仍应把诊断绘图插件限定为只读。

当前同目录 test_help() 仅覆盖 -h。本篇未执行绘图性能、异常时间戳、NaN/Inf、轴编辑或跨 pane 交互测试;地图、相机/缩略图、日志浏览器和导出仍待专题分析。

常见问题

绘图里的曲线抽样会不会丢数据?

抽样只影响显示。app_decimate_samples_impl() 把样本分桶、每桶保留首点/最小/最大/末点并按原索引去重,源码明确它是显示抽样、不是无损数据导出,保留极值并不保证每个瞬态的形状与准确持续时间。导出与控制决策必须回到原始数据。

状态块是按事件边沿画的吗?

不是。draw_state_blocks_pane() 在可见曲线全部具有枚举元数据时,按连续相同状态聚合为彩色块,块按 llround 后的整数状态划分,悬停显示标签、时间区间和持续时间。源码明确这不是原始事件边沿的独立校验。

跟踪时间的图例值能当控制插值用吗?

不能。图例值由采样函数获得:阶梯曲线取前一值,普通曲线线性插值,越界时间取端点值。源码说明这是显示语义,不应直接作为控制回路的时序/插值语义;状态块、阶梯与线图也各有不同采样语义,不应合并成一个无差别插件接口。

JotPluggler 能直接控制车辆吗?

不能。绘图区的点击与播放都只是 UI 时间光标行为,源码明确它们不会直接控制车辆或改变日志源。车端部署应把诊断绘图插件限定为只读,不能因其消费 CAN 曲线而获得 CAN TX 或安全策略写权限。

延伸阅读

参考资料

  • 项目 02 —— openpilot-research/JOTPLUGGLER_PLOT_INTERACTION_DEEP_DIVE.md(取用了曲线进入绘图路径、抽样与状态块语义、时间窗/播放/轴编辑边界与插件约束)。
  • 项目 02 —— openpilot-research/JOTPLUGGLER_LAYOUT_SESSION_DEEP_DIVE.md(仅用于确认撤销栈「修改前快照」语义的接续关系,未取用其未公开内容)。

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

更新记录

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

评论

发表回复

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