Files
DLD154V4B/docs/env-resync-trigger-line-analysis.md
wangfq 6c82bf61b8 fix(V4B): V4.36.43 — 修正 V4.36.42 引入的离开变慢 (G3 计时器递减位置错误)
用户反馈: 烧 V4.36.42 后离开 3、4 秒才释放。

定位(46µH/113kHz 日志): 4 次离开全是 Car_OFF(band_timeout), 延迟 3.5~4.7s; RLN 正常
(有车态 = Origin−41, 说明 G1 生效、100ms 自激已消失) ⇒ 问题出在"正常释放路径被挡住"。

根因: G3 的 g_rel_hold_tick-- 误放 if(!loop1_VD_FLAG) 无车分支(原 :1308) ⇒ 有车期间永不递减
⇒ 首次 Car_In 后常驻 30 ⇒ g_rel_hold_tick==0 永假 ⇒ 正常释放被永久挡住, 只能等 2.5s 环带兜底
+ 平滑值抬升 ⇒ 3.5~4.7s。

修复: 倒计时移到 } else { 有车分支顶部 (TaskLoop.c:1531 后、#if USE_FLATNESS_EXIT 之前,
每 tick 必过且在释放判定之前); 误放旧行改注释留痕。REL_MIN_HOLD_TICKS 仍 30(300ms)。

单测: 8/8 全绿(场景 7 语义不变); 新增"镜像能力边界"警告 —— 镜像只验证算法语义, 验证不了固件内
执行路径/放置位置, 此类问题必须靠日志 Car_OFF 原因字段(band_timeout/FLAT/普通)暴露。
文档: devlog V2.80(+条目) / spec 头 V2.62 / manual 表 V2.19 / cmcng.h 4.36.42→4.36.43 REV 43
/ 分析稿 §13 返工记录。
待现场: 离开延迟回到 ~300ms 内; 缓慢接近/停边界仍不跳。
2026-09-11 17:06:05 +08:00

18 KiB
Raw Permalink Blame History

黄灯常亮(环境重校准)后触发高度变低 — 根因分析

适用版本:V4.36.31 及以后(进入判定已切到 vac_ref();判据 = DBG 行是否含 VEM= 字段) 现象:黄灯常亮故障补偿结束后,金属板进入触发的"高度"明显变低(要压得更近才触发);断电上电后恢复正常。 结论(一句话):环境重校准 env_resync_apply() 原子替换了 loop1_Origin,但没有同步真空 EMA g_vacuum_ema;而进入判定用的是 vac_ref() = min(Origin, EMA),于是补偿后进入线被留在旧真空上、整体低了 Δ(≥4×dlt)⇒ 必须多压出 Δ 的感应量才触发。断电上电时 EMA 由判稳重建 = Origin,故立刻恢复。


1. 判定链(代码位置,全部可复核)

环节 位置 内容
进入线 TaskLoop.c:1432 if (loop1_CAPVD < (vac_ref() - loop1_dlt_ORG) && ...)用 vac_ref,不是 Origin
参考函数 TaskLoop.c:287-299 vac_ref() = (Origin EMA > VAC_REF_DELTA_DLT×dlt) ? EMA : Origin(阈值 =1×dlt,取低者
进入阈值 TaskLoop.c:1278 dlt = Origin × SensTable[SENS] >> 16SENS3=25 ⇒ 0.038%SENS0=337 ⇒ 0.514%
补偿替换 TaskLoop.c:945-958 env_resync_apply()loop1_Origin = new_origin; + 清 freeze/ORG/entry + snap slow没有 EMA
EMA 写点(全仓仅 3 处) 564 / 1246 / 1309-1321 INIT 清零 / 判稳完成 EMA = Origin / 无车对称窗内跟踪(α=128 τ≈1.28s,每步 ±3×dlt25% 自愈)
黄灯常亮 TaskLoop.c:437-442 模式4 = g_env_warn || g_env_resync(环境异常预判 / 重校准学习)
补偿参数 TaskLoop.c:81,82,1082-1084 预判 2s → 补偿 7s → 学习窗 2s/窗 + 连续 2 窗判稳(0.1%) → 替换 → 恢复确认 2s 灭灯

关键事实g_vacuum_ema 的三个写点里没有环境重校准路径 ⇒ 补偿把 Origin 一次性抬到新真空电平后,EMA 仍停在补偿前的旧真空值。


2. 数值推演(Δ 必然 ≥ 4×dlt)

  1. 触发补偿的前提是 dev = CAPVD Origin > +4×dlt 连续 7sENV_RESYNC_WAIT=700)。
  2. 期间处于"负 variation 高位"分支 ⇒ Origin 与 EMA 都被冻结1327-1425),EMA 保持旧真空值。
  3. 学习得到的 B = 当前 CAPVD 的 2s 窗均值(1397-1416)⇒ Origin_new ≈ Origin_old + Δ,其中 Δ ≥ 4×dlt
  4. 替换后:Origin EMA = Δ ≥ 4×dlt > 1×dltvac_ref() 返回 EMA(旧真空)
  5. 进入线 = 旧真空 dlt,而物理正确应为 新真空 − dlt = 旧真空 + Δ − dlt进入线整整低了 Δ(≥4×dlt
    • SENS3dlt=48@130k):Δ ≥ 192 counts ⇒ 0.15% Δf/f 起的额外感应量才触发;
    • devlog J1 实测位移 130~1513 counts ⇒ 观测即"触发高度下降一半甚至更多"。

