Files
DLD154V4B/docs/devlog.md
wangfq 935e11e006 docs: 四文档同步更新至 V2.5
- devlog: 修订记录修正 30s→10s, 新增 V2.5
- release-notes: V1.6→V2.5, 新增 M4 优化特性 + 完整版本历程
- product-manual: V1.5→V2.5, 补充 V1.6~V2.5 版本历史
- technical-spec: V1.5→V2.5, 重写 §§4.2-4.5/5.2/12.1/13:
  - §4.2: 双路 IIR 架构(慢速基线 τ=135ms + 快速检测 τ=28ms)
  - §4.3.2: 进入确认机制(CAPVD_fast + ENTRY_CONFIRM=3)
  - §4.4: 斜率限幅 5% + 基线更新速率 1s (10ms tick)
  - §4.5: 冻结超时恢复演进史 V1.5→V2.5,完整逻辑 + 常量表
  - §5.2: Tick 改为 10ms,新增 FREEZE_TIMEOUT 参数
  - §12.1: 进入延迟 ~530ms,瞬态抑制,温漂 1s 补偿
  - §13: 新增 M4 优化编译选项
2026-06-29 10:57:24 +08:00

495 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# DLD154V4B 开发日志
> MCU: AT32F421F8P7 (Cortex-M4, 120MHz) | 线圈通道: 1路 | 调试口: TTL Tx
---
## 2026-06-23 — 跳出框框:去掉 >>6 精度浪费
### 背景
DLD154V4B 的检测算法继承自 M1HSTC12C5202, 2008和 TLD-110P87LPC762, 2003但有几个关键硬件差异
| 特性 | M1H / TLD-110 | DLD154V4B |
|------|--------------|-----------|
| 主频 | ~12MHz (8051) | 120MHz (Cortex-M4) |
| 分频芯片 | CD4060 (Pin5, ÷32) | **无**,直连 PA7 |
| MCU 内分频 | 无 | TIM3 DIV_4 → **DIV_2** |
| 有效分频比 | ÷32 | ÷2 |
| 捕获方式 | PCA 周期捕获 / 门控计数 | TIM3_CH2 硬件输入捕获 |
### 问题:<<6 和 >>6 的精度浪费
原始代码的数据流存在一个"膨胀再压缩"的精度陷阱:
```
TMR3 ISR:
Xn = 周期差值 (4800 ticks @ DIV_4)
LPCNT = (32768 << 6) / Xn = 437 ← 窗口开大 64×降噪
Value = Σ 437次 Xn ≈ 2,097,600 ← 437 样本精密累加
vd1_task:
tmp = Value >> 6 = 32,775 ← 6bit 精度全部丢弃!
CAPVD = IIR(tmp...) ← IIR 只看到 ~6 样本有效信息
```
`<<6``>>6` 在数学上互相抵消——437 个样本累加,然后右移 6 位,等效仅用 ~7 个样本437/64 ≈ 6.8)。窗口开大降噪了,但精度被自己扔掉了。
### 方案:去掉 <<6/>>6用 MEASUREMENT_BASE
```
TMR3 ISR:
LPCNT = 131072 / Xn = 27~54 ← 自适应窗口,不丢精度
Value = Σ Xn ≈ 131072 ← 直接作为检测输入
vd1_task:
CAPVD = IIR(Value) ← 全精度参与滤波
```
| 参数 | 旧方案 | 新方案 |
|------|--------|--------|
| LPCNT (100kHz, DIV_2) | — | **54** |
| 测量窗口 | 17.5ms (DIV_4) | **~1ms** |
| 有效精度/窗口 | ~6 样本 (>>6 后) | **54 样本 (全保留)** |
| Origin 范围 | ~32K | **~131K** |
| 灵敏度阈值 | 百分比公式,自动按比例缩放 ✓ | 同 ✓ |
| 进入响应 | ~50ms | **~3ms** |
### 同步改进
| # | 改动 | 原因 |
|---|------|------|
| 1 | TIM3 分频 DIV_4 → DIV_2 | 提高采样率,中断率 2× 仍安全 (CPU<3%) |
| 2 | MEASUREMENT_BASE = 131072 (2^17) | 平衡精度和 Origin 范围 |
| 3 | 去除所有 >>6 | 保留全采样精度 |
| 4 | 离开增加 `cnt_release >= 3` 防抖 | 防瞬间噪声导致误落杆 |
| 5 | 基线冻结 + 100 窗口慢跟踪 | 仿 TLD-110有车不更新基线 |
### 为什么不需要 CD4060 外部分频
AT32F421 的 TIM3 有内置输入分频器,配合 120MHz 主频:
| 线圈频率 | DIV_2 中断率 | CPU 占用 (50周期/ISR) |
|---------|------------|----------------------|
| 100 kHz | 50k/s | 2.1% |
| 150 kHz | 75k/s | 3.1% |
| 200 kHz | 100k/s | 4.2% |
均在安全范围内,无需外部分频芯片。
### 对比 M1H/TLD-110
| 指标 | M1H (CD4060÷32) | DLD154V4B (新) |
|------|----------------|---------------|
| 测量间隔 | ~50ms (固定) | **~1ms (自适应)** |
| 进入防抖 | 500ms IN_DELAY | 500ms IN_DELAY |
| 离开防抖 | 1.9s OUT_DELAY | **3次连续确认** + 1.9s OUT_DELAY |
| 基线跟踪 | 100次 (有车也跟) | 100次 (有车冻结) |
---
## 2026-06-23 — 精简重构,对齐参考实现
- 删除二阶差分滤波(计算但从未参与判决)
- 删除 FltHistoryManager 死代码20+ 未用字段)
- 删除 StageRangeConfig区间约束未引用
- 删除动态窗口切换LOOP_WINDOW_SIZE_LOW→FAST
- 时序状态机简化:去掉 PLUSE_IN_F/PLUSE_IN 中间层
- 拨码去抖 10→5对齐 M1H
- 代码量1177→706 行(-40%
---
## 2026-06-22 — 参考分析
见 [reference_analysis.md](reference_analysis.md)M1H + TLD-110 完整算法分析。
---
## 2026-06-23 — 指示灯行为规范化
### LED 硬件对应
| 硬件 | 引脚 | 宏 | 行为 |
|------|------|-----|------|
| 红灯 | PB1 | *(无宏, TMR14 PWM)* | 始终呼吸,不干预 |
| 绿灯 (LEDA) | PA9 | `LEDA_ON`/`LEDA_OFF` | 自检慢闪 / 有车亮 / 无车灭 |
| 黄灯 (LEDC) | PA10 | `LED_YELLOW_ON`/`OFF` | 故障快闪 / 断开次数编码 |
> 注: `LEDA` 宏在 BSP 遗留代码中指向 PB1红灯已修正为 PA9绿灯。`LEDB` 宏无实际 IO已删除。
### 绿灯行为
| 状态 | 绿灯 |
|------|------|
| 上电自检 (Origin 未确立) | 慢闪 200ms |
| 数值稳定期 (128 样本 ≈ 128ms) | 慢闪 200ms |
| 正常工作,无车 | 灭 |
| 正常工作,有车 | 亮 |
| 线圈断开中 | **灭**(黄灯快闪负责故障指示) |
### 黄灯行为
| 条件 | 黄灯 |
|------|------|
| 上电后从未接线圈 | 快闪 200ms |
| 线圈当前断开中 | 快闪 200ms |
| 重连后,断开过 1 次 | 1 短闪 (80ms 亮) + 1.2s 间隔 |
| 重连后,断开过 2 次 | 2 短闪 + 1.2s 间隔 |
| 重连后,断开过 3+ 次 | 3 短闪 + 1.2s 间隔 |
| 正常,无断开记录 | 灭 |
> "不接线圈上电,上电后再接线圈"不计入断开次数。
---
## 2026-06-23 — 上电稳定期 & 线圈重连
### 上电稳定期
Origin 首次确立后,线圈振荡需要时间稳定。新增 `g_loop_stable` 标志:
- `INIT_VD()``g_loop_stable = 0`
- `vd1_task()`: 稳定期内只做 IIR + 基线跟踪,**跳过进入检测**
- 128 样本 (~128ms) 后 `g_loop_stable = 1`,正式启用检测
- 安全复位时重置 `g_loop_stable = 0`
### 线圈重连状态保持
断开时**不丢 VD_FLAG**,重连后**快速收敛 IIR**
```
断开: 保留 loop1_VD_FLAG仅关断继电器
重连: loop1_CAPVD = 0 → 首个样本直锁 Value → IIR 后续正常跟踪
```
| 断开前 | 断开期间 | 重连后 | 检测结果 |
|--------|---------|--------|---------|
| 有车 | 车还在,绿灯灭 | 车还在 | CAPVD < Origin-dlt → VD_FLAG=1 → 绿灯亮 |
| 有车 | 车离开 | 车离开 | CAPVD ≈ Origin → cnt_release→3 → VD_FLAG=0 |
| 无车 | 车进入 | 车进入 | CAPVD < Origin-dlt → VD_FLAG=1 → 绿灯亮 |
---
## 2026-06-23 — 移植平坦性离开判定CN200910309382
### 来源
专利 CN200910309382中山大学张辉/黄永强/陈古典)提出**平坦性三条件判定法**
解决大车通行时频率曲线的负波峰和近零波谷导致单一阈值法多次误触发的问题。
### 算法
```
车辆到达 → 单一阈值法 f(i) - f_b > Δ1 (沿用 SensTable)
车辆离开 → 平坦性三条件 同时满足:
① |f - f_b| < Δ1 频率回归基频 (SensTable_1)
② |f'| < Δ2 一阶导数近零
③ |f''| < Δ3 二阶导数近零
```
**Phase 1** (g_exit_state=0): 车辆进入线圈后,追踪第一上升坡面,
记录最大 |f'| 和 |f''|。当斜率连续 3 次降到 `SLOPE_FLAT_THRESH` 以下,
计算动态阈值:
```
Δ2 = max_slope / K1 (K1=8)
Δ3 = max_slope_rate / K2 (K2=8)
```
**Phase 2** (g_exit_state=1): 每帧检查三条件。连续 3 次全部满足 → 车辆离开。
### 与传统方法对比
| 方法 | 离开判据 | 大车防误检 |
|------|---------|-----------|
| M1H/TLD-110 | 单一滞回阈值 | ❌ 多峰可能多次触发 |
| 原 cnt_release | 连续 3 次超阈值 | ⚠️ 固定阈值,大车波谷可能误判 |
| **平坦性判定** | |f-f_b|+一阶+二阶 三条件 | ✅ 动态阈值,跟随坡面特征 |
### 参数说明
| 常量 | 值 | 含义 |
|------|-----|------|
| K1, K2 | 8 | 动态阈值除数(专利推荐值) |
| SLOPE_FLAT_THRESH | 100 | 斜率趋零判断阈值 |
| MIN_DELTA2 | 5 | Δ2 下限(防除数过小) |
| MIN_DELTA3 | 2 | Δ3 下限 |
| FLAT_CONFIRM_CNT | 3 | 平坦连续确认次数 |
### 整数化适配
专利原实现使用 floatHz 频率值DLD154V4B 用 uint32 定点Origin≈131K
导数计算同样用 int32 整数差分,阈值 /K1、/K2 做整数除法,精度足够。
---
## 2026-06-23 — Origin 污染保护 & 宏开关
### USE_FLATNESS_EXIT 宏
新增编译开关,一行切换离开判据:
```c
#define USE_FLATNESS_EXIT 1 // 1=平坦性, 0=cnt_release防抖
```
所有平坦性状态变量和逻辑用 `#if USE_FLATNESS_EXIT` 包裹。
### Origin 基线污染保护
**问题**:实测发现车辆驶入时 Xn 偶尔先增大再减小。无车状态下 Origin 跟踪上升,
被污染到虚高值。进入检测虽触发,但离开时 Origin 冻在虚高处,
`|f-f_b|` 远超 SensTable_1 阈值 → 绿灯常亮、永远不释放。
**根因**CAPVD 异常上升期间moving average 持续将虚高值写入 Origin。
**修复**:在基线跟踪前增加保护条件:
```c
dev = CAPVD - Origin;
if (dev < dlt_ORG × 4)
update_moving_average(...); // 安全跟踪
else
freeze; // 冻结 + 重置累计
```
冻结阈值 = 进入阈值的 4 倍,随灵敏度自动缩放。
| 场景 | CAPVD | Origin | dev | 阈值(×4) | 结果 |
|------|-------|--------|-----|----------|------|
| 正常无车 | 127081 | 127080 | +1 | 276 | 跟踪 ✓ |
| 异常上升 | 127884 | 127085 | +799 | 276 | 冻结 ✓ |
---
## 2026-06-24 — 时序参数修正 & 接口更正
### 时序参数修正
OUT_DELAY 和 PULSE_DELAY 之前沿用 M1H 的值1.9s / 950ms现修正为 500ms
| 参数 | M1H 旧值 | DLD154V4B 新值 |
|------|---------|---------------|
| OUT_DELAY | 1.9s (38 tick) | **500ms (10 tick)**SW_4 控制开关 |
| PULSE_DELAY | 950ms (19 tick) | **500ms (10 tick)**,固定 |
SW_4 语义0 = 无离开延时立即脉冲1 = 离开延时 500ms。删除 OUT_DELAY_FAST / PULSE_DELAY_FAST 宏,因只有一组值。
```c
// TaskLoop.h
#define OUT_DELAY 10 // 离开防抖 500ms仅 SW_4=ON 时生效)
#define PULSE_DELAY 10 // 脉冲宽度 500ms
// TaskLoop.c
if (SET_DLY) {
loop1_OUTCNT++;
if (loop1_OUTCNT > OUT_DELAY) { ... } // 500ms 延时
} else {
// 无离开延时:立即 FLAG_PLUSE
}
```
### 接口修正
- 去除所有 RS485 相关描述:本产品只有 TTL 电平 UART Tx 调试口
- 产品手册端子定义RS485-A/B → Tx TTL 调试输出
- 技术规格书 §9整节从 RS485 协议改为 TTL Tx 调试接口说明
## 2026-06-26 — M4 核心优化:双路 IIR + 进入确认 + 斜率限幅
### 背景
DLD154V4B 的 8051 时代设计在 M4 上可以做得更好。8051 的 50ms tick 是 CPU 限制,不是最优选择。
### 三项改进
#### 1. 10ms tick + 双路 IIR
| 滤波器 | α | τ | 用途 |
|--------|---|-----|------|
| CAPVD (慢速) | 18/256 ≈ 0.07 | 135ms | 基线跟踪,等效原 50ms 的 79/256 |
| CAPVD_fast (快速) | 128/256 = 0.5 | 28ms | 进入/离开检测判定 |
tick 提升到 10ms但通过调整 α 保持与 50ms 设计相同的滤波强度。快速 IIR 用于检测,慢速 IIR 用于基线,两路各司其职。
#### 2. 斜率限幅
EMI/闪电等瞬态干扰会造成 CAPVD 瞬间跳变。物理车辆不可能让线圈频率瞬间改变 >5%
```c
max_step = CAPVD × 5 / 100; // 5% 限幅
if (|delta| > max_step) delta = clamp(delta, -max_step, max_step);
```
尖峰被截断,真实车辆信号(缓慢的频率漂移)不受影响。
#### 3. 进入确认
原设计:单次 CAPVD < Origin-dlt → 立即 VD_FLAG=1同 8051
新设计CAPVD_fast 连续 3 次低于阈值 → 才判定有车
```c
if (CAPVD_fast < Origin - dlt)
entry_cnt++;
if (entry_cnt >= 3) { VD_FLAG = 1; ... }
else
entry_cnt = 0; // 一旦恢复就重置
```
对真实车辆CAPVD_fast τ=28ms3 次确认 = 30ms加上 IN_DELAY 500ms = 总响应 ~530ms比原来的 550ms 还快。
对瞬态干扰:单个尖峰无法连续 3 次 → 被过滤。
### 对比
| 指标 | 8051 原设计 | M4 优化 |
|------|-----------|---------|
| tick 周期 | 50ms | **10ms** |
| IIR τ (检测) | 135ms | **28ms** (5× 快) |
| IIR τ (基线) | 135ms | 135ms (相同) |
| 进入判定 | 单次阈值 | **3 次连续确认** |
| 瞬态抑制 | 无 | **斜率限幅 + 确认** |
| 进入响应 | ~550ms | **~530ms** |
| 误触发风险 | 中 | **低** |
### 兼容性
- 离开检测路径不变cnt_release 平坦性均已带确认)
- 灵敏度表、IN_DELAY、OUT_DELAY 等参数不变
- 可通过 `ENTRY_CONFIRM` 宏调整确认次数,`MAX_SLOPE_RATE` 调整限幅强度
---
## 2026-06-26 — CAPVD_fast 初始化修复
### 问题
M4 V2.0 引入双路 IIR 后,进入检测使用 `CAPVD_fast`(快速 IIRα=0.5)。但 `CAPVD_fast` 始终为 0导致首次进入判定就触发有车。
### 根因
```c
// 原代码(错误):
if (loop1_CAPVD_fast != 0) {
loop1_CAPVD_fast = (loop1_CAPVD_fast + loop1_CAPVD) / 2;
}
```
TMR3 ISR 首次捕获时直接设置 `loop1_CAPVD`(不为 0`INIT_VD``CAPVD_fast` 初始化为 0。`vd1_task` 进入时 `CAPVD != 0``if (CAPVD_fast != 0)` 永远为 false → `CAPVD_fast` 保持 0 不更新。
### 修复
条件反转:`== 0` 时首次锁定当前 CAPVD 值,之后正常执行快速 IIR。
```c
// 修复后:
if (loop1_CAPVD_fast == 0) {
loop1_CAPVD_fast = loop1_CAPVD; // 首次直接锁定
} else {
loop1_CAPVD_fast = (loop1_CAPVD_fast + loop1_CAPVD) / 2;
}
```
---
## 2026-06-26 — 稳定期绕过 IIR 和斜率限幅
### 问题
上电后很快输出有车状态。日志显示:
```
First_capSum:177406, Origin:177406 ← 首测为瞬态高值
Loop stable, Origin:149755 ← 稳定期后 Origin 仍偏高
Car_In, Value:128654, Origin:149755 ← Origin - Value = 21067 >> dlt=82
```
### 根因
V2.0 引入的 **5% 斜率限幅** 在稳定期内仍然生效。首测 CAPVD=177406 是瞬态高值(~38% 偏高),斜率限幅让 CAPVD 在 128 个采样周期内只能缓慢下降到 149755 — 无法在稳定期内充分收敛。100 窗口滑动平均被前半段的高值污染,导致 Origin 停在 149755远高于真实值 ~128688。
### 修复
稳定期内不走 IIR 和斜率限幅——**直接用 raw Value** 建立基线:
```c
if (!g_loop_stable) {
/* 稳定期内不做斜率限幅和 IIR — 直接用 Value 快速收敛到真实基线 */
loop1_CAPVD = loop1_Value;
loop1_CAPVD_fast = loop1_Value;
// ... 滑动平均跟踪 ...
return;
}
```
稳定期结束后恢复正常 IIR + 斜率限幅用于检测。
| 场景 | 修复前 | 修复后 |
|------|--------|--------|
| 首测瞬态 | 177406 被限幅缓慢下降 | 直接被 128688 覆盖 |
| Origin 收敛 | 149755 (偏离 +16%) | ~128688 (正确) |
| 稳定期结束 | Origin 偏离 → 误判有车 | Origin 准确 → 正常检测 |
---
## 2026-06-29 — 基线冻结超时自动恢复
### 问题
V1.5 引入的 Origin 污染保护机制在 CAPVD 异常偏高时**永久冻结**基线。如果线圈因环境变化温度漂移、器件老化、更换线圈稳定在新的频率值Origin 永远不会更新 → 绿灯常亮、永久误判有车。
### 方案
在冻结路径上增加**超时自动恢复 + 稳定性验证**
```c
#define FREEZE_TIMEOUT 3000 // ~30s @ 10ms/tick
#define FREEZE_STABILITY_RATE 2 // 稳定性窗口: 参考值的 ±2%
if (dev >= dlt_ORG × 4) {
if (freeze_cnt == 0)
freeze_ref = CAPVD; // 记录冻结起始值
else if (|CAPVD - freeze_ref| > freeze_ref × 2%)
reset(freeze_cnt, freeze_ref); // 波动过大 → 重新计时
freeze_cnt++;
if (freeze_cnt >= 3000) {
Origin = CAPVD; // 连续30s稳定在±2%窗口内 → 接受
}
}
```
### 行为表格
| 场景 | CAPVD 行为 | 稳定性检查 | 结果 |
|------|-----------|-----------|------|
| 车辆驶入Xn 先增) | 偏高 2~3s 后下降 | 中途恢复 → `freeze_cnt` 清零 | 正常冻结保护 ✓ |
| 温度漂移 | 缓慢爬升到新值并稳定 | 爬升阶段频繁重置;稳定后连续 10s | Origin 更新 ✓ |
| 更换线圈 | 瞬间跳到新频率 | 稳定后连续 10s | Origin 更新 ✓ |
| 车辆缓慢驶入CAPVD 持续爬升) | 持续偏高且不断上升 | 每超出 ±2% 就重置 → 永远到不了 10s | **不触发更新** ✓ |
最后一行是关键:如果没有稳定性检查,车辆缓慢驶入时 CAPVD 持续偏高 10s 也会触发 Origin 更新——这是错误的。
---
## 修订记录
| 版本 | 时间 | 说明 |
|------|------|------|
| V2.5 | 2026-06-29 | 冻结超时 30s→10s加快环境变化响应 |
| V2.4 | 2026-06-29 | 冻结超时增加稳定性检查: CAPVD 需连续稳定在 ±2% 窗口内 |
| V2.3 | 2026-06-29 | 基线冻结超时: 持续偏高后强制更新 Origin防止环境变化死锁 |
|------|------|------|
| V2.2 | 2026-06-26 | 稳定期绕过 IIR/斜率限幅,用 Value 直接收敛基线 |
| V2.1 | 2026-06-26 | CAPVD_fast 初始化修复(条件反转为 ==0 首次锁定) |
| V2.0 | 2026-06-26 | M4 优化: 双路 IIR + 斜率限幅 + 进入确认tick 10ms |
| V1.7 | 2026-06-26 | vTaskDelay 10→50ms 修正(对齐原始 TMR15 5ms×10 设计);基线更新 1s→5s |
| V1.6 | 2026-06-24 | 时序修正OUT_DELAY/PULSE_DELAY→500msRS485→TTL Tx |
| V1.5 | 2026-06-23 | Origin 污染保护、USE_FLATNESS_EXIT 开关 |
| V1.4 | 2026-06-23 | 移植 CN200910309382 平坦性离开判定 |
| V1.3 | 2026-06-23 | 指示灯行为、稳定期、重连状态保持 |
| V1.2 | 2026-06-23 | <<6/>>6 精度浪费分析与改进 |
| V1.1 | 2026-06-23 | 精简重构,对齐 M1H/TLD-110 |
| V1.0 | 2026-06-22 | 参考分析文档 |