Files
vd-analysis/docs/measurement-window-quantization.md
T
wangfq 91ed16a9b8 docs(measurement): 新增 PD132T 端到端时序图(三段横轴)+ 符号约定勘误 + 生成工具
- 图示 docs/img/timing-chain-pd132t.{svg,png}:① 一个窗(0~11.2ms)② 检测到输出(0~600ms)③ 基线块平均(0~12s)
  · 曲线按 §1~§4 公式真算:Xn=480 tick/160µs、窗=68×480/3MHz=10.880ms、α=79(τ=35.26ms)、
    dlt=(32640×60)>>16=29、判据线 32611、大信号(+0.5%) 第 1 窗即越线(t≈10.9ms)、小信号(+0.1%) ≈2.2τ≈78ms
  · 同时标注:两种检测路径、50ms 输出节拍量化、上电时 Origin 只有首窗单点样本
- 新工具 tools/plot_timing_chain.py:纯 stdlib 手写 SVG;chromium --headless 转 PNG(零第三方依赖)
- ⚠ 新增 §0.1 符号约定:车辆 ⇒ L↓ ⇒ f↑ ⇒ 周期/Xn↓ ⇒ Value/CAPVD↓(判据取下降侧)
  ⇒ 修正 5 处「深占压(频偏 −5%)⇒ 窗 +5.3%」的方向错误:车辆是频率升高、窗长应【缩短】(−4.8%)
- README:目录树补 img/ 与两个 tools 脚本;表格校验 0 异常(15 文件)
2026-09-11 10:21:42 +08:00

17 KiB
Raw Blame History

测量节拍不是常数:周期累积法的窗口量化(floor(BASE/Xn)·Xn

2026-09-11 · 起因:指出 void CAP1_isr()loop1_Value = loop1_CapSum(累加 LPCNT 个 Xn)与 loop1_LPCNT = 32768/loop1_Xn整数除法测量节拍是区间,不是定值。 适用:PD132T / PD136HA / M1HBASE=32768 同族,代码逐行同构);DLD154V4B / 154Pro 同理(BASE 不同,误差更小,见 §7)。

0. 结论

# 结论 说明
1 测量窗 = floor(BASE/Xn₀) · Xn₁ / f_cap Xn₀ = 学习时算得并冻结的周期计数;Xn₁ = 当前周期计数
2 标称值 BASE/f_cap10.92 / 21.85ms)是上限,不是真值 实际窗 = BASE (BASE mod Xn₀) 个 tick ⇒ 只短不长
3 站点偏差 = (BASE mod Xn₀)/BASE ⇒ 带内 0 ~ 6.2% Xn₀ 越接近带低端(2050)越短;典型 100kHz 线圈(Xn₀=480)为 0.39%
4 另有频率跟踪分量:窗时长 ∝ 1/f_now 阈值量级(≤0.46%)可忽略;深占压(线圈频偏 −5%)时窗变长 +5.3%
5 Δf/f 口径完全不受影响 Value/Origin = Xn₁/Xn₀ 精确成立(整除余数 r 在分子分母完全约掉)⇒ 既有灵敏度/交付结论全部保持
6 loop1_LPCNT 声明为 INT8U(≤255 同族靠 406 < Xn < 2050 保证 LPCNT ≤ 80PD136HA 已把该窗口注释掉Xn < 129(线圈 >372kHz)会 8 位溢出(§5

0.1 符号约定(先钉死,避免把曲线画反)

环节 车辆进入时的方向
线圈电感 L (涡流去磁为主;铁磁体不改变结论方向)
谐振频率 f f = 1/(2π√(LC)));典型 Δf/f = +0.02% ~ +1%,大车/深占压可达 +5%
分频后周期 / Xn∝ 1/f
Value / CAPVD∝ Xn ⇒ 判据 CAPVD < Origin dlt 取的正是下降侧
测量窗时长(∝ 1/f (−0.02% ~ 4.8%

与交付报告一致的关系式:CAPVD ≈ BASE·f₀/f(Origin CAPVD)/Origin ≈ Δf/f车辆时 > 0)✓ ⇒ "深占压"= 频率升高、窗长缩短;旧稿"频偏 5% ⇒ 窗 +5.3%"方向写反,2026-09-11 已改正(窗长公式 ∝ f₀/f_now 本身对两个方向都成立,只是场景标注错了)。

1. 代码机理(PD132T / PD136HA 逐行同构)

/* PD132T main.c:96-105 / PD136HA main.c:163-172 —— 学习期:LPCNT 算一次后冻结 */
if (loop1_INI_LOOP) {
    if (loop1_LPCNT == 0) {
        if (loop1_CapCnt > 100) {
            loop1_LPCNT = 32768 / loop1_Xn;   // ← 整数除法,截断
            if (loop1_LPCNT == 0) loop1_LPCNT = 1;   // Xn>32768 时兜底
            loop1_CAPVD = 0; loop1_CapSum = 0; loop1_CapCnt = 0;
        }
    }
}
/* 测量期:累加 LPCNT 个 Xn */
loop1_CapSum += loop1_Xn;
if (loop1_CapCnt >= loop1_LPCNT) {
    loop1_Value = loop1_CapSum;    // = Σ(LPCNT 个 Xn)
    loop1_CapSum = 0; loop1_CapCnt = 0;
}

关键性质(两条都容易被忽略):

  1. LPCNT 是"一次冻结":只在 LPCNT == 0CapCnt > 100 时计算一次,此后不再重算;只有 INIT_VD()(学习/复位)才 LPCNT = 0 重新标定。
  2. CapCnt++ 发生在累加之前PD132T:83 / PD136HA:148)⇒ 判据触发时 CapSum 恰好含 LPCNT 项 ⇒ Value = LPCNT × XnXn 恒定时)✓

