fix(V4B): 负variation铁块锁死修复 — 基线跟踪双向对称保护 (V4.20)
现场: 特殊铁块(磁导率主导)靠近 → variation负 → Origin被污染 → 离开时假进入 → 锁死不释放
根因: 基线跟踪单边保护(只挡dev>+4×dlt), 负偏差(CAPVD>Origin)照常更新Origin
FREEZE_TIMEOUT=10s后直接跳污染值更彻底
修复:
1. 偏差窗口对称 [-4×dlt, +4×dlt] — 负偏差也冻结基线
2. 负偏差只冻结、永不超时更新Origin(铁块停留多久不污染)
3. 正偏差保留FREEZE_TIMEOUT兜底(车辆长时间信号不丢)
4. 新增 EVT|neg_var_freeze 事件打印
单测: tests/test_neg_variation.c 6项全过(决策/超时/场景仿真)
文档: devlog V2.19
This commit is contained in:
@@ -4,6 +4,42 @@
|
||||
|
||||
---
|
||||
|
||||
## 2026-09-01 — 负variation铁块场景修复:基线跟踪双向对称保护(固件 V4.20)
|
||||
|
||||
> **固件版本:V4.20**。devlog 修订 V2.19。
|
||||
|
||||
### 现场现象(用户)
|
||||
|
||||
特殊形状铁块(磁导率主导)靠近线圈 → variation 为**负**(频率下降,CAPVD 高于 Origin):
|
||||
- 靠近过程无输出(正常——检测只认正 variation,物理限制)
|
||||
- 铁块慢慢离开时:有时一直无输出,有时**一旦输出就释放不了**(锁定)
|
||||
|
||||
### 根因(TaskLoop.c 基线跟踪单边保护)
|
||||
|
||||
```c
|
||||
// 修复前: 只挡 dev > +4×dlt —— 负variation(CAPVD>Origin) 时基线照常更新
|
||||
if (dev < (int32_t)(loop1_dlt_ORG * 4)) {
|
||||
update_moving_average(..., &loop1_Origin, loop1_CAPVD, ...); // Origin 被污染
|
||||
}
|
||||
```
|
||||
|
||||
污染链:铁块靠近 → CAPVD 升 Origin+500 → 基线跟踪"无车"照常更新(或 FREEZE_TIMEOUT=10s 后直接跳污染值)→ Origin 抬高 → 铁块离开 CAPVD 回落 → 跨过**抬高的进入线** → 假进入 → Origin 冻结(有车)→ 释放线 = 污染 Origin - dlt(高)→ CAPVD 永远够不到 → **锁死不释放**。
|
||||
|
||||
### 修复(双向对称保护)
|
||||
|
||||
1. **偏差窗口对称**:`-4×dlt ≤ dev ≤ +4×dlt` 才正常跟踪(负偏差也冻结)
|
||||
2. **负偏差(dev>0, CAPVD>Origin)只冻结、永不超时更新 Origin**——铁块停留多久都不污染
|
||||
3. **正偏差(dev<0, 疑似车)保留原 FREEZE_TIMEOUT 兜底**(车辆长时间信号不丢)
|
||||
4. 权衡:长时间负温漂基线不跟随(安全方向,正车检测仍正常)
|
||||
5. 新增 `EVT|neg_var_freeze` 事件打印:负偏差首中时输出 CAPVD/Origin/dev/dlt/频率
|
||||
|
||||
### 验证
|
||||
|
||||
- `tests/test_neg_variation.c` 回归单测 6 项全过:决策方向 / 负偏差超时不污染 / 正偏差兜底保留 / 完整场景仿真(铁块靠近→停留→离开:无假进入、无锁定)✓
|
||||
- 现有单测(方案A/B)不受影响
|
||||
|
||||
---
|
||||
|
||||
## 2026-09-01 — Debug 串口打印重组:频率/采样/基准全量输出(固件 V4.20)
|
||||
|
||||
> **固件版本:V4.20**。devlog 修订 V2.18。
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* test_neg_variation.c — 负variation铁块场景回归单测 (V4.20)
|
||||
*
|
||||
* 背景: 特殊铁块(磁导率主导)靠近线圈 → CAPVD 高于 Origin(频率降)
|
||||
* 原单边基线保护 → Origin 被污染 → 铁块离开时假进入 → 释放线抬高 → 锁死不释放
|
||||
* 修复: 双向对称保护 — 负偏差(dev>0) 只冻结, 永不超时更新 Origin
|
||||
*
|
||||
* 验证:
|
||||
* 1. 小偏差(±4×dlt内) → 正常基线更新
|
||||
* 2. 正variation(dev<0, 疑似车) → 冻结 + 超时兜底更新(保留)
|
||||
* 3. 负variation(dev>0, 铁块) → 冻结 + 超时也绝不更新 Origin ← 核心修复
|
||||
* 4. 场景仿真: 铁块靠近(负var) → 离开 → 修复前假进入+锁定 / 修复后无输出
|
||||
*===========================================================================*/
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define ORIGIN 131072UL
|
||||
#define DLT_ORG 38 /* SENS=3 进入 counts (表值25@100000 ≈ 38@131072) */
|
||||
#define FREEZE_BAND (DLT_ORG * 4) /* 对称窗口 ±152 */
|
||||
#define FREEZE_TIMEOUT 1000
|
||||
|
||||
/* 基线决策 — 与 TaskLoop.c 同逻辑 (简化版, 只测决策方向) */
|
||||
typedef enum { DEC_UPDATE = 0, DEC_FREEZE_POS, DEC_FREEZE_NEG } dec_t;
|
||||
|
||||
static dec_t baseline_decide(int32_t capvd, int32_t origin, uint32_t *freeze_cnt)
|
||||
{
|
||||
int32_t dev = capvd - origin;
|
||||
int32_t band = FREEZE_BAND;
|
||||
if (dev < band && dev > -band) {
|
||||
*freeze_cnt = 0;
|
||||
return DEC_UPDATE;
|
||||
} else if (dev < 0) {
|
||||
(*freeze_cnt)++;
|
||||
return DEC_FREEZE_POS;
|
||||
} else {
|
||||
if (*freeze_cnt == 0) { /* 首中记录 */ }
|
||||
(*freeze_cnt)++;
|
||||
if (*freeze_cnt > FREEZE_TIMEOUT) *freeze_cnt = FREEZE_TIMEOUT;
|
||||
return DEC_FREEZE_NEG;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
uint32_t freeze_cnt = 0;
|
||||
|
||||
printf("=== 负variation铁块场景回归单测 (V4.20) ===\n");
|
||||
|
||||
/* 1. 小偏差 → 正常更新 */
|
||||
assert(baseline_decide(ORIGIN + 10, ORIGIN, &freeze_cnt) == DEC_UPDATE);
|
||||
assert(baseline_decide(ORIGIN - 10, ORIGIN, &freeze_cnt) == DEC_UPDATE);
|
||||
assert(baseline_decide(ORIGIN - 100, ORIGIN, &freeze_cnt) == DEC_UPDATE);
|
||||
printf("[OK] 小偏差(±%d内) → 正常基线更新\n", FREEZE_BAND);
|
||||
|
||||
/* 2. 正variation (CAPVD低于Origin, 疑似车) → 冻结 */
|
||||
freeze_cnt = 0;
|
||||
assert(baseline_decide(ORIGIN - 300, ORIGIN, &freeze_cnt) == DEC_FREEZE_POS);
|
||||
assert(freeze_cnt == 1);
|
||||
printf("[OK] 正variation(CAPVD<Origin, 疑似车) → 冻结计数\n");
|
||||
|
||||
/* 3. 负variation (CAPVD高于Origin, 铁块) → 冻结 */
|
||||
freeze_cnt = 0;
|
||||
assert(baseline_decide(ORIGIN + 500, ORIGIN, &freeze_cnt) == DEC_FREEZE_NEG);
|
||||
assert(freeze_cnt == 1);
|
||||
printf("[OK] 负variation(CAPVD>Origin, 铁块) → 冻结计数\n");
|
||||
|
||||
/* 4. 核心修复: 负variation超时(铁块停留>10s) → Origin 绝不更新 */
|
||||
freeze_cnt = 0;
|
||||
for (int i = 0; i < FREEZE_TIMEOUT + 100; i++) {
|
||||
assert(baseline_decide(ORIGIN + 500, ORIGIN, &freeze_cnt) == DEC_FREEZE_NEG);
|
||||
}
|
||||
assert(freeze_cnt == FREEZE_TIMEOUT); /* 饱和不更新 */
|
||||
printf("[OK] 负variation停留超时(%d tick) → Origin 保持 %lu (核心修复: 不污染)\n",
|
||||
FREEZE_TIMEOUT + 100, ORIGIN);
|
||||
|
||||
/* 5. 正variation超时 → 兜底更新仍保留 (模拟 FREEZE_TIMEOUT 后 Origin=CAPVD) */
|
||||
freeze_cnt = 0;
|
||||
int updated = 0;
|
||||
for (int i = 0; i < FREEZE_TIMEOUT + 50; i++) {
|
||||
if (baseline_decide(ORIGIN - 300, ORIGIN, &freeze_cnt) == DEC_FREEZE_POS) {
|
||||
if (freeze_cnt >= FREEZE_TIMEOUT) { updated = 1; break; }
|
||||
}
|
||||
}
|
||||
assert(updated == 1);
|
||||
printf("[OK] 正variation停留超时 → 兜底更新仍保留 (车辆长时间信号不丢)\n");
|
||||
|
||||
/* 6. 完整场景: 铁块靠近→停留→离开 (修复后: 不假进入, 无锁定) */
|
||||
{
|
||||
int32_t origin = (int32_t)ORIGIN;
|
||||
int32_t capvd = (int32_t)ORIGIN;
|
||||
int fake_entry = 0;
|
||||
uint32_t fcnt = 0;
|
||||
|
||||
/* 铁块靠近: CAPVD 升 +500 (负variation) */
|
||||
capvd = ORIGIN + 500;
|
||||
for (int i = 0; i < 50; i++) { /* 5s */
|
||||
dec_t d = baseline_decide(capvd, origin, &fcnt);
|
||||
assert(d == DEC_FREEZE_NEG);
|
||||
}
|
||||
assert(origin == (int32_t)ORIGIN); /* Origin 未被污染 */
|
||||
|
||||
/* 铁块离开: CAPVD 回落正常 */
|
||||
capvd = ORIGIN;
|
||||
for (int i = 0; i < 50; i++) {
|
||||
dec_t d = baseline_decide(capvd, origin, &fcnt);
|
||||
(void)d;
|
||||
}
|
||||
/* 修复后: Origin 正常 → 无假进入 (CAPVD 不低于进入线 Origin-38) */
|
||||
if (capvd < origin - DLT_ORG) fake_entry = 1;
|
||||
assert(fake_entry == 0);
|
||||
printf("[OK] 场景仿真: 铁块靠近→停留→离开, Origin 未污染, 无假进入, 无锁定\n");
|
||||
}
|
||||
|
||||
printf("\n全部通过 ✓\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -764,15 +764,23 @@ void vd1_task(void)
|
||||
*================================================================*/
|
||||
|
||||
/*--- 基线跟踪(仿 TLD-110:有车时冻结)
|
||||
* 额外保护: CAPVD 异常上升时暂停跟踪 → 冻结超时 + 稳定性检查 ---*/
|
||||
* V4.20 修复: 双向对称保护 — 负偏差(负variation)也冻结基线
|
||||
* 原单边保护只挡 dev>+4×dlt, 负variation铁块(磁导率主导, CAPVD>Origin)
|
||||
* 会把 Origin 污染抬高 → 铁块离开时假进入 → 释放线抬高 → 锁死不释放
|
||||
* 修复: ① 偏差窗口对称 [-4×dlt, +4×dlt]
|
||||
* ② 负偏差(dev>0, CAPVD高于Origin) 只冻结, 永不超时更新 Origin
|
||||
*---*/
|
||||
loop1_dlt_ORG = ((uint32_t)loop1_Origin * SensTable[loop1_SensLevel]) >> 16;
|
||||
{
|
||||
int32_t dev = (int32_t)loop1_CAPVD - (int32_t)loop1_Origin;
|
||||
if (dev < (int32_t)(loop1_dlt_ORG * 4)) {
|
||||
int32_t freeze_band = (int32_t)(loop1_dlt_ORG * 4);
|
||||
if (dev < freeze_band && dev > -freeze_band) {
|
||||
/* 对称窗口内 → 正常基线跟踪 */
|
||||
loop1_freeze_cnt = 0;
|
||||
update_moving_average(&loop1_ORG_SUM, &loop1_ORG_CNT,
|
||||
&loop1_Origin, loop1_CAPVD, WINDOW_ORIGIN);
|
||||
} else {
|
||||
} else if (dev < 0) {
|
||||
/* 正variation (CAPVD低于Origin, 疑似车辆) → 冻结, 保留原超时兜底更新 */
|
||||
if (loop1_freeze_cnt == 0) {
|
||||
loop1_freeze_ref = loop1_CAPVD;
|
||||
} else {
|
||||
@@ -795,6 +803,19 @@ void vd1_task(void)
|
||||
loop1_ORG_CNT = 0;
|
||||
loop1_ORG_SUM = 0;
|
||||
}
|
||||
} else {
|
||||
/* 负variation (CAPVD高于Origin, 特殊铁块磁导率主导/异常高) → 只冻结, 永不更新 Origin
|
||||
* 防 Origin 污染锁死; 权衡: 长时间负温漂基线不跟随(安全方向, 检测仍正常) */
|
||||
if (loop1_freeze_cnt == 0) {
|
||||
loop1_freeze_ref = loop1_CAPVD;
|
||||
PRINT("EVT|neg_var_freeze CAPVD:%d Origin:%d dev:%d dlt:%d f:%dkHz\n",
|
||||
loop1_CAPVD, loop1_Origin, (int)dev, loop1_dlt_ORG,
|
||||
calc_freq_khz(loop1_CAPVD, loop1_LPCNT));
|
||||
}
|
||||
loop1_freeze_cnt++;
|
||||
if (loop1_freeze_cnt > FREEZE_TIMEOUT) loop1_freeze_cnt = FREEZE_TIMEOUT; // 饱和, 不更新
|
||||
loop1_ORG_CNT = 0;
|
||||
loop1_ORG_SUM = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user