3. 为什么断电上电就正常

g_vacuum_ema 在 INIT 清 0564),判稳完成时 g_vacuum_ema = loop1_Origin1246)⇒ 两者重合 ⇒ vac_ref() = Origin ⇒ 进入线回到 Origin dlt


4. 为什么"黄灯灭"不等于灵敏度回来了

黄灯灭的条件只有非高位、空闲连续 2sENV_RECOVER_WAIT=2001290-1300),不检查 EMA 是否已追上新 Origin。 补偿后 dev≈0 才落入 ±4×dlt 对称窗 ⇒ EMA 这时才开始以 τ≈1.28s 向上爬:

  • 黄灯灭(替换后 2s)时 EMA 仅追回 1e^(2/1.28) ≈ 79%
  • Δ=130 时仍差 ~27 counts(≈0.56×dlt

5. 为什么"反复试板"会自持(越试越钝)

EMA 更新窗 = 无车 + dev ∈ ±4×dlt1302-1326)。把板拿到感应边缘(未触发、dev −20~−40)时 EMA 被向下拖V4.36.33 devlog 实测 23s 拉低 2040 counts);进入线 = min(Origin, EMA) dlt每次试板都把线再拖低一截 ⇒ 主观上"补偿结束了但灵敏度回不来",直到彻底离开真空若干秒才慢慢回正。

这与 V4.36.33 已记录的老现象("金属板间隔几秒再压,触发高度下降一半甚至更多")是同一机理;本次新增的点是:环境重校准会把 Δ 一次性甩到 4×dlt 以上,比边缘悬停(0.4~0.8×dlt)严重一个量级,且黄灯灭的时刻刚好是 EMA 还差一截的时刻。


6. 第二个机制:真空 EMA 的整数步进死区(2026-09-11 单测实测)

第 5 节解释了"补偿后一次性被压低 Δ",但为什么反复试板后仍感觉回不来、而断电上电就彻底好?单测 tests/test_vac_ref_reanchor.c 实测出第二个机制:

