fix(V4B): V4.36.41 — 环境重校准后触发高度变低根治 (基准/派生参考同步 + EMA 余数累加)

用户反馈: 黄灯常亮故障补偿后, 金属板进入触发的高度比断电上电后低很多, 补偿结束也恢复不了正常灵敏度。

根因(两处, 均可复核): V4B 进入判定用 vac_ref()=min(Origin, 真空EMA) 而非 Origin (TaskLoop.c:1432)
 ① env_resync_apply() 只原子换 loop1_Origin, 没同步 g_vacuum_ema。补偿前提是 dev>+4×dlt 连续 7s,
    学到的 B≈当前 CAPVD ⇒ Δ≥4×dlt 必然 ⇒ Origin−EMA=Δ>1×dlt ⇒ vac_ref 必然切旧 EMA ⇒ 进入线整整低 Δ
 ② g_vacuum_ema += _ema_d/128 为整数除法, |d|<128 时步进恒 0 ⇒ EMA 粘在真空下方 ≤127 counts
    (2.59×dlt > 1×dlt 门槛) ⇒ 进入线常驻偏低 ≈0.100% Δf/f 且不自愈; 断电上电 EMA=Origin(偏差 0) 故持久正常

修复:
 - 新增 origin_set_atomic() (TaskLoop.c:308): "一步换 Origin"时 |ΔOrigin| > ORIGIN_JUMP_REANCH_DLT(=1)×dlt
   则同步重锚 g_vacuum_ema; 与 VAC_REF_DELTA_DLT 同口径 ⇒ 维持 |Origin−EMA| ≤ 1×dlt 不变式; 小步不动 EMA
 - 3 处 Origin 写入点改经它: env_resync_apply(:968) / 重连判稳完成(:1207) / 冻结超时更新(:1372)
 - EMA 更新改余数累加器 g_vac_frac(:1344) 消 ±127 counts 死区; g_vac_frac 在 INIT(:583)/判稳(:1266) 清零

单测(新增 tests/test_vac_ref_reanchor.c, 5 场景; gcc -O0 -Wall -Wextra 0 warning; 全套 8/8 全绿):
 补偿后进入线 130457(低 130 counts = 2.65×dlt = 0.100% Δf/f) → 130587(低 0); EMA 追赶偏差 127→0 counts

文档: devlog V2.78(+条目) / spec 文档头 V2.60 / manual 版本表 V2.17 / cmcng.h 4.36.40→4.36.41 (REV 41)
归档: docs/env-resync-trigger-line-analysis.md + docs/patches/v4.36.41-{A-B,D}*.diff

