feat(V4B): V4.26 — 线圈电感量告警(一长一短) + 故障补偿 60s→10s (4点拍板)

1. 默认低频档频率合理窗 20~120kHz, 出界即告警(不做档位判定/灰带):
   TaskLoop.c 新增 coil_monitor() — 稳定+无车空闲由无车主路径调用,
   Origin 反推空场 f, f<20kHz→L偏大/f>120kHz→L偏小, 1s 防抖置位/清除
2. 告警形式: 黄灯"一长一短" 200ms亮/100ms灭/100ms亮/800ms灭 循环 (模式5)
3. 黄灯优先级: 断开快闪 > 故障补偿(学习)常亮 > 电感量一长一短 > N短闪 > 灭
4. 故障补偿等待 ENV_RESYNC_WAIT 6000→1000 (60s→10s): 总补偿 ~66s→~21s

验证: tests/test_coil_range.c 5场景(边界19/20/120/121+防抖) ALL PASS;
      test_env_resync.c 适配10s(applied_at=1600tick) 5场景 ALL PASS; 语法 0 error
文档: devlog V2.24/spec V2.5/manual V2.9/design doc v0.3(标记已实现)
修复: 源文件行尾多CR污染(\\r\\r\\r\\r\\n)统一规范化回标准CRLF
This commit is contained in:
wangfq
2026-09-03 10:50:26 +08:00
parent 433b7f472d
commit 92f675e2d3
9 changed files with 465 additions and 228 deletions
+5 -3
View File
@@ -1,7 +1,7 @@
# 线圈电感量估算与不合理范围告警 — 设计讨论稿 # 线圈电感量估算与不合理范围告警 — 设计讨论稿
> **状态:设计讨论稿(未实现)** — 本文仅存档讨论结论,代码未落地 > **状态:已实现(V4.262026-09-03)** — 本文为设计讨论与实现归档;落地代码见 TaskLoop.c coil_monitor()vd1_task 无车主路径调用)与 poll_yellow_led() 模式 5
> 固件版本基线:V4.25cmcng.h);涉及产品:DLD154V4BDLD154V4)。 > 固件版本基线:V4.26cmcng.h);涉及产品:DLD154V4BDLD154V4)。
**修订记录:** **修订记录:**
@@ -9,6 +9,7 @@
|------|------|------| |------|------|------|
| v0.1 | 2026-09-02 | 初稿(合理范围按 50~300 µH,三区制) | | v0.1 | 2026-09-02 | 初稿(合理范围按 50~300 µH,三区制) |
| v0.2 | 2026-09-03 | 合理范围改 **50 ~ 1000 µH(规格书口径,两档任意一档合法即不告警)**,三区窗口重算 | | v0.2 | 2026-09-03 | 合理范围改 **50 ~ 1000 µH(规格书口径,两档任意一档合法即不告警)**,三区窗口重算 |
| v0.3 | 2026-09-03 | **用户 4 点拍板收口(取代 v0.2 三区/灰带)**:默认低频档频率窗 **20~120 kHz** 出界即告警;黄灯**一长一短**;优先级 1 断开快闪 > 4 故障补偿常亮 > 5 电感一长一短 > 3 N短亮 > 2 灭;故障补偿 ENV_RESYNC_WAIT **60s→10s**。已实现于 V4.26672f143 后) |
## 1. 背景 ## 1. 背景
@@ -17,7 +18,8 @@ DLD154V4 线圈振荡电路:固定电容 **33 nF** + 拨码开关可并联另
需求(用户最终口径): 需求(用户最终口径):
1. 由实测振荡频率推算线圈电感量 L(粗略值即可) 1. 由实测振荡频率推算线圈电感量 L(粗略值即可)
2. 对不合理电感量范围的线圈告警**两档任意 L ∈ [50, 1000] µH 合法**(规格书范围),超出才告警 2. 对不合理电感量范围的线圈告警**v0.3 拍板,取代 §4 三区制**):按默认低频档计算,频率合理范围
**20 ~ 120 kHz,不在合理范围内一律判定电感量不合理 → 告警**(不做档位判定、不做灰带提示)
3. 预留 C_eff 校准公式/系数,测试时微调 3. 预留 C_eff 校准公式/系数,测试时微调
4. 持续监视 4. 持续监视
+40
View File
@@ -4,6 +4,45 @@
--- ---
## 2026-09-03 — 线圈电感量告警 + 故障补偿提速(固件 V4.26)
> **固件版本:V4.26**cmcng.hFIRMWARE_VER="4.26" + MAIN=4/SUB=26)。devlog 修订 V2.24。
### 背景
DLD154V4 线圈振荡两级频率(33/66nF 拨码,拨码不引 IO)。现场抓到 f=16.3kHz(Xn=3681) 超范围
线圈(L≈1.44mH@66nF),检测异常难排查 → 要求对不合理电感量自动告警。
另:环境重校准(V4.24/25)补偿总时长 ~66s 偏长 → 拍板提速。
### 拍板规格(2026-09-03
1. 按默认低频档计算,频率合理范围 **20 ~ 120 kHz**,出界即判定电感量不合理 → 告警(不做档位判定/灰带)
2. 告警形式:黄灯**一长一短**(长亮200ms/灭100ms/短亮100ms/灭800ms 循环)
3. 黄灯优先级:线圈断开快闪 > 故障补偿(学习补偿)常亮 > 电感量不合理一长一短 > 线圈断开 N 短亮 > 灭
4. 故障补偿黄灯常亮(等待学习)时间 **60s → 10s**ENV_RESYNC_WAIT 6000→1000
### 改动
- TaskLoop.c:新增 coil_monitor()(稳定+无车空闲由无车主路径调用;Origin 反推空场 f;
f<20kHz→L偏大 / f>120kHz→L偏小,1s 防抖置位/清除;EVT|coil_fault/coil_ok
- poll_yellow_led():新增模式5(一长一短 40/20/20/160 tick);优先级 1>4>5>3>2
- ENV_RESYNC_WAIT 6000→10005s 预判 → 10s 学习 → ~16s 替换 → 空闲 5s 确认灭,总 ~21s)
- TaskLoop.hCOIL_F_OK_MIN_KHZ=20 / COIL_F_OK_MAX_KHZ=120 / COIL_DEF_C_NF=66
- 行尾修复:V4.24~25 编辑期间源文件行尾被写成多 CR(\r\r\r\r\n),本次统一规范化回标准 CRLF
### 验证
- tests/test_coil_range.c 5 场景:L偏大/偏小 1s 防抖置位、恢复 1s 防抖清除、边界 19/20/120/121、抖动不误报 ALL PASS
- tests/test_env_resync.cENV_RESYNC_WAIT=1000):永久变化 applied_at=1600tick、瞬态<10s 不学、
锁存保持、打断重算、放弃不污染 ALL PASS
- gcc -fsyntax-only 0 error
### 待现场验证
- 超范围线圈(如 f=16.3kHz)应黄灯一长一短;正常 100~300µH 线圈(f 20~120k 内)不误报
- 故障补偿总时长 ~21s;环境高位 10s 即学(铁块静止 >10s 会被学入,黄灯常亮提示请勿移动目标)
- 电感告警阈值边界用标定线圈(100µH/300µH)实测微调
## 2026-09-02 — 环境异常黄灯预判:5s 常亮提示将进入重学(固件 V4.25) ## 2026-09-02 — 环境异常黄灯预判:5s 常亮提示将进入重学(固件 V4.25)
> **固件版本:V4.25**cmcng.hFIRMWARE_VER="4.25" + MAIN=4/SUB=25)。devlog 修订 V2.23。 > **固件版本:V4.25**cmcng.hFIRMWARE_VER="4.25" + MAIN=4/SUB=25)。devlog 修订 V2.23。
@@ -1065,6 +1104,7 @@ if (dev >= dlt_ORG × 4) {
| 版本 | 时间 | 说明 | | 版本 | 时间 | 说明 |
|------|------|------| |------|------|------|
| V2.24 | 2026-09-03 | V4.26 线圈电感量告警(频率窗 20~120kHz 一长一短)+ 故障补偿 60s→10s;行尾规范化 CRLF |
| V2.23 | 2026-09-02 | V4.25 环境异常黄灯预判(锁存):dev>+4dlt 高位连续 5s 触发 g_env_warn 锁存常亮;回摆/目标靠近保持,环境空闲稳定 5s(ENV_RECOVER_WAIT)才解除;60s 才真正学习 | | V2.23 | 2026-09-02 | V4.25 环境异常黄灯预判(锁存):dev>+4dlt 高位连续 5s 触发 g_env_warn 锁存常亮;回摆/目标靠近保持,环境空闲稳定 5s(ENV_RECOVER_WAIT)才解除;60s 才真正学习 |
| V2.20 | 2026-09-02 | V4.22 DEBUG 改编译器 -DDEBUG 控制(cmcng.h DEBUG_ENABLE 删除) | | V2.20 | 2026-09-02 | V4.22 DEBUG 改编译器 -DDEBUG 控制(cmcng.h DEBUG_ENABLE 删除) |
| V2.19 | 2026-09-02 | V4.20 负variation铁块锁死修复:基线跟踪双向对称保护(±4×dlt 对称窗口) | | V2.19 | 2026-09-02 | V4.20 负variation铁块锁死修复:基线跟踪双向对称保护(±4×dlt 对称窗口) |
+6 -4
View File
@@ -1,6 +1,6 @@
# DLD154V4B 产品手册 # DLD154V4B 产品手册
> 单路线圈车辆检测器 | 型号: DLD154V4B | 版本: V2.8 > 单路线圈车辆检测器 | 型号: DLD154V4B | 版本: V2.9
> 适用于停车场、收费站、交通卡口等场景的车辆到达/离开检测 > 适用于停车场、收费站、交通卡口等场景的车辆到达/离开检测
--- ---
@@ -177,6 +177,7 @@ DLD154V4B 是一款基于 ARM Cortex-M4 微控制器的**单通道环形线圈
| 故障类型 | 红灯 | 绿灯 | 黄灯 | 说明 | | 故障类型 | 红灯 | 绿灯 | 黄灯 | 说明 |
|---------|------|------|------|------| |---------|------|------|------|------|
| 线圈断开(当前) | 呼吸 | 灭 | **快闪** (200ms) | 检查线圈连接 | | 线圈断开(当前) | 呼吸 | 灭 | **快闪** (200ms) | 检查线圈连接 |
| 线圈电感量不合理(V4.26) | 呼吸 | 灭(无车) | **一长一短** (200ms亮/100ms灭/100ms亮/800ms灭 循环) | 空场频率出 20~120kHz(默认低频档):L偏大(f<20k)/L偏小(f>120k),检查线圈匝数/尺寸 |
| 线圈重连,断开 1 次 | 呼吸 | 正常 | **1 短闪** + 1.2s 灭 | 发生过 1 次断开,已恢复 | | 线圈重连,断开 1 次 | 呼吸 | 正常 | **1 短闪** + 1.2s 灭 | 发生过 1 次断开,已恢复 |
| 线圈重连,断开 2 次 | 呼吸 | 正常 | **2 短闪** + 1.2s 灭 | 发生过 2 次断开,已恢复 | | 线圈重连,断开 2 次 | 呼吸 | 正常 | **2 短闪** + 1.2s 灭 | 发生过 2 次断开,已恢复 |
| 线圈重连,断开 3+ 次 | 呼吸 | 正常 | **3 短闪** + 1.2s 灭 | 多次断开,建议检查接头 | | 线圈重连,断开 3+ 次 | 呼吸 | 正常 | **3 短闪** + 1.2s 灭 | 多次断开,建议检查接头 |
@@ -185,14 +186,15 @@ DLD154V4B 是一款基于 ARM Cortex-M4 微控制器的**单通道环形线圈
| 状态 | 红灯 | 绿灯 | 黄灯 | 说明 | | 状态 | 红灯 | 绿灯 | 黄灯 | 说明 |
|------|------|------|------|------| |------|------|------|------|------|
| 环境校准学习中 | 呼吸 | 灭(无车) | **常亮** | 检测到**永久环境变化**(如旁置金属板被移走使基准被学低):高位持续 **5 s** 黄灯预判**锁存**常亮,累计 **60 s** 后自动学习新基线并替换,恢复灵敏度 | | 环境校准学习中 | 呼吸 | 灭(无车) | **常亮** | 检测到**永久环境变化**(如旁置金属板被移走使基准被学低):高位持续 **5 s** 黄灯预判**锁存**常亮,累计 **10 s**V4.26 起,原 60s后自动学习新基线并替换,恢复灵敏度 |
- 线圈旁金属等目标在**上电时**在场会把空车基准学低;目标移走后检测高度会明显变低(V4.22 及以前需断电重上电或等基线慢跟恢复)。V4.24 起自动恢复;V4.25 起环境变化 **~5 s** 黄灯即常亮预判(提示"检测器已感知环境变化") - 线圈旁金属等目标在**上电时**在场会把空车基准学低;目标移走后检测高度会明显变低(V4.22 及以前需断电重上电或等基线慢跟恢复)。V4.24 起自动恢复;V4.25 起环境变化 **~5 s** 黄灯即常亮预判V4.26 起故障补偿提速:约 **5s 预判 → 10s 学习 → ~16s 替换 → 空闲 5s 恢复确认后灭(总 ~21s)**
- **黄灯常亮(锁存)= 环境异常预判/学习中**:一旦点亮,目标移走/回摆**不会立即灭灯**——需环境恢复正常(空闲)且稳定 **~5 s** 才解除。请勿在黄灯常亮期间移动线圈附近的金属目标(铁块静止 60 s+ 也会被学入) - **黄灯常亮(锁存)= 环境异常预判/学习中**:一旦点亮,目标移走/回摆**不会立即灭灯**——需环境恢复正常(空闲)且稳定 **~5 s** 才解除。请勿在黄灯常亮期间移动线圈附近的金属目标(铁块静止 60 s+ 也会被学入)
- 黄灯优先级:断开快闪 > 环境预判/学习常亮 > N 短闪(断开史)> 灭 - 黄灯优先级V4.26:断开快闪 > 故障补偿(环境预判/学习常亮 > **线圈电感量不合理一长一短** > N 短闪(断开史)> 灭
- **快闪**200ms 亮 / 200ms 灭 - **快闪**200ms 亮 / 200ms 灭
- **N 短闪**N × (80ms 亮 + 200ms 灭),循环间隔 1.2s - **N 短闪**N × (80ms 亮 + 200ms 灭),循环间隔 1.2s
- **一长一短**V4.26):200ms 亮 + 100ms 灭 + 100ms 亮 + 800ms 灭,循环(线圈电感量不合理)
- **呼吸**:PWM 渐变,系统运行指示(周期≈2.6sV4.23 起对齐 vd960Loop 观感) - **呼吸**:PWM 渐变,系统运行指示(周期≈2.6sV4.23 起对齐 vd960Loop 观感)
- **绿灯慢闪(上电自检)**:200ms 亮/灭,持续至基准值稳定(连续窗口 Origin 漂移 ≤0.1%)才停止 (V4.23) - **绿灯慢闪(上电自检)**:200ms 亮/灭,持续至基准值稳定(连续窗口 Origin 漂移 ≤0.1%)才停止 (V4.23)
+27 -17
View File
@@ -1,6 +1,6 @@
# DLD154V4B 技术规格书 # DLD154V4B 技术规格书
> 单路线圈车辆检测器 | 固件版本: V4.25 | 文档版本: V2.4 > 单路线圈车辆检测器 | 固件版本: V4.26 | 文档版本: V2.5
> 本文档面向工程开发、系统集成及故障深度分析 > 本文档面向工程开发、系统集成及故障深度分析
--- ---
@@ -364,7 +364,7 @@ if (!VD_FLAG) {
| 4× 冻结阈值 | 防止异常 CAPVD 上升污染基线(V1.5 | | 4× 冻结阈值 | 防止异常 CAPVD 上升污染基线(V1.5 |
| 5% 斜率限幅 | 过滤 EMI/闪电尖峰,保护 IIRV2.0 | | 5% 斜率限幅 | 过滤 EMI/闪电尖峰,保护 IIRV2.0 |
| 冻结超时 + 稳定性检查 | 防止永久冻结死锁(V2.3~2.5 | | 冻结超时 + 稳定性检查 | 防止永久冻结死锁(V2.3~2.5 |
| 环境重校准(V4.24 | dev>+4×dlt 高位连续 60sENV_RESYNC_WAIT)→ 判定永久环境变化 → 学习基线 B(空闲窗均值+判稳,黄灯常亮)→ 原子替换 Origin;铁块类瞬态 60s 内仍只冻结不污染 | | 环境重校准(V4.24/26 | dev>+4×dlt 高位连续 10sENV_RESYNC_WAITV4.26 由 60s 提速)→ 判定永久环境变化 → 学习基线 B(空闲窗均值+判稳,黄灯常亮)→ 原子替换 Origin;铁块类瞬态 10s 内仍只冻结不污染 |
#### 4.4.3 基线更新速率(两阶段) #### 4.4.3 基线更新速率(两阶段)
@@ -451,9 +451,12 @@ if (dev < dlt_ORG * 4) {
| FREEZE_TIMEOUT | 1000 | **~10 s** | 基线冻结超时(连续稳定后更新 Origin) | | FREEZE_TIMEOUT | 1000 | **~10 s** | 基线冻结超时(连续稳定后更新 Origin) |
| ENV_WARN_WAIT | 500 tick | **~5 s** | 环境预判锁存(V4.25):dev>+4×dlt 高位连续 5s → 黄灯常亮预判(锁存) | | ENV_WARN_WAIT | 500 tick | **~5 s** | 环境预判锁存(V4.25):dev>+4×dlt 高位连续 5s → 黄灯常亮预判(锁存) |
| ENV_RECOVER_WAIT | 500 tick | ~5 s | 恢复确认(V4.25):环境回正常空闲连续 5s 才解除预判锁存、黄灯灭(回摆/目标靠近保持常亮) | | ENV_RECOVER_WAIT | 500 tick | ~5 s | 恢复确认(V4.25):环境回正常空闲连续 5s 才解除预判锁存、黄灯灭(回摆/目标靠近保持常亮) |
| ENV_RESYNC_WAIT | 6000 tick | **~60 s** | 环境重校准等待:dev>+4×dlt 高位连续 60s 判定永久环境变化(V4.24 | | ENV_RESYNC_WAIT | 1000 tick | **~10 s** | 环境重校准等待:dev>+4×dlt 高位连续 10s 判定永久环境变化(V4.26 拍板:60s→10s |
| ENV_LEARN_WINDOW | 200 tick | ~2 s | 环境学习窗口(基线 B 均值窗,学习期黄灯常亮) | | ENV_LEARN_WINDOW | 200 tick | ~2 s | 环境学习窗口(基线 B 均值窗,学习期黄灯常亮) |
| ENV_LEARN_MAX_TICKS | 3000 tick | **~30 s** | 学习预算:窗口不稳超时放弃,保留旧 Origin(防死锁) | | ENV_LEARN_MAX_TICKS | 3000 tick | **~30 s** | 学习预算:窗口不稳超时放弃,保留旧 Origin(防死锁) |
| COIL_F_OK_MIN_KHZ | 20 kHz | — | 线圈频率合理窗下界(V4.26):默认低频档口径,f<20kHz → L偏大告警 |
| COIL_F_OK_MAX_KHZ | 120 kHz | — | 线圈频率合理窗上界(V4.26):f>120kHz → L偏小告警 |
| COIL_DEF_C_NF | 66 nF | — | 默认档电容(拨码不可读,粗估 L 打印用;33nF 档结果 ×2) |
> **M4 优化**V2.0 将 tick 从 50ms (TMR15 5ms×10) 提升到 10ms (vTaskDelay 10ms)。IIR 系数同步调整(α 从 79/256→18/256),保持等效时间常数。进入确认 3×10ms + 500ms IN_DELAY = ~530ms,比旧设计 (50ms + 500ms = 550ms) 略快。基线更新从 5s 加速到 1s。 > **M4 优化**V2.0 将 tick 从 50ms (TMR15 5ms×10) 提升到 10ms (vTaskDelay 10ms)。IIR 系数同步调整(α 从 79/256→18/256),保持等效时间常数。进入确认 3×10ms + 500ms IN_DELAY = ~530ms,比旧设计 (50ms + 500ms = 550ms) 略快。基线更新从 5s 加速到 1s。
@@ -498,23 +501,28 @@ void poll_green_led(void) {
### 6.3 黄灯行为表 ### 6.3 黄灯行为表
| 条件 | 行为 | 参数 | 诊断含义 | | 优先级 | 条件 | 行为 | 参数 | 诊断含义 |
|------|------|------|---------| |--------|------|------|------|---------|
| `g_disconnect_active == 1` | **快闪** | 200ms 亮 / 200ms 灭 | 线圈当前断开中 | | 1 | `g_disconnect_active == 1``!g_loop_power_up_state` | **快闪** | 200ms 亮 / 200ms 灭 | 线圈当前断开中 / 从无线圈上电 |
| `!g_loop_power_up_state` | **快闪** | 200ms 亮 / 200ms 灭 | 上电后从未连接线圈 | | 4 | `g_env_warn``g_env_resync` | **常亮** | — | 环境异常预判/重校准学习(V4.24/25;故障补偿 ~10 s 内完成) |
| `g_disconnect_count == 1` | **1 短闪** | 80ms 亮 / 200ms 灭 → 1.2s 灭(循环 | 断开 1 次,已恢复 | | 5 | `g_coil_fault`V4.26 | **一长一短** | 长亮 200ms / 灭 100ms / 短亮 100ms / 灭 800ms 循环 | 电感量不合理:1=L偏大(f<20kHz) 2=L偏小(f>120kHz) |
| `g_disconnect_count == 2` | **2 短闪** | 同上 | 断开 2 次 | | 3 | `g_disconnect_count == 1` | **1 短闪** | 80ms 亮 / 200ms 灭 → 1.2s 灭(循环) | 断开 1,已恢复 |
| `g_disconnect_count >= 3` | **3 短闪** | 同上 | 断开 3+ 次 | | 3 | `g_disconnect_count == 2` | **2 短闪** | 同上 | 断开 2 次 |
| 正常 | **** | | 一切正常 | | 3 | `g_disconnect_count >= 3` | **3 短闪** | 同上 | 断开 3+ 次 |
| 2 | 正常 | **灭** | — | 一切正常 |
```c ```c
void poll_yellow_led(void) { void poll_yellow_led(void) { // 优先级 1 > 4 > 5 > 3 > 2 (V4.26)
if (g_disconnect_active || !g_loop_power_up_state) if (g_disconnect_active || !g_loop_power_up_state)
fast_blink_200ms(); // 断开中快闪 fast_blink_200ms(); // 断开中快闪
else if (g_env_warn || g_env_resync)
led_on(); // ④ 环境预判/学习中常亮 (V4.24/25)
else if (g_coil_fault)
long_short_blink(); // ⑤ 电感量不合理: 一长一短 (V4.26)
else if (g_disconnect_count > 0) else if (g_disconnect_count > 0)
n_short_flash(g_disconnect_count); // N 短闪编码 n_short_flash(g_disconnect_count); // N 短闪编码
else else
led_off(); // 正常 led_off(); // 正常
} }
``` ```
@@ -617,14 +625,15 @@ if (!g_loop_stable) {
|--------|---------|---------| |--------|---------|---------|
| 线圈断开 | `loop1_RF_FLAG == 0` 持续 | 黄灯快闪 / 继电器释放 | | 线圈断开 | `loop1_RF_FLAG == 0` 持续 | 黄灯快闪 / 继电器释放 |
| 线圈短路 | Xn 持续为 0 或极小 | 黄灯快闪 / 继电器释放 | | 线圈短路 | Xn 持续为 0 或极小 | 黄灯快闪 / 继电器释放 |
| 频率过低 | Value 持续 < MIN_FREQ | 黄灯编码闪烁 | | 电感量不合理(L偏大) | 空场 f 持续 < 20 kHz(默认低频档,V4.26 | 黄灯**一长一短**g_coil_fault=1/ 持续监视 1s 防抖 |
| 频率过高 | Value 持续 > MAX_FREQ | 黄灯编码闪烁 | | 电感量不合理(L偏小) | 空场 f 持续 > 120 kHz | 黄灯**一长一短**g_coil_fault=2/ 持续监视 1s 防抖 |
### 10.2 黄灯编码设计 ### 10.2 黄灯编码设计
断开次数编码提供了比 TLD-110 的均匀闪烁更丰富的诊断信息: 断开次数编码提供了比 TLD-110 的均匀闪烁更丰富的诊断信息:
- **快闪=当前故障** → 现场立即处理 - **快闪=当前故障** → 现场立即处理
- **N短闪=历史故障** → 判断是偶发还是持续问题 - **N短闪=历史故障** → 判断是偶发还是持续问题
- **一长一短=线圈电感量不合理**(V4.26):空场频率出 20~120kHz 窗(默认低频档);用标定线圈实测后微调阈值
--- ---
@@ -760,3 +769,4 @@ DLD154V4B V1.4 起实现该专利的整数化版本,用于大车通行时的
| V2.2 | 2026-09-02 | 更新至 V4.23:上电稳定期判稳语义(基准漂移≤0.1%×2窗 + 5s兜底)、红灯呼吸周期≈2.6s、绿灯自检描述 | | V2.2 | 2026-09-02 | 更新至 V4.23:上电稳定期判稳语义(基准漂移≤0.1%×2窗 + 5s兜底)、红灯呼吸周期≈2.6s、绿灯自检描述 |
| V2.3 | 2026-09-02 | 更新至 V4.24:环境重校准(dev>+4dlt 高位连续 60s → 学习基线 B → 空闲原子替换 Origin,黄灯常亮指示;瞬态 60s 内不污染、30s 学习预算兜底) | | V2.3 | 2026-09-02 | 更新至 V4.24:环境重校准(dev>+4dlt 高位连续 60s → 学习基线 B → 空闲原子替换 Origin,黄灯常亮指示;瞬态 60s 内不污染、30s 学习预算兜底) |
| V2.4 | 2026-09-02 | 更新至 V4.25:环境异常黄灯预判锁存(ENV_WARN_WAIT=5s 触发;回摆保持,ENV_RECOVER_WAIT 空闲 5s 才解除);预判≠学习 | | V2.4 | 2026-09-02 | 更新至 V4.25:环境异常黄灯预判锁存(ENV_WARN_WAIT=5s 触发;回摆保持,ENV_RECOVER_WAIT 空闲 5s 才解除);预判≠学习 |
| V2.5 | 2026-09-03 | 更新至 V4.26:线圈电感量告警(频率窗 20~120kHz 单窗,黄灯一长一短);故障补偿 ENV_RESYNC_WAIT 60s→10s;黄灯优先级 1>4>5>3>2 |
+116
View File
@@ -0,0 +1,116 @@
/*
* test_coil_range.c 线 gcc (DLD154V4B V4.26)
*
* : TaskLoop.c coil_monitor() (V4.26 )
* : 20~120kHz,
* g_coil_fault: 0= 1=L偏大(f<20kHz) 2=L偏小(f>120kHz)
* : / 100tick(=1s) / (=)
*
* ( COIL_* !)
*/
#include <assert.h>
#include <stdio.h>
#include <stdint.h>
#define COIL_F_OK_MIN_KHZ 20
#define COIL_F_OK_MAX_KHZ 120
#define COIL_DEF_C_NF 66
static uint8_t g_coil_fault = 0;
static uint16_t g_coil_bad_cnt = 0;
/* coil_monitor() 纯逻辑镜像 (不含 Origin==0 守卫, 由调用点保证) */
static void coil_step(uint32_t f_khz)
{
uint8_t want = 0;
if (f_khz < COIL_F_OK_MIN_KHZ)
want = 1;
else if (f_khz > COIL_F_OK_MAX_KHZ)
want = 2;
if (want == g_coil_fault) {
g_coil_bad_cnt = 0;
} else {
g_coil_bad_cnt++;
if (g_coil_bad_cnt >= 100) {
g_coil_fault = want;
g_coil_bad_cnt = 0;
}
}
}
static void run_ticks(uint32_t f_khz, int n)
{
int i;
for (i = 0; i < n; i++)
coil_step(f_khz);
}
static void test_too_large(void)
{
g_coil_fault = 0; g_coil_bad_cnt = 0;
/* f=16.3kHz(实测超限线圈) 持续 <1s 不置位 */
run_ticks(16, 99);
assert(g_coil_fault == 0);
/* 满 1s → L 偏大 */
coil_step(16);
assert(g_coil_fault == 1);
/* 保持 → 状态稳定不抖 */
run_ticks(16, 1000);
assert(g_coil_fault == 1);
printf("1. L偏大 (f=16kHz, 1s 防抖置位, 保持稳定) OK\n");
}
static void test_too_small(void)
{
g_coil_fault = 0; g_coil_bad_cnt = 0;
run_ticks(150, 100);
assert(g_coil_fault == 2);
printf("2. L偏小 (f=150kHz, 1s 防抖置位) OK\n");
}
static void test_recover(void)
{
g_coil_fault = 1; g_coil_bad_cnt = 0;
run_ticks(62, 99);
assert(g_coil_fault == 1); /* 恢复防抖 <1s 不清除 */
coil_step(62);
assert(g_coil_fault == 0); /* 满 1s 恢复正常 */
printf("3. 恢复正常 (1s 防抖清除) OK\n");
}
static void test_boundaries(void)
{
g_coil_fault = 0; g_coil_bad_cnt = 0;
run_ticks(19, 100); assert(g_coil_fault == 1); /* 19 → 偏大 */
run_ticks(62, 100); assert(g_coil_fault == 0); /* 20~120 正常 */
run_ticks(20, 100); assert(g_coil_fault == 0); /* 边界 20 正常 */
run_ticks(120, 100); assert(g_coil_fault == 0); /* 边界 120 正常 */
run_ticks(121, 100); assert(g_coil_fault == 2); /* 121 → 偏小 */
printf("4. 边界 19/20/120/121 OK\n");
}
static void test_jitter_no_alarm(void)
{
/* 抖动: 16 与 62 交替 (每 50tick) → 计数永远到不了 100 → 不误报 */
int i;
g_coil_fault = 0; g_coil_bad_cnt = 0;
for (i = 0; i < 2000; i++)
coil_step((i & 1) ? 62 : 16);
assert(g_coil_fault == 0);
printf("5. 频率抖动不误报 OK\n");
}
int main(void)
{
test_too_large();
test_too_small();
test_recover();
test_boundaries();
test_jitter_no_alarm();
printf("\nALL PASS (窗口 20~120kHz, 防抖 1s)\n");
return 0;
}
+185 -185
View File
@@ -1,32 +1,43 @@
/* /*
* test_env_resync.c (线) gcc (DLD154V4B V4.24) * test_env_resync.c (线) gcc (DLD154V4B V4.26)
* *
* : TaskLoop.c vd1_task() dev>0 (V4.24 ) * : TaskLoop.c vd1_task() dev>0 (V4.24/25 , V4.26 )
* (2026-09-02 ): 线A上电 Origin (128096) * (2026-09-02 ): 线A上电 Origin (128096)
* A CAPVD (128856) dev=+761 (36321a1) * A CAPVD 128856 dev=+761(0.59%) > +4dlt
* B 809cnt ( 54cnt) * V4.26 : ENV_RESYNC_WAIT 60s10s(),
* : dev>+4dlt 60s(ENV_RESYNC_WAIT)
* 线 B ( + settle ) Origin
* *
* : gcc -Wall tests/test_env_resync.c && ./a.out * (poll_yellow_led): 5s (g_env_warn, )
* : 4 ALL PASS ( 60s ; ~66s ) * 10s (g_env_resync, ) Origin 5s
*
* ( ENV_RESYNC_WAIT !)
*/ */
#include <assert.h> #include <assert.h>
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
/*========== 与固件一致的参数宏 (TaskLoop.c vd1_task 内) ==========*/ #define ENV_WARN_WAIT 500
#define STABLE_ORIGIN_PPT 1 // 判稳带: 窗口均值漂移 ≤ Origin×1/1000 (0.1%) #define ENV_RECOVER_WAIT 500
#define STABLE_SETTLE_WINDOWS 2 // 连续 2 窗漂移达标 #define ENV_RESYNC_WAIT 1000 /* V4.26: 60s->10s (拍板4) */
#define FREEZE_TIMEOUT 1000 // 打印饱和(dev>0 不再超时更新 Origin) #define ENV_LEARN_WINDOW 200
#define ENV_RESYNC_WAIT 6000 // V4.24: dev>+4dlt 高位连续 60s → 判定永久环境变化 #define ENV_LEARN_MAX_TICKS 3000
#define ENV_WARN_WAIT 500 // V4.25: 高位连续 ~5s → 黄灯预判锁存 (TaskLoop.h) #define STABLE_ORIGIN_PPT 1
#define ENV_RECOVER_WAIT 500 // V4.25: 环境回正常空闲连续 ~5s → 解除预判 (TaskLoop.h) #define STABLE_SETTLE_WINDOWS 2
#define ENV_LEARN_WINDOW 200 // V4.24: 环境学习窗口 2s
#define ENV_LEARN_MAX_TICKS 3000 // V4.24: 学习预算 30s, 超时放弃
/*========== 与固件同名的 env 全局镜像 (TaskLoop.c :80-90) ==========*/ #define FREEZE_BAND_MULT 4 /* freeze_band = dlt*4 */
/* --- 镜像状态 --- */
static uint32_t sim_Origin = 128095UL;
static uint32_t sim_CAPVD = 128856UL;
static uint32_t sim_dlt = 48UL;
static uint8_t sim_learn_start_cnt = 0;
static uint8_t sim_applied = 0;
static uint32_t sim_applied_origin = 0;
static uint32_t sim_applied_tick = 0;
static uint8_t sim_abort = 0;
static uint32_t sim_tick_cnt = 0;
/* --- g_env_* 镜像 (与固件同名) --- */
static uint8_t g_env_resync = 0; static uint8_t g_env_resync = 0;
static uint16_t g_env_wait_cnt = 0; static uint16_t g_env_wait_cnt = 0;
static uint32_t g_env_sum = 0; static uint32_t g_env_sum = 0;
@@ -37,16 +48,6 @@ static uint16_t g_env_tick = 0;
static uint8_t g_env_warn = 0; static uint8_t g_env_warn = 0;
static uint16_t g_env_recover_cnt = 0; static uint16_t g_env_recover_cnt = 0;
/* 场景镜像 (loop1_* 对应量) */
static uint32_t sim_Origin;
static uint32_t sim_CAPVD;
static int32_t sim_dlt; /* 进阈 dlt */
/* 事件计数 */
static int ev_learn_start = 0;
static int ev_applied = 0;
static int ev_abort = 0;
static void env_reset(void) static void env_reset(void)
{ {
g_env_resync = 0; g_env_resync = 0;
@@ -58,46 +59,43 @@ static void env_reset(void)
g_env_tick = 0; g_env_tick = 0;
g_env_warn = 0; g_env_warn = 0;
g_env_recover_cnt = 0; g_env_recover_cnt = 0;
sim_learn_start_cnt = 0;
sim_applied = 0;
sim_abort = 0;
sim_tick_cnt = 0;
} }
/* V4.25: 黄灯模式4 条件镜像 (TaskLoop.c poll_yellow_led) — 预判锁存 */
static int env_warn_active(void) static int env_warn_active(void)
{ {
return g_env_resync || g_env_warn; return g_env_resync || g_env_warn; /* poll_yellow_led 模式4 条件镜像 */
} }
/* 与 TaskLoop.c env_resync_abort/apply 逐行一致 */
static void env_resync_abort(void) static void env_resync_abort(void)
{ {
/* V4.25: abort 只退出学习, 不清预判锁存 g_env_warn (环境未恢复前黄灯保持) */
g_env_resync = 0; g_env_resync = 0;
g_env_sum = 0; g_env_sum = 0;
g_env_cnt = 0; g_env_cnt = 0;
g_env_prev = 0; g_env_prev = 0;
g_env_settle = 0; g_env_settle = 0;
g_env_tick = 0; g_env_tick = 0;
sim_abort++;
} }
static void env_resync_apply(uint32_t new_origin) /* vd1_task 无车段 dev 三分支的 env 相关逻辑镜像 (V4.26) */
{
(void)new_origin;
/* 固件此处打印 + 替换 Origin + 清 freeze/ORG/entry + slow snap */
ev_applied++;
env_resync_abort();
}
/*========== 模拟 vd1_task 无车段基线块 env 逻辑 (逐行复刻 V4.24) ==========*/
static void sim_tick(void) static void sim_tick(void)
{ {
int32_t dev = (int32_t)sim_CAPVD - (int32_t)sim_Origin; int32_t dev;
int32_t freeze_band = sim_dlt * 4; int32_t freeze_band;
sim_tick_cnt++;
dev = (int32_t)sim_CAPVD - (int32_t)sim_Origin;
freeze_band = (int32_t)(sim_dlt * FREEZE_BAND_MULT);
if (dev < freeze_band && dev > -freeze_band) { if (dev < freeze_band && dev > -freeze_band) {
/* 对称窗口内 → 正常基线跟踪 (env 相关) */ /* 对称窗口内 → 正常基线跟踪 (env 相关) */
g_env_wait_cnt = 0; g_env_wait_cnt = 0;
if (g_env_resync) ev_abort++; if (g_env_resync) env_resync_abort();
env_resync_abort(); /* V4.25: 恢复确认 — 真空闲(未进进入线)连续 5s 解除预判锁存 */
/* V4.25: 恢复确认 — 空闲(dev>-dlt)连续 5s → 解除预判锁存 */
if (dev > -(int32_t)sim_dlt) { if (dev > -(int32_t)sim_dlt) {
if (g_env_warn) { if (g_env_warn) {
g_env_recover_cnt++; g_env_recover_cnt++;
@@ -107,229 +105,231 @@ static void sim_tick(void)
} }
} }
} else { } else {
g_env_recover_cnt = 0; /* 目标进入确认区 → 恢复计时重来 */ g_env_recover_cnt = 0; /* 目标进入确认区 → 恢复计时重来 */
} }
} else if (dev < 0) { } else if (dev < 0) {
/* 车方向 → 冻结 (env 相关) */ /* 车方向 → 冻结 (env 相关) */
g_env_wait_cnt = 0; g_env_wait_cnt = 0;
if (g_env_resync) ev_abort++; g_env_recover_cnt = 0;
env_resync_abort(); if (g_env_resync) env_resync_abort();
g_env_recover_cnt = 0; /* 目标深压 → 恢复计时清零 */
} else { } else {
/* dev>0 高位 — V4.24 两段式 / V4.25 预判锁存 */ /* dev>0 高位 — 两段式 (V4.24) + 预判锁存 (V4.25) */
g_env_recover_cnt = 0; /* 高位异常中 → 恢复确认失效 */ g_env_recover_cnt = 0;
if (!g_env_resync) { if (!g_env_resync) {
g_env_wait_cnt++; g_env_wait_cnt++;
if (g_env_wait_cnt >= ENV_WARN_WAIT) if (g_env_wait_cnt >= ENV_WARN_WAIT)
g_env_warn = 1; /* 预判锁存 (只增不清) */ g_env_warn = 1; /* 5s 预判锁存 */
if (g_env_wait_cnt >= ENV_RESYNC_WAIT) { if (g_env_wait_cnt >= ENV_RESYNC_WAIT) {
sim_learn_start_cnt++;
g_env_resync = 1; g_env_resync = 1;
g_env_wait_cnt = 0; g_env_wait_cnt = 0;
g_env_sum = 0; g_env_sum = 0; g_env_cnt = 0; g_env_prev = 0;
g_env_cnt = 0; g_env_settle = 0; g_env_tick = 0;
g_env_prev = 0;
g_env_settle = 0;
g_env_tick = 0;
ev_learn_start++;
} }
} else { } else {
/* 学习中: 窗口均值 + settle 判稳 */
g_env_tick++; g_env_tick++;
g_env_sum += sim_CAPVD; g_env_sum += sim_CAPVD;
g_env_cnt++; g_env_cnt++;
if (g_env_cnt >= ENV_LEARN_WINDOW) { if (g_env_cnt >= ENV_LEARN_WINDOW) {
uint32_t _m = g_env_sum / g_env_cnt; uint32_t _m = g_env_sum / g_env_cnt;
uint32_t _drift, _band;
g_env_sum = 0; g_env_sum = 0;
g_env_cnt = 0; g_env_cnt = 0;
if (g_env_prev != 0) { if (g_env_prev != 0) {
_drift = (_m > g_env_prev) ? (_m - g_env_prev) : (g_env_prev - _m); uint32_t _drift = (_m > g_env_prev) ? (_m - g_env_prev) : (g_env_prev - _m);
_band = _m * STABLE_ORIGIN_PPT / 1000; uint32_t _band = _m * STABLE_ORIGIN_PPT / 1000;
if (_drift <= _band) { if (_drift <= _band)
g_env_settle++; g_env_settle++;
} else { else
g_env_settle = 0; g_env_settle = 0;
}
if (g_env_settle >= STABLE_SETTLE_WINDOWS) { if (g_env_settle >= STABLE_SETTLE_WINDOWS) {
sim_Origin = _m; /* env_resync_apply 替换 */ sim_applied = 1;
env_resync_apply(_m); sim_applied_origin = _m;
return; /* 本 tick 不再走进入判定 */ sim_applied_tick = sim_tick_cnt;
sim_Origin = _m; /* 原子替换 (空闲由上下文保证) */
g_env_resync = 0;
g_env_wait_cnt = 0;
g_env_sum = 0; g_env_cnt = 0;
g_env_prev = 0; g_env_settle = 0; g_env_tick = 0;
return;
} }
} }
g_env_prev = _m; g_env_prev = _m;
} }
if (g_env_tick >= ENV_LEARN_MAX_TICKS) { if (g_env_tick >= ENV_LEARN_MAX_TICKS) {
ev_abort++; env_resync_abort(); /* 预算超时放弃 (保留旧 Origin) */
env_resync_abort();
} }
} }
} }
} }
/*========== 测试工具 ==========*/ /* ============ 场景 1: 永久变化 (A拿走) ~21s 恢复 ============ */
static void t_set(uint32_t origin, uint32_t capvd, int32_t dlt)
{
sim_Origin = origin;
sim_CAPVD = capvd;
sim_dlt = dlt;
env_reset();
ev_learn_start = 0;
ev_applied = 0;
ev_abort = 0;
}
/*========== 场景 ==========*/
static void test_permanent_change_recovers(void) static void test_permanent_change_recovers(void)
{ {
/* ① 常量高位稳定 (A 拿走): Origin=128095, 空场 CAPVD=128856, dev=+761>192 */ int tick, warn_at = 0;
t_set(128095UL, 128856UL, 48);
int tick = 0; env_reset();
int warn_at = 0; sim_Origin = 128095UL; sim_CAPVD = 128856UL;
for (; tick < 7000 && !ev_applied; tick++) {
for (tick = 0; tick < 4000 && !sim_applied; tick++) {
sim_tick(); sim_tick();
if (!warn_at && env_warn_active()) if (!warn_at && env_warn_active())
warn_at = tick + 1; /* 首次黄灯预判条件成立 (wait>=500) */ warn_at = (int)sim_tick_cnt;
} }
printf("1. 永久变化: warn_at=%d learn=%d applied_at=%d Origin=%u (期望 warn 500, learn>=1000, applied~1600)\n",
warn_at, sim_learn_start_cnt, (int)sim_applied_tick, sim_Origin);
printf("常量高位稳定: 黄灯预判=%d 学习启动=%d 替换=%d 替换tick=%d resync=%d\n", assert(warn_at == 500); /* 5s 预判锁存 */
warn_at, ev_learn_start, ev_applied, tick, g_env_resync); assert(sim_learn_start_cnt == 1);
assert(warn_at == 500); /* V4.25: 高位 5s 即黄灯常亮预判 */ assert(sim_applied == 1);
assert(ev_learn_start == 1); assert(sim_applied_origin == 128856UL); /* 替换到真空场 */
assert(ev_applied == 1);
assert(tick == 6600); /* 6000 等待 + 3×200 窗 */
assert(sim_Origin == 128856UL); assert(sim_Origin == 128856UL);
assert(g_env_resync == 0); /* 学习结束 */ assert(env_warn_active() == 1); /* 替换后黄灯保持 (等待恢复确认) */
assert(env_warn_active() == 1);/* V4.25: 替换后黄灯保持 (恢复确认前不灭) */
/* 替换后 dev≈0 → 窗口分支; 空闲连续 5s 恢复确认 → 黄灯灭 */ /* 空闲 5s 恢复确认 → 灭 */
for (int i = 0; i < 500; i++) for (tick = 0; tick < 600; tick++)
sim_tick(); sim_tick();
assert(ev_applied == 1); /* 不再重复替换 */ assert(env_warn_active() == 0);
assert(env_warn_active() == 0);/* 恢复确认 5s 后黄灯灭 */ printf(" 恢复确认后黄灯灭 OK\n");
printf(" 场景1 PASS — 永久环境变化 5s 预判锁存, ~66s 自动恢复, 恢复确认后灭\n\n");
} }
static void test_transient_never_learns(void) /* ============ 场景 2: 瞬态高位 (<10s 回摆) 不学, 锁存保持到恢复确认 ============ */
static void test_transient_no_learn(void)
{ {
/* ② 瞬态高位短停 (铁块模拟): 40s(4000tick) 高位后回摆 → 不学习 int i;
* 5s (wait>=500) */
t_set(128095UL, 128856UL, 48);
for (int i = 0; i < 4000; i++)
sim_tick();
assert(ev_learn_start == 0);
assert(env_warn_active() == 1); /* 黄灯预判已亮 (wait=4000) */
printf("瞬态高位短停: 4000tick 时黄灯预判亮=%d (未学习)\n", env_warn_active());
/* 回摆进窗口 (CAPVD 回 Origin 附近): wait 清零, 但预判锁存保持 (V4.25 核心语义) */ env_reset();
sim_CAPVD = 128100UL; /* dev=+5 窗口内 */ sim_Origin = 128095UL; sim_CAPVD = 128856UL;
/* 高位 9s (900tick): 预判 5s 亮, 但 <10s 不学习 */
for (i = 0; i < 900; i++)
sim_tick();
assert(env_warn_active() == 1);
assert(sim_learn_start_cnt == 0);
/* 回摆进窗口: wait 清零但预判锁存保持 */
sim_CAPVD = 128100UL; /* dev=+5 */
sim_tick(); sim_tick();
assert(g_env_wait_cnt == 0); assert(g_env_wait_cnt == 0);
assert(env_warn_active() == 1); /* 回摆不清黄灯, 保持常亮 */ assert(env_warn_active() == 1); /* 回摆不清黄灯 (拍板锁存语义) */
/* 窗口内连续 499 tick (累计 500 恢复确认) → 环境稳定, 解除预判 */ /* 窗口空闲 499tick (累计500恢复确认) → */
for (int i = 0; i < 499; i++) for (i = 0; i < 499; i++)
sim_tick(); sim_tick();
assert(env_warn_active() == 0); /* 新环境稳定 5s, 黄灯灭 */ assert(env_warn_active() == 0);
assert(g_env_recover_cnt == 0); assert(g_env_recover_cnt == 0);
/* 再高位 59s(5900tick): 5s 后预判重新锁存, 但 <60s 不学习 */ /* 再高位 9s → 又锁存, 仍不学习 */
sim_CAPVD = 128856UL; sim_CAPVD = 128856UL;
for (int i = 0; i < 5900; i++) for (i = 0; i < 900; i++)
sim_tick(); sim_tick();
assert(ev_learn_start == 0); assert(env_warn_active() == 1);
assert(ev_applied == 0); assert(sim_learn_start_cnt == 0);
assert(sim_applied == 0);
assert(sim_Origin == 128095UL); assert(sim_Origin == 128095UL);
assert(env_warn_active() == 1); /* 高位重现 → 预判重新锁存 */ printf("2. 瞬态高位(<10s)不学, 锁存保持, 恢复确认后灭 OK\n");
printf("瞬态高位短停: 回摆保持常亮→稳定5s灭→高位重现再锁存 (未学习)\n");
printf(" 场景2 PASS — 铁块类瞬态(60s内回摆)不触发重校准, 预判锁存语义正确\n\n");
} }
static void test_learn_interrupted_restarts(void) /* ============ 场景 3: 学习期打断重算 + 最终恢复 ============ */
static void test_learn_interrupt_retry(void)
{ {
/* ③ 学习期打断重算: 60s 后进入学习, 途中目标靠近(回窗) → abort → 重来并最终恢复 */ int i;
t_set(128095UL, 128856UL, 48); int learn_start_before;
/* 第一次 60s + 学习 100 tick 后, B 靠近把 CAPVD 压回窗口 */ env_reset();
for (int i = 0; i < 6000; i++) sim_Origin = 128095UL; sim_CAPVD = 128856UL;
sim_tick(); /* 进入学习 */
/* 10s 高位 → 学习开始 */
for (i = 0; i < 1000; i++)
sim_tick();
assert(sim_learn_start_cnt == 1);
assert(g_env_resync == 1); assert(g_env_resync == 1);
for (int i = 0; i < 100; i++)
sim_tick(); /* 学习窗1 未完 */ /* 学习 100 tick 后目标靠近 (dev=-95 进入确认区) → 打断 */
sim_CAPVD = 128000UL; /* dev=-95 窗口内(B 目标) */ for (i = 0; i < 100; i++)
sim_tick();
sim_CAPVD = 128000UL; /* dev=-95 */
sim_tick(); sim_tick();
assert(g_env_resync == 0); /* 打断退出学习 */ assert(g_env_resync == 0); /* 打断退出学习 */
assert(ev_abort == 1); assert(sim_abort >= 1);
assert(env_warn_active() == 1); /* 预判锁存保持 (环境未确认恢复) */ assert(env_warn_active() == 1); /* 预判锁存保持 (环境未确认恢复) */
assert(g_env_recover_cnt == 0); /* 目标在进入确认区, 恢复计时清零 */ assert(g_env_recover_cnt == 0); /* 目标在进入确认区 恢复计时清零 */
/* 目标离开, 高位重新 60s 学习 3 窗 → 替换 */ /* 目标离开, 高位重现 → 重新等待 10s + 学习 3 窗 → 替换 */
learn_start_before = sim_learn_start_cnt;
sim_CAPVD = 128856UL; sim_CAPVD = 128856UL;
for (int i = 0; i < 7000 && !ev_applied; i++) for (i = 0; i < 4000 && !sim_applied; i++)
sim_tick(); sim_tick();
assert(sim_learn_start_cnt == learn_start_before + 1);
printf("学习期打断: abort=%d 重新学习启动=%d 最终替换tick计数=%d Origin=%lu\n", assert(sim_applied == 1);
ev_abort, ev_learn_start, 0, (unsigned long)sim_Origin);
assert(ev_applied == 1);
assert(ev_learn_start == 2);
assert(sim_Origin == 128856UL); assert(sim_Origin == 128856UL);
assert(env_warn_active() == 1); /* 替换后黄灯保持到恢复确认 */ assert(env_warn_active() == 1); /* 替换后保持到恢复确认 */
for (int i = 0; i < 500; i++)
sim_tick(); /* 窗口恢复确认 5s */ /* 空闲 5s → 黄灯灭 */
assert(env_warn_active() == 0); /* 黄灯灭 */ for (i = 0; i < 600; i++)
printf(" 场景3 PASS — 学习期打断重算+锁存保持, 恢复确认后灭\n\n"); sim_tick();
assert(env_warn_active() == 0);
printf("3. 学习期打断重算, 最终恢复 OK\n");
} }
static void test_unstable_learn_gives_up(void) /* ============ 场景 4: 学习期持续不稳 → 30s 预算放弃 ============ */
static void test_learn_unstable_abort(void)
{ {
/* ④ 学习期每窗均值漂移 >0.1% (CAPVD 高位大幅交替 128856±200..400) int i;
* settle 2 30s , Origin */
t_set(128095UL, 128856UL, 48); env_reset();
for (int i = 0; i < 6000; i++) sim_Origin = 128095UL;
sim_tick(); /* 进入学习 */
/* 高位 10s → 学习开始; 然后窗口均值交替漂移 >0.1% (129056/129256) */
for (i = 0; i < 1000; i++) {
sim_CAPVD = 128856UL;
sim_tick();
}
assert(g_env_resync == 1); assert(g_env_resync == 1);
int tick = 0; for (i = 0; i < 3500; i++) {
for (; tick < 3500 && !ev_applied && g_env_resync; tick++) { /* 每窗 200 样本交替 129056 / 129256 → 窗间漂移 200 > band~129 → settle 恒 0 */
/* 交替高值: 窗与窗之间均值漂移 ~200 > band(~129), 且 dev 恒 >192 */ sim_CAPVD = ((i / 200) & 1) ? 129256UL : 129056UL;
sim_CAPVD = ((tick / ENV_LEARN_WINDOW) & 1) ? 129256UL : 129056UL;
sim_tick(); sim_tick();
} }
/* 1000(等待) + 3000(学习预算) ≈ 放弃于 tick 4000; 总 tick 4500 < 再学阈值(5000) */
printf("学习期不稳: 预算放弃 tick=%d applied=%d resync=%d warn=%d Origin=%lu\n", assert(sim_learn_start_cnt == 1); /* 只有一次学习启动 */
tick, ev_applied, g_env_resync, g_env_warn, (unsigned long)sim_Origin); assert(sim_applied == 0); /* 未替换 */
assert(ev_applied == 0); assert(sim_Origin == 128095UL); /* 旧 Origin 保留 (不冒险污染) */
assert(g_env_resync == 0); /* 超时退出学习 */ assert(g_env_resync == 0); /* 预算 30s 放弃 */
assert(env_warn_active() == 1); /* 环境仍异常 → 黄灯保持锁存 */ assert(env_warn_active() == 1); /* 环境仍异常 → 黄灯保持 */
assert(ev_abort >= 1); printf("4. 学习不稳 30s 放弃, 保留旧 Origin, 黄灯保持 OK\n");
assert(sim_Origin == 128095UL); /* Origin 不被污染 */
printf(" 场景4 PASS — 学习不稳定 30s 放弃保留旧 Origin, 黄灯保持异常指示\n\n");
} }
static void test_no_learning_after_replace(void) /* ============ 场景 5: 替换后不震荡, 无重复学习 ============ */
static void test_no_oscillation_after_apply(void)
{ {
/* ⑤ 替换后 Origin≈CAPVD → dev≈0 正常窗口跟踪, 不再触发二次学习 */ int i, lsc;
t_set(128095UL, 128856UL, 48);
for (int i = 0; i < 7000 && !ev_applied; i++)
sim_tick();
assert(ev_applied == 1);
int before = ev_learn_start; env_reset();
for (int i = 0; i < 6000; i++) sim_Origin = 128095UL; sim_CAPVD = 128856UL;
sim_tick(); /* 新 Origin 下高位条件消失 */
assert(ev_learn_start == before); /* 无新学习 */ for (i = 0; i < 4000 && !sim_applied; i++)
assert(env_warn_active() == 0); /* 恢复确认 5s 后黄灯已灭 */ sim_tick();
printf("替换后稳态: 高位条件消失, 无重复学习, 黄灯灭 (Origin=%lu)\n", assert(sim_applied == 1);
(unsigned long)sim_Origin);
printf(" 场景5 PASS — 恢复后进入正常跟踪, 不震荡\n\n"); lsc = sim_learn_start_cnt;
for (i = 0; i < 6000; i++)
sim_tick(); /* 窗口内持续 (dev≈0) */
assert(sim_learn_start_cnt == lsc); /* 无重复学习 */
assert(sim_applied == 1); /* 未被再次改写 */
assert(sim_Origin == 128856UL);
assert(env_warn_active() == 0); /* 恢复确认后黄灯已灭 */
printf("5. 替换后不震荡无重复学习 OK\n");
} }
int main(void) int main(void)
{ {
test_permanent_change_recovers(); test_permanent_change_recovers();
test_transient_never_learns(); test_transient_no_learn();
test_learn_interrupted_restarts(); test_learn_interrupt_retry();
test_unstable_learn_gives_up(); test_learn_unstable_abort();
test_no_learning_after_replace(); test_no_oscillation_after_apply();
printf("ALL PASS (5 scenarios)\n"); printf("\nALL PASS (ENV_RESYNC_WAIT=%d)\n", ENV_RESYNC_WAIT);
return 0; return 0;
} }
@@ -48,6 +48,9 @@
#define FREEZE_STABILITY_RATE 2 // 冻结稳定性窗口: 参考值的 ±2% (CAPVD 需在此范围内才累计超时) #define FREEZE_STABILITY_RATE 2 // 冻结稳定性窗口: 参考值的 ±2% (CAPVD 需在此范围内才累计超时)
#define ENV_WARN_WAIT 500 // 环境预判 (V4.25): dev>+4dlt 高位连续 ~5s → 黄灯预判锁存(回摆保持) #define ENV_WARN_WAIT 500 // 环境预判 (V4.25): dev>+4dlt 高位连续 ~5s → 黄灯预判锁存(回摆保持)
#define ENV_RECOVER_WAIT 500 // 恢复确认 (V4.25): 环境回正常(空闲)连续 ~5s 才解除预判, 黄灯灭 #define ENV_RECOVER_WAIT 500 // 恢复确认 (V4.25): 环境回正常(空闲)连续 ~5s 才解除预判, 黄灯灭
#define COIL_F_OK_MIN_KHZ 20 // 线圈频率合理窗 (V4.26): 默认低频档口径, <20kHz → L偏大
#define COIL_F_OK_MAX_KHZ 120 // 线圈频率合理窗 (V4.26): >120kHz → L偏小
#define COIL_DEF_C_NF 66 // 默认档电容 (V4.26): 拨码不可读, 粗估 L 打印用; 33nF 档 ×2
/*=========================================================================== /*===========================================================================
* *
+2 -2
View File
@@ -14,10 +14,10 @@
#define __CMCNG_H__ #define __CMCNG_H__
#define PRODUCT_MODEL "DLD154" #define PRODUCT_MODEL "DLD154"
#define FIRMWARE_VER "4.25" #define FIRMWARE_VER "4.26"
#define HARDWARE_VER "3.00" #define HARDWARE_VER "3.00"
#define FIRMWARE_VER_MAIN 4 #define FIRMWARE_VER_MAIN 4
#define FIRMWARE_VER_SUB 25 #define FIRMWARE_VER_SUB 26
#define HARDWARE_VER_MAIN 3 #define HARDWARE_VER_MAIN 3
#define HARDWARE_VER_SUB 0 #define HARDWARE_VER_SUB 0
@@ -139,6 +139,8 @@ uint8_t g_disconnect_count = 0;
uint8_t g_disconnect_active = 0; uint8_t g_disconnect_active = 0;
uint8_t g_fault_phase = 0; uint8_t g_fault_phase = 0;
uint16_t g_fault_tick = 0; uint16_t g_fault_tick = 0;
uint8_t g_coil_fault = 0; // V4.26: 线圈电感量异常 0=正常 1=L偏大(f<20kHz) 2=L偏小(f>120kHz)
uint16_t g_coil_bad_cnt = 0; // V4.26: 电感异常防抖计数 (100tick=1s 置位/清除)
/*=========================================================================== /*===========================================================================
* *
@@ -300,12 +302,16 @@ void poll_green_led(void)
* *
* TMR15 ISR 5ms * TMR15 ISR 5ms
* *
* * (V4.26 : 1>4>5>3>2):
* 线 线: * 1 线 线:
* (200ms / 200ms ) * (200ms / 200ms )
* 线, (g_disconnect_count >= 1): * 4 / (g_env_warn/g_env_resync):
* (V4.24/25, 10s )
* 5 线 (g_coil_fault, 20~120kHz ):
* (200ms/100ms/100ms/800ms )
* 3 线, (g_disconnect_count >= 1):
* N (80ms亮/200ms灭 × N, 1.2s间隔, N=3) * N (80ms亮/200ms灭 × N, 1.2s间隔, N=3)
* 线, : * 2 线, :
* *
*===========================================================================*/ *===========================================================================*/
void poll_yellow_led(void) void poll_yellow_led(void)
@@ -338,6 +344,24 @@ void poll_yellow_led(void)
return; return;
} }
/*--- V4.26 模式5: 线圈电感量不合理 → 一长一短
* 200ms(40tick) 100ms(20) 100ms(20) 800ms(160),
* g_coil_fault: 1=L偏大(f<20kHz) 2=L偏小(f>120kHz)
* : (1) > (4) > (5) > N短闪(3) > ---*/
if (g_coil_fault) {
static const uint16_t dur[4] = { 40, 20, 20, 160 };
if (g_fault_phase > 3) g_fault_phase = 0;
if (g_fault_phase & 1)
LED_YELLOW_OFF;
else
LED_YELLOW_ON;
if (g_fault_tick >= dur[g_fault_phase]) {
g_fault_tick = 0;
g_fault_phase++;
}
return;
}
uint8_t N = g_disconnect_count; uint8_t N = g_disconnect_count;
if (N == 0) { if (N == 0) {
@@ -776,6 +800,43 @@ static void env_resync_apply(uint32_t new_origin)
#endif #endif
env_resync_abort(); env_resync_abort();
} }
/*===========================================================================
* V4.26: 线
* : , 20~120kHz,
* ("一长一短", poll_yellow_led 5)
* : + + (//)
* g_coil_fault: 0= 1=L偏大(f<20kHz) 2=L偏小(f>120kHz)
* : / 100tick(=1s) /
*===========================================================================*/
static void coil_monitor(void)
{
uint32_t f_khz = calc_freq_khz(loop1_Origin, loop1_LPCNT);
uint8_t want = 0;
if (loop1_Origin == 0 || f_khz == 0)
return; /* 无有效基准 → 维持原状 */
if (f_khz < COIL_F_OK_MIN_KHZ)
want = 1; /* L 偏大 (>合理上限) */
else if (f_khz > COIL_F_OK_MAX_KHZ)
want = 2; /* L 偏小 (<合理下限) */
if (want == g_coil_fault) {
g_coil_bad_cnt = 0; /* 状态一致 → 计时复位 */
} else {
g_coil_bad_cnt++;
if (g_coil_bad_cnt >= 100) {
if (g_coil_fault == 0)
PRINT("EVT|coil_fault f:%d kHz L~%lu uH(C66/×2C33) %s\n",
f_khz, 25330000UL / (uint32_t)(f_khz * f_khz * COIL_DEF_C_NF),
want == 1 ? "too_large" : "too_small");
else
PRINT("EVT|coil_ok f:%d kHz\n", f_khz);
g_coil_fault = want;
g_coil_bad_cnt = 0;
}
}
}
/*=========================================================================== /*===========================================================================
* vd1_task M1H VD1_TASK * vd1_task M1H VD1_TASK
@@ -792,7 +853,7 @@ void vd1_task(void)
#define STABLE_ORIGIN_PPT 1 // 基准稳定判据: 窗口均值漂移 ≤ Origin×1/1000 (0.1%) #define STABLE_ORIGIN_PPT 1 // 基准稳定判据: 窗口均值漂移 ≤ Origin×1/1000 (0.1%)
#define STABLE_SETTLE_WINDOWS 2 // 连续 2 个窗口(每窗100样本≈1s)漂移达标 → 判稳 #define STABLE_SETTLE_WINDOWS 2 // 连续 2 个窗口(每窗100样本≈1s)漂移达标 → 判稳
#define STABLE_MAX_SAMPLES 500 // 硬兜底: 5s 未判稳强制结束自检(防绿灯无限闪) #define STABLE_MAX_SAMPLES 500 // 硬兜底: 5s 未判稳强制结束自检(防绿灯无限闪)
#define ENV_RESYNC_WAIT 6000 // V4.24: dev>+4dlt 高位连续 60s → 判定永久环境变化 #define ENV_RESYNC_WAIT 1000 // V4.26: dev>+4dlt 高位连续 10s → 判定永久环境变化 (拍板: 故障补偿 60s→10s)
#define ENV_LEARN_WINDOW 200 // V4.24: 环境学习窗口 2s (200×10ms) #define ENV_LEARN_WINDOW 200 // V4.24: 环境学习窗口 2s (200×10ms)
#define ENV_LEARN_MAX_TICKS 3000 // V4.24: 学习预算 30s, 超时放弃 (防黄灯常亮死锁) #define ENV_LEARN_MAX_TICKS 3000 // V4.24: 学习预算 30s, 超时放弃 (防黄灯常亮死锁)
@@ -935,7 +996,7 @@ void vd1_task(void)
} else { } else {
/* 负variation (CAPVD高于Origin, 特殊铁块磁导率主导/异常高) /* 负variation (CAPVD高于Origin, 特殊铁块磁导率主导/异常高)
* V4.24 : * V4.24 :
* 60s(ENV_RESYNC_WAIT): , Origin * 10s(ENV_RESYNC_WAIT): , Origin
* (36321a1 : Origin ) * (36321a1 : Origin )
* 60s "永久环境变化"(A拿走) * 60s "永久环境变化"(A拿走)
* : B + settle Origin * : B + settle Origin
@@ -1006,6 +1067,9 @@ void vd1_task(void)
} }
} }
/*--- V4.26: 线圈电感量监视 (调用点=稳定无车空闲, 见 coil_monitor) ---*/
coil_monitor();
/*--- 进入确认 — 连续 ENTRY_CONFIRM 次低于阈值才判有车 ---*/ /*--- 进入确认 — 连续 ENTRY_CONFIRM 次低于阈值才判有车 ---*/
if (loop1_CAPVD < (loop1_Origin - loop1_dlt_ORG)) { if (loop1_CAPVD < (loop1_Origin - loop1_dlt_ORG)) {
if (loop1_entry_cnt == 0) { if (loop1_entry_cnt == 0) {