⇒ 因此:窗长(tick= LPCNT × Xn₁ = floor(BASE/Xn₀) × Xn₁,而不是恒定 BASE


2. 代数:窗为什么"只短不长",而口径为什么不受影响

BASE = 32768r₀ = BASE mod Xn₀,则

LPCNT = (BASE  r₀) / Xn₀            (整数除法)
窗长(tick) = LPCNT · Xn₁ = (BASE  r₀) · (Xn₁ / Xn₀)      (Xn₁ = 当前值)
窗时长     = 窗长(tick) / f_cap

两条分量

分量 表达式 量级 性质
① 整除余数量化 (BASE r₀)/BASE 带内 0 ~ 6.2% 站点确定值(不抖动;随该站点线圈频率而定)
② 频率跟踪 Xn₁/Xn₀ = f₀/f_now 阈值内 ≤ +0.46%;深占压 +5% 量级 与频偏同向(频率跌 ⇒ 窗变长)

口径为什么不受影响(最重要):

Origin = LPCNT · Xn₀ = BASE  r₀          (学习值)
Value  = LPCNT · Xn₁                      (当前值)
Value / Origin = Xn₁ / Xn₀   ← 精确,r₀ 完全约掉 ✓

⇒ 相对量 Δf/f = 1 Value/Origin 与 r₀、与 BASE、与 f_cap 全部无关 ✓(数值验证:Xn₀ = 406/480/800/2050 四种站点,频偏 0.5% 时 Value/Origin 均精确等于 Xn₁/Xn₀)。

唯一残留误差是判据阈值的截断dlt = Origin × 表值 >> 16 ⇒ 有效阈值 = 表值/65536 ± 1/Origin,即 ±0.003%1/32768)—— 可忽略。

3. 数值表(按站点线圈频率)

Xn₀ = C / f_coil(族内 C = 32·f_cap/边沿系数 = 4.8×10⁷PD132T/PD136HA 通用):

线圈频率 Xn₀ LPCNT 窗长(tick) PD132T 窗@3MHz 偏差 PD136HA 窗@1.5MHz 偏差 M1H 窗@4MHz
240 kHz 200 163 32 600 10.867 ms 0.51% 21.733 ms 0.51% 8.150 ms
100 kHz(标称) 480 68 32 640 10.880 ms 0.39% 21.760 ms 0.39% 8.160 ms
60 kHz 800 40 32 000 10.667 ms 2.34% 21.333 ms 2.34% 8.000 ms
40 kHz 1200 27 32 400 10.800 ms 1.12% 21.600 ms 1.12% 8.100 ms
23.4 kHz(带低端) 2050 15 30 750 10.250 ms 6.16% 20.500 ms 6.16% 7.688 ms
(带内最差) 2049 15 30 735 10.245 ms 6.20% 20.490 ms 6.20% 7.684 ms

读法10.92 ms / 21.85 ms标称上限;具体站点按上表由线圈频率决定(偏差随 LPCNT 变小而变大 —— 低频线圈的窗更短、量化更粗)。