现状公式 (V4.36.35): g_vacuum_ema += _ema_d / VACUUM_EMA_ALFA;   /* 有符号整数除法 */
                     当 |_ema_d| < 128 时, 步进恒为 0  ⇒ 死区 ±127 counts
  • 实测:让 EMA 从真空下方 200 counts 追赶,40s 后停在真空下方 127 counts= 2.59×dlt@SENS3)⇒ 因为 127 > 1×dltvac_ref() 持续选中这个偏低的 EMA ⇒ 进入线常驻偏低 127 counts ≈ 0.10% Δf/f
  • 该偏差不会自愈EMA 只会在 |d| ≥ 128 的样本上移动,而板离开后 d 稳定在 127 附近 ⇒ 需要长时间 CAPVD 噪声偶然越过阈值才缓慢爬升。
  • 断电上电EMA = Origin(判稳建立,精确相等,偏差 0),且此后 |d| < 128 ⇒ EMA 被"粘"在原地不动 ⇒ 永久保持正常灵敏度 ⇒ 这就是"断电上电就好了"的另一半原因。
  • ⇒ 三层叠加:① 补偿一步拉开 Δ(≥4×dlt)② 边缘悬停把 EMA 拖低(≤4×dlt)③ 整数死区把 EMA 钉在真空下方 ~127 counts。三者都让 vac_ref() 选到偏低的参考。

7. 现场验证(建议顺序)

第 0 步先确认版本DBG 行含 VEM=/RLN= ⇒ V4.36.31+,本分析适用;若老版本无 VEM,则走"Origin 被学高"的旧机理,需另判。

  1. 抓日志TTL):补偿会打印
    • EVT|env_learn_start Origin:.. CAPVD:.. dev:..
    • EVT|env_resync Origin:..->.. CAPVD:..
    • 之后每 2s 的 DBG|... Origin=.. VEM=.. RLN=.. 判据Origin VEM > 1×dlt 的持续时间 ≈ "触发高度异常"的持续时间;RLNOrigindlt 的差 ≈ 触发高度损失。
  2. 单变量对照(同一块板、同一高度序列): (a) 补偿后立刻测 → (b) 黄灯灭后静置 10s(板远离)再测 → (c) 断电上电后测 预期 (b) 明显好于 (a)、(b)≈(c)。若 (b) 仍远差于 (c) ⇒ 另有固化路径(学习窗污染 Origin),需再取日志。
  3. 固件对照实验(有权限时,最直接):临时在 env_resync_apply() 内加一行 g_vacuum_ema = new_origin; 复测 ⇒ 触发高度应与断电上电一致 ⇒ 根因钉死。

8. 修复候选(待拍板,暂不改源码)

方案 内容 风险/代价
A(最小对症) env_resync_apply()g_vacuum_ema = new_origin;(与判稳完成建初值同语义:Origin 原子换 ⇒ 真空参考同步换) 低。1 行;补偿后判定参考与 Origin 同时就位,黄灯灭即恢复正常灵敏度
B(通用护栏,建议叠加 A "Origin 大步跳变(|Δ| > k×dlt)即同步重锚 EMA":一次收口 1188(重连判稳对齐 Origin)、1344(冻结超时更新 Origin)等同源路径 低。把"Origin 动、EMA 不动"这类隐患统一堵住
C(稳健性,可选) 黄灯灭条件追加"|Origin EMA| ≤ 1×dlt" ⇒ 让"灯灭 = 判定参考已就位" 低。可能让黄灯多亮 1~3s(现场需接受)
D(建议叠加) EMA 更新改余数累加器(消 d/128 的 ±127 counts 整数死区) 低。单测实测同条件偏差 127→0 counts;τ/噪声不变
✗ 不建议 进入线改回 Origin dlt 会退回 V4.36.31 之前"Origin 学高 ⇒ 假 Car_In"devlog 20260904 已实锤)

9. 待确认(现场)

  • 测试固件的版本号V4.36.31+ 才适用本文)
  • 黄灯是常亮(=环境重校准,模式4)还是其他花样:一长一短=电感量出窗(模式5)/一长两短=156~171k(模式6)/快闪=断开·短路级(模式1)
  • 线圈与 Q 档(33µH/23µH/35µH 现场差异大;V4.36.33 devlog 记 23µH 仍可复现)
  • 触发补偿的动作("金属板拿走/搁置")与位移量 Δ 的量级

