Files
vd_960/vd960Loop/docs/devlog.md
T
wangfq af95660c68 fix(vd960Loop): init_vd_single 灵敏度从配置读, 不再硬编码 2
现场日志: 配置 SENS=3/2/1/0, 检测全用 sens_in:36 (SENS=2 表值)

根因: main→para_store_init() 同步 loop_SensLevel=配置, 但任务启动后
INIT_VDs()→init_vd_single() 硬编码 loop_SensLevel=2 覆盖配置 → 全通道 SENS=2

修复: init_vd_single 改为 sens_level_from_config(
  g_loop_cng_info.loop_cng[unit->loop_num].sensitvity)

另: flash 灵敏度表 sens[0]={512,400} 为坏数据(出厂应为{216,108}),
修复后 loop_3(SENS=0) 会撞上异常值, 需恢复出厂清配置区
2026-08-25 14:23:33 +08:00

385 lines
18 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.
# vd960Loop 开发日志
> MCU: AT32F421 (Cortex-M4, 120MHz) | 线圈通道: 4路 | 调试口: TTL Tx (3.3V 9600 8N1)
---
## 2026-08-25 — 灵敏度 4 级制修复(协议 V1.08)
### 背景
现场用金属块测感应高度,设置不同灵敏度档位**感应高度无区别**。排查发现三个根因:
| # | 根因 | 位置 |
|---|------|------|
| 1 | **运行中配置命令不同步检测单元**`unpack_pkg_set_mcjq_param()` 只更新 `g_loop_cng_info.loop_cng[].sensitvity` + 写 flash**没有同步 `g_loop_states.loop_unit[].loop_SensLevel`** → 改灵敏度不重启不生效(对比上电路径 `para_store_init()` 有同步) | main.c:576 |
| 2 | **9级制映射 `& 0x03` 折叠**:协议 0~9 级(默认 7),`sensitvity & 0x03` 只取低 2 位 → 7→3、**3→3(从默认 7 改到 3 无效)**、8→0(反向折叠) | storage.c:184 |
| 3 | 测试陷阱:金属块过大/过近信号饱和,4 档阈值都在饱和区 → 感应高度差异不可见(建议小金属块/测临界距离) | 测试方法 |
### 修复(方案 B4 级制)
- 提取映射函数 `sens_level_from_config()`storage.c/h):**4级制 0~3 一一对应**,9 级制后续实现时只改这一处
- `unpack_pkg_set_mcjq_param()``loop_SensLevel` 同步 → 运行中配置立即生效
- `para_store_init()` 上电路径改用同一函数(消除双处映射漂移)
- 协议 V1.08Sensitivity 语义 0~9 级 → **4 级制 0~3**(默认 2
- 新增 `tests/test_sens_mapping.c` gcc 隔离单测(mock flash.h/FreeRTOS.h/task.h + FLP GPIO 宏):验证 0~3 无折叠、兼容旧默认 7→3、SensTable 单调
### 改动
| 文件 | 内容 |
|------|------|
| `src/storage.c` | 新增 `sens_level_from_config()``para_store_init` 改用 |
| `src/main.c` | `unpack_pkg_set_mcjq_param` 补 loop_SensLevel 同步(运行中生效) |
| `inc/storage.h` | sensitvity 注释 4 级制;函数声明 |
| `tests/test_sens_mapping.c` | 新增映射单测(gcc PASS |
| `tests/mock/` | flash.h/FreeRTOS.h/task.h 隔离测试 mock |
| `docs/DLD960Loop_串口通信协议.md` | V1.08:灵敏度 4 级制 |
### 验证
gcc 单测 PASS0→0, 1→1, 2→2, 3→3 无折叠;`7&0x0F → 3` 兼容旧默认。实机待金属块复测(建议小金属块测临界感应高度)。
### 补充根因(同日):INIT_VDs 硬编码覆盖配置 + flash 坏数据
现场串口日志(`Read_Cng_Store`)显示:配置 4 通道 SENS=3/2/1/0,但检测时 `Car_In` 全部打印 `sens_in:36`(SENS=2 表值)——4 通道检测全用 SENS=2。定位两个根因:
**`init_vd_single()` 硬编码 `loop_SensLevel = 2`TaskLoop.c:111**
时序:`main → para_store_init()`(同步 loop_SensLevel=配置)→ 任务启动 → `INIT_VDs()``init_vd_single()` **把 loop_SensLevel 重置为 2**,覆盖配置值 → 全通道 SENS=2。
修复:`init_vd_single` 改为从配置读——`sens_level_from_config(g_loop_cng_info.loop_cng[unit->loop_num].sensitvity)`unit->loop_num 由 INIT_VDs 先设置)。
**② flash 灵敏度表 sens[0]={512,400} 坏数据**
解析 `Read_Cng_Store` flash 数据(0x40 起小端 2B):`{512,400},{108,72},{36,18},{10,9}`——出厂默认应为 `{216,108},...`sens[0] 被写成 512/400(非正常值)。修复 ① 后 loop_3(SENS=0)会撞上 512 → dlt≈281 异常灵敏。**需恢复出厂(test_factory/清配置区)重新写入出厂表。**
---
## 2026-08-25 — variation ↔ ΔL/ΔL-L 物理对应分析与工具
### 背景
后台需要把上报的 `variation`(CAPVD 计数域)换算成物理电感变化量,用于标定灵敏度、分析车辆信号强度。
### 核心推导
CAPVD ∝ 周期 T = 2π√(LC),即 **CAPVD ∝ √L 而非 L**
```
variation/Origin = 1 √(1 + ΔL/L₀) 精确
ΔL/L₀ ≈ 2 × variation/Origin 一阶(残差 <0.2%
Δf/f ≈ variation/Origin 频域对应
```
- 系数 2 来源于 √(1+x) ≈ 1+x/2 展开
- 符号:车辆进入 ΔL<0 → variation 恒为正,与 V1.05 协议语义一致
- **电容档无关性**Origin 与 CAPVD 均 ∝ √Cvariation/Origin 中 C 消掉 → 33/43/66/76nF 四档共用同一换算表,现场换档不换标定
### 灵敏度档位 → ΔL/L 触发阈值(SensTable {216,108,36,10}
| SENS | Δf/f 进入 (=SensTable/65536) | ΔL/L 进入 ≈ 2× |
|------|------------------------------|-----------------|
| 0 低 | 0.330% | 0.659% |
| 1 中 | 0.165% | 0.330% |
| 2 高 | 0.055% | 0.110% |
| 3 最高 | 0.015% | 0.031% |
交叉验证:DLD154Pro 技术文档 sens_in {108,54,28,14} 标注 ΔL/L = 0.330%/0.165%/0.085%/0.043%,恰为 SensTable/65536 的 2 倍——产品线口径一致。
### 改动
| 文件 | 内容 |
|------|------|
| `docs/variation-analysis.md` | 新增 §9(物理链路/公式/档位表/电容无关性/示例/注意事项) |
| `tools/variation_calc.py` | 新增换算工具:正向(freq+cap+variation+origin→L₀,ΔL,ΔL/L)、反推、3B LE 补码解析(V1.05)、灵敏度对照、交互模式 |
### 验证
工具四模式实测通过,正反算自洽(ΔL/L=0.33% ↔ variation=216 @ Origin=131072)。
### 协议文字勘误(协议 V1.07)
用户指出 §5.6.3 记录的口径差异应直接改协议:原文 `±8388607` 不对称,负向边界少了 1。统一为 **8388608 ~ +8388607**3B 补码标准值域 −2²³ ~ +2²³−1,对齐代码负向饱和边界 0x800000),后台按文档实现时不会对 0x800000 边界犯嘀咕。
| 文件 | 变更 |
|------|------|
| `DLD960Loop_串口通信协议.md` | variation 字段值域文字修正 + 修订记录 V1.07 |
| `variation-analysis.md` | 头部版本引用 → V1.07;§1 值域;§5.1 表格;§5.6.3 口径小注改写 |
代码无改动(`main.c` 饱和边界本就是 8388608 ~ +8388607)。
### 环境健康度算法(§10
用户提出零成本环境健康度方案:无车时 variation 锯齿的峰 = 漂移信号,后台逐窗统计画健康度曲线。模拟验证后修正两处口径并落地:
1. **绝对峰 ≠ 窗口漂移**:Origin 更新到窗口均值(滞后半窗 2.5s),稳态绝对峰 = **1.5×** 窗口漂移 → 改用**峰谷差**maxmin,误差 <1%
2. **600ms 采样系统性低估 ~12%**:峰恒在窗口末(500 tick),末采样点 480 tick 永远错过 → 单边偏差,趋势可用,绝对标定 ×1.14
3. **冻结态失效**:漂移 > 4×dlt_ORG(中档 0.13%/s)→ 锯齿消失,速率口径失效 → 双指标:`漂移速率 = 峰谷差/Origin/5s` + `冻结占比`
4. 冻结判定双判据:跨窗跳变消失(|jump|≈0)+ 峰谷差突变(>4×前 4 窗中位数),最后一窗也可判
| 文件 | 内容 |
|------|------|
| `docs/variation-analysis.md` | 新增 §10(口径修正表/双指标/冻结判定/工具用法) |
| `tools/drift_health.py` | 新增参考实现:CSV 输入 + 逐窗统计 + 冻结检测 + JSON 输出 + 自测 |
自测:前 6 窗慢漂移 0.02%/s 全正常,后 2 窗快漂移正确标冻结,平均速率/冻结占比准确。
---
## 2026-07-14 — variation 上报量 2B→3B 有符号 (协议 V1.05)
### 背景
`uart_report_packet_loop_acs` 上报的变化量 `variation` 用于**后台离线跟踪分析车检器算法**(判决裕量、基线跟踪行为)。原实现为 **2 字节无符号 + 取绝对值**,存在两个致命缺陷:
1. **回绕污染**`variation` 是 CAPVD 域的量(量级 ~131072,非 Hz)。大车/大金属全覆盖时偏差可超 65535 → 2B 回绕成小值,后台看到"大信号突然变小"的假象,分析数据作废。
2. **方向丢失**`|Origin CAPVD|` 抹掉符号,分不清"车/金属进入(CAPVD 掉到基线下)"与"反向漂移/异常抬升(CAPVD 升到基线上)",两者判决意义相反。
### 改动 (组包段)
```c
// 旧: uint32_t variation = |Origin - CAPVD| (2B LE)
// 新: 3B LE 有符号补码, 方案B定义
int32_t variation = (int32_t)unit->loop_Origin - (int32_t)unit->loop_CAPVD;
if (variation > 8388607) variation = 8388607; // +2^23-1 饱和防回绕
if (variation < -8388608) variation = -8388608; // -2^23 饱和
// 低字节在前, 发 3 字节
```
- **方案B**`variation = Origin CAPVD`。**正值** = 当前值低于基线(车/裕量方向,与旧版语义连续);**负值** = 反向漂移。
- **饱和限幅** ±2²³,彻底杜绝回绕。
- 每通道单元 **11B → 12B**,整帧 52B → **56B**(仍 < `BUFF_STACK_SIZE=64`,缓冲安全)。
### 影响范围 & 联动
| 端 | 变更 |
|------|------|
| `main.c` 组包 | `uint32_t``int32_t`3B LE + 饱和 |
| 协议文档 | `DLD960Loop_串口通信协议.md` V1.05:字段表、Len 47→51、示例报文重算 |
| **vd960DBN** | 解析步长 11→12、misc 偏移右移 1B、3B 解码 + **bit23 符号扩展**、fast_mode 改 `abs(variation)` |
⚠️ **Loop 固件与 DBN 固件必须同版本发布**——步长死绑定,任一端用旧固件则 4 通道数据整体错位。
### 验证
本地 gcc 隔离单测(编解码往返 / 符号扩展 / 双向饱和 / 方向语义 / fast_mode)全通过。关键:大车 diff=100000 旧版回绕成 34464,新版正确保留 100000。
---
## 2026-06-26 — 多项可配置化改造
### 1. 上报频率从 raw CAPVD 改为实际线圈频率
`uart_report_packet_loop_acs``CMD_DBN_GET_MCJQ_PARAM` 中,频率字段原来直接上报 `loop_CAPVD`(IIR 累加值),现改为经过公式转换的实际频率:
```c
freq = (sclk_freq × input_div × LPCNT) / CAPVD
```
使用 `uint64_t` 先乘后除避免整数截断,`LPCNT>0 && CAPVD>0` 防除零。
### 2. 有限存在时长可配置 (hold_time)
- `Loop154_Unit` 新增 `uint16_t hold_time` 字段
- `storage.c``exist_mode` 计算: `hold_time = exist_mode × 20 × 5`tick 数,每 tick 50ms
- `vd1_task_per_channel` 判定有车时: 若 `hold_time > 0``loop_VD_HOLD = 1`,启用有限存在计时
- `TMR15_GLOBAL_IRQHandler`: `Hold_CNT > unit->hold_time` 替代固定 `HOLD_TIME`
- 超时后执行通道完整重启(`LC_Reset=1, loop_INI_LOOP=1`),相当于线圈重启重建基线,避免继电器释放后立即重新吸合
- `exist_mode=0` 时不启用有限存在,兼容无限制存在场景
### 3. 离开延时可配置 (relay_delay)
- `Loop154_Unit` 新增 `uint16_t relay_delay` 字段
- `storage.c``delay_time` 计算: `relay_delay = delay_time × 2`tick 数)
- `TMR15_GLOBAL_IRQHandler`: `OUTCNT > unit->relay_delay` 替代固定 `OUT_DELAY`
### 4. vTaskDelay 修正: 10ms → 50ms
`loop_task_function` 的主循环 `vTaskDelay``10ms` 改为 `50ms`,对齐 DLD154V4B 原始设计(TMR15 5ms×10)。
**影响:**
- 基线更新周期: 100×10ms=1s → 100×50ms=5s(对齐原始设计,Origin 更稳定)
- CPU 占用: 主循环轮询频率降低 5×,节省 CPU
**原因:** TMR3 ISR ~1ms 产出一个 CAP_OK 样本,50ms 内有 ~50 个样本。`vd1_task_per_channel` 每个 tick 只取最新一个做 IIR10ms 轮询时 CAP_OK 总是 ready,改为 50ms 不影响有效测量速率——采集由 TMR3 ISR 硬件驱动,基线更新速率受 `vd1_task` tick 限制。
---
## 2026-06-29 — DLD154V4B V2.0~V2.5 M4 优化移植
> 参考: DLD154V4B 单路 M4 优化版算法,移植到 vd960Loop 四路并联
### 1. 时序加速: vTaskDelay 50ms → 10ms
主循环 tick 从 50ms 提升到 **10ms**(加速 5×)。TMR15 ISR 保持 5msTM1cnt 10 分频得到 50ms 中断,`Hold_CNT/OUTCNT/INCNT` 仍以 50ms 为单位。
**影响:**
- 检测响应更快:进入确认 3×10ms=30ms 即可判定
- 冻结超时 1000×10ms=10s
- WINDOW_ORIGIN=500 → 基线跟踪窗口 500×10ms=5s(等效原 100×50ms
### 2. 斜率限幅 (Slope Limiter)
```c
MAX_SLOPE_RATE = 5% // 单次 IIR 输入变化不超过 CAPVD 的 5%
```
IIR 之前先对 `loop_Value` 做斜率限幅:与当前 `loop_CAPVD` 比较,变化幅度限制在 ±5% 以内(下限 100),防止突发电磁干扰污染 IIR 状态。
### 3. 进入确认 (Entry Confirm)
```c
ENTRY_CONFIRM = 3 // 连续 3 次低于阈值才判定有车
```
替代原来的单次触发:`loop_CAPVD < (Origin - dlt_ORG)` 计数 `loop_entry_cnt++`,达到 3 次才置 `loop_VD_FLAG=1`。中途高于阈值则清零,消除偶发毛刺导致的误触发。
### 4. 基线冻结超时 (Freeze Timeout)
```c
FREEZE_TIMEOUT = 1000 // 10s @10ms/tick
FREEZE_STABILITY_RATE = 2 // 稳定性窗口 ±2%
```
有车期间基线被冻结(`ORG_CNT/ORG_SUM` 清零不更新),但新增超时机制:
- 冻结时记录参考值 `loop_freeze_ref`
- 若 CAPVD 波动超过 ±2%,重新计时(车还在动)
- 若 CAPVD 在 ±2% 内稳定超过 10s,强制 `Origin = CAPVD`(视为长时间停车,基线应更新)
- 解决长时间停车导致基线永久偏离的问题
### 5. IIR 简化: 双路 → 单路
初始移植了双路 IIR(慢速 α=18/256 + 快速 α=0.5),经测试发现快速 IIR 引入额外噪声,最终**去掉快速 IIR**,仅保留慢速:
```c
ALFA_CAP1 = 79 // α = 79/256 ≈ 0.31, @10ms → τ ≈ 32ms
```
斜率限幅直接对慢速 IIR 输入做限制,简化逻辑且效果一致。
### 6. 稳定期快速收敛
稳定期 `!loop_stable` 绕过 IIR + 斜率限幅:
- 直接用 `loop_CAPVD = loop_Value`raw value
- `update_moving_average` 窗口改为 **100**(小窗口快速收敛)
- 128 样本约 1.28s 完成初始化
稳定后切换回正常 IIR + WINDOW_ORIGIN=500 慢跟踪。
### 7. 参数修正
| 参数 | 旧值 | 新值 | 说明 |
|------|------|------|------|
| `WINDOW_ORIGIN` | 100 | 500 | 基线窗口 5s @10ms |
| `update_moving_average` window | `uint8_t` | `uint16_t` | 修复 500→256 溢出 |
| `loop_entry_cnt` | — | `uint8_t` | 新增: 进入确认计数器 |
| `loop_freeze_cnt` | — | `uint16_t` | 新增: 冻结持续计数 |
| `loop_freeze_ref` | — | `uint32_t` | 新增: 冻结参考值 |
---
## 2026-07-03 — 时间量上报单位改为 10ms
### 背景
`MISC_TYPE_TIME` 上报的通过时间 / 车间距原来以 5ms 为单位(直接 `report_counter - 时间戳`),与外部协议期望的 10ms 单位不一致。
### 方案
内部时间戳 `report_counter` / `passtime_start` / `last_exit_tick` 保持 5ms 精度不动,仅在上报 `misc_value``/2` 转为 10ms
```c
// 进场 — 车间距 (10ms)
misc_value = (report_counter - last_exit_tick) / 2;
// 离场 — 通过时间 (10ms)
misc_value = (report_counter - passtime_start) / 2;
```
### 影响范围
| 位置 | 变更 |
|------|------|
| `TaskLoop.c` 进场路径 | `misc_value` /2 |
| `TaskLoop.c` 离场 flatness | `misc_value` /2 |
| `TaskLoop.c` 离场 cnt_release | `misc_value` /2 |
| `TaskLoop.h` 注释 | 标注 `/2→10ms` |
---
## 2026-07-03 — 时间量上报单位改为 50ms
### 背景
上一步 `misc_value` 以 10ms 为单位(内部 5ms tick `/2`),但协议期望 50ms。统一为 50ms 后与 `Hold_CNT/OUTCNT/INCNT` 一致。
### 方案
1. **`report_counter` 移入 50ms tick 块**:原来在 5ms ISR 外层 `++`,现移到 `if (TM1cnt >= 10)` 内部,每次加 1 = 50ms
2. **去掉 `/2`**`misc_value = report_counter - timestamp`(直接减,无除法)
3. **上报间隔宏同步**`REPORT_IDLE_TICKS 120→12``REPORT_EVENT_TICKS 30→3`
### 影响范围
| 文件 | 变更 |
|------|------|
| `TaskLoop.c` ISR | `report_counter++` 从 5ms 外层移入 50ms 块 |
| `TaskLoop.c` 进场/离场 | 去掉 `/2`,注释 10ms→50ms |
| `TaskLoop.h` | `REPORT_*_TICKS` /10,注释 5ms→50ms |
| `main.c` | 间隔注释更新 |
---
## 2026-07-03 — ARMCC 编译修复 + 时间戳/上报完善 (V2.8~V3.1)
### 1. ARMCC 编译错误修复 (V2.8)
- **#991**: `g_loop_states` 初始化去多余嵌套大括号 `{{0}, {{{0}}}}``{0}`
- **#177-D**: 删除未引用变量 `_counter1_init`
- **#188-D**: 7处 `at32_led_*()` 调用加 `(led_type)` 强转
### 2. 车间距时间补齐 (V2.9)
`Loop154_Unit` 新增 `last_exit_tick` 字段:
- **进场**: `misc_value = misc_counter - last_exit_tick`(车间距,首车=0
- **离场**: 记录 `last_exit_tick = misc_counter``misc_value = misc_counter - passtime_start`(通过时间)
- 四通道独立计算,50ms tick 单位
### 3. report_counter 清零导致下溢修复 (V3.0)
**根因**: `report_counter` 被 UART 上报清零后,`last_exit_tick`/`passtime_start` 存旧值,再次进场时差值下溢 → `0xFFFFFFF7`
**修复**: 新增 `misc_counter` 独立自由运行计数器(50ms,永不归零),所有时间戳操作改用 `misc_counter`
```
Loop154_States:
report_counter → 上报间隔调度 (会清零)
misc_counter → 时间戳 (永不清零)
```
`report_counter` 只负责上报调度,`misc_counter` 负责时间量计算,互不干扰。
### 4. 上电 3 秒抑制上报 (V3.1)
`uart_report_packet_loop_acs` 开头新增检查:`misc_counter < 60`3s @50ms)时跳过上报并清零 `report_counter`,等线圈基线稳定后再开始主动上报。
---
## 修订记录
| 版本 | 时间 | 说明 |
|------|------|------|
| V3.1 | 2026-07-03 | 上电3秒抑制主动上报 |
| V3.0 | 2026-07-03 | misc_counter 自由运行替代 report_counter 做时间戳 |
| V2.9 | 2026-07-03 | 补齐车间距时间 (gap time) |
| V2.8 | 2026-07-03 | ARMCC 编译修复 (多余大括号/未引用变量/枚举混用) |
| V2.7 | 2026-07-03 | report_counter 改 50ms tick,时间量单位 10ms→50ms |
| V2.6 | 2026-07-03 | misc_value 时间量单位 5ms→10ms |
| V2.5 | 2026-06-29 | 稳定期窗口 100 快速收敛 |
| V2.4 | 2026-06-29 | 去掉快速 IIRALFA_CAP1=79 @10ms |
| V2.3 | 2026-06-29 | 基线冻结超时 + 稳定性检查 |
| V2.2 | 2026-06-29 | 进入确认 ENTRY_CONFIRM=3 |
| V2.1 | 2026-06-29 | 斜率限幅 MAX_SLOPE_RATE=5% |
| V2.0 | 2026-06-29 | vTaskDelay 50→10ms, 双路 IIR 移植 |
| V1.3 | 2026-06-26 | vTaskDelay 10→50ms 对齐原始设计 |
| V1.2 | 2026-06-26 | hold_time/relay_delay 可配置,有限存在完整重启 |
| V1.1 | 2026-06-26 | 频率上报 CAPVD→实际频率转换 |