积分深度(每窗平均的有效线圈周期数)同样按此缩放:PD132T ≈ 16·LPCNT、PD136HA ≈ 32·LPCNT100kHz 站点:1088 / 2176 个线圈周期)。


4. 频率跟踪分量(窗随时间"伸缩")

因为 LPCNT 冻结、而窗累加的是当前周期,所以:

窗时长(t) = 窗长(tick)/f_cap = (BASE  r₀)/f_cap × (f₀ / f_now)
线圈频偏 窗时长变化 现场含义
0.032%PD132T 档 3 阈值) +0.032% 可忽略
0.458%PD132T 档 0 阈值) +0.460% 可忽略
1% +1.01% 温度漂移量级
5% +5.26% 大车/拖车深占压:窗变长 ⇒ 恢复更慢

⇒ 结论:在"检测阈值"量级上可忽略(≤0.5%),在"深占压"场景下会把响应/恢复时间再拉长百分之几。这也解释了为什么"深占压后基线跟踪慢"的代码设计(冻结 + LC_HOLD 重学)是必要的。


5. ⚠ 位宽与窗口检查(本轮新发现)

变量 类型 上限 约束 风险
loop1_LPCNT INT8UPD132T main.h:37 / PD136HA main.h:64 255 LPCNT = 32768/Xn ≤ 255 ⇒ 需 Xn ≥ 129 ⚠ 见下
loop1_CapCnt INT8U 255 LPCNT ≤ 255(同上) 同上
loop1_CapSum / Value / CAPVD INT16U 65 535 标称 32 768余量 2× 频率下漂 >100%(Xn 翻倍)才溢出 ⇒ 现实不可达

同族靠频率窗口保护 8 位 LPCNT406 < Xn < 2050LPCNT ≤ 80 ✓(PD132T main.c:85、M1H 同构)。 PD136HA 已把该窗口注释掉// if(loop1_Xn>400 && loop1_Xn<2030)main.c:150V3.14 去环路 LED 时一并屏蔽)⇒ 保护消失

  • Xn < 129(等效线圈 > 372 kHz,例如线圈断线/短路的异常振荡、或强干扰产生的窄脉冲)⇒ LPCNT 真值 >255 ⇒ 8 位截断(如 327 → 71)⇒ 窗长骤降(71×Xn)而 Origin ≈ 32768Value ≪ Origin dlt判为"持续有车" ⇒ 继电器吸合 → HOLD 300s 超时 → 整机复位。
  • 对比 PD132T:同样的异常会走 LOOP_OK = 0 + LED 快闪(故障指示,不产生有车输出)。
  • 行为差异:同一种线圈异常,PD132T 报"故障"PD136HA 可能报"堵车"。
  • 建议(固件侧,待评估):PD136HA 恢复窗口检查,或至少加 if (loop1_LPCNT > 80) → 判故障 的兜底钳制。

同类检查也适用于 M1H(同 8 位 LPCNT + 无窗口检查 ⇒ 同风险)。注意:PD132T 的窗口检查同时承担"线圈异常 → 故障"的职责,恢复/删除前要确认异常语义有替代路径。


6. 受影响 / 不受影响(既有数值的正确读法)

类别 项目 处置
不受影响 Δf/f = 表值/65536 口径、灵敏度表换算、跨型号档位对比、进入/离开判据、滞回语义 保持不动(§2 代数保证)
⚠️ 同一因子缩放 测量节拍、基线更新周期、IIR 时间常数 τ、进入/离开响应时间、最小可检占压、积分深度 表中数值标注"标称(上限)",实际 = 标称 × (BASEr₀)/BASE × f₀/f_now

修正后的表述模板(建议全仓沿用):

测量节拍 ≈10.88 ms(标称上限 10.923 ms = 32768/3MHz;实际 = floor(32768/Xn₀)·Xn₀/f_cap100kHz 站点 0.39%,带内最差 6.2%)

例:PD132T 基线更新周期 = 100 × 10.88ms = 1.088 s(标称上限 1.092s);PD136HA = 256 × 21.76ms = 5.571 s(标称上限 5.593s)。


7. 跨型号意义:BASE 越大量化误差越小

站点量化误差上限 = Xn₀/BASE= 1/LPCNT):

