diff --git a/docs/coil-range-alarm-design.md b/docs/coil-range-alarm-design.md index a5eb4c3..3abf158 100644 --- a/docs/coil-range-alarm-design.md +++ b/docs/coil-range-alarm-design.md @@ -13,6 +13,7 @@ | v0.4 | 2026-09-03 | **测频 4 倍偏差修正(V4.27)**:calc_freq_khz 60000→240000(TMR3 实际 120MHz 计数 + 通道捕获 DIV_2)。本文 §3 中"Xn=3681→f=16.3kHz→L≈1.4mH 超限案例"为**测频 bug 假象**——修正后实为 f≈65.2kHz、L≈90µH 的正常线圈;该"红区案例"作废,需按真实频率重新采集现场数据 | | v0.5 | 2026-09-03 | **L 估算标定(V4.28)**:五组 LCR 实测反推振荡回路等效谐振电容 C_eff=41.3~45.5nF(均值 43),与名义 66nF 明显不符 → 固件改标定值 COIL_CAL_C_NF=43(L_uH=2.533e7/(f²×C_eff));COIL_DEF_C_NF 保留为名义口径。残差 ±6% 内,待多线圈/标定电感进一步收口 | | v0.6 | 2026-09-03 | **硬件修复后定稿(V4.29)**:拨码三极管 5.1K→0Ω → 低频档并联全导通,C_eff=68.1~68.9(均值 68.4)≈名义 66。v0.5 的 43 为 5.1K 旧板带病特性(开关未饱和),**作废**;COIL_CAL_C_NF 43→68,残差 ≤1.5%。33nF 档标定 34(旧板数据)待 0Ω 板复测 | +| v0.7 | 2026-09-03 | **故障补偿时序(V4.30)**:预判 3s(ENV_WARN_WAIT 300)→ 补偿 8s(ENV_RESYNC_WAIT 800)→ 学习 3×2s → 替换后空闲 2s 恢复确认(ENV_RECOVER_WAIT 200)黄灯灭;总恢复 ~16s | ## 1. 背景 diff --git a/docs/devlog.md b/docs/devlog.md index 87b3e3b..8859039 100644 --- a/docs/devlog.md +++ b/docs/devlog.md @@ -4,6 +4,40 @@ --- +## 2026-09-03 — 故障补偿时序调整:预判3s + 补偿8s + 恢复2s(固件 V4.30) + +> **固件版本:V4.30**(cmcng.h:FIRMWARE_VER="4.30" + MAIN=4/SUB=30)。devlog 修订 V2.28。 + +### 拍板规格(2026-09-03) + +故障补偿节奏整体提速、现场手感更紧凑: +1. **预判 3 秒**(ENV_WARN_WAIT 500→300):高位持续 3s 黄灯预判锁存常亮(原 5s) +2. **故障补偿 8 秒**(ENV_RESYNC_WAIT 1000→800):累计 8s 高位进入基线学习(原 10s) +3. **新环境稳定 +2 秒**(ENV_RECOVER_WAIT 500→200):学习完成原子替换 Origin 后,空闲连续 2s 恢复确认 → 黄灯灭(原 5s) + +总恢复:~21s → **~16s**(3s 预判 → 8s 触发 → 6s 学习(3×200tick) → 2s 确认灭) + +### 改动 + +- ENV_WARN_WAIT 500→300(~5s→~3s) / ENV_RESYNC_WAIT 1000→800(~10s→~8s) / ENV_RECOVER_WAIT 500→200(~5s→~2s) +- tests/test_env_resync.c 重写(镜像与固件逐分支一致:学习中压线=搁置不退出、学习仅高位段计数、abort 后循环重触发语义) +- 版本 V4.29→V4.30;spec V2.9 / devlog V2.28 + +### 验证(5 场景 ALL PASS) + +| 场景 | 期望 | 实测 | +|------|------|------| +| 永久变化 | warn@300 / applied@~1400 / 替换后 200tick 灭 | warn_at=300 ✓ applied_at=1400 ✓ 灭 ✓ | +| 瞬态 7s(<8s) | 锁存不学, 空闲 2s 灭, 二次 7s 再锁存 | ✓ | +| 学习期压线 | resync 保持=搁置, 走后再学完替换 | ✓ (applied_at=1401) | +| 不稳学习 | 预算 30s 放弃, 不污染, 黄灯保持 | abort=1 ✓ Origin 不变 ✓ warn=1 ✓ | +| 替换后稳态 | 无重复学习, 恢复确认后灭 | ✓ | + +### 待现场验证 + +- A 拿走:~3s 黄灯亮 → ~14s 基线切 B(黄灯仍亮)→ 空场稳定 ~16s 黄灯灭 +- ⚠ 补偿触发提前到 8s:持续 8~10s 的强磁干扰会触发学习(原 10s 边界)——现场观察确认可接受 + ## 2026-09-03 — C_eff 标定定稿 68(0Ω 板低频档)+ 硬件疑点闭环(固件 V4.29) > **固件版本:V4.29**(cmcng.h:FIRMWARE_VER="4.29" + MAIN=4/SUB=29)。devlog 修订 V2.27。 @@ -1229,6 +1263,7 @@ if (dev >= dlt_ORG × 4) { | 版本 | 时间 | 说明 | |------|------|------| +| V2.28 | 2026-09-03 | V4.30 故障补偿时序: 预判3s(300)/补偿8s(800)/恢复2s(200), 总 ~16s | | V2.27 | 2026-09-03 | V4.29 硬件修复(拨码三极管5.1K→0Ω)后 C_eff=68.1~68.9nF≈名义66, COIL_CAL_C_NF 43→68 (旧43为5.1K带病特性作废) | | V2.26 | 2026-09-03 | V4.28 电感量估算引入标定 COIL_CAL_C_NF=43(名义 66nF vs 实测 C_eff≈43,5 组反推 41.3~45.5nF);残差 ±6% | | V2.25 | 2026-09-03 | V4.27 频率测量 4 倍偏差修正(calc 60000→240000:120MHz×DIV_2);coil_l_uH 随 f 修正;旧 f/L 日志需 ×4 | diff --git a/docs/technical-spec.md b/docs/technical-spec.md index c49fb35..10f6e5b 100644 --- a/docs/technical-spec.md +++ b/docs/technical-spec.md @@ -1,6 +1,6 @@ # DLD154V4B 技术规格书 -> 单路线圈车辆检测器 | 固件版本: V4.29 | 文档版本: V2.8 +> 单路线圈车辆检测器 | 固件版本: V4.30 | 文档版本: V2.9 > 本文档面向工程开发、系统集成及故障深度分析 --- @@ -365,7 +365,7 @@ if (!VD_FLAG) { | 4× 冻结阈值 | 防止异常 CAPVD 上升污染基线(V1.5) | | 5% 斜率限幅 | 过滤 EMI/闪电尖峰,保护 IIR(V2.0) | | 冻结超时 + 稳定性检查 | 防止永久冻结死锁(V2.3~2.5) | -| 环境重校准(V4.24/26) | dev>+4×dlt 高位连续 10s(ENV_RESYNC_WAIT,V4.26 由 60s 提速)→ 判定永久环境变化 → 学习基线 B(空闲窗均值+判稳,黄灯常亮)→ 原子替换 Origin;铁块类瞬态 10s 内仍只冻结不污染 | +| 环境重校准(V4.24/26/30) | dev>+4×dlt 高位连续 8s(ENV_RESYNC_WAIT,V4.26 60s→10s,V4.30→8s)→ 判定永久环境变化 → 学习基线 B(空闲窗均值+判稳,黄灯常亮)→ 原子替换 Origin;铁块类瞬态 8s 内仍只冻结不污染 | #### 4.4.3 基线更新速率(两阶段) @@ -450,9 +450,9 @@ if (dev < dlt_ORG * 4) { | STABLE_SETTLE_WINDOWS | 2 | ~2 s | 连续窗口漂移达标数 | | STABLE_MAX_SAMPLES | 500 tick | **~5 s** | 自检硬兜底上限(超时强制判稳) | | FREEZE_TIMEOUT | 1000 | **~10 s** | 基线冻结超时(连续稳定后更新 Origin) | -| ENV_WARN_WAIT | 500 tick | **~5 s** | 环境预判锁存(V4.25):dev>+4×dlt 高位连续 5s → 黄灯常亮预判(锁存) | -| ENV_RECOVER_WAIT | 500 tick | ~5 s | 恢复确认(V4.25):环境回正常空闲连续 5s 才解除预判锁存、黄灯灭(回摆/目标靠近保持常亮) | -| ENV_RESYNC_WAIT | 1000 tick | **~10 s** | 环境重校准等待:dev>+4×dlt 高位连续 10s 判定永久环境变化(V4.26 拍板:60s→10s) | +| ENV_WARN_WAIT | 300 tick | **~3 s** | 环境预判锁存(V4.30):dev>+4×dlt 高位连续 3s → 黄灯常亮预判(锁存) | +| ENV_RECOVER_WAIT | 200 tick | ~2 s | 恢复确认(V4.30):新基线替换后 环境空闲连续 2s 才解除预判锁存、黄灯灭(回摆/目标靠近保持常亮) | +| ENV_RESYNC_WAIT | 800 tick | **~8 s** | 环境重校准等待:dev>+4×dlt 高位连续 8s 判定永久环境变化(V4.30 拍板:60s→10s→8s) | | ENV_LEARN_WINDOW | 200 tick | ~2 s | 环境学习窗口(基线 B 均值窗,学习期黄灯常亮) | | ENV_LEARN_MAX_TICKS | 3000 tick | **~30 s** | 学习预算:窗口不稳超时放弃,保留旧 Origin(防死锁) | | COIL_F_OK_MIN_KHZ | 20 kHz | — | 线圈频率合理窗下界(V4.26):默认低频档口径,f<20kHz → L偏大告警 | @@ -774,3 +774,4 @@ DLD154V4B V1.4 起实现该专利的整数化版本,用于大车通行时的 | V2.6 | 2026-09-03 | 更新至 V4.27:频率测量 4 倍偏差修正(calc_freq_khz 60000→240000:TMR3 实际 120MHz 计数 + 通道 DIV_2 边沿分频,原按 60MHz 假设漏乘 2×2);§4.1.2 流程文字更正 | | V2.7 | 2026-09-03 | 更新至 V4.28:L 估算引入标定等效谐振电容 COIL_CAL_C_NF=43(名义 66nF 与实测 C_eff≈43 不符;5 组实测 41.3~45.5nF) | | V2.8 | 2026-09-03 | 更新至 V4.29:硬件修复(拨码三极管 5.1K→0Ω)使低频档并联全导通,C_eff 43→68.4≈名义66;COIL_CAL_C_NF 43→68 | +| V2.9 | 2026-09-03 | 更新至 V4.30:故障补偿时序 预判 5s→3s(300)/补偿 10s→8s(800)/恢复确认 5s→2s(200),总恢复 ~16s | diff --git a/tests/test_env_resync.c b/tests/test_env_resync.c index 2575861..186b140 100644 --- a/tests/test_env_resync.c +++ b/tests/test_env_resync.c @@ -1,133 +1,101 @@ /* - * test_env_resync.c — 环境重校准(受控基线更新) gcc 隔离单测 (DLD154V4B V4.26) + * test_env_resync.c — 环境重校准(受控基线更新) gcc 隔离单测 (DLD154V4B V4.30) * - * 验证对象: TaskLoop.c vd1_task() 无车段 dev>0 分支 (V4.24/25 两段式, V4.26 参数更新) - * ─ 现场场景 (2026-09-02 实测): 线圈旁金属板A上电 → Origin 学低(128096) - * A 拿走 → CAPVD 弹回 128856 dev=+761(0.59%) > +4dlt → 冻结累计 - * ─ V4.26 拍板: ENV_RESYNC_WAIT 60s→10s(故障补偿提速), 其余语义不变 - * - * 黄灯语义 (poll_yellow_led): 5s 预判锁存(g_env_warn, 回摆保持) → - * 10s 进入学习(g_env_resync, 常亮) → 空闲原子替换 Origin → 空闲 5s 恢复确认 → 灭 - * - * 镜像宏与固件一致(改 ENV_RESYNC_WAIT 时同步此处!) + * 镜像 = TaskLoop.c vd1_task() 无车段 dev>0 分支 (V4.24/25/26/30), 逻辑与固件一致: + * - 高位 dev>+4dlt: 等待确认(g_env_wait_cnt) → 满 ENV_WARN_WAIT 预判锁存 g_env_warn + * - 满 ENV_RESYNC_WAIT → 进入学习 g_env_resync(高位段才计数; 压线/回摆=搁置) + * - 学习: 200 样本/窗, 窗均值漂移 ≤0.1% 连续 2 窗 → env_resync_apply 原子替换 + * - 预算 ENV_LEARN_MAX_TICKS 超时 → abort 不污染 + * - 非高位空闲 dev>-dlt 连续 ENV_RECOVER_WAIT → 清 g_env_warn (黄灯灭) + * V4.30 拍板: 预判 3s + 补偿 8s + 替换后空闲 2s 灭 + * 镜像宏与固件一致(改 ENV_* 时同步此处!) */ - -#include #include +#include +#include +#include #include -#define ENV_WARN_WAIT 500 -#define ENV_RECOVER_WAIT 500 -#define ENV_RESYNC_WAIT 1000 /* V4.26: 60s->10s (拍板4) */ +/* ===== 镜像固件宏 (TaskLoop.c/TaskLoop.h 同步) ===== */ +#define ENV_WARN_WAIT 300 /* V4.30: 预判 3s */ +#define ENV_RECOVER_WAIT 200 /* V4.30: 替换后空闲 2s 恢复确认 */ +#define ENV_RESYNC_WAIT 800 /* V4.30: 补偿(学习等待) 8s */ #define ENV_LEARN_WINDOW 200 #define ENV_LEARN_MAX_TICKS 3000 #define STABLE_ORIGIN_PPT 1 #define STABLE_SETTLE_WINDOWS 2 +#define dlt_4x 192 /* dlt=48 → 4×dlt (SENS=3) */ -#define FREEZE_BAND_MULT 4 /* freeze_band = dlt*4 */ +/* ===== 镜像全局 ===== */ +static uint32_t sim_Origin = 128095UL; /* A 在场基线 */ +static uint32_t sim_CAPVD = 128095UL; +static uint32_t g_env_wait_cnt = 0; +static uint32_t g_env_recover_cnt = 0; +static uint32_t g_env_resync = 0; /* 学习中 */ +static uint32_t g_env_warn = 0; /* 预判锁存 */ +static uint32_t g_env_tick = 0; /* 学习预算计数 */ +static uint32_t g_env_sum = 0; /* 学习窗累加 */ +static uint32_t g_env_cnt = 0; /* 学习窗样本数 */ +static uint32_t g_env_prev = 0; /* 上一窗均值 */ +static uint32_t g_env_settle = 0; /* 连续稳定窗数 */ +static uint32_t sim_learn_start_cnt = 0; +static uint32_t sim_applied_cnt = 0; +static uint32_t sim_abort_cnt = 0; +static uint32_t sim_tick_cnt = 0; -/* --- 镜像状态 --- */ -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; +static int env_warn_active(void) { return (g_env_warn != 0 || g_env_resync != 0); } -/* --- g_env_* 镜像 (与固件同名) --- */ -static uint8_t g_env_resync = 0; -static uint16_t g_env_wait_cnt = 0; -static uint32_t g_env_sum = 0; -static uint16_t g_env_cnt = 0; -static uint32_t g_env_prev = 0; -static uint8_t g_env_settle = 0; -static uint16_t g_env_tick = 0; -static uint8_t g_env_warn = 0; -static uint16_t g_env_recover_cnt = 0; - -static void env_reset(void) +/* ===== 镜像: env_resync_apply / env_resync_abort (TaskLoop.c 逻辑复刻) ===== */ +static void env_resync_apply(uint32_t new_origin) { - g_env_resync = 0; + sim_Origin = new_origin; /* 空闲原子替换 */ + sim_applied_cnt++; + 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; - g_env_warn = 0; - g_env_recover_cnt = 0; - sim_learn_start_cnt = 0; - sim_applied = 0; - sim_abort = 0; - sim_tick_cnt = 0; -} - -static int env_warn_active(void) -{ - return g_env_resync || g_env_warn; /* poll_yellow_led 模式4 条件镜像 */ + g_env_sum = 0; + g_env_cnt = 0; + g_env_prev = 0; + g_env_settle = 0; + g_env_tick = 0; + g_env_recover_cnt = 0; /* 替换后重新计恢复确认 */ } static void env_resync_abort(void) { - g_env_resync = 0; - g_env_sum = 0; - g_env_cnt = 0; - g_env_prev = 0; - g_env_settle = 0; - g_env_tick = 0; - sim_abort++; + sim_abort_cnt++; + g_env_resync = 0; + g_env_sum = 0; + g_env_cnt = 0; + g_env_prev = 0; + g_env_settle = 0; + g_env_tick = 0; + g_env_wait_cnt = 0; } -/* vd1_task 无车段 dev 三分支的 env 相关逻辑镜像 (V4.26) */ -static void sim_tick(void) +/* ===== 镜像: vd1_task 无车段 dev 分支 (TaskLoop.c 复刻, V4.30) ===== */ +static void vd_env_branch(void) { - int32_t dev; - int32_t freeze_band; + int32_t dev = (int32_t)sim_CAPVD - (int32_t)sim_Origin; - 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) { - /* 对称窗口内 → 正常基线跟踪 (env 相关) */ - g_env_wait_cnt = 0; - if (g_env_resync) env_resync_abort(); - /* V4.25: 恢复确认 — 真空闲(未进进入线)连续 5s 解除预判锁存 */ - if (dev > -(int32_t)sim_dlt) { - if (g_env_warn) { - g_env_recover_cnt++; - if (g_env_recover_cnt >= ENV_RECOVER_WAIT) { - g_env_warn = 0; - g_env_recover_cnt = 0; - } - } - } else { - g_env_recover_cnt = 0; /* 目标在进入确认区 → 恢复计时重来 */ - } - } else if (dev < 0) { - /* 车方向 → 冻结 (env 相关) */ - g_env_wait_cnt = 0; - g_env_recover_cnt = 0; - if (g_env_resync) env_resync_abort(); - } else { - /* dev>0 高位 — 两段式 (V4.24) + 预判锁存 (V4.25) */ - g_env_recover_cnt = 0; + if (dev > (int32_t)dlt_4x) { + /* ── 高位: 等待确认 / 学习 ── */ if (!g_env_resync) { g_env_wait_cnt++; + g_env_recover_cnt = 0; /* 高位异常中恢复确认失效 */ if (g_env_wait_cnt >= ENV_WARN_WAIT) - g_env_warn = 1; /* 5s 预判锁存 */ + g_env_warn = 1; /* 预判锁存 (只增不清) */ 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_sum = 0; g_env_cnt = 0; g_env_prev = 0; - g_env_settle = 0; g_env_tick = 0; + g_env_sum = 0; + g_env_cnt = 0; + g_env_prev = 0; + g_env_settle = 0; + g_env_tick = 0; + sim_learn_start_cnt++; } } else { - /* 学习中: 窗口均值 + settle 判稳 */ + /* ── 学习基线 B: 连续空闲窗口均值 + settle 判稳 ── */ g_env_tick++; g_env_sum += sim_CAPVD; g_env_cnt++; @@ -138,198 +106,249 @@ static void sim_tick(void) if (g_env_prev != 0) { uint32_t _drift = (_m > g_env_prev) ? (_m - g_env_prev) : (g_env_prev - _m); uint32_t _band = _m * STABLE_ORIGIN_PPT / 1000; - if (_drift <= _band) + if (_drift <= _band) { g_env_settle++; - else - g_env_settle = 0; - if (g_env_settle >= STABLE_SETTLE_WINDOWS) { - sim_applied = 1; - sim_applied_origin = _m; - 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; + if (g_env_settle >= STABLE_SETTLE_WINDOWS) { + env_resync_apply(_m); /* 连续 2 窗稳定 → B 可信, 原子替换 */ + return; + } + } else { + g_env_settle = 0; /* 窗口不稳 → 重算 */ } } g_env_prev = _m; } - if (g_env_tick >= ENV_LEARN_MAX_TICKS) { - env_resync_abort(); /* 预算超时放弃 (保留旧 Origin) */ - } + if (g_env_tick >= ENV_LEARN_MAX_TICKS) + env_resync_abort(); /* 预算超时 → 放弃不污染 */ } + return; + } + + /* ── 非高位: 高位累计清零 (回摆/压线/空闲); 学习中则搁置(不计数) ── */ + g_env_wait_cnt = 0; + if (dev < -48) { /* 目标压入进入确认区 */ + g_env_recover_cnt = 0; + return; + } + /* 空闲 (dev > -dlt): 环境恢复正常 → 恢复确认计时 → 清预判(黄灯灭) */ + g_env_recover_cnt++; + if (g_env_recover_cnt >= ENV_RECOVER_WAIT) { + g_env_warn = 0; /* 稳定 2s → 解除预判 */ + g_env_recover_cnt = 0; } } -/* ============ 场景 1: 永久变化 (A拿走) ~21s 恢复 ============ */ +static void sim_tick(void) +{ + sim_tick_cnt++; + vd_env_branch(); +} + +/* ===== 场景隔离 ===== */ +static void reset_all(void) +{ + sim_Origin = 128095UL; + sim_CAPVD = 128095UL; + g_env_wait_cnt = 0; + g_env_recover_cnt = 0; + g_env_resync = 0; + g_env_warn = 0; + g_env_tick = 0; + g_env_sum = 0; + g_env_cnt = 0; + g_env_prev = 0; + g_env_settle = 0; + sim_learn_start_cnt = 0; + sim_applied_cnt = 0; + sim_abort_cnt = 0; + sim_tick_cnt = 0; +} + +/* ===== 场景 1: 永久变化 (A 拿走) — 3s 预判 → 8s 触发 → 3窗学完 ~14s 替换 → +2s 灭 ===== */ static void test_permanent_change_recovers(void) { - int tick, warn_at = 0; + int tick, warn_at = 0; + int loop; - env_reset(); - sim_Origin = 128095UL; sim_CAPVD = 128856UL; - - for (tick = 0; tick < 4000 && !sim_applied; tick++) { + reset_all(); + for (loop = 0; loop < 2500; loop++) { + sim_CAPVD = 128856UL; /* dev=761 恒高位 */ sim_tick(); if (!warn_at && env_warn_active()) warn_at = (int)sim_tick_cnt; + if (sim_applied_cnt) + break; } - 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); - - assert(warn_at == 500); /* 5s 预判锁存 */ + printf("1. 永久变化: warn_at=%d learn_start=%d applied_at=%d Origin=%u (期望 300 / 1 / ~1400 / 128856)\n", + warn_at, sim_learn_start_cnt, (int)sim_tick_cnt, sim_Origin); + assert(warn_at == 300); assert(sim_learn_start_cnt == 1); - assert(sim_applied == 1); - assert(sim_applied_origin == 128856UL); /* 替换到真空场 */ + assert(sim_applied_cnt == 1); assert(sim_Origin == 128856UL); - assert(env_warn_active() == 1); /* 替换后黄灯保持 (等待恢复确认) */ - /* 空闲 5s 恢复确认 → 灭 */ - for (tick = 0; tick < 600; tick++) + /* 替换后 +2s (200tick) 恢复确认 → 黄灯灭 */ + for (tick = 0; tick < 200; tick++) { + sim_CAPVD = 128856UL; /* dev=0 空闲稳态 */ sim_tick(); - assert(env_warn_active() == 0); + } + printf(" 替换后 200tick: warn=%d resync=%d (期望 0/0 → 灭)\n", g_env_warn, g_env_resync); + assert(g_env_warn == 0); + assert(g_env_resync == 0); + assert(sim_applied_cnt == 1); printf(" 恢复确认后黄灯灭 OK\n"); } -/* ============ 场景 2: 瞬态高位 (<10s 回摆) 不学, 锁存保持到恢复确认 ============ */ -static void test_transient_no_learn(void) +/* ===== 场景 2: 瞬态高位 7s (<8s) 不学 + 锁存保持 + 空闲 2s 灭 ===== */ +static void test_transient_keeps_warn(void) { - int i; + int loop; - env_reset(); - sim_Origin = 128095UL; sim_CAPVD = 128856UL; - - /* 高位 9s (900tick): 预判 5s 亮, 但 <10s 不学习 */ - for (i = 0; i < 900; i++) + reset_all(); + for (loop = 0; loop < 700; loop++) { /* 7s 高位 */ + sim_CAPVD = 128856UL; sim_tick(); + } + printf("2. 瞬态 7s 高位: warn=%d learn=%d (期望 1 / 0)\n", g_env_warn, sim_learn_start_cnt); assert(env_warn_active() == 1); assert(sim_learn_start_cnt == 0); - /* 回摆进窗口: wait 清零但预判锁存保持 */ - sim_CAPVD = 128100UL; /* dev=+5 */ + sim_CAPVD = 128000UL; /* 回摆 dev=-95 (目标压入) → 锁存保持 */ sim_tick(); - assert(g_env_wait_cnt == 0); - assert(env_warn_active() == 1); /* 回摆不清黄灯 (拍板锁存语义) */ + printf(" 回摆后: warn=%d resync=%d (期望 warn 1 保持)\n", g_env_warn, g_env_resync); + assert(g_env_warn == 1); + assert(g_env_resync == 0); - /* 窗口空闲 499tick (累计500恢复确认) → 灭 */ - for (i = 0; i < 499; i++) + for (loop = 0; loop < 200; loop++) { /* 空闲 2s → 恢复确认 → 灭 */ + sim_CAPVD = 128095UL; sim_tick(); - assert(env_warn_active() == 0); - assert(g_env_recover_cnt == 0); - - /* 再高位 9s → 又锁存, 仍不学习 */ - sim_CAPVD = 128856UL; - for (i = 0; i < 900; i++) - sim_tick(); - assert(env_warn_active() == 1); + } + printf(" 空闲 200tick: warn=%d (期望 0 → 灭)\n", g_env_warn); + assert(g_env_warn == 0); assert(sim_learn_start_cnt == 0); - assert(sim_applied == 0); - assert(sim_Origin == 128095UL); - printf("2. 瞬态高位(<10s)不学, 锁存保持, 恢复确认后灭 OK\n"); + + for (loop = 0; loop < 700; loop++) { /* 再 7s 高位 → 重新锁存, 仍不学 */ + sim_CAPVD = 128856UL; + sim_tick(); + } + printf(" 二次 7s 高位: warn=%d learn=%d (期望 1 / 0)\n", g_env_warn, sim_learn_start_cnt); + assert(g_env_warn == 1); + assert(sim_learn_start_cnt == 0); + printf("2. 瞬态高位(<8s)不学, 锁存保持, 恢复确认后灭 OK\n"); } -/* ============ 场景 3: 学习期打断重算 + 最终恢复 ============ */ -static void test_learn_interrupt_retry(void) +/* ===== 场景 3: 学习期目标压线 = 搁置(不退出) → 走后再学完 → 替换 ===== */ +static void test_interrupted_learn_recomputes(void) { - int i; - int learn_start_before; + int loop; + int applied_at = 0; - env_reset(); - sim_Origin = 128095UL; sim_CAPVD = 128856UL; - - /* 10s 高位 → 学习开始 */ - for (i = 0; i < 1000; i++) + reset_all(); + for (loop = 0; loop < 800; loop++) { /* 8s → 学习开始 */ + sim_CAPVD = 128856UL; sim_tick(); + } assert(sim_learn_start_cnt == 1); assert(g_env_resync == 1); - /* 学习 100 tick 后目标靠近 (dev=-95 进入确认区) → 打断 */ - for (i = 0; i < 100; i++) + for (loop = 0; loop < 100; loop++) { /* 学习 100tick (801..900) */ + sim_CAPVD = 128856UL; sim_tick(); - sim_CAPVD = 128000UL; /* dev=-95 */ + } + sim_CAPVD = 128000UL; /* 目标压线 dev=-95: 学习中搁置 */ sim_tick(); - assert(g_env_resync == 0); /* 打断退出学习 */ - assert(sim_abort >= 1); - assert(env_warn_active() == 1); /* 预判锁存保持 (环境未确认恢复) */ - assert(g_env_recover_cnt == 0); /* 目标在进入确认区 → 恢复计时清零 */ + printf("3. 压线打断: resync=%d tick=%u learn=%d warn=%d (期望 resync 1 保持=搁置)\n", + g_env_resync, g_env_tick, sim_learn_start_cnt, g_env_warn); + assert(g_env_resync == 1); /* 不退出学习 */ + assert(g_env_warn == 1); - /* 目标离开, 高位重现 → 重新等待 10s + 学习 3 窗 → 替换 */ - learn_start_before = sim_learn_start_cnt; - sim_CAPVD = 128856UL; - for (i = 0; i < 4000 && !sim_applied; i++) + for (loop = 0; loop < 1600; loop++) { /* 目标走 → 继续高位学习 → 替换 */ + sim_CAPVD = 128856UL; sim_tick(); - assert(sim_learn_start_cnt == learn_start_before + 1); - assert(sim_applied == 1); + if (sim_applied_cnt) { applied_at = (int)sim_tick_cnt; break; } + } + printf(" 继续学后: applied_at=%d learn=%d Origin=%u (期望 ~1400 / 1 / 128856)\n", + applied_at, sim_learn_start_cnt, sim_Origin); + assert(sim_applied_cnt == 1); + assert(sim_learn_start_cnt == 1); /* 全程仅一次学习启动 */ assert(sim_Origin == 128856UL); - assert(env_warn_active() == 1); /* 替换后保持到恢复确认 */ - /* 空闲 5s → 黄灯灭 */ - for (i = 0; i < 600; i++) + for (loop = 0; loop < 200; loop++) { /* +2s 恢复确认 → 灭 */ + sim_CAPVD = 128856UL; sim_tick(); - assert(env_warn_active() == 0); - printf("3. 学习期打断重算, 最终恢复 OK\n"); + } + assert(g_env_warn == 0); + printf("3. 学习期压线=搁置, 恢复后学完替换, 灭 OK\n"); } -/* ============ 场景 4: 学习期持续不稳 → 30s 预算放弃 ============ */ -static void test_learn_unstable_abort(void) +/* ===== 场景 4: 学习窗不稳 → 预算 30s 放弃 — 不污染, 黄灯保持 ===== */ +static void test_unstable_learn_gives_up(void) { - int i; + int loop; + int i; - env_reset(); - sim_Origin = 128095UL; - - /* 高位 10s → 学习开始; 然后窗口均值交替漂移 >0.1% (129056/129256) */ - for (i = 0; i < 1000; i++) { + reset_all(); + for (loop = 0; loop < 800; loop++) { sim_CAPVD = 128856UL; sim_tick(); } assert(g_env_resync == 1); + assert(sim_learn_start_cnt == 1); - for (i = 0; i < 3500; i++) { - /* 每窗 200 样本交替 129056 / 129256 → 窗间漂移 200 > band~129 → settle 恒 0 */ + for (i = 0; i < 3800 && !sim_abort_cnt; i++) { /* 每窗 200 样本交替 → 漂移 200 > band~129 → 恒不稳 */ sim_CAPVD = ((i / 200) & 1) ? 129256UL : 129056UL; sim_tick(); } - /* 1000(等待) + 3000(学习预算) ≈ 放弃于 tick 4000; 总 tick 4500 < 再学阈值(5000) */ - assert(sim_learn_start_cnt == 1); /* 只有一次学习启动 */ - assert(sim_applied == 0); /* 未替换 */ - assert(sim_Origin == 128095UL); /* 旧 Origin 保留 (不冒险污染) */ - assert(g_env_resync == 0); /* 预算 30s 放弃 */ - assert(env_warn_active() == 1); /* 环境仍异常 → 黄灯保持 */ - printf("4. 学习不稳 30s 放弃, 保留旧 Origin, 黄灯保持 OK\n"); + printf("4. 不稳学习: resync=%d abort=%d applied=%d warn=%d Origin=%u (期望 0/1/0/1/128095)\n", + g_env_resync, sim_abort_cnt, sim_applied_cnt, g_env_warn, sim_Origin); + assert(g_env_resync == 0); + assert(sim_abort_cnt >= 1); + assert(sim_applied_cnt == 0); + assert(sim_Origin == 128095UL); /* 不污染 */ + assert(g_env_warn == 1); /* 环境仍异常 → 黄灯保持 */ + printf("4. 学习放弃不污染, 黄灯保持 OK\n"); } -/* ============ 场景 5: 替换后不震荡, 无重复学习 ============ */ -static void test_no_oscillation_after_apply(void) +/* ===== 场景 5: 替换后稳态无重复学习 ===== */ +static void test_no_repeat_learn(void) { - int i, lsc; + int loop; - env_reset(); - sim_Origin = 128095UL; sim_CAPVD = 128856UL; - - for (i = 0; i < 4000 && !sim_applied; i++) + reset_all(); + for (loop = 0; loop < 800; loop++) { + sim_CAPVD = 128856UL; sim_tick(); - assert(sim_applied == 1); - - 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); /* 未被再次改写 */ + } + for (loop = 0; loop < 1000; loop++) { + sim_CAPVD = 128856UL; + sim_tick(); + if (sim_applied_cnt) + break; + } + printf("5. 替换后稳态: applied=%d learn=%d warn=%d Origin=%u (期望 1/1/1/128856)\n", + sim_applied_cnt, sim_learn_start_cnt, g_env_warn, sim_Origin); + assert(sim_applied_cnt == 1); + assert(sim_learn_start_cnt == 1); assert(sim_Origin == 128856UL); - assert(env_warn_active() == 0); /* 恢复确认后黄灯已灭 */ - printf("5. 替换后不震荡无重复学习 OK\n"); + assert(g_env_warn == 1); /* 替换后 warn 锁存, 待恢复确认 */ + + for (loop = 0; loop < 1000; loop++) { /* 空闲稳态 10s → 灭且无重复学习 */ + sim_CAPVD = 128856UL; + sim_tick(); + } + assert(sim_applied_cnt == 1); + assert(sim_learn_start_cnt == 1); + assert(g_env_warn == 0); + printf("5. 替换后无重复学习, 恢复确认后灭 OK\n"); } int main(void) { + printf("test_env_resync (V4.30: 预判3s/补偿8s/恢复2s)\n"); test_permanent_change_recovers(); - test_transient_no_learn(); - test_learn_interrupt_retry(); - test_learn_unstable_abort(); - test_no_oscillation_after_apply(); - printf("\nALL PASS (ENV_RESYNC_WAIT=%d)\n", ENV_RESYNC_WAIT); + test_transient_keeps_warn(); + test_interrupted_learn_recomputes(); + test_unstable_learn_gives_up(); + test_no_repeat_learn(); + printf("ALL PASS (ENV_WARN 300 / RESYNC 800 / RECOVER 200)\n"); return 0; } diff --git a/utilities/at32f421_freertos_demo/inc/TaskLoop.h b/utilities/at32f421_freertos_demo/inc/TaskLoop.h index 348e292..23ae914 100644 --- a/utilities/at32f421_freertos_demo/inc/TaskLoop.h +++ b/utilities/at32f421_freertos_demo/inc/TaskLoop.h @@ -46,8 +46,8 @@ #define ENTRY_CONFIRM 3 // 进入确认: 连续 N 次低于阈值才判定有车 #define FREEZE_TIMEOUT 1000 // 基线冻结超时: ~10s @ 10ms/tick, 持续偏高且稳定后强制更新基线 #define FREEZE_STABILITY_RATE 2 // 冻结稳定性窗口: 参考值的 ±2% (CAPVD 需在此范围内才累计超时) -#define ENV_WARN_WAIT 500 // 环境预判 (V4.25): dev>+4dlt 高位连续 ~5s → 黄灯预判锁存(回摆保持) -#define ENV_RECOVER_WAIT 500 // 恢复确认 (V4.25): 环境回正常(空闲)连续 ~5s 才解除预判, 黄灯灭 +#define ENV_WARN_WAIT 300 // 环境预判 (V4.30): dev>+4dlt 高位连续 ~3s → 黄灯预判锁存(回摆保持) +#define ENV_RECOVER_WAIT 200 // 恢复确认 (V4.30): 新基线替换后 环境空闲连续 ~2s 才解除预判, 黄灯灭 #define COIL_F_OK_MIN_KHZ 20 // 线圈频率合理窗 (V4.26): 默认低频档口径, <20kHz → L偏大 #define COIL_F_OK_MAX_KHZ 120 // 线圈频率合理窗 (V4.26): >120kHz → L偏小 #define COIL_CAL_C_NF 68 // 标定等效谐振电容 nF (V4.29): 0Ω板 低频档(66nF名义)实测 C_eff=68.1~68.9 (均值68.4) diff --git a/utilities/at32f421_freertos_demo/inc/cmcng.h b/utilities/at32f421_freertos_demo/inc/cmcng.h index 6da5b49..9bc84bb 100644 --- a/utilities/at32f421_freertos_demo/inc/cmcng.h +++ b/utilities/at32f421_freertos_demo/inc/cmcng.h @@ -14,10 +14,10 @@ #define __CMCNG_H__ #define PRODUCT_MODEL "DLD154" -#define FIRMWARE_VER "4.29" +#define FIRMWARE_VER "4.30" #define HARDWARE_VER "3.00" #define FIRMWARE_VER_MAIN 4 -#define FIRMWARE_VER_SUB 29 +#define FIRMWARE_VER_SUB 30 #define HARDWARE_VER_MAIN 3 #define HARDWARE_VER_SUB 0 diff --git a/utilities/at32f421_freertos_demo/src/TaskLoop.c b/utilities/at32f421_freertos_demo/src/TaskLoop.c index deecb3d..f4286a9 100644 --- a/utilities/at32f421_freertos_demo/src/TaskLoop.c +++ b/utilities/at32f421_freertos_demo/src/TaskLoop.c @@ -875,7 +875,7 @@ void vd1_task(void) #define STABLE_ORIGIN_PPT 1 // 基准稳定判据: 窗口均值漂移 ≤ Origin×1/1000 (0.1%) #define STABLE_SETTLE_WINDOWS 2 // 连续 2 个窗口(每窗100样本≈1s)漂移达标 → 判稳 #define STABLE_MAX_SAMPLES 500 // 硬兜底: 5s 未判稳强制结束自检(防绿灯无限闪) -#define ENV_RESYNC_WAIT 1000 // V4.26: dev>+4dlt 高位连续 10s → 判定永久环境变化 (拍板: 故障补偿 60s→10s) +#define ENV_RESYNC_WAIT 800 // V4.30: dev>+4dlt 高位连续 8s → 判定永久环境变化 (拍板: 预判3s + 补偿8s + 恢复2s) #define ENV_LEARN_WINDOW 200 // V4.24: 环境学习窗口 2s (200×10ms) #define ENV_LEARN_MAX_TICKS 3000 // V4.24: 学习预算 30s, 超时放弃 (防黄灯常亮死锁)