待现场(编译烧录后回填 devlog 条目): 4 项 [ ] + 2 处日志待填 (EVT|env_resync / DBG Origin·VEM·RLN)
This commit is contained in:
wangfq
2026-09-11 14:08:25 +08:00
parent 1de4b4c8f3
commit 5fd58e035a
10 changed files with 551 additions and 9 deletions
+39
View File
@@ -4,6 +4,44 @@
--- ---
## 2026-09-11 — 环境重校准后触发高度变低根治:基准/派生参考同步 + EMA 余数累加(固件 V4.36.41
用户反馈:黄灯常亮故障补偿后,金属板进入触发的高度比断电上电后低很多,感觉补偿结束后恢复不了正常灵敏度的触发高度。
**根因(两处,均可复核)**V4B 的**进入判定用的是 `vac_ref() = min(Origin, 真空EMA)`,不是 `Origin`**`TaskLoop.c:1432`)。而 V4.36.31 引入真空 EMA 后,**补偿路径没有同步它**:
- **机制1(一次性,量级大)**`env_resync_apply()` 只原子替换 `loop1_Origin`EMA 停在旧真空。补偿的前提是 `dev > +4×dlt` 连续 7s,学到的 B ≈ 当前 CAPVD ⇒ **Δ ≥ 4×dlt 是必然**,于是 `Origin EMA = Δ > 1×dlt``vac_ref()` **必然**切到旧 EMA ⇒ 进入线整整低 Δ(SENS3@130k:Δ≥192 counts ≈ 0.15% Δf/f 的额外触发门槛)。断电上电时 EMA 由判稳重建 = Origin,故立刻恢复。
- **机制2(常驻,单测实测)**`g_vacuum_ema += _ema_d/128` 是**整数除法**`|d| < 128` 时步进恒为 0 ⇒ EMA 被"粘"在**真空下方最多 127 counts2.59×dlt > 1×dlt 门槛)** ⇒ 进入线常驻偏低 ≈ 0.100% Δf/f 且不自愈;断电上电因 `EMA = Origin` 精确相等(偏差 0)而持久正常。
**实现**
- 新增 `origin_set_atomic()``TaskLoop.c:308`):"一步换 Origin"时,若 `|ΔOrigin| > ORIGIN_JUMP_REANCH_DLT(=1)×dlt` 则同步重锚 `g_vacuum_ema`;与 `VAC_REF_DELTA_DLT` 同口径 ⇒ 维持 `|OriginEMA| ≤ 1×dlt` 不变式;小步(≤1×dlt,如悬停抖动/重连对齐)不动 EMA,不干扰对称窗内的实时跟踪。
- 3 处 Origin 写入点改经它:**`env_resync_apply`(:968,补偿路径)**、重连判稳完成(:1207)、冻结超时更新(:1372)。
- EMA 更新改**余数累加器** `g_vac_frac`:1344):消 ±127 counts 整数死区,τ/噪声不变;`g_vac_frac` 在 INIT:583/判稳(:1266)同步清零。
- 新增镜像单测 `tests/test_vac_ref_reanchor.c`:5 场景(复现+修复对比 / 护栏大小步 / V4.36.33 门控回归 / 上电语义 / 死区实测)。
**单测实测数字**`Origin 130506→130636`,Δ=130dlt=49):
| 场景 | 现状 | V4.36.41 |
|---|---|---|
| 补偿后进入线 | 130457(低 130 = 2.65×dlt = 0.100% Δf/f | **130587(低 0** |
| EMA 追赶 40s 后偏差 | 127 counts2.59×dlt | **0** |
**现场验证(待填)**
- [ ] 制造黄灯常亮补偿 → 抓 `EVT|env_learn_start` / `EVT|env_resync Origin:..->..`
- [ ]`DBG|… Origin=… VEM=… RLN=…`:确认 `Origin VEM` 是否长期 > 1×dlt(本根因的直接判据)
- [ ] 单变量对照(同一块板同一高度序列):(a)补偿后立刻测 /(b)灭灯后静置 10s 再测 /(c)断电上电测 —— 本版应 (a)≈(b)≈(c)
- [ ] 现场日志片段粘贴区:
```
(待填)EVT|env_resync Origin:……->……
(待填)DBG|…… Origin=…… VEM=…… RLN=…… dlt=……
```
语法:待 MRS 编译确认(本机无 AT32 工具链);**镜像单测 8/8 全绿**gcc -O0 -Wall -Wextra0 warning);REV 4.36.41spec V2.60 / devlog V2.78。
> 归档:根因分析稿 `docs/env-resync-trigger-line-analysis.md`;补丁 `docs/patches/v4.36.41-A-B-origin-ema-sync.diff`、`docs/patches/v4.36.41-D-ema-frac-no-deadband.diff`。
> ⚠ 提交前:`cmcng.h` 的 `FIRMWARE_VER` 字符与 `FIRMWARE_VER_REV` 需 40→41`technical-spec.md` 文档头(固件版本 V4.36.41 / 文档版本 V2.60)同步;本条目日期按实际 commit 日校准。
## 2026-09-08 — 就绪巩固期 Q 分级:低Q 上电首压防卡(固件 V4.36.36) ## 2026-09-08 — 就绪巩固期 Q 分级:低Q 上电首压防卡(固件 V4.36.36)
用户反馈:电感偏低/低Q 线圈上电自检结束(绿灯灭)马上压板,大概率绿灯常亮;10s 后再测正常。要求绿灯自检闪时长分级:高Q 5s/中Q 7s/低Q ≥10s。 用户反馈:电感偏低/低Q 线圈上电自检结束(绿灯灭)马上压板,大概率绿灯常亮;10s 后再测正常。要求绿灯自检闪时长分级:高Q 5s/中Q 7s/低Q ≥10s。
@@ -2151,6 +2189,7 @@ if (dev >= dlt_ORG × 4) {
| 版本 | 时间 | 说明 | | 版本 | 时间 | 说明 |
|------|------|------| |------|------|------|
| V2.78 | 2026-09-11 | V4.36.41 环境重校准后触发高度变低根治: 新增 origin_set_atomic() 同步真空 EMA(3 处写入点; 维持 OriginEMA ≤1×dlt 不变式) + EMA 余数累加消 ±127 counts 整数死区; 新增镜像单测 test_vac_ref_reanchor 5 场景全绿 |
| V2.77 | 2026-09-08 | 注释同步: TaskLoop.c/h + tests 全部 疾闪 → 一长两短 (V4.36.38 现役描述, 历史版本行保留变更记录) | | V2.77 | 2026-09-08 | 注释同步: TaskLoop.c/h + tests 全部 疾闪 → 一长两短 (V4.36.38 现役描述, 历史版本行保留变更记录) |
| V2.76 | 2026-09-08 | V4.36.40 热切换修复: fault3 强制期 Origin/LPCNT 冻结 → recover 改 TMR3 实时 Xn 判回窗(240000/Xn); 恢复后 LOOP_OK 回位自动发起重连确认+判稳重建 Origin, 换线圈不断电 ~6s 自愈 | | V2.76 | 2026-09-08 | V4.36.40 热切换修复: fault3 强制期 Origin/LPCNT 冻结 → recover 改 TMR3 实时 Xn 判回窗(240000/Xn); 恢复后 LOOP_OK 回位自动发起重连确认+判稳重建 Origin, 换线圈不断电 ~6s 自愈 |
| V2.75 | 2026-09-08 | V4.36.39: COIL_UNDER_MIN_AS_OPEN 默认 0→1 (用户拍板) —— L<20µH 短路级出厂等同断开 | | V2.75 | 2026-09-08 | V4.36.39: COIL_UNDER_MIN_AS_OPEN 默认 0→1 (用户拍板) —— L<20µH 短路级出厂等同断开 |
+151
View File
@@ -0,0 +1,151 @@
# 黄灯常亮(环境重校准)后触发高度变低 — 根因分析
> 适用版本:**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] >> 16`SENS3=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` **连续 7s**`ENV_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×dlt``vac_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 清 0`564`),判稳完成时 `g_vacuum_ema = loop1_Origin``1246`)⇒ 两者重合 ⇒ `vac_ref() = Origin` ⇒ 进入线回到 `Origin dlt`
---
## 4. 为什么"黄灯灭"不等于灵敏度回来了
黄灯灭的条件只有**非高位、空闲连续 2s**(`ENV_RECOVER_WAIT=200``1290-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×dlt``1302-1326`)。把板拿到感应边缘(未触发、dev −20~−40)时 **EMA 被向下拖**V4.36.33 devlog 实测 2~3s 拉低 20~40 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×dlt`vac_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` 的持续时间 ≈ "触发高度异常"的持续时间;`RLN``Origindlt` 的差 ≈ 触发高度损失。
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 |
| 补丁 D(EMA 余数累加,消死区) | `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 -R``git 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)?
---
## 附:相关历史(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` 逐行核对*
@@ -0,0 +1,67 @@
diff --git a/utilities/at32f421_freertos_demo/inc/TaskLoop.h b/utilities/at32f421_freertos_demo/inc/TaskLoop.h
index afece45..35d3d36 100644
--- a/utilities/at32f421_freertos_demo/inc/TaskLoop.h
+++ b/utilities/at32f421_freertos_demo/inc/TaskLoop.h
@@ -43,6 +43,7 @@
#define VACUUM_EMA_ALFA 128 // V4.36.31: 真空 EMA 平滑因子 (τ≈1.28s@10ms): B3 判定参考解耦
// 无车 dev 窗内跟踪实时真空; 噪声 σ_ema≈σ/16 (σ1-2 → 0.1 counts)
#define VAC_REF_DELTA_DLT 1 // V4.36.33: vac_ref 重锚阈值 (×dlt): 仅 OriginEMA > 1×dlt(真锚错)
+#define ORIGIN_JUMP_REANCH_DLT 1 // V4.36.41: Origin 单步跳变 > 1×dlt ⇒ 同步重锚真空 EMA (B1 护栏)
// 才用 EMA 锚低 — 防金属板悬感应边缘(dev20~40<4dlt)快速拉低 EMA
// → 触发线漂移(触发高度下降); 小偏差仍用 Origin
// 150(→35)不够: 23µH 现场实测板停临界回摆 Origin-36, 需释放线<Origin-37 才释放;
diff --git a/utilities/at32f421_freertos_demo/src/TaskLoop.c b/utilities/at32f421_freertos_demo/src/TaskLoop.c
index afbe2d1..2a16a6a 100644
--- a/utilities/at32f421_freertos_demo/src/TaskLoop.c
+++ b/utilities/at32f421_freertos_demo/src/TaskLoop.c
@@ -297,6 +297,23 @@ static uint32_t vac_ref(void)
return loop1_Origin;
}
+/* V4.36.41: 原子换 Origin ⇒ 同步真空参考 (B1 通用护栏)
+ * 背景: 进入判定用 vac_ref()=min(Origin,EMA); 任何"一步换 Origin"的路径若不换 EMA,
+ * 现场环境重校准后 Origin 已到新真空而 EMA 停在旧真空 ⇒ 固定偏差
+ * (实测拉开 ≥4×dlt) ⇒ 进入线被拖低同量 ⇒ 触发高度明显变低, 断电上电才恢复
+ * 策略: 步长 > ORIGIN_JUMP_REANCH_DLT×dlt 才重锚 EMA; 小步(≤1×dlt)不动,
+ * 与 VAC_REF_DELTA_DLT 同口径 ⇒ 保持 |OriginEMA| ≤ 1×dlt 不变式,
+ * 不干扰 EMA 在对称窗内的实时真空跟踪 */
+static void origin_set_atomic(uint32_t new_origin)
+{
+ int32_t _jump = (int32_t)new_origin - (int32_t)loop1_Origin;
+ if (_jump < 0) _jump = -_jump;
+ loop1_Origin = new_origin;
+ if (g_vacuum_ema == 0 || loop1_dlt_ORG == 0 ||
+ _jump > (int32_t)(loop1_dlt_ORG * ORIGIN_JUMP_REANCH_DLT))
+ g_vacuum_ema = new_origin;
+}
+
static uint8_t rel_release_hit(void)
{
uint32_t _cmp = loop1_CAPVD;
@@ -946,7 +963,7 @@ static void env_resync_apply(uint32_t new_origin)
{
PRINT("EVT|env_resync Origin:%d->%d CAPVD:%d\n",
loop1_Origin, new_origin, loop1_CAPVD);
- loop1_Origin = new_origin;
+ origin_set_atomic(new_origin); /* V4.36.41: 同步真空 EMA — 补偿后进入线不再被旧真空拖低 */
loop1_freeze_cnt = 0;
loop1_freeze_ref = 0;
loop1_ORG_CNT = 0;
@@ -1185,7 +1202,7 @@ void vd1_task(void)
if (_d < 0) _d = -_d;
if (_d <= (int32_t)_dlt) {
/* 回带内=空场确认 → Origin 对齐当前空场(消基线差) */
- loop1_Origin = loop1_CAPVD;
+ origin_set_atomic(loop1_CAPVD); /* V4.36.41: 护栏(小步≤1×dlt 不动 EMA) */
loop1_CAPVD_slow = loop1_CAPVD;
loop1_dlt_ORG = ((uint32_t)loop1_Origin *
SensTable[loop1_SensLevel]) >> 16;
@@ -1341,7 +1358,7 @@ void vd1_task(void)
}
loop1_freeze_cnt++;
if (loop1_freeze_cnt >= FREEZE_TIMEOUT) {
- loop1_Origin = loop1_CAPVD;
+ origin_set_atomic(loop1_CAPVD); /* V4.36.41: 护栏(大步下跳也同步重锚) */
loop1_freeze_cnt = 0;
loop1_freeze_ref = 0;
loop1_ORG_CNT = 0;
@@ -0,0 +1,49 @@
diff --git a/utilities/at32f421_freertos_demo/src/TaskLoop.c b/utilities/at32f421_freertos_demo/src/TaskLoop.c
index afbe2d1..a8bc9d6 100644
--- a/utilities/at32f421_freertos_demo/src/TaskLoop.c
+++ b/utilities/at32f421_freertos_demo/src/TaskLoop.c
@@ -48,6 +48,7 @@ uint16_t g_origin_freeze_tick = 0; // V4.36.26/32: 释放后 Origin 全冻
uint16_t g_origin_lowlearn_tick = 0; // V4.36.32: 冻结后降权低速补偿计数 (2s, 残余低值降权学入)
uint16_t g_rel_band_cnt = 0; // V4.36.30: 低Q 环带超时兜底计数 (离开触发深度持续 2.5s → 强制释放)
uint32_t g_vacuum_ema = 0; // V4.36.31: 真空 EMA 基线 (B3: 释放/触发参考与锚错 Origin 解耦)
+static int32_t g_vac_frac = 0; // V4.37(D): EMA 余数累加器 (消 ±127 counts 整数死区)
uint16_t g_ready_hold_tick = 0; // V4.36.36: 判稳后就绪巩固期计数 (5ms tick, poll_green_led 递减; 按 Q 档置 5s/7s/10s)
uint32_t loop1_Origin;
uint32_t loop1_ORG_SUM;
@@ -562,6 +563,7 @@ void INIT_VD(void)
g_origin_freeze_tick = 0; /* V4.36.26: 释放冻结计数清零 */
g_rel_band_cnt = 0; /* V4.36.30: 环带兜底计数清零 */
g_vacuum_ema = 0; /* V4.36.31: 真空 EMA 清零 (判稳后重建) */
+ g_vac_frac = 0; /* V4.37(D): 余数累加器同步清零 */
g_ready_hold_tick = 0; /* V4.36.36: 就绪巩固期清零 (重建判稳时会再置) */
g_origin_lowlearn_tick = 0; /* V4.36.32: 降权期计数清零 */
loop1_CAP_OK = 0;
@@ -1244,6 +1246,7 @@ void vd1_task(void)
g_stable_cnt = 0; // 计数退出时清零: 二次稳定期(安全复位)从零开始
g_settle_cnt = 0;
g_vacuum_ema = loop1_Origin; /* V4.36.31: 判稳完成建立真空基线 */
+ g_vac_frac = 0; /* V4.37(D): 余数累加器同步清零 */
/* V4.36.36: 就绪巩固期按 Q 分档 (高Q 5s/中Q 7s/低Q 10s) — 绿灯继续慢闪期间系统收敛 */
{
uint32_t _f0 = calc_freq_khz(loop1_Origin, loop1_LPCNT);
@@ -1315,10 +1318,18 @@ void vd1_task(void)
int32_t _ema_lim = (int32_t)(loop1_dlt_ORG * 3);
if (_ema_d > _ema_lim) _ema_d = _ema_lim;
else if (_ema_d < -_ema_lim) _ema_d = -_ema_lim;
- if (_ema_d != 0)
+ if (_ema_d != 0) {
/* V4.36.35: cast 顺序修复 — (uint32_t)负值/128 = +3355万/样本(爆炸源!)
* 必须 有符号先除: -10/128=0, -200/128=-1(wrap 减) — 20260908 实锤根因 */
- g_vacuum_ema += (uint32_t)(_ema_d / VACUUM_EMA_ALFA);
+ /* V4.37(D): 余数累加 —— d/128 整数截断有 ±127 counts 死区
+ * (|d|<128 步进=0 ⇒ EMA 常驻真空下方 ~127 counts ⇒ vac_ref 被拉低
+ * ⇒ 进入线偏低 ⇒ 触发高度漂移, 只能断电上电复位; 2026-09-11 单测实测 127) */
+ g_vac_frac += _ema_d;
+ { int32_t _st = g_vac_frac / VACUUM_EMA_ALFA;
+ g_vac_frac -= _st * VACUUM_EMA_ALFA;
+ g_vacuum_ema += (uint32_t)_st;
+ }
+ }
if (((int32_t)loop1_Origin - (int32_t)g_vacuum_ema) > (int32_t)(loop1_Origin >> 2) ||
((int32_t)g_vacuum_ema - (int32_t)loop1_Origin) > (int32_t)(loop1_Origin >> 2))
g_vacuum_ema = loop1_Origin;
+2 -1
View File
@@ -1,6 +1,6 @@
# DLD154V4B 产品手册 # DLD154V4B 产品手册
> 单路线圈车辆检测器 | 型号: DLD154V4B | 版本: V2.12 > 单路线圈车辆检测器 | 型号: DLD154V4B | 版本: V2.17
> 适用于停车场、收费站、交通卡口等场景的车辆到达/离开检测 > 适用于停车场、收费站、交通卡口等场景的车辆到达/离开检测
--- ---
@@ -281,6 +281,7 @@ SW_5 = ON 时:车辆(金属)持续压线超过 **4 分钟**LC_HOLD_TIME
| V2.14 | 2026-09-08 | 电感分级降级(固件 V4.36.37 方案A):f>156k 拆分——156~171k 可用级(恒判车)、f>171k 短路级快闪200ms(宏=1 等同断开);真短路阈 20µH | | V2.14 | 2026-09-08 | 电感分级降级(固件 V4.36.37 方案A):f>156k 拆分——156~171k 可用级(恒判车)、f>171k 短路级快闪200ms(宏=1 等同断开);真短路阈 20µH |
| V2.15 | 2026-09-08 | 可用级黄灯改**一长两短**(固件 V4.36.38):900/300/100/300/100/700ms 循环,与一长一短(出窗)/快闪(断开)区分 | | V2.15 | 2026-09-08 | 可用级黄灯改**一长两短**(固件 V4.36.38):900/300/100/300/100/700ms 循环,与一长一短(出窗)/快闪(断开)区分 |
| V2.16 | 2026-09-08 | 短路级宏默认=1(固件 V4.36.39,用户拍板):L<20µH 出厂即等同断开;=0 可关恢复仅指示 | | V2.16 | 2026-09-08 | 短路级宏默认=1(固件 V4.36.39,用户拍板):L<20µH 出厂即等同断开;=0 可关恢复仅指示 |
| V2.17 | 2026-09-11 | **环境重校准后灵敏度恢复**(固件 V4.36.41):黄灯常亮故障补偿结束后,进入判定参考与基准同步;此前会残留"旧真空参考" ⇒ 金属板进入触发高度偏低、需断电重启才恢复 |
--- ---
+2 -1
View File
@@ -1,6 +1,6 @@
# DLD154V4B 技术规格书 # DLD154V4B 技术规格书
> 单路线圈车辆检测器 | 固件版本: V4.36.40 | 文档版本: V2.59 > 单路线圈车辆检测器 | 固件版本: V4.36.41 | 文档版本: V2.60
> 本文档面向工程开发、系统集成及故障深度分析 > 本文档面向工程开发、系统集成及故障深度分析
--- ---
@@ -863,3 +863,4 @@ DLD154V4B V1.4 起实现该专利的整数化版本,用于大车通行时的
| V2.57 | 2026-09-08 | V4.36.39: COIL_UNDER_MIN_AS_OPEN 默认值 0→1 (用户拍板) —— L<20µH 短路级出厂即等同断开(判车停+继电器释放+绿灯灭) | | V2.57 | 2026-09-08 | V4.36.39: COIL_UNDER_MIN_AS_OPEN 默认值 0→1 (用户拍板) —— L<20µH 短路级出厂即等同断开(判车停+继电器释放+绿灯灭) |
| V2.58 | 2026-09-08 | V4.36.40 热切换修复: recover 恢复判定改 TMR3 实时 Xn(240000/Xn) —— 原 calc_freq_khz(冻结Origin,LPCNT) 永不回窗致换线圈死锁; 恢复后自然走重连+判稳重建 Origin | | V2.58 | 2026-09-08 | V4.36.40 热切换修复: recover 恢复判定改 TMR3 实时 Xn(240000/Xn) —— 原 calc_freq_khz(冻结Origin,LPCNT) 永不回窗致换线圈死锁; 恢复后自然走重连+判稳重建 Origin |
| V2.59 | 2026-09-08 | 注释同步: 代码/测试全部 疾闪 → 一长两短 (V2.55~58 历史版本行保留当时变更记录) | | V2.59 | 2026-09-08 | 注释同步: 代码/测试全部 疾闪 → 一长两短 (V2.55~58 历史版本行保留当时变更记录) |
| V2.60 | 2026-09-11 | **V4.36.41 环境重校准后触发高度变低根治**:进入判定用 `vac_ref()=min(Origin,真空EMA)`,环境重校准路径未同步该参考 ⇒ 新增 `origin_set_atomic()`(3 处 Origin 写入点;维持 OriginEMA ≤1×dlt 不变式)+ EMA 余数累加消 ±127 counts 整数死区;新增镜像单测 test_vac_ref_reanchor5 场景) |
+205
View File
@@ -0,0 +1,205 @@
/*
* test_vac_ref_reanchor.c V4.36.41 (gcc , DLD154V4B)
*
* = TaskLoop.c V4.36.41 : vac_ref() + origin_set_atomic() + env_resync_apply()
* 5 :
* : env () 线 Δ ()
* : origin_set_atomic 线 Origindlt ()
* : V4.36.33 1×dlt ; EMA ; EMA=0
* (2026-09-11): EMA d/128 ±127 counts EMA
* ~127 counts(2.6×dlt) vac_ref ()
* D() EMA 0 counts
*
* : gcc -O0 -Wall -o /tmp/t_vac tests/test_vac_ref_reanchor.c && /tmp/t_vac
* ( VAC_/ORIGIN_JUMP_/SENS !)
*/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdint.h>
/* ===== 镜像固件宏 (TaskLoop.h / TaskLoop.c) ===== */
static const uint16_t SensTable[4] = { 337, 71, 38, 25 }; /* SENS3 = 25 */
#define SENS_LEVEL 3
#define VAC_REF_DELTA_DLT 1 /* V4.36.33: vac_ref 重锚阈值 (×dlt) */
#define ORIGIN_JUMP_REANCH_DLT 1 /* V4.36.41 草案: 原子换 Origin 重锚阈值 (×dlt) */
#define VACUUM_EMA_ALFA 128 /* V4.36.31: EMA 平滑因子 (τ≈1.28s@10ms) */
/* ===== 镜像全局 ===== */
static uint32_t loop1_Origin = 0, loop1_CAPVD = 0, loop1_dlt_ORG = 0;
static uint32_t g_vacuum_ema = 0;
static int32_t g_vac_frac = 0; /* 修复 D: EMA 余数累加器 */
static uint32_t calc_dlt(uint32_t origin)
{
return (uint32_t)(((uint64_t)origin * SensTable[SENS_LEVEL]) >> 16);
}
/* ===== 镜像: vac_ref() (TaskLoop.c:287-299) ===== */
static uint32_t vac_ref(void)
{
if (g_vacuum_ema == 0) return loop1_Origin;
if ((int32_t)loop1_Origin - (int32_t)g_vacuum_ema >
(int32_t)(loop1_dlt_ORG * VAC_REF_DELTA_DLT))
return g_vacuum_ema;
return loop1_Origin;
}
/* 进入线 (TaskLoop.c:1432 的判定阈值面) */
static uint32_t enter_line(void) { return vac_ref() - loop1_dlt_ORG; }
/* ===== 镜像: origin_set_atomic() (V4.36.41 草案, B1 护栏) ===== */
static void origin_set_atomic(uint32_t new_origin)
{
int32_t _jump = (int32_t)new_origin - (int32_t)loop1_Origin;
if (_jump < 0) _jump = -_jump;
loop1_Origin = new_origin;
if (g_vacuum_ema == 0 || loop1_dlt_ORG == 0 ||
_jump > (int32_t)(loop1_dlt_ORG * ORIGIN_JUMP_REANCH_DLT)) {
g_vacuum_ema = new_origin;
g_vac_frac = 0;
}
}
/* ===== 镜像: env_resync_apply() 修复前 / 修复后 ===== */
static void env_resync_apply_old(uint32_t new_origin) /* V4.36.40: 只换 Origin */
{
loop1_Origin = new_origin;
}
static void env_resync_apply_new(uint32_t new_origin) /* V4.36.41 草案 (A+B) */
{
origin_set_atomic(new_origin);
}
/* ===== 镜像: EMA 步进 —— 现状公式 vs 修复 D ===== */
static void ema_step_cur(void) /* V4.36.35 现状: d/128 整数截断 (死区 ±127) */
{
int32_t d = (int32_t)loop1_CAPVD - (int32_t)g_vacuum_ema;
int32_t lim = (int32_t)(loop1_dlt_ORG * 3);
if (d > lim) d = lim; else if (d < -lim) d = -lim;
if (d != 0) g_vacuum_ema += (uint32_t)(d / VACUUM_EMA_ALFA);
}
static void ema_step_fix(void) /* 修复 D: 余数累加 (无死区) */
{
int32_t d = (int32_t)loop1_CAPVD - (int32_t)g_vacuum_ema;
int32_t lim = (int32_t)(loop1_dlt_ORG * 3);
if (d > lim) d = lim; else if (d < -lim) d = -lim;
if (d != 0) {
g_vac_frac += d;
int32_t st = g_vac_frac / VACUUM_EMA_ALFA;
g_vac_frac -= st * VACUUM_EMA_ALFA;
g_vacuum_ema += (uint32_t)st;
}
}
/* ===== 夹具 ===== */
#define VAC_TRUE 130506UL /* 真实真空 CAPVD (devlog J1 实测) */
#define VAC_ANCHOR 130636UL /* env 学习后 Origin (锚高 130 counts = 2.65×dlt) */
static void reset_all(uint32_t origin)
{
loop1_Origin = origin;
loop1_CAPVD = origin;
loop1_dlt_ORG = calc_dlt(origin);
g_vacuum_ema = origin;
g_vac_frac = 0;
}
int main(void)
{
printf("test_vac_ref_reanchor (V4.36.41 草案: A+B 同步 + D 死区)\n");
/* ================= 场景 1: 复现 + 补丁效果 ================= */
reset_all(VAC_ANCHOR);
uint32_t dlt_anchor = loop1_dlt_ORG;
uint32_t dlt_true = calc_dlt(VAC_TRUE);
printf("1. 参数: VAC_TRUE=%u(真空) VAC_ANCHOR=%u(补偿后 Origin) Δ=%u counts | dlt=%u(SENS3)\n",
(unsigned)VAC_TRUE, (unsigned)VAC_ANCHOR, (unsigned)(VAC_ANCHOR - VAC_TRUE),
(unsigned)dlt_anchor);
reset_all(VAC_TRUE); /* 补偿前: Origin=真空, EMA=真空 */
env_resync_apply_old(VAC_ANCHOR); /* 修复前: 只换 Origin, EMA 停旧真空 */
uint32_t line_old = enter_line();
printf(" ① 修复前: Origin=%u EMA=%u 进入线=%u | 正确应为 %u ⇒ 低 %u counts"
" = %.2f×dlt = %.3f%% Δf/f\n",
(unsigned)loop1_Origin, (unsigned)g_vacuum_ema, (unsigned)line_old,
(unsigned)(VAC_ANCHOR - dlt_anchor),
(unsigned)((VAC_ANCHOR - dlt_anchor) - line_old),
((VAC_ANCHOR - dlt_anchor) - line_old) / (double)dlt_anchor,
((VAC_ANCHOR - dlt_anchor) - line_old) * 100.0 / VAC_ANCHOR);
assert(g_vacuum_ema == VAC_TRUE); /* EMA 未同步(缺陷) */
assert(line_old == VAC_TRUE - dlt_true); /* 进入线被留在旧真空 */
assert((VAC_ANCHOR - dlt_anchor) - line_old == 130); /* 整整低 Δ=130 */
reset_all(VAC_TRUE);
env_resync_apply_new(VAC_ANCHOR); /* 修复后 (A+B) */
printf(" ② 修复后: Origin=%u EMA=%u 进入线=%u | 与断电上电口径一致 ⇒ 低 0 counts\n",
(unsigned)loop1_Origin, (unsigned)g_vacuum_ema, (unsigned)enter_line());
assert(g_vacuum_ema == VAC_ANCHOR);
assert(vac_ref() == VAC_ANCHOR);
assert(enter_line() == VAC_ANCHOR - dlt_anchor);
printf("1. 复现+修复 OK(补偿后进入线由'低 Δ'恢复为 Origindlt\n");
/* ================= 场景 2: B1 护栏(大步重锚 / 小步不动) ================= */
reset_all(VAC_TRUE);
origin_set_atomic(VAC_TRUE - 20); /* 步长 20 ≤ 1×dlt ⇒ EMA 不动 */
printf("2. 小步 20(≤1×dlt): Origin=%u EMA=%u ⇒ EMA 未动 ✓\n",
(unsigned)loop1_Origin, (unsigned)g_vacuum_ema);
assert(g_vacuum_ema == VAC_TRUE);
origin_set_atomic(VAC_TRUE + 130); /* 步长 150 > 1×dlt ⇒ 重锚 */
printf(" 大步 150(>1×dlt): Origin=%u EMA=%u ⇒ 已重锚 ✓\n",
(unsigned)loop1_Origin, (unsigned)g_vacuum_ema);
assert(g_vacuum_ema == VAC_TRUE + 130);
printf("2. 护栏 OK(不吃掉 EMA 的正常跟踪,也不容忍大偏差)\n");
/* ================= 场景 3: V4.36.33 门控语义回归 ================= */
reset_all(VAC_ANCHOR);
g_vacuum_ema = VAC_ANCHOR - 30; /* 边缘悬停拉低 0.61×dlt */
printf("3. OriginEMA=30(0.61×dlt): vac_ref=%u ⇒ 用 Origin ✓(门控保留)\n",
(unsigned)vac_ref());
assert(vac_ref() == VAC_ANCHOR);
g_vacuum_ema = VAC_ANCHOR - 130; /* 老锚错 2.65×dlt */
printf(" OriginEMA=130(2.65×dlt): vac_ref=%u ⇒ 用 EMA ✓(老场景保留)\n",
(unsigned)vac_ref());
assert(vac_ref() == VAC_ANCHOR - 130);
printf("3. 门控回归 OK\n");
/* ================= 场景 4: 上电/判稳语义 ================= */
reset_all(VAC_ANCHOR);
g_vacuum_ema = 0; /* INIT 清零 */
assert(vac_ref() == VAC_ANCHOR);
g_vacuum_ema = loop1_Origin; /* 判稳完成建立初值 */
assert(vac_ref() == VAC_ANCHOR && enter_line() == VAC_ANCHOR - loop1_dlt_ORG);
printf("4. 上电(EMA=0→Origin)语义 OK: 进入线=%u\n", (unsigned)enter_line());
/* ================= 场景 5: EMA 整数步进死区(新发现) ================= */
reset_all(VAC_ANCHOR);
loop1_CAPVD = VAC_ANCHOR; /* 真空就在 Origin */
g_vacuum_ema = VAC_ANCHOR - 200; /* 被拖低 200 counts 后追赶 */
{ int i; for (i = 0; i < 4000; i++) ema_step_cur(); }
{ uint32_t gap = loop1_Origin - g_vacuum_ema;
printf("5. 现状公式 d/128 追赶 4000 样本(40s): EMA=%u 停在真空下方 %u counts"
" (%.2f×dlt) ⇒ vac_ref=%s ⇒ 进入线低 %u counts\n",
(unsigned)g_vacuum_ema, (unsigned)gap, gap / (double)loop1_dlt_ORG,
(vac_ref() == loop1_Origin) ? "Origin" : "EMA", (unsigned)gap);
assert(gap == 127); /* 死区: |d|<128 ⇒ 步进 0 */
assert(vac_ref() == g_vacuum_ema); /* 127 > 1×dlt ⇒ 判定参考被拉低 */
assert(enter_line() == g_vacuum_ema - loop1_dlt_ORG);
}
reset_all(VAC_ANCHOR);
loop1_CAPVD = VAC_ANCHOR;
g_vacuum_ema = VAC_ANCHOR - 200;
g_vac_frac = 0;
{ int i; for (i = 0; i < 4000; i++) ema_step_fix(); }
{ uint32_t gap = loop1_Origin - g_vacuum_ema;
printf(" 修复 D(余数累加)同条件: EMA=%u 偏差 %u counts ⇒ vac_ref=%s ⇒ 进入线=%u ✓\n",
(unsigned)g_vacuum_ema, (unsigned)gap,
(vac_ref() == loop1_Origin) ? "Origin" : "EMA", (unsigned)enter_line());
assert(gap == 0);
assert(vac_ref() == loop1_Origin);
assert(enter_line() == loop1_Origin - loop1_dlt_ORG);
}
printf("5. 死区实测 OK(现状 127 counts 常驻偏差 → 修复 D 归零)\n");
printf("ALL PASS (A+B 同步 / 护栏 / 门控回归 / 上电语义 / 死区)\n");
return 0;
}
@@ -43,6 +43,7 @@
#define VACUUM_EMA_ALFA 128 // V4.36.31: 真空 EMA 平滑因子 (τ≈1.28s@10ms): B3 判定参考解耦 #define VACUUM_EMA_ALFA 128 // V4.36.31: 真空 EMA 平滑因子 (τ≈1.28s@10ms): B3 判定参考解耦
// 无车 dev 窗内跟踪实时真空; 噪声 σ_ema≈σ/16 (σ1-2 → 0.1 counts) // 无车 dev 窗内跟踪实时真空; 噪声 σ_ema≈σ/16 (σ1-2 → 0.1 counts)
#define VAC_REF_DELTA_DLT 1 // V4.36.33: vac_ref 重锚阈值 (×dlt): 仅 OriginEMA > 1×dlt(真锚错) #define VAC_REF_DELTA_DLT 1 // V4.36.33: vac_ref 重锚阈值 (×dlt): 仅 OriginEMA > 1×dlt(真锚错)
#define ORIGIN_JUMP_REANCH_DLT 1 // V4.36.41: Origin 单步跳变 > 1×dlt ⇒ 同步重锚真空 EMA (B1 护栏)
// 才用 EMA 锚低 — 防金属板悬感应边缘(dev20~40<4dlt)快速拉低 EMA // 才用 EMA 锚低 — 防金属板悬感应边缘(dev20~40<4dlt)快速拉低 EMA
// → 触发线漂移(触发高度下降); 小偏差仍用 Origin // → 触发线漂移(触发高度下降); 小偏差仍用 Origin
// 150(→35)不够: 23µH 现场实测板停临界回摆 Origin-36, 需释放线<Origin-37 才释放; // 150(→35)不够: 23µH 现场实测板停临界回摆 Origin-36, 需释放线<Origin-37 才释放;
+2 -2
View File
@@ -14,11 +14,11 @@
#define __CMCNG_H__ #define __CMCNG_H__
#define PRODUCT_MODEL "DLD154" #define PRODUCT_MODEL "DLD154"
#define FIRMWARE_VER "4.36.40" // 主.子.修订 (2026-09-03 起: 小改动只增 REV; MAIN/SUB 由产品定) #define FIRMWARE_VER "4.36.41" // 主.子.修订 (2026-09-03 起: 小改动只增 REV; MAIN/SUB 由产品定)
#define HARDWARE_VER "3.00" #define HARDWARE_VER "3.00"
#define FIRMWARE_VER_MAIN 4 #define FIRMWARE_VER_MAIN 4
#define FIRMWARE_VER_SUB 36 #define FIRMWARE_VER_SUB 36
#define FIRMWARE_VER_REV 40 #define FIRMWARE_VER_REV 41
#define HARDWARE_VER_MAIN 3 #define HARDWARE_VER_MAIN 3
#define HARDWARE_VER_SUB 0 #define HARDWARE_VER_SUB 0
@@ -48,6 +48,7 @@ uint16_t g_origin_freeze_tick = 0; // V4.36.26/32: 释放后 Origin 全冻
uint16_t g_origin_lowlearn_tick = 0; // V4.36.32: 冻结后降权低速补偿计数 (2s, 残余低值降权学入) uint16_t g_origin_lowlearn_tick = 0; // V4.36.32: 冻结后降权低速补偿计数 (2s, 残余低值降权学入)
uint16_t g_rel_band_cnt = 0; // V4.36.30: 低Q 环带超时兜底计数 (离开触发深度持续 2.5s → 强制释放) uint16_t g_rel_band_cnt = 0; // V4.36.30: 低Q 环带超时兜底计数 (离开触发深度持续 2.5s → 强制释放)
uint32_t g_vacuum_ema = 0; // V4.36.31: 真空 EMA 基线 (B3: 释放/触发参考与锚错 Origin 解耦) uint32_t g_vacuum_ema = 0; // V4.36.31: 真空 EMA 基线 (B3: 释放/触发参考与锚错 Origin 解耦)
static int32_t g_vac_frac = 0; // V4.37(D): EMA 余数累加器 (消 ±127 counts 整数死区)
uint16_t g_ready_hold_tick = 0; // V4.36.36: 判稳后就绪巩固期计数 (5ms tick, poll_green_led 递减; 按 Q 档置 5s/7s/10s) uint16_t g_ready_hold_tick = 0; // V4.36.36: 判稳后就绪巩固期计数 (5ms tick, poll_green_led 递减; 按 Q 档置 5s/7s/10s)
uint32_t loop1_Origin; uint32_t loop1_Origin;
uint32_t loop1_ORG_SUM; uint32_t loop1_ORG_SUM;
@@ -297,6 +298,23 @@ static uint32_t vac_ref(void)
return loop1_Origin; return loop1_Origin;
} }
/* V4.36.41: 原子换 Origin ⇒ 同步真空参考 (B1 通用护栏)
* : vac_ref()=min(Origin,EMA); "一步换 Origin" EMA,
* Origin EMA
* ( 4×dlt) 线 ,
* : > ORIGIN_JUMP_REANCH_DLT×dlt EMA; (1×dlt),
* VAC_REF_DELTA_DLT |OriginEMA| 1×dlt ,
* EMA */
static void origin_set_atomic(uint32_t new_origin)
{
int32_t _jump = (int32_t)new_origin - (int32_t)loop1_Origin;
if (_jump < 0) _jump = -_jump;
loop1_Origin = new_origin;
if (g_vacuum_ema == 0 || loop1_dlt_ORG == 0 ||
_jump > (int32_t)(loop1_dlt_ORG * ORIGIN_JUMP_REANCH_DLT))
g_vacuum_ema = new_origin;
}
static uint8_t rel_release_hit(void) static uint8_t rel_release_hit(void)
{ {
uint32_t _cmp = loop1_CAPVD; uint32_t _cmp = loop1_CAPVD;
@@ -562,6 +580,7 @@ void INIT_VD(void)
g_origin_freeze_tick = 0; /* V4.36.26: 释放冻结计数清零 */ g_origin_freeze_tick = 0; /* V4.36.26: 释放冻结计数清零 */
g_rel_band_cnt = 0; /* V4.36.30: 环带兜底计数清零 */ g_rel_band_cnt = 0; /* V4.36.30: 环带兜底计数清零 */
g_vacuum_ema = 0; /* V4.36.31: 真空 EMA 清零 (判稳后重建) */ g_vacuum_ema = 0; /* V4.36.31: 真空 EMA 清零 (判稳后重建) */
g_vac_frac = 0; /* V4.37(D): 余数累加器同步清零 */
g_ready_hold_tick = 0; /* V4.36.36: 就绪巩固期清零 (重建判稳时会再置) */ g_ready_hold_tick = 0; /* V4.36.36: 就绪巩固期清零 (重建判稳时会再置) */
g_origin_lowlearn_tick = 0; /* V4.36.32: 降权期计数清零 */ g_origin_lowlearn_tick = 0; /* V4.36.32: 降权期计数清零 */
loop1_CAP_OK = 0; loop1_CAP_OK = 0;
@@ -946,7 +965,7 @@ static void env_resync_apply(uint32_t new_origin)
{ {
PRINT("EVT|env_resync Origin:%d->%d CAPVD:%d\n", PRINT("EVT|env_resync Origin:%d->%d CAPVD:%d\n",
loop1_Origin, new_origin, loop1_CAPVD); loop1_Origin, new_origin, loop1_CAPVD);
loop1_Origin = new_origin; origin_set_atomic(new_origin); /* V4.36.41: 同步真空 EMA — 补偿后进入线不再被旧真空拖低 */
loop1_freeze_cnt = 0; loop1_freeze_cnt = 0;
loop1_freeze_ref = 0; loop1_freeze_ref = 0;
loop1_ORG_CNT = 0; loop1_ORG_CNT = 0;
@@ -1185,7 +1204,7 @@ void vd1_task(void)
if (_d < 0) _d = -_d; if (_d < 0) _d = -_d;
if (_d <= (int32_t)_dlt) { if (_d <= (int32_t)_dlt) {
/* 回带内=空场确认 → Origin 对齐当前空场(消基线差) */ /* 回带内=空场确认 → Origin 对齐当前空场(消基线差) */
loop1_Origin = loop1_CAPVD; origin_set_atomic(loop1_CAPVD); /* V4.36.41: 护栏(小步≤1×dlt 不动 EMA) */
loop1_CAPVD_slow = loop1_CAPVD; loop1_CAPVD_slow = loop1_CAPVD;
loop1_dlt_ORG = ((uint32_t)loop1_Origin * loop1_dlt_ORG = ((uint32_t)loop1_Origin *
SensTable[loop1_SensLevel]) >> 16; SensTable[loop1_SensLevel]) >> 16;
@@ -1244,6 +1263,7 @@ void vd1_task(void)
g_stable_cnt = 0; // 计数退出时清零: 二次稳定期(安全复位)从零开始 g_stable_cnt = 0; // 计数退出时清零: 二次稳定期(安全复位)从零开始
g_settle_cnt = 0; g_settle_cnt = 0;
g_vacuum_ema = loop1_Origin; /* V4.36.31: 判稳完成建立真空基线 */ g_vacuum_ema = loop1_Origin; /* V4.36.31: 判稳完成建立真空基线 */
g_vac_frac = 0; /* V4.37(D): 余数累加器同步清零 */
/* V4.36.36: 就绪巩固期按 Q 分档 (高Q 5s/中Q 7s/低Q 10s) — 绿灯继续慢闪期间系统收敛 */ /* V4.36.36: 就绪巩固期按 Q 分档 (高Q 5s/中Q 7s/低Q 10s) — 绿灯继续慢闪期间系统收敛 */
{ {
uint32_t _f0 = calc_freq_khz(loop1_Origin, loop1_LPCNT); uint32_t _f0 = calc_freq_khz(loop1_Origin, loop1_LPCNT);
@@ -1315,10 +1335,18 @@ void vd1_task(void)
int32_t _ema_lim = (int32_t)(loop1_dlt_ORG * 3); int32_t _ema_lim = (int32_t)(loop1_dlt_ORG * 3);
if (_ema_d > _ema_lim) _ema_d = _ema_lim; if (_ema_d > _ema_lim) _ema_d = _ema_lim;
else if (_ema_d < -_ema_lim) _ema_d = -_ema_lim; else if (_ema_d < -_ema_lim) _ema_d = -_ema_lim;
if (_ema_d != 0) if (_ema_d != 0) {
/* V4.36.35: cast 顺序修复 — (uint32_t)负值/128 = +3355万/样本(爆炸源!) /* V4.36.35: cast 顺序修复 — (uint32_t)负值/128 = +3355万/样本(爆炸源!)
* : -10/128=0, -200/128=-1(wrap ) 20260908 */ * : -10/128=0, -200/128=-1(wrap ) 20260908 */
g_vacuum_ema += (uint32_t)(_ema_d / VACUUM_EMA_ALFA); /* V4.37(D): 余数累加 —— d/128 整数截断有 ±127 counts 死区
* (|d|<128 =0 EMA ~127 counts vac_ref
* 线 , ; 2026-09-11 127) */
g_vac_frac += _ema_d;
{ int32_t _st = g_vac_frac / VACUUM_EMA_ALFA;
g_vac_frac -= _st * VACUUM_EMA_ALFA;
g_vacuum_ema += (uint32_t)_st;
}
}
if (((int32_t)loop1_Origin - (int32_t)g_vacuum_ema) > (int32_t)(loop1_Origin >> 2) || if (((int32_t)loop1_Origin - (int32_t)g_vacuum_ema) > (int32_t)(loop1_Origin >> 2) ||
((int32_t)g_vacuum_ema - (int32_t)loop1_Origin) > (int32_t)(loop1_Origin >> 2)) ((int32_t)g_vacuum_ema - (int32_t)loop1_Origin) > (int32_t)(loop1_Origin >> 2))
g_vacuum_ema = loop1_Origin; g_vacuum_ema = loop1_Origin;
@@ -1341,7 +1369,7 @@ void vd1_task(void)
} }
loop1_freeze_cnt++; loop1_freeze_cnt++;
if (loop1_freeze_cnt >= FREEZE_TIMEOUT) { if (loop1_freeze_cnt >= FREEZE_TIMEOUT) {
loop1_Origin = loop1_CAPVD; origin_set_atomic(loop1_CAPVD); /* V4.36.41: 护栏(大步下跳也同步重锚) */
loop1_freeze_cnt = 0; loop1_freeze_cnt = 0;
loop1_freeze_ref = 0; loop1_freeze_ref = 0;
loop1_ORG_CNT = 0; loop1_ORG_CNT = 0;