型号 BASE 100kHz 站点 Xn₀ LPCNT 窗量化误差上限
M1H / PD132L / PD132T / PD136HA 32 768 480PD132T/136HA 68 ≤1.46%(带低端 ≤6.25%
DLD154V4B 131 072 1200 109 ≤0.92%
DLD154Pro 524 288 600 873 ≤0.11%

大基数的第二个好处(除既有"绝对分辨率高 4×"之外):同比降低窗量化误差 —— 因为 LPCNT 更大,"floor 截断"的相对损失更小。V4B 的 1.09ms 窗实际为 ≈1.09ms(−0.9% 上限),154Pro 的 8.74ms 窗几乎无量化损失。


8. 验证与复算方法

  1. 纯计算(现场 5 分钟):量出线圈频率 f_coilXn₀ = C/f_coilC = 4.8×10⁷PD132T/PD136HA 通用)⇒ LPCNT = ⌊32768/Xn₀⌋ ⇒ 窗长 = LPCNT·Xn₀ tick ⇒ 窗时长 = 窗长/f_cap。可算到 0.1% 以内。
  2. 固件侧:把 loop1_LPCNT / loop1_Value 引到可用观测(PD132T 无 UART ⇒ 需临时改 LED 或 ISP 读 RAMPD136HA 有 SWD ⇒ 直接在线看变量最省事)。
  3. 回归验证:若实测"基线更新时间"与 窗长×100(256) 不符,说明 LPCNT 被二次重算过 —— 检查是否存在第二处 LPCNT = 0PD132T INIT_VD()、PD136HA main.c:411;其 430 行为注释状态)。
  4. 台架:信号发生器替代 CD4060 输出已知 f_in ⇒ 交替改频率 0.1%/0.5%/2%,观察继电器翻转窗口数与理论 dlt 是否一致(验证 §2 的"口径精确"结论)。

9. 时序链详解:六个量的更新时间(2026-09-11 补,用户提出)

PD132T 端到端时序图:三段横轴 —— ① 一个窗(011.2ms)② 检测到输出(0600ms)③ 基线块平均(0~12s)

矢量版(可放大/edit):docs/img/timing-chain-pd132t.svg(由 tools/plot_timing_chain.py 生成,纯 stdlib 手写 SVG;图中曲线按 §1~§4 公式真算而非示意): python3 tools/plot_timing_chain.py && chromium --headless=new --disable-gpu --no-sandbox --default-background-color=FFFFFFFF --window-size=1680,1560 --screenshot=docs/img/timing-chain-pd132t.png file://$PWD/docs/img/timing-chain-pd132t.svg

整条链是三级降采样:捕获级(每 Xn 一次)→ 窗级(每 LPCNT 次捕获一次)→ 基线级(每 100/256 窗一次)。

捕获中断(PCA/ADTIM4)          窗结账(ISR 内)              主循环(紧随其后)         块平均
      │                          │                          │                    │
  每 t_Xn 一次              每 LPCNT 次捕获            每次 Value 更新     每 100/256 窗
      ├── Xn = 相邻捕获差 ────► ├── Value = Σ Xn ────────► ├── CAPVD = IIR(Value) ──► ├── Origin = ΣCAPVD/N
      │   (6.25 kHz)            │   (92 Hz)                │   (92 Hz)               │   (0.92 Hz)
      └── 频率窗口检查(若启用)   └── CAP_OK = 1             └── 判据 dlt = Origin×表>>16

