From 9391e1b8086b7e95b5520ada805462086d8af170 Mon Sep 17 00:00:00 2001 From: wangfq Date: Mon, 31 Aug 2026 16:52:45 +0800 Subject: [PATCH] =?UTF-8?q?feat(V4B):=20=E9=87=8A=E6=94=BE=E6=89=8B?= =?UTF-8?q?=E6=84=9F=E5=AF=B9=E9=BD=90=20PD132T=20=E2=80=94=20=E9=87=8A?= =?UTF-8?q?=E6=94=BE=E6=85=A2=E6=BB=A4=E6=B3=A2(=E6=96=B9=E6=A1=88A,=20?= =?UTF-8?q?=E5=9B=BA=E4=BB=B6=20V4.03)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 现场金属板测试: 灵敏度表已对齐, 但释放高度 V4B 比 PD132T 低 (刚离开触发位置即释放 vs 需离更远)。根因: IIR 滤波时间常数差异 (V4B 10ms+α79→τ32ms vs PD132T 43.7ms+α79/64→τ142~175ms), 释放阈值浅(进入20%)放大差异。 修复(USE_SLOW_RELEASE 开关, 默认 1): - 新增独立慢滤波 loop1_CAPVD_slow(α=18/256 @10ms→τ≈142ms), 吃原始 Value 不经斜率限幅, 复刻 PD132T 恢复速度 - 离开判定改用慢滤波值, 进入路径不变 - RELEASE_ALFA=18 可调(PD132T SET_FLT=ON 时用 15≈τ170ms) - 同步: INIT_VD/稳定期/线圈重连 - 版本 V4.03(顺带修正 cmcng.h 字符串"4.00"与数字 4/2 不一致) 验证: gcc 单测 tests/test_release_slow_filter.c τ fast=30ms/slow=140ms; 释放时机差 260ms ✓ 文档: technical-spec §4.3.3 + devlog V2.12 待板级: 真机金属板测试 + MRS 编译 --- docs/devlog.md | 42 ++++++++++ docs/technical-spec.md | 2 + tests/test_release_slow_filter.c | 84 +++++++++++++++++++ .../at32f421_freertos_demo/inc/TaskLoop.h | 3 + utilities/at32f421_freertos_demo/inc/cmcng.h | 4 +- .../at32f421_freertos_demo/src/TaskLoop.c | 40 ++++++++- 6 files changed, 172 insertions(+), 3 deletions(-) create mode 100644 tests/test_release_slow_filter.c diff --git a/docs/devlog.md b/docs/devlog.md index c554add..1085fbd 100644 --- a/docs/devlog.md +++ b/docs/devlog.md @@ -4,6 +4,48 @@ --- +## 2026-08-31 — 释放手感对齐 PD132T:释放慢滤波(方案 A,固件 V4.03) + +> **固件版本:V4.03**(cmcng.h:FIRMWARE_VER="4.03" + MAIN=4/SUB=3;⚠ 顺带修正字符串 "4.00" 与数字 4/2 不一致的历史问题)。devlog 修订 V2.12。 + +### 背景(现场测试) + +PD132T 替换交付后金属板测试(从上往下接近触发、从下往上离开释放):**触发高度两边一致,释放高度不一致**——V4B 刚离开触发位置一点点就释放,PD132T 要离得更远才释放。 + +### 根因分析 + +| 项 | V4B | PD132T | +|----|-----|--------| +| 检测节拍 | 10ms | 43.7ms | +| IIR 系数 | ALFA_CAP1=79 | 79(SET_FLT=ON)/64(OFF) | +| τ | ≈32ms | ≈142~175ms | +| 释放确认 | 3×10ms | 单次 | + +- 判定公式与阈值表**完全一致**(`(Origin-dlt_ORG) < CAPVD`,表 {60,32,21,14} 已对齐) +- 差异在 **CAPVD 恢复速度**:V4B 快 4.4~5.5× → 刚离开即恢复过浅阈值 → 早释放 +- 释放阈值浅(进入的 20%)放大差异;触发阈值深 + 接近过程信号斜率大 → 触发高度差不明显 + +### 方案 A 实现(USE_SLOW_RELEASE) + +- 新增独立慢滤波 `loop1_CAPVD_slow`(α=18/256 @10ms → τ≈142ms,复刻 PD132T 恢复速度),吃原始 Value(不经斜率限幅) +- 离开判定改用慢滤波值,进入路径不变 +- 开关 `USE_SLOW_RELEASE`(TaskLoop.h 默认 1,0=原快滤波释放,现场可对比回退) +- 同步:INIT_VD 复位、稳定期直锁、线圈重连强制收敛 +- ⚠ 若现场 PD132T SET_FLT=ON(α64 → τ≈175ms),调 `RELEASE_ALFA` 15(τ≈170ms)更贴近 + +### 验证 + +- `tests/test_release_slow_filter.c`(gcc 单测):τ fast=30ms/slow=140ms(理论 32/142ms);模拟金属板离开(信号每 10ms +80),释放时机 fast=150ms/slow=410ms,**差 260ms** ✓ +- 括号结构三版本(原始/改动/回退)计数一致,无结构影响 +- ⚠ 待板级:真机金属板测试确认释放高度对齐;MRS 编译 + +### 文档同步 + +- technical-spec §4.3.3 模式 1 补 V4.03 慢滤波说明 +- release-notes 待发布时更新 + +--- + ## 2026-08-28 — 灵敏度进入/离开对齐 PD132T(V2.11) ### 背景 diff --git a/docs/technical-spec.md b/docs/technical-spec.md index 9b65f33..3f1dd43 100644 --- a/docs/technical-spec.md +++ b/docs/technical-spec.md @@ -274,6 +274,8 @@ if ((Origin - dlt_ORG) < CAPVD) { // CAPVD 回升至距基线不 > **离开分档设计依据(V2.11)**:离开表 {60,32,21,14}(0.092%/0.049%/0.032%/0.021% Δf/f)对齐 PD132T **SENUP 标贴 OFF**(拨码**低有效**:标贴 OFF=电平 1=SET_ASB=1 → `if(SET_ASB)` 分档分支,**出厂默认态**)。物理意义:离开滞回比进入小一档(档0: 20%、档1: 53%、档2: 58%、档3: 70%),保证释放快于触发、车走即释放,同时防瞬态误落杆。若现场 PD132T 的 SENUP 标贴为 **ON**(电平0=SET_ASB=0 → 恒 14),需将 `SensTable_1` 改回恒 14 表对齐。 +> **⚠ V4.03(2026-08-31)释放慢滤波(方案 A,USE_SLOW_RELEASE)**:现场金属板测试——灵敏度表已对齐,但**释放高度 V4B 比 PD132T 低**(V4B 刚离开触发位置即释放,PD132T 需离得更远)。根因:**IIR 滤波时间常数差异**(V4B 10ms tick + α79 → τ≈32ms;PD132T 43.7ms tick + α79/64 → τ≈142~175ms,慢 4.4~5.5×),释放阈值浅(进入的 20%)放大差异。修复:离开判定改用**独立慢滤波值** `loop1_CAPVD_slow`(α=18/256 @10ms → τ≈142ms,复刻 PD132T 恢复速度,吃原始 Value 不经斜率限幅),进入路径不变。开关 `USE_SLOW_RELEASE`(TaskLoop.h,默认 1;0=原快滤波释放)。单测:τ fast=30ms/slow=140ms,释放时机差 260ms(`tests/test_release_slow_filter.c`)。⚠ 若现场 PD132T 的 SET_FLT=ON(α64 → τ≈175ms),可调 `RELEASE_ALFA` 15(τ≈170ms)更贴近。 + ##### 模式 2:平坦性判定 (USE_FLATNESS_EXIT=1) — ⚠ 默认禁用(#if 0,代码保留) 基于专利 **CN200910309382**(中山大学),解决大车通行时频率多峰导致的多次误触发。 diff --git a/tests/test_release_slow_filter.c b/tests/test_release_slow_filter.c new file mode 100644 index 0000000..64451ea --- /dev/null +++ b/tests/test_release_slow_filter.c @@ -0,0 +1,84 @@ +/* + * test_release_slow_filter.c — 方案A 释放慢滤波单测 + * + * 验证: V4B 主滤波(ALFA=79 @10ms, τ≈32ms) vs 释放慢滤波(ALFA=18 @10ms, τ≈142ms) + * 在"金属板离开"(信号缓慢恢复)场景下,慢滤波释放时机应显著晚于快滤波, + * 从而对齐 PD132T 释放手感。 + * + * 编译: gcc -o /tmp/test_release_slow test_release_slow_filter.c && /tmp/test_release_slow + */ +#include +#include +#include +#include + +#define ALFA_FAST 79 +#define ALFA_SLOW 18 +#define TICK_MS 10 + +/* 与 TaskLoop.c get_flt_value 同构 */ +static uint32_t flt(uint32_t new_v, uint32_t last_v, uint32_t alfa) +{ + uint32_t delta = (new_v > last_v) ? (new_v - last_v) : (last_v - new_v); + uint32_t scaled = (delta * alfa) >> 8; + return (new_v > last_v) ? last_v + scaled : last_v - scaled; +} + +static void test_tau(void) +{ + /* 阶跃响应: 从 100000 跳到 99000, 算 63.2% 收敛时间 */ + uint32_t v_fast = 100000, v_slow = 100000; + int t_fast = -1, t_slow = -1; + double target_fast = 100000 - 0.632 * 1000; /* 63.2% of 1000 */ + double target_slow = 100000 - 0.632 * 1000; + for (int t = 1; t <= 100; t++) { + v_fast = flt(99000, v_fast, ALFA_FAST); + v_slow = flt(99000, v_slow, ALFA_SLOW); + if (t_fast < 0 && v_fast <= target_fast) t_fast = t; + if (t_slow < 0 && v_slow <= target_slow) t_slow = t; + } + printf("[tau] fast=%dms slow=%dms (理论 32ms/142ms)\n", t_fast * TICK_MS, t_slow * TICK_MS); + assert(t_fast * TICK_MS <= 60); /* 快滤波 τ < 60ms */ + assert(t_slow * TICK_MS >= 100); /* 慢滤波 τ > 100ms */ + assert(t_slow > t_fast * 3); /* 慢/快 > 3× */ + printf("[OK] tau 差异符合预期\n"); +} + +static void test_release_timing(void) +{ + /* 模拟: Origin=100000, 档0: 进入阈值300→dlt_enter=457, 离开阈值60→dlt_leave=91 + * 有车: Value=99000(CAPVD 收敛) + * 离开: 金属板缓慢上移, 信号每 10ms 恢复 +80(模拟手速) + * 释放条件: CAPVD 恢复到 Origin-dlt_leave = 99909 */ + const uint32_t Origin = 100000; + const uint32_t dlt_leave = (Origin * 60) >> 16; /* = 91 */ + const uint32_t release_line = Origin - dlt_leave; /* = 99909 */ + + uint32_t v_fast = 99000, v_slow = 99000; + int rel_fast = -1, rel_slow = -1; + uint32_t value = 99000; + for (int t = 1; t <= 200; t++) { + /* 金属板离开: 信号恢复(每 10ms +80) */ + value += 80; + if (value > Origin) value = Origin; + v_fast = flt(value, v_fast, ALFA_FAST); + v_slow = flt(value, v_slow, ALFA_SLOW); + if (rel_fast < 0 && v_fast >= release_line) rel_fast = t; + if (rel_slow < 0 && v_slow >= release_line) rel_slow = t; + } + printf("[release] fast=%dms slow=%dms 差=%dms\n", + rel_fast * TICK_MS, rel_slow * TICK_MS, (rel_slow - rel_fast) * TICK_MS); + assert(rel_fast > 0 && rel_slow > 0); + assert(rel_slow > rel_fast); /* 慢滤波释放必晚于快滤波 */ + assert((rel_slow - rel_fast) * TICK_MS >= 100); /* 释放延迟差 ≥100ms(等效高度差明显) */ + printf("[OK] 慢滤波释放显著晚于快滤波, 对齐 PD132T 手感\n"); +} + +int main(void) +{ + printf("=== 方案A 释放慢滤波单测 ===\n"); + test_tau(); + test_release_timing(); + printf("\n全部通过 ✓\n"); + return 0; +} diff --git a/utilities/at32f421_freertos_demo/inc/TaskLoop.h b/utilities/at32f421_freertos_demo/inc/TaskLoop.h index 94046cd..fc4abd6 100644 --- a/utilities/at32f421_freertos_demo/inc/TaskLoop.h +++ b/utilities/at32f421_freertos_demo/inc/TaskLoop.h @@ -35,6 +35,8 @@ * - 进入确认 (ENTRY_CONFIRM) 替代单次阈值判定 *===========================================================================*/ #define ALFA_CAP1 79 // IIR α = 79/256 ≈ 0.31 (@10ms → τ≈32ms) +#define RELEASE_ALFA 18 // 释放慢滤波 α = 18/256 ≈ 0.07 (@10ms → τ≈142ms, 方案A: 对齐 PD132T Flt_Reg=79 @43.7ms 释放手感) +#define USE_SLOW_RELEASE 1 // 方案A总开关: 1=释放走慢滤波(对齐PD132T) 0=原快滤波释放 #define MAX_SLOPE_RATE 5 // 斜率限幅: 单次最大变化 5% (物理限制, 拒绝 EMI 尖峰) #define ENTRY_CONFIRM 3 // 进入确认: 连续 N 次低于阈值才判定有车 #define FREEZE_TIMEOUT 1000 // 基线冻结超时: ~10s @ 10ms/tick, 持续偏高且稳定后强制更新基线 @@ -87,6 +89,7 @@ extern uint16_t loop1_CapCnt; // 当前窗口已累加次数 extern uint32_t loop1_CapSum; // 当前窗口累加和 extern uint32_t loop1_Value; // 一个测量窗口的原始 ΣXn extern uint32_t loop1_CAPVD; // IIR 滤波后的频率值 +extern uint32_t loop1_CAPVD_slow; // 释放判定慢滤波值 (方案A: 独立 IIR, τ≈142ms, 对齐 PD132T 释放手感) extern uint32_t loop1_Origin; // 基线(无车时的参考频率) extern uint32_t loop1_ORG_SUM; // 基线跟踪累加和 extern uint16_t loop1_ORG_CNT; // 基线跟踪计数(窗口=100) diff --git a/utilities/at32f421_freertos_demo/inc/cmcng.h b/utilities/at32f421_freertos_demo/inc/cmcng.h index 2a051ef..25e6c24 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.00" +#define FIRMWARE_VER "4.03" #define HARDWARE_VER "3.00" #define FIRMWARE_VER_MAIN 4 -#define FIRMWARE_VER_SUB 2 +#define FIRMWARE_VER_SUB 3 #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 ae3c5a9..c0546d2 100644 --- a/utilities/at32f421_freertos_demo/src/TaskLoop.c +++ b/utilities/at32f421_freertos_demo/src/TaskLoop.c @@ -30,6 +30,7 @@ uint16_t loop1_CapCnt; uint32_t loop1_CapSum; uint32_t loop1_Value; uint32_t loop1_CAPVD; +uint32_t loop1_CAPVD_slow = 0; // 释放判定慢滤波值 (方案A) uint32_t loop1_Origin; uint32_t loop1_ORG_SUM; uint16_t loop1_ORG_CNT; @@ -168,6 +169,25 @@ uint32_t get_flt_value(uint32_t new_value, uint32_t last_Value) return value_Flt; } +/* 释放慢滤波(方案 A): 独立系数 RELEASE_ALFA, 不复用 Flt_Reg 全局 + * 复刻 PD132T 滤波路径(Value -> IIR), tau~142ms @10ms */ +uint32_t get_flt_value_slow(uint32_t new_value, uint32_t last_value) +{ + uint32_t value_Flt; + uint32_t delta = (new_value > last_value) + ? (new_value - last_value) + : (last_value - new_value); + uint32_t scaled_delta = (delta * (uint32_t)RELEASE_ALFA) >> 8; + + if (new_value > last_value) { + value_Flt = last_value + scaled_delta; + } else { + value_Flt = last_value - scaled_delta; + } + + return value_Flt; +} + /*=========================================================================== * 滑动平均基线更新 * 累加 new_value,当计数达到 window 时计算平均值并重置 @@ -351,6 +371,7 @@ void INIT_VD(void) loop1_LOOP_OK0 = 0; loop1_CAP_OK = 0; loop1_CAPVD = 0; + loop1_CAPVD_slow = 0; // 方案A: 释放慢滤波复位 loop1_SensLevel = 2; // 默认中灵敏度 Flt_Reg = ALFA_CAP1; // 79 @@ -684,9 +705,21 @@ void vd1_task(void) loop1_CAPVD = get_flt_value(clamped_value, loop1_CAPVD); } +#if USE_SLOW_RELEASE + /* 方案A: 释放慢滤波独立跟踪(吃原始 Value, 复刻 PD132T 滤波路径) */ + if (loop1_CAPVD_slow == 0) { + loop1_CAPVD_slow = loop1_Value; + } else { + loop1_CAPVD_slow = get_flt_value_slow(loop1_Value, loop1_CAPVD_slow); + } +#endif + /*--- 2. 稳定期:绕过 IIR 和斜率限幅,小窗口快速收敛 ---*/ if (!g_loop_stable) { loop1_CAPVD = loop1_Value; +#if USE_SLOW_RELEASE + loop1_CAPVD_slow = loop1_Value; // 方案A: 稳定期同步直锁 +#endif update_moving_average(&loop1_ORG_SUM, &loop1_ORG_CNT, &loop1_Origin, loop1_CAPVD, 100); @@ -844,7 +877,11 @@ void vd1_task(void) *================================================================*/ loop1_dlt_ORG = ((uint32_t)loop1_Origin * SensTable_1[loop1_SensLevel]) >> 16; - if ((loop1_Origin - loop1_dlt_ORG) < loop1_CAPVD) { +#if USE_SLOW_RELEASE + if ((loop1_Origin - loop1_dlt_ORG) < loop1_CAPVD_slow) { // 方案A: 用慢滤波值对齐 PD132T 释放手感 +#else + if ((loop1_Origin - loop1_dlt_ORG) < loop1_CAPVD) { // 原逻辑: 快滤波直接判释放 +#endif loop1_cnt_release++; if (loop1_cnt_release >= 3) { PRINT("Car_OFF, Value:%d CAPVD:%d Origin:%d dlt:%d\n", @@ -881,6 +918,7 @@ void loop_task_function(void *pvParameters) loop1_LOOP_OK0 = 1; g_disconnect_active = 0; // 重连,清除断开标记 loop1_CAPVD = 0; // 强制 IIR 从首个有效值重新收敛 + loop1_CAPVD_slow = 0; // 方案A: 释放慢滤波同步收敛 } if (loop1_CAP_OK) {