10. 补丁草案与验证(已应用,未提交

产物 路径 规模
补丁 A+BOrigin/EMA 同步不变式) docs/patches/v4.36.41-A-B-origin-ema-sync.diff TaskLoop.h +1 TaskLoop.c +20 3
补丁 DEMA 余数累加,消死区) docs/patches/v4.36.41-D-ema-frac-no-deadband.diff TaskLoop.c +13 2
新增单测 tests/test_vac_ref_reanchor.c 5 场景,gcc -O0 -Wall -Wextra 0 warning

A+B 核心:新增 origin_set_atomic(),把"一步换 Origin"的 3 处(env_resync_apply / 重连判稳完成 :1188 / 冻结超时更新 :1344)改为经由它 ⇒ 步长 > 1×dlt 时同步重锚 g_vacuum_ema(与 VAC_REF_DELTA_DLT 同口径 ⇒ 维持 |OriginEMA| ≤ 1×dlt 不变式)。上电首窗那处(:701)不需要改:EMA 在判稳完成建立、此前进入判定被 g_loop_stable 门控挡住。

单测实测结论

场景 现状行为 打补丁后
补偿(Δ=130 counts)后进入线 130457(低 130 = 2.65×dlt = 0.100% Δf/f 130587(低 0
护栏:小步 20(≤1×dlt) / 大步 150 小步不动 EMA ✓ 大步重锚 ✓
V4.36.33 门控(OriginEMA = 30 / 130 用 Origin ✓ 用 EMA ✓ 语义不变 ✓
上电(EMA=0 → 判稳=Origin 进入线 = Origindlt ✓ 不变 ✓
EMA 追赶 40s(现状公式,起点 −200) 停在真空下方 127 counts2.59×dlt 修复 D:偏差 0

已完成的验证:① 两个补丁对干净树 git apply --check 通过,且 A+B → D 顺序 apply 通过;② origin_set_atomic() 独立 gcc -fsyntax-only 通过(确认在文件作用域,不在 vac_ref() 内);③ 大括号平衡 202/201 → 205/204(成对增加);④ 8 个单测全绿(原 7 个未受影响);⑤ 生成补丁过程中已修正两处结构缺陷(helper 误置于 vac_ref() 内、D 的 if (_ema_d != 0) 缺大括号)。

应用/回退git apply docs/patches/v4.36.41-A-B-*.diff(可再叠 -D-*.diff;两者 hunk 不重叠,已顺序 apply 验证);回退 git apply -Rgit checkout -- <file>2026-09-11 更新:A+B 与 D 已按采纳决定应用到工作树(仍未提交) —— TaskLoop.h +1 / TaskLoop.c +33 5(合计 +34 5);CRLF 保持(1825 行全 CR)、大括号 205/204 平衡、8 个单测全绿。回退:git checkout -- utilities/

待拍板:① 采用 A+B?② 叠加 D?③ 提交前要我同步 version/spec 表 + devlog 条目(V4.36.41)?


11. 现场日志实证(2026-09-11 154log260911a.txt):低Q 自激振荡的完整链条

现象:金属板缓慢接近并停在触发边界时,Car_In/Car_OFF 以 ~100ms 周期连续反复(16:34:50.17916:34:53.168 约 3s 内 ~30 次)。线圈 22µH / f=163kHzEVT|coil_fault below_l_min_20_24u)⇒ g_q_lo=1 低Q 路径。

关键实测值Origin=129254、进表 dlt=49SensTable 25)、释表 dlt=23SensTable_1 12)、EMA≈129220CAPVD/慢滤波≈129204(深度 50 counts

三步链条(全部可复核)

  1. 无车loop1_dlt_ORG=49 ⇒ vac_ref() 门控阈值 = 1×49 = 49OriginEMA=34 49 ⇒ 参考 = Origin ⇒ 进入线 = 12925449 = 129205。CAPVD=129204(差 1 count+ 5 样本确认 ⇒ Car_In
  2. 进车瞬间loop1_dlt_ORG 被切成释表值 23TaskLoop.c:1577)⇒ 同一个 gap(34) 现在 > 1×23vac_ref() 改用 EMA ⇒ 释放线 = EMA 1.8×23 = 12922041 = 129179= 日志 RLN 字段精确吻合 ✓)
  3. 释放g_rel_smooth(129204) > 129179 ⇒ 命中 ⇒ 5 样本确认 ⇒ Car_OFF ✓ ⇒ 回到步骤 1 ⇒ 自激

⇒ 滞回带反向重叠:进入需"深于 49",释放只需"浅于 75"= 34 + 41)⇒ 深度落在 (49, 75) 的静止凹脉冲同时满足两条判定 ⇒ 必然振荡。周期 = 进入确认 5 + 释放确认 5 = 10 样本 = 100ms(与日志完全一致)。