9.1 六个量逐一(PD132T @ 100kHz 站点,Xn₀=480LPCNT=68

更新触发 更新周期 物理含义
loop1_Xn 每次捕获中断(相邻两次捕获之差) t_Xn = Xn/f_cap = **160 µs**6 250 次/s ÷32 后信号的半周期 = 16 个线圈周期(双沿捕获)
loop1_Value ISR 内 CapCnt >= LPCNT LPCNT·t_Xn = **10.880 ms**92 次/s LPCNTXn 之和(= 窗)⇒ 同步置 CAP_OK=1
loop1_CAPVD 紧随其后调用 VD1_TASK()(主循环) 10.880 ms(与 Value 同拍) IIR 平滑值:CAPVD += (ValueCAPVD)·Flt_Reg>>8
loop1_Origin VD1_TASK()ORG_CNT == 99无车 100 窗 = 1.088 s0.92 次/s Origin = ORG_SUM/100块平均,非滑动)
测量节拍 = Value/CAPVD/判据的共同节拍 10.880 ms(标称上限 10.923 见 §1-§3(区间值,按站点 −0.4%~−6.2%)
基线更新周期 = Origin 的更新间隔 1.088 s(标称上限 1.092 s 块粒度 ⇒ 基线跟踪"阶梯式"
IIR 时间常数 τ α79: 35.3 ms= 3.24 窗/ α64: 43.5 ms= 4.0 窗 τ = T·256/α

9.2 同表:PD136HA @ 100kHzXn₀=480LPCNT=68f_cap 减半)

更新周期 与 PD132T 的比
loop1_Xn 480/1.5MHz = **320 µs**3 125 次/s ×2(单沿 ⇒ 整周期 = 32 个线圈周期
loop1_Value 68 × 320µs = **21.760 ms**46 次/s ×2
loop1_CAPVD 21.760 ms ×2
loop1_Origin 256 窗 = **5.571 s** ×5.1256/100 × 2
τ(α64 87.0 ms = 4.0 窗 窗数相同、绝对 ×2

9.3 四条容易搞混的关系(重点)

  1. t_Xn = 前端分频比 /(边沿系数 · f_coil) —— PD132T = 16/f_coil、PD136HA = 32/f_coil、M1H = 32/f_coil(双沿 ⇒ 16,单沿 ⇒ 32)。Xn 是"测量结果"而不是"时间源":它本身随线圈频率变化(Xn ∝ 1/f),所以"更新周期"必须写成 Xn/f_cap(随站点变),而不是常数。
  2. Value 是"计数域降采样",不是抽点Value = Σ(LPCNT 个 Xn)箱式积分boxcar),因此 Xn 的 6.25kHz 信息在 92Hz 处被平均而不是被丢弃(无混叠问题);这也是 Δf/f 口径精确的来源(§2)。
  3. τ 换算成"窗"后与型号无关τ/T = 256/αα79 = 3.24 窗、α64 = 4.0 窗(与 BASE、f_cap、分频比全无关)⇒ 讨论"滤波快慢"时用"窗"最直观。
  4. Origin 是块平均:每 100PD136HA 256)个 CAPVD 才整体替换一次 ⇒ 基线跟踪的时间分辨率 = 1 个块1.09 s / 5.57 s),块内一直用"上一个块的均值"。有车期间 ORG_SUM 被清零且 ORG_CNT 停止 ⇒ 有车冻结 + 重新累计(不是"有车也跟踪")。

9.4 响应链:三段叠加

延迟 说明
① 窗内平均 ≤ 1 窗(≤10.9 / 21.8 ms 变化要"填满"当前窗才在 Value 中完整体现
② IIR 上升到阈值 ≈ τ·ln(Δ/(Δ−阈值)) 大信号快(Δ=1% vs 阈值 0.09% ⇒ ≈0.09τ ≈ 3ms);小信号慢(Δ≈阈值 ⇒ 多个 τ)
③ 输出级 50 ms 节拍(PD132T/M1HPD136HA tick ⚠待实测) FLAG_IN 在 T1 ISR 里被消费;进入确认 PD132T = 0(立即,无 IN_DELAY 判定)、M1H = IN_DELAY=10≈0.6 s

⇒ 例:PD132T 大车(Δ≈1%)⇒ ≈10.9 + 3 + ≤50 ≈ ≤65 msPD136HA 同条件 ⇒ ≈21.8 + 6 + ≤50 ≈ ≤80 msIIR 更慢但窗长翻倍是主因)。

9.5 上电初期的一个易忽略点

INI_LOOP 结束时(CapCnt>100 后第一个满窗)执行的是:

loop1_CAPVD = loop1_CapSum;  loop1_Origin = loop1_CAPVD;  loop1_INI_LOOP = 0;

上电时 Origin 只有一个窗的样本92Hz 单点、噪声最大),要等第一个块(100/256 窗 = 1.09 s / 5.57 s 完成才变成块平均 ⇒ 这就是"上电静默期/学习期"必须留足时间的根本原因(PD136HA 侧更长,5.6 s)。


相关文档

  • 双型号对比:docs/pd132t-vs-pd136ha-comparison.md(§3 测量与归一化)
  • 跨型号总表:docs/cross-model-comparison.md(测量节拍/基线周期行)
  • 滤波系数专题:docs/filter-coefficient-79-vs-64.md(τ 口径)
  • 交付对齐:docs/pd136ha-to-dld154v4b-delivery-alignment.mddocs/pd132t-to-v4b-delivery-alignment.md