与 V4.36.41A+B/D)的关系

  • vac_ref() 切到 EMA 的根因是「门控阈值用了会随状态切换的 loop1_dlt_ORG」+「低Q ×1.8 释放带浅于 ×1.0 进入线」——V4.36.31/33 引入 vac_ref 时就埋下,与 A+B/D 无关
  • D(EMA 精确化)是"改变表象"的那一环:旧版 EMA 有 ±127 死区,此深度下会被粘在 CAPVD 上方 ⇒ gap 为负 ⇒ 参考恒为 Origin ⇒ 释放线 = Origin41 = 129213 ⇒ 够不着 ⇒ 表现为锁死(不释放)而非抖动。⇒ D 把"锁死"变成了"抖",两者都不对。
  • 另注:本文 §1–§10 的主结论(补偿后触发高度变低)不受影响 —— 那段日志里 OriginVEM ≤ 34 < 1×dltvac_ref() 全程取 Origin ✓(先前仿真的"EMA 追赶扫开关"在实机上被"释放后 1s 冻结"抑制,量级被高估)。

修复方向(待评审,先出方案不动代码)

方案 内容 说明
G1 根因 vac_ref() 门控阈值改用固定进入表 dlt(状态无关) 参考线不再"进门瞬间跳 34 counts";进/出判定共用同一参考
G2 滞回一致 低Q 释放线强制浅于进入线:rel_line ≥ vac_ref k×dlt_进k<1,如 0.5)或夹紧 rel_line ≥ entry_line + margin 消除 (49,75) 重叠带,恢复正向滞回
G3 通用抗抖 最小保持时间(进入后/释放后各 N ms 禁止反向) 对所有 Q 档生效,掐断 100ms 级自激
G4 现场 22µH/163kHz 落在电感量告警带(20–24µH);确认量产线圈选型 非量产线圈也要做 G1–G3(30µH 也可能落进重叠带)

12. 修复 G1+G2+G32026-09-11 采纳并已应用到工作树)

决定:D 保留 + 上 G1+G2+G3E 单向方案留在 docs/patches/v4.36.42-E-ema-oneway.diff 备查,未采用)。 ⚠ 关键前提(devlog 救命):REL_BAND_LO_MULT=180=41@SENS3)是 V4.36.28 现场验证过的值 —— 23µH 板"快释放停住"回摆只到 Origin36,靠 41 才释放;任何"缩小释放带"的改法都会把那个锁死改回来 ⇒ G2 只做兜底夹紧,绝不动 180。

方案 改动 位置
G1 根因 新增 vac_gate_dlt()= Origin×SensTable[SENS]>>16状态无关)并让 vac_ref() 用它做门控 ⇒ 进/出车用同一参考,不再在进门瞬间阶跃;与 band_timeout 的既有写法统一 TaskLoop.c:297 / :307
G2 滞回兜底 低Q 释放带夹紧:if (_band > vac_gate_dlt()) _band = vac_gate_dlt()-1; ⇒ 未来参数变化若造成"反向滞回"自动兜住;今天 41<49 不生效(行为零变化) TaskLoop.c:341
G3 通用抗抖 REL_MIN_HOLD_TICKS=30300ms):Car_In 后最小保持期内禁释放(rel_release_hit() 调用点加门),Car_OFF 清零;覆盖两种 USE_SLOW_RELEASE 配置。不延迟检出(只挡释放) TaskLoop.h:68 + TaskLoop.c:1504/1600/1602/1628

单测实证tests/test_vac_ref_reanchor.c 场景 7,现场参数逐位复现 Origin=129254 / dlt 49·23 / EMA≈129220 / CAPVD 深 50):

版本 静止悬停 3s 内 Car_In / OFF
现状 V4.36.41 300 / 300(100ms 周期自激,与现场日志一致)
同上,35µH/128kHz 参数(Origin=129141 / EMA 129109 / 深 53,日志 154log260911b 300 / 300 → 修后 1 / 0
G1 1 / 0 ✓
G1+G2 1 / 0 ✓
G1+G2+G3 1 / 0 ✓

回归保护:撤板仍释放(1/1)✓;V4.36.28 回摆释放(深度 36 < 带回 41)仍成立 ✓;全套 8 个单测全绿 ✓。

双线圈差分验证(2026-09-11 两份现场日志)22µH/163kHz(深 50、gap 34)与 35µH/128kHz(深 53、gap 32同构自激 ⇒ 机理与线圈无关,只取决于 ① f>100kHz(两者都判 QLO=1,而设计窗上限 120125kHz 对应 L≳3740µH ⇒ 量产线圈同样落在低Q 档)② 板停在深度 (49, 41+gap) 的重叠带。 ⇒ 这不是测试线圈特例;停走排队车辆恰好停在环线边缘时同样会踩到(对流量计数即重复计数)。

G3 权衡(现场可选调)300ms 最小保持会让真实车辆离开时的释放最多晚 300ms(对存在型/流量计数无影响;若做速度估计或需要更快的释放沿,可把 REL_MIN_HOLD_TICKS 降到 10=100ms)。

13. 返工记录:V4.36.42 的 G3 放置错误(V4.36.43 修正)

现象:烧 V4.36.42 后离开 3、4 秒才释放;日志 4 次 Car_OFF 全是 band_timeout(环带兜底),延迟 3.5~4.7sRLN 正常(有车态 = Origin41)⇒ G1 目标达成(100ms 自激消失),但正常释放从未命中。

根因:g_rel_hold_tick-- 被放进 if (!loop1_VD_FLAG)(无车)分支(原 :1308)⇒ 有车期间永不递减 ⇒ 首次 Car_In 后常驻 30 ⇒ g_rel_hold_tick == 0 永假 ⇒ 正常释放被永久挡住。

修正:移到 } else {(有车)分支顶部:1531 后、#if USE_FLATNESS_EXIT 之前,每 tick 必过且在释放判定之前)。

教训(已同步进单测注释与 skill

  1. 镜像单测只验证算法语义验证不了固件内执行路径/位置(计数器在哪个分支递减、是否每 tick 必过)⇒ 这类错误必须靠日志的原因字段Car_OFF(band_timeout) / Car_OFF_FLAT / 普通)暴露。
  2. 凡是"进入后 N 秒/N tick 生效"的守卫,实现后先查递减调用点是否在每 tick 必经路径上,再上板。

附:相关历史(devlog

版本 内容
V4.36.29/30 Origin 过冲 + 释放带压缩 → 低Q 卡死 75s;引入环带超时兜底
V4.36.31 引入真空 EMA vac_ref()=min(Origin,EMA),修"Origin 学高 ⇒ 释放线偏高/假触发"
V4.36.33 vac_ref 加 1×dlt 门控,修"边缘悬停拉低 EMA ⇒ 触发高度漂移"(本文同机理,量级更大)
V4.36.34/35 EMA 内存污染与 cast 爆炸源修复((uint32_t)(_ema_d/128)
V4.36.36/38 就绪巩固期 Q 分级;电感量告警模式 6

分析日期:2026-09-11 证据:TaskLoop.c / TaskLoop.h / tests/test_env_resync.c / docs/devlog.md 逐行核对