1316 lines
42 KiB
Markdown
1316 lines
42 KiB
Markdown
# 方案B DDS注入式检测器——AD8302接口电路与IQ解调算法详解
|
||
|
||
> **作者:** 小德
|
||
> **分类:** 线圈车检 · DDS注入 · AD8302 · IQ解调算法
|
||
> **版本:** V1.0
|
||
> **日期:** 2026-07-05
|
||
> **关联文档:** 铝制车架有效感应方法_v2.0.md(方案B概述)
|
||
|
||
---
|
||
|
||
## 前言
|
||
|
||
方案B(DDS注入 + 阻抗分析)的核心是实现复阻抗 Z = R + jX 的实时测量。常见的芯片实现路径有两条:
|
||
|
||
| 路径 | 芯片 | 原理 | 精度 | 成本 |
|
||
|------|------|------|------|------|
|
||
| **路径A:幅度-相位检测** | **AD8302** | 测两路信号的幅度比和相位差 | ★★★★ | 低 |
|
||
| 路径B:正交IQ解调 | AD8333 / LTC5599 | 真正正交混频输出 I/Q | ★★★★★ | 中高 |
|
||
| 路径C:数字IQ解调(纯MCU) | MCU ADC + 软件 | 直接采样后数字混频 | ★★★ | 零 |
|
||
|
||
本文以**路径A(AD8302)**为主、**路径C(纯MCU)**为辅展开,因为这是性价比最高、最适合产品化的路线。
|
||
|
||
---
|
||
|
||
## 一、AD8302 芯片核心特性
|
||
|
||
### 1.1 引脚功能
|
||
|
||
```
|
||
AD8302(14引脚 TSSOP)
|
||
|
||
┌─────────────┐
|
||
│ INPA COMM │← 通道A输入、地
|
||
│ OFSA OFSB │← 输入偏置/耦合方式选择
|
||
│ INPB VREF │← 通道B输入、1.8V参考电压输出
|
||
│ VREF VPOS │← 参考电压、正电源(2.7~5.5V)
|
||
│ V_MAG V_PHS │← 幅度比输出、相位差输出
|
||
│ FLTR MSET │← 幅度测量滤波器设置
|
||
│ PHSD PSET │← 相位测量滤波器设置
|
||
└─────────────┘
|
||
```
|
||
|
||
### 1.2 输入输出特性
|
||
|
||
**输入通道特性:**
|
||
|
||
| 参数 | 值 | 说明 |
|
||
|------|-----|------|
|
||
| 输入频率范围 | DC ~ 2.7GHz | 远超车检需求(80~200kHz) |
|
||
| 输入幅度范围 | -60dBm ~ 0dBm | (1mVpp ~ 630mVpp @ 50Ω) |
|
||
| 输入阻抗 | 3kΩ // 2pF | 高阻抗,可直接耦合 |
|
||
| 输入耦合方式 | AC/DC 可选 | 通过 OFSA/OFSB 引脚配置 |
|
||
|
||
**输出特性:**
|
||
|
||
```
|
||
V_MAG 引脚(幅度比):
|
||
|
||
V_MAG = 0.9V + 0.03V × (PA - PB) [dB]
|
||
|
||
其中 PA、PB 为 INPA、INPB 的输入功率(dBm)
|
||
|
||
测量范围:-30dB ~ +30dB(即 0.0316x ~ 31.6x 倍幅度比)
|
||
输出范围:0 ~ 1.8V
|
||
灵敏度:30mV/dB
|
||
比例因子:约 0.26mV/0.01dB
|
||
|
||
V_PHS 引脚(相位差):
|
||
|
||
V_PHS = 0.9V + 0.01V × (ΦA - ΦB) [度]
|
||
|
||
其中 ΦA、ΦB 为 INPA、INPB 的相位
|
||
|
||
测量范围:-180° ~ +180°
|
||
输出范围:0 ~ 1.8V
|
||
灵敏度:10mV/度
|
||
比例因子:约 0.1mV/0.01°
|
||
```
|
||
|
||
### 1.3 传递函数曲线
|
||
|
||
```
|
||
V_MAG 输出 V_PHS 输出
|
||
│ │
|
||
1.8V ┌──────── 1.8V ┌────────
|
||
│ │ │ │
|
||
│ │ │ │
|
||
0.9V ┼─────────── 0.9V ┼───────────
|
||
│ │ │ │
|
||
│ │ │ │
|
||
0V └─────────── 0V └───────────
|
||
+30dB 0dB -30dB +180° 0° -180°
|
||
(INPA > INPB) (INPA 超前 INPB)
|
||
```
|
||
|
||
**车检场景下的工作区间:**
|
||
|
||
- 车辆覆盖时,幅度变化约 ±0.01~±0.5dB(对应 V_MAG 变化 ±0.3~±15mV)
|
||
- 相位变化约 ±0.01°~±2°(对应 V_PHS 变化 ±0.1~±20mV)
|
||
- **关键看 ADC 分辨率能否分辨这几个 mV 级别的变化量**
|
||
|
||
---
|
||
|
||
## 二、AD8302 与 MCU 的接口电路设计
|
||
|
||
### 2.1 系统整体框图
|
||
|
||
```
|
||
┌─────────────────────┐
|
||
│ DDS │
|
||
│ AD9834 │
|
||
│ f₀ = 120kHz │
|
||
└──────┬──────────────┘
|
||
│ 正弦波 V_dds
|
||
▼
|
||
┌─────────────────────┐
|
||
│ 功放/缓冲 │
|
||
│ OPA2188 │
|
||
│ Gain = 2~5 │
|
||
└──────┬──────────────┘
|
||
│
|
||
┌──────────────┼──────────────┐
|
||
│ │ │
|
||
▼ ▼ ▼
|
||
┌───────────┐ ┌───────────┐ ┌───────────┐
|
||
│ 衰减器 │ │ 变压器 │ │ 阻抗匹配 │
|
||
│ 电阻分压 │ │ 1:N升压 │ │ 网络 │
|
||
│ -20~-30dB │ │ │ │ │
|
||
└─────┬─────┘ └─────┬─────┘ └─────┬─────┘
|
||
│ │ │
|
||
│ ┌────┴────┐ │
|
||
│ │ 线圈 │ │
|
||
│ │ (100~ │ │
|
||
│ │ 300μH) │ │
|
||
│ └─────────┘ │
|
||
│ │ │
|
||
│ ┌────┴────┐ │
|
||
│ │ 变压器 │ │
|
||
│ │ N:1降压 │ │
|
||
│ └────┬────┘ │
|
||
│ │ │
|
||
▼ ▼ ▼
|
||
┌───────────────────────────────────────┐
|
||
│ 信号调理 │
|
||
│ ┌─────────┐ ┌─────────┐ │
|
||
│ │ 缓冲器 │ │ 缓冲器 │ │
|
||
│ │ OPA2188 │ │ OPA2188 │ │
|
||
│ └────┬────┘ └────┬────┘ │
|
||
│ │ │ │
|
||
│ ┌────┴────┐ ┌────┴────┐ │
|
||
│ │ LPF │ │ LPF │ │
|
||
│ │ 300kHz │ │ 300kHz │ │
|
||
│ └────┬────┘ └────┬────┘ │
|
||
│ │ │ │
|
||
│ Ref_IN Meas_IN │
|
||
└───────────────────────────────────────┘
|
||
│ │
|
||
▼ ▼
|
||
┌──────────────────────────┐
|
||
│ AD8302 │
|
||
│ INPA(参考) INPB(测量) │
|
||
│ │
|
||
│ V_MAG V_PHS │
|
||
└──────┬──────────┬────────┘
|
||
│ │
|
||
▼ ▼
|
||
┌──────────────────────────┐
|
||
│ MCU (STM32G474) │
|
||
│ ADC1_IN1 ADC1_IN2 │
|
||
│ (12bit, 过采样至16bit) │
|
||
│ │
|
||
│ 定时器触发 ADC 采样 │
|
||
│ f_sample = 1kHz │
|
||
│ │
|
||
│ 软件 IQ 解算: │
|
||
│ → ΔR, ΔL 提取 │
|
||
│ → 基线跟踪 │
|
||
│ → 触发判决 │
|
||
└──────────────────────────┘
|
||
```
|
||
|
||
### 2.2 参考通道设计(INPA)
|
||
|
||
参考通道从 DDS 输出端直接取信号,经过固定衰减后送入 AD8302 的 INPA。
|
||
|
||
```
|
||
DDS 输出 ──→ 电阻分压衰减器 ──→ 缓冲器 ──→ LPF ──→ INPA
|
||
|
||
衰减器设计(典型值 -26dB ≈ 1/20):
|
||
|
||
R1=1kΩ
|
||
IN ──┬─/\/\/\/──┬── OUT
|
||
│ │
|
||
│ R2=51Ω │
|
||
│ │
|
||
GND GND
|
||
|
||
衰减比 = R2/(R1+R2) ≈ 51/1051 ≈ 1/20 ≈ -26dB
|
||
```
|
||
|
||
**注意:** 衰减器必须使用**低温度系数**电阻(±25ppm/°C 或更好),因为它是整个系统的幅度参考基准,直接影响 ΔR 的测量精度。
|
||
|
||
### 2.3 测量通道设计(INPB)
|
||
|
||
测量通道从线圈响应端取信号,经缓冲后的送入 AD8302 的 INPB。
|
||
|
||
```
|
||
线圈 ──→ 变压器 N:1 ──→ 缓冲器 ──→ LPF ──→ INPB
|
||
|
||
变压器匝比:
|
||
- 激励侧升压:1:3 ~ 1:5(提升线圈激励电流,增加 SNR)
|
||
- 接收侧降压:5:1 ~ 3:1(恢复至 ADC/AD8302 可接受电平)
|
||
```
|
||
|
||
### 2.4 低通滤波器(LPF)设计
|
||
|
||
目的是滤除带外噪声和高次谐波,同时保持激励频率(120kHz)信号不衰减。
|
||
|
||
```
|
||
二阶巴特沃斯 LPF,截止频率 300kHz:
|
||
|
||
R1=10k R2=10k
|
||
IN ──┬─/\/\/\/──┬─/\/\/\/──┬── OUT
|
||
│ │ │
|
||
│ C1=47pF │ │ C2=100pF
|
||
│ │ │
|
||
GND GND GND
|
||
|
||
f_c = 1 / (2π × √(R1×R2×C1×C2))
|
||
= 1 / (2π × √(10k×10k×47p×100p))
|
||
= 1 / (2π × 6.86e-6)
|
||
≈ 23.2kHz ❌ 太低了!
|
||
```
|
||
|
||
调整元件值使截止频率为 300kHz:
|
||
|
||
```
|
||
重新计算:
|
||
|
||
R1=2k R2=2k
|
||
IN ──┬─/\/\/\/──┬─/\/\/\/──┬── OUT
|
||
│ │ │
|
||
│ C1=470pF │ │ C2=220pF
|
||
│ │ │
|
||
GND GND GND
|
||
|
||
f_c ≈ 1 / (2π × √(2k×2k×470p×220p))
|
||
≈ 1 / (2π × 4.55e-7)
|
||
≈ 350kHz ✅
|
||
|
||
120kHz 处衰减 < 0.5dB
|
||
1MHz 处衰减 > 20dB
|
||
```
|
||
|
||
### 2.5 输入电平匹配
|
||
|
||
AD8302 的输入幅度范围是 -60dBm ~ 0dBm(50Ω参考)。为保证最佳线性度,推荐工作点在 **-30dBm ~ -10dBm**。
|
||
|
||
**电平计算(以 120kHz, 3.3V DDS 输出 Vpp 为例):**
|
||
|
||
```
|
||
DDS 输出(AD9834):3.3Vpp (50% 占空比时 ≈ 1.65V 振幅)
|
||
|
||
参考通道:
|
||
- 经 -26dB 衰减器:1.65V × 1/20 = 82.5mVpp
|
||
- 82.5mVpp 在 50Ω 上对应功率:P = Vrms²/50
|
||
- Vrms = 82.5mVpp / 2.828 = 29.2mVrms
|
||
- P = 0.0292²/50 = 17μW = -17.7dBm ✅
|
||
|
||
测量通道(线圈空载时):
|
||
- 变压器匝比 1:3(激励升压),接收 3:1(降压):总增益 ≈ 0dB
|
||
- 但线圈有 1~2Ω 直流电阻 + 辐射损耗
|
||
- 信号幅度约 DDS 的 0.7~0.9 倍
|
||
- 预期:60~75mVpp → -20dBm ~ -18dBm ✅
|
||
```
|
||
|
||
### 2.6 MCU ADC 接口
|
||
|
||
```
|
||
AD8302 输出:
|
||
|
||
V_MAG ──┬─ 100Ω ──┬── ADC1_IN1 (PA0)
|
||
│ │
|
||
│ 1nF === 10nF (去耦)
|
||
│ │
|
||
GND GND
|
||
|
||
V_PHS ──┬─ 100Ω ──┬── ADC1_IN2 (PA1)
|
||
│ │
|
||
│ 1nF === 10nF
|
||
│ │
|
||
GND GND
|
||
|
||
100Ω + 1nF 构成简单 RC 低通:
|
||
f_c = 1/(2π × 100 × 1nF) ≈ 1.6MHz(去噪,不影响信号)
|
||
```
|
||
|
||
**ADC 配置(以 STM32G474 为例):**
|
||
|
||
```
|
||
ADC 参数:
|
||
分辨率:12bit(过采样至 16bit,OSR=16)
|
||
采样率:1ksps(每个通道,两个通道)
|
||
采样时间:47.5 个 ADC 时钟周期
|
||
ADC 时钟:60MHz(APB2)
|
||
参考电压:3.3V(VDDA)
|
||
|
||
过采样(硬件过采样):
|
||
OVFS=4 → 过采样率 16 → 有效分辨率 ~16bit
|
||
等效 LSB = 3.3V / 65536 ≈ 50μV
|
||
|
||
V_MAG 信号灵敏度评估:
|
||
铝车架 ΔR = 3% → V_MAG 变化约 1.5~2mV
|
||
50μV LSB → 约 30~40 个 LSB 的变化
|
||
SNR ≈ 36dB → 足够可靠(需求 SNR > 20dB)
|
||
```
|
||
|
||
**DMA 配置:**
|
||
|
||
```
|
||
双通道 DMA 循环采样:
|
||
ADC1 → DMA1_CH1 → buffer[128](每通道 64 点双缓冲)
|
||
半满中断 / 满中断 → 触发数据处理
|
||
|
||
定时器触发:
|
||
TIM6(1kHz)→ TRGO → ADC1 启动转换
|
||
```
|
||
|
||
### 2.7 PCB 布局关键要点
|
||
|
||
```
|
||
┌────────────────────────────────────┐
|
||
│ AD8302 布局注意事项 │
|
||
├────────────────────────────────────┤
|
||
│ │
|
||
│ ① 模拟地与数字地单点连接 │
|
||
│ AD8302下方开槽隔离 │
|
||
│ │
|
||
│ ② 参考通道和测量通道走线等长 │
|
||
│ 减少路径引入的额外相位差 │
|
||
│ │
|
||
│ ③ V_MAG 和 V_PHS 输出走线远离 │
|
||
│ DDS 高频信号线 │
|
||
│ │
|
||
│ ④ 电源去耦: │
|
||
│ VPOS 引脚:100nF + 10μF 就近放置 │
|
||
│ 每颗运放:100nF │
|
||
│ │
|
||
│ ⑤ DDS 时钟源远离模拟前端 │
|
||
│ 晶振/时钟走线包地 │
|
||
│ │
|
||
│ ⑥ 变压器靠近 AD8302 输入引脚 │
|
||
│ LPF 元件靠近 AD8302 │
|
||
│ │
|
||
└────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 三、AD8302 标定与校准
|
||
|
||
### 3.1 出厂/上电校准
|
||
|
||
在已知空载条件下(无车、已知环境),测量 V_MAG 和 V_PHS 作为空载基准:
|
||
|
||
```
|
||
步骤:
|
||
1. 确认线圈已连接,周围 2m 内无金属
|
||
2. 等待 5s(让 AD8302 和 DDS 温漂稳定)
|
||
3. 采集 100 个 V_MAG 和 V_PHS 样本
|
||
4. 计算中值作为 V_MAG₀ 和 V_PHS₀
|
||
5. 计算噪声标准差 σ_MAG 和 σ_PHS
|
||
|
||
存储校准参数:
|
||
Cal = {V_MAG₀, V_PHS₀, σ_MAG, σ_PHS, T_cal}
|
||
```
|
||
|
||
### 3.2 增益/相位系数标定
|
||
|
||
虽然 AD8302 的 datasheet 给出了 30mV/dB 和 10mV/° 的标称值,实际每颗芯片有细微差异,建议在产线标定:
|
||
|
||
```
|
||
标定方法(使用标准阻抗盒):
|
||
|
||
① 接入已知 R 和 L 的标准负载:
|
||
R_load = R₀ + ΔR_cal(如 0Ω、0.5Ω、1Ω、2Ω)
|
||
L_load = L₀ + ΔL_cal(如 +1μH、+2μH、+5μH)
|
||
|
||
② 记录每种负载下的 V_MAG、V_PHS
|
||
|
||
③ 线性回归得到实际系数:
|
||
k_mag = ΔV_MAG / Δ(dB) [V/dB]
|
||
k_phs = ΔV_PHS / Δ(deg) [V/deg]
|
||
|
||
④ 每台设备存储 k_mag、k_phs 到 EEPROM/Flash
|
||
```
|
||
|
||
### 3.3 温漂校准
|
||
|
||
```
|
||
在工作温度范围(-20°C ~ +70°C)内采集:
|
||
|
||
ΔV_MAG_T(T) = V_MAG(T) - V_MAG(25°C)
|
||
ΔV_PHS_T(T) = V_PHS(T) - V_PHS(25°C)
|
||
|
||
拟合为二阶多项式:
|
||
ΔV_MAG_T(T) = a₂T² + a₁T + a₀
|
||
ΔV_PHS_T(T) = b₂T² + b₁T + b₀
|
||
|
||
运行时补偿:
|
||
V_MAG_comp = V_MAG_raw - ΔV_MAG_T(T_current)
|
||
V_PHS_comp = V_PHS_raw - ΔV_PHS_T(T_current)
|
||
```
|
||
|
||
---
|
||
|
||
## 四、IQ 解调算法——从 AD8302 输出到 ΔR、ΔL
|
||
|
||
### 4.1 算法概述
|
||
|
||
AD8302 直接给出的是**幅度比 G**和**相位差 Φ**,我们需要将其转换为**电阻变化 ΔR**和**电感变化 ΔL**。
|
||
|
||
```
|
||
AD8302 输出 ──→ 幅度比 G (dB) ──→ |Z|/|Z₀| ──→ ΔR, ΔL
|
||
──→ 相位差 Φ (deg) ──→ ∠Z - ∠Z₀ ──┘
|
||
```
|
||
|
||
### 4.2 第一步:原始量采样
|
||
|
||
```
|
||
每 1ms(1kHz 采样率):
|
||
|
||
[0] 读取 V_MAG_raw(ADC 码值)
|
||
[1] 读取 V_PHS_raw(ADC 码值)
|
||
|
||
[2] 转换为电压:
|
||
V_MAG = V_MAG_raw × (3.3V / 65536) [16bit ADC]
|
||
V_PHS = V_PHS_raw × (3.3V / 65536)
|
||
|
||
[3] 温度补偿:
|
||
V_MAG = V_MAG - ΔV_MAG_T(T_sensor)
|
||
V_PHS = V_PHS - ΔV_PHS_T(T_sensor)
|
||
|
||
[4] 转换为幅度比和相位差:
|
||
G_dB = (V_MAG - 0.9V) / k_mag [dB]
|
||
Φ_deg = (V_PHS - 0.9V) / k_phs [度]
|
||
|
||
注意:k_mag ≈ 0.03V/dB, k_phs ≈ 0.01V/°
|
||
```
|
||
|
||
### 4.3 第二步:相对于空载基线的变化量
|
||
|
||
```
|
||
ΔG_dB = G_dB - G₀_dB (幅度比变化,单位 dB)
|
||
ΔΦ_deg = Φ_deg - Φ₀_deg (相位差变化,单位 度)
|
||
|
||
其中 G₀_dB、Φ₀_deg 为空载基准值(从校准参数读取)
|
||
```
|
||
|
||
### 4.4 第三步:从幅度和相位提取 ΔR 和 ΔL
|
||
|
||
**关键数学推导:**
|
||
|
||
设空载时线圈复阻抗为 Z₀ = R₀ + jωL₀,模和相角为:
|
||
```
|
||
|Z₀| = √(R₀² + (ωL₀)²)
|
||
∠Z₀ = arctan(ωL₀ / R₀)
|
||
```
|
||
|
||
车辆覆盖后阻抗变为 Z = R₀ + ΔR + jω(L₀ + ΔL):
|
||
```
|
||
|Z| = √((R₀+ΔR)² + (ωL₀+ωΔL)²)
|
||
∠Z = arctan((ωL₀+ωΔL) / (R₀+ΔR))
|
||
```
|
||
|
||
AD8302 输出反映了这些变化:
|
||
```
|
||
幅度比变化:ΔG_dB = 20 × log₁₀(|Z| / |Z₀|)
|
||
相位差变化:ΔΦ_deg = (∠Z - ∠Z₀) × 180/π × sign_correction
|
||
```
|
||
|
||
但由于 Z 是 R 和 L 的耦合函数,从 ΔG 和 ΔΦ 反推 ΔR 和 ΔL 需要对非线性方程组求解。
|
||
|
||
**对小信号(ΔR ≪ R₀, ΔL ≪ L₀)可用一阶线性近似:**
|
||
|
||
```
|
||
定义阻抗矩阵 J:
|
||
|
||
[ ΔG_linear ] = J × [ ΔR ]
|
||
[ ΔΦ_rad ] [ ωΔL ]
|
||
|
||
其中:
|
||
ΔG_linear = ln(10)/20 × ΔG_dB ≈ 0.1151 × ΔG_dB
|
||
ΔΦ_rad = ΔΦ_deg × π/180
|
||
|
||
J = [ ∂G/∂R ∂G/∂(ωL) ] (在 R₀, ωL₀ 处计算)
|
||
[ ∂Φ/∂R ∂Φ/∂(ωL) ]
|
||
```
|
||
|
||
**解析结果(非常重要):**
|
||
|
||
```
|
||
设 α = R₀ / |Z₀| = cos(∠Z₀)
|
||
β = ωL₀ / |Z₀| = sin(∠Z₀)
|
||
|
||
∂G/∂R = α / |Z₀|
|
||
∂G/∂(ωL) = β / |Z₀|
|
||
∂Φ/∂R = -β / |Z₀|
|
||
∂Φ/∂(ωL) = α / |Z₀|
|
||
```
|
||
|
||
**即:**
|
||
|
||
```
|
||
ΔG_linear ≈ (α × ΔR + β × ωΔL) / |Z₀|
|
||
ΔΦ_rad ≈ (-β × ΔR + α × ωΔL) / |Z₀|
|
||
```
|
||
|
||
**反解 ΔR 和 ΔL:**
|
||
|
||
```
|
||
ΔR = |Z₀| × (α × ΔG_linear - β × ΔΦ_rad)
|
||
ωΔL = |Z₀| × (β × ΔG_linear + α × ΔΦ_rad)
|
||
|
||
注意 α² + β² = 1(因为 α = cosθ₀, β = sinθ₀),该变换是正交的!
|
||
```
|
||
|
||
**即 ΔR 和 ΔL 的解耦计算公式:**
|
||
|
||
```
|
||
ΔR = |Z₀| × (cos(∠Z₀) × ΔG_linear - sin(∠Z₀) × ΔΦ_rad)
|
||
ΔL = |Z₀| / ω × (sin(∠Z₀) × ΔG_linear + cos(∠Z₀) × ΔΦ_rad)
|
||
```
|
||
|
||
**这个公式是方案B从 AD8302 原始输出到 ΔR、ΔL 的核心算法,务必正确实现。**
|
||
|
||
### 4.5 算法伪代码
|
||
|
||
```c
|
||
// ============================================================
|
||
// 核心算法:AD8302 → ΔR, ΔL
|
||
// 运行频率:1kHz(每 1ms 调用一次)
|
||
// ============================================================
|
||
|
||
// 校准参数(上电从 EEPROM 读取)
|
||
typedef struct {
|
||
float V_MAG_0; // 空载 V_MAG 基准电压
|
||
float V_PHS_0; // 空载 V_PHS 基准电压
|
||
float k_mag; // 幅度比系数(V/dB)
|
||
float k_phs; // 相位差系数(V/deg)
|
||
float R0; // 空载线圈电阻(Ω)
|
||
float L0; // 空载线圈电感(H)
|
||
float omega; // 激励角频率 = 2π×f₀
|
||
// 温度补偿系数
|
||
float a2, a1, a0; // V_MAG 温漂二阶系数
|
||
float b2, b1, b0; // V_PHS 温漂二阶系数
|
||
} CalParam_t;
|
||
|
||
CalParam_t cal; // 全局校准参数
|
||
|
||
// 由校准参数计算的中间量
|
||
float Z0; // |Z₀| = sqrt(R0² + (ωL0)²)
|
||
float cos_theta; // cos(∠Z₀) = R0 / Z0
|
||
float sin_theta; // sin(∠Z₀) = ωL0 / Z0
|
||
|
||
void Cal_Init(void) {
|
||
cal.omega = 2.0f * 3.14159265f * 120000.0f; // 120kHz
|
||
Z0 = sqrtf(cal.R0 * cal.R0 + powf(cal.omega * cal.L0, 2));
|
||
cos_theta = cal.R0 / Z0;
|
||
sin_theta = (cal.omega * cal.L0) / Z0;
|
||
}
|
||
|
||
// 温度补偿
|
||
float Compensate_MAG(float V_mag_raw, float temp) {
|
||
float dt = temp - 25.0f;
|
||
return V_mag_raw - (cal.a2*dt*dt + cal.a1*dt + cal.a0);
|
||
}
|
||
|
||
float Compensate_PHS(float V_phs_raw, float temp) {
|
||
float dt = temp - 25.0f;
|
||
return V_phs_raw - (cal.b2*dt*dt + cal.b1*dt + cal.b0);
|
||
}
|
||
|
||
// 核心:从 AD8302 输出计算 ΔR 和 ΔL
|
||
void AD8302_To_DeltaRL(
|
||
uint16_t adc_mag, // V_MAG ADC 码值
|
||
uint16_t adc_phs, // V_PHS ADC 码值
|
||
float temp, // 当前温度(°C)
|
||
float *delta_R, // 输出:ΔR(Ω)
|
||
float *delta_L // 输出:ΔL(H)
|
||
) {
|
||
// Step 1: ADC 码值 → 电压
|
||
float V_mag_raw = (float)adc_mag * 3.3f / 65536.0f;
|
||
float V_phs_raw = (float)adc_phs * 3.3f / 65536.0f;
|
||
|
||
// Step 2: 温度补偿
|
||
float V_mag = Compensate_MAG(V_mag_raw, temp);
|
||
float V_phs = Compensate_PHS(V_phs_raw, temp);
|
||
|
||
// Step 3: 电压 → 幅度比(dB)和相位差(度)
|
||
float G_dB = (V_mag - 0.9f) / cal.k_mag;
|
||
float Phi_deg = (V_phs - 0.9f) / cal.k_phs;
|
||
|
||
// Step 4: 相对于空载基线的变化量
|
||
float dG_dB = G_dB - (cal.V_MAG_0 - 0.9f) / cal.k_mag;
|
||
float dPhi_deg = Phi_deg - (cal.V_PHS_0 - 0.9f) / cal.k_phs;
|
||
|
||
// Step 5: dB→线性, deg→rad
|
||
float dG_lin = 0.115129f * dG_dB; // ln(10)/20 ≈ 0.1151
|
||
float dPhi_rad = dPhi_deg * 0.0174533f; // π/180 ≈ 0.01745
|
||
|
||
// Step 6: 解耦提取 ΔR 和 ΔL(核心公式)
|
||
*delta_R = Z0 * (cos_theta * dG_lin - sin_theta * dPhi_rad);
|
||
*delta_L = (Z0 / cal.omega) * (sin_theta * dG_lin + cos_theta * dPhi_rad);
|
||
}
|
||
```
|
||
|
||
### 4.6 简化实现(MCU 资源受限时)
|
||
|
||
如果 MCU 不支持浮点运算或三角函数,可以用预计算表:
|
||
|
||
```c
|
||
// 预计算表(在初始化时计算一次)
|
||
// 对于固定频率,cos_theta 和 sin_theta 是常数
|
||
|
||
// 简化公式(避免除法):
|
||
// ΔR ≈ K_RR × dG_dB + K_RP × dPhi_deg
|
||
// ΔL ≈ K_LR × dG_dB + K_LP × dPhi_deg
|
||
|
||
// 预计算常数(在 Cal_Init 中计算):
|
||
K_RR = Z0 * cos_theta * 0.115129f; // ΔR 对 dG_dB 的系数
|
||
K_RP = -Z0 * sin_theta * 0.0174533f; // ΔR 对 dPhi_deg 的系数
|
||
K_LR = Z0/cal.omega * sin_theta * 0.115129f; // ΔL 对 dG_dB
|
||
K_LP = Z0/cal.omega * cos_theta * 0.0174533f; // ΔL 对 dPhi_deg
|
||
|
||
// 运行时只需要 4 次乘法和 2 次加法:
|
||
void AD8302_To_DeltaRL_Fast(float dG_dB, float dPhi_deg,
|
||
float *dR, float *dL) {
|
||
*dR = K_RR * dG_dB + K_RP * dPhi_deg;
|
||
*dL = K_LR * dG_dB + K_LP * dPhi_deg;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 五、数字滤波与信号处理流水线
|
||
|
||
### 5.1 处理流水线框架
|
||
|
||
```
|
||
ADC 采样 (1kHz) ──→ 预处理 ──→ ⅡR 低通 ──→ 基线跟踪 ──→ 触发判决 ──→ 输出
|
||
|
||
每个阶段的数据率:
|
||
1kHz → 1kHz → 100Hz → 100Hz → 事件驱动
|
||
↑
|
||
基线更新率:0.01~0.05Hz
|
||
```
|
||
|
||
### 5.2 ⅡR 低通滤波器设计
|
||
|
||
推荐二阶巴特沃斯低通,截止频率 20Hz(滤除电源纹波和噪声,保留车辆信号):
|
||
|
||
```c
|
||
// 二阶 IIR 低通滤波器
|
||
// 设计参数:f_c = 20Hz, f_s = 1kHz, 巴特沃斯
|
||
// 传递函数:H(z) = (b₀ + b₁z⁻¹ + b₂z⁻²) / (1 + a₁z⁻¹ + a₂z⁻²)
|
||
|
||
typedef struct {
|
||
float b0, b1, b2; // 前馈系数
|
||
float a1, a2; // 反馈系数(注意符号约定:y = b0*x + b1*x1 + b2*x2 - a1*y1 - a2*y2)
|
||
float x1, x2; // 输入历史
|
||
float y1, y2; // 输出历史
|
||
} IIRFilter_t;
|
||
|
||
// 20Hz 低通 @ 1kHz 的系数(巴特沃斯)
|
||
// 使用双线性变换法计算得到:
|
||
void IIR_LPF20_Init(IIRFilter_t *f) {
|
||
f->b0 = 0.001837f;
|
||
f->b1 = 0.003674f;
|
||
f->b2 = 0.001837f;
|
||
f->a1 = -1.822623f;
|
||
f->a2 = 0.830472f;
|
||
f->x1 = f->x2 = f->y1 = f->y2 = 0.0f;
|
||
}
|
||
|
||
float IIR_Process(IIRFilter_t *f, float x) {
|
||
float y = f->b0 * x + f->b1 * f->x1 + f->b2 * f->x2
|
||
- f->a1 * f->y1 - f->a2 * f->y2;
|
||
f->x2 = f->x1;
|
||
f->x1 = x;
|
||
f->y2 = f->y1;
|
||
f->y1 = y;
|
||
return y;
|
||
}
|
||
|
||
// 实际使用时,对 ΔR 和 ΔL 各用一个 IIR 滤波器
|
||
IIRFilter_t iir_dR, iir_dL;
|
||
|
||
float filtered_dR = IIR_Process(&iir_dR, raw_dR);
|
||
float filtered_dL = IIR_Process(&iir_dL, raw_dL);
|
||
```
|
||
|
||
### 5.3 降采样
|
||
|
||
滤波后数据从 1kHz 降到 100Hz(每 10ms 处理一次):
|
||
|
||
```c
|
||
// 每 10ms 执行一次(由定时器触发)
|
||
void ProcessLoop_100Hz(void) {
|
||
// AD8302 读取已经在 1kHz ISR 中完成
|
||
// 这里处理降采样后的数据
|
||
|
||
float dR_raw = latest_dR; // 最近一次 1kHz 计算的 ΔR
|
||
float dL_raw = latest_dL; // 最近一次 1kHz 计算的 ΔL
|
||
|
||
// IIR 滤波
|
||
float dR_filt = IIR_Process(&iir_dR, dR_raw);
|
||
float dL_filt = IIR_Process(&iir_dL, dL_raw);
|
||
|
||
// 基线跟踪
|
||
BaselineTrack(dR_filt, dL_filt);
|
||
|
||
// 触发判决
|
||
TriggerDecision(dR_filt - baseline_dR, dL_filt - baseline_dL);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 六、基线跟踪算法(详细实现)
|
||
|
||
### 6.1 双基线结构
|
||
|
||
维护两组基线:**快基线**(快速恢复)和**慢基线**(温漂跟踪)。
|
||
|
||
```c
|
||
typedef struct {
|
||
// 快基线(车辆离开后快速恢复)
|
||
float dR_fast; // ΔR 快基线值
|
||
float dL_fast; // ΔL 快基线值
|
||
uint32_t cooldown; // 离开计数器
|
||
|
||
// 慢基线(温度漂移跟踪)
|
||
float dR_slow; // ΔR 慢基线值
|
||
float dL_slow; // ΔL 慢基线值
|
||
|
||
// 参数
|
||
float alpha_fast; // 快基线学习率(0.01)
|
||
float alpha_slow; // 慢基线学习率(0.0005)
|
||
uint32_t cooldown_max; // 冷却周期数(100 = 1s @ 100Hz)
|
||
|
||
// 输出
|
||
float dR_baseline; // 当前使用的基线值
|
||
float dL_baseline;
|
||
} BaselineTracker_t;
|
||
|
||
void Baseline_Init(BaselineTracker_t *bt) {
|
||
bt->dR_fast = bt->dR_slow = bt->dR_baseline = 0.0f;
|
||
bt->dL_fast = bt->dL_slow = bt->dL_baseline = 0.0f;
|
||
bt->cooldown = 0;
|
||
bt->alpha_fast = 0.01f;
|
||
bt->alpha_slow = 0.0005f;
|
||
bt->cooldown_max = 100; // 1 秒冷却
|
||
}
|
||
|
||
void Baseline_Track(BaselineTracker_t *bt,
|
||
float dR, float dL,
|
||
uint8_t car_present) {
|
||
|
||
if (car_present) {
|
||
// 有车 → 冻结所有基线
|
||
bt->cooldown = 0;
|
||
return;
|
||
}
|
||
|
||
if (bt->cooldown < bt->cooldown_max) {
|
||
// 冷却期(车辆刚离开,可能有拖尾→基线冻结)
|
||
bt->cooldown++;
|
||
|
||
// 快基线快速恢复(每步 ~1% 向当前值靠近)
|
||
bt->dR_fast += bt->alpha_fast * (dR - bt->dR_fast);
|
||
bt->dL_fast += bt->alpha_fast * (dL - bt->dL_fast);
|
||
return;
|
||
}
|
||
|
||
// 冷却结束 → 确认无车
|
||
|
||
// 快基线保持与当前值一致,准备下次车辆到来时快速恢复
|
||
bt->dR_fast = dR;
|
||
bt->dL_fast = dL;
|
||
|
||
// 慢基线慢速跟踪温漂
|
||
bt->dR_slow += bt->alpha_slow * (dR - bt->dR_slow);
|
||
bt->dL_slow += bt->alpha_slow * (dL - bt->dL_slow);
|
||
|
||
// 输出基线 = 慢基线(温漂补偿后的真实空载值)
|
||
bt->dR_baseline = bt->dR_slow;
|
||
bt->dL_baseline = bt->dL_slow;
|
||
}
|
||
```
|
||
|
||
### 6.2 快速恢复机制(车辆离开后)
|
||
|
||
车辆离开瞬间,由于变压器磁芯剩磁、放大器偏置恢复等效应,信号可能需 10~50ms 才能回到真实空载值。
|
||
|
||
```c
|
||
// 快速恢复状态机
|
||
// 车辆刚离开 → "预恢复"状态(50ms)
|
||
// → 使用快基线
|
||
// → "稳定"状态(500ms 后)
|
||
// → 切换到慢基线
|
||
|
||
typedef enum {
|
||
BASELINE_STABLE, // 稳态(使用慢基线)
|
||
BASELINE_RECOVER, // 预恢复(使用快基线)
|
||
} BaselineState_t;
|
||
|
||
// 在触发判决中:
|
||
// 检测到车辆离开事件:
|
||
if (just_left) {
|
||
bt->cooldown = 0;
|
||
bt->state = BASELINE_RECOVER;
|
||
bt->recover_timer = 50; // 50 个 10ms 周期 = 500ms
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 七、触发判决算法(详细实现)
|
||
|
||
### 7.1 多特征模糊触发
|
||
|
||
```c
|
||
// ============================================================
|
||
// 触发判决参数
|
||
// ============================================================
|
||
typedef struct {
|
||
// 原始阈值
|
||
float dR_trigger; // ΔR 触发阈值(Ω) 推荐:0.005~0.015
|
||
float dL_trigger; // ΔL 触发阈值(μH) 推荐:0.1~0.3
|
||
float dR_release; // ΔR 释放阈值(Ω) 推荐:dR_trigger × 0.6
|
||
float dL_release; // ΔL 释放阈值(μH) 推荐:dL_trigger × 0.6
|
||
|
||
// 微分阈值
|
||
float dRdt_trigger; // ΔR 变化率阈值(Ω/s)
|
||
float dLdt_trigger; // ΔL 变化率阈值(μH/s)
|
||
|
||
// 模糊权重
|
||
float w_dR; // ΔR 权重 推荐:0.4~0.6
|
||
float w_dL; // ΔL 权重 推荐:0.2~0.3
|
||
float w_dRdt; // 微分权重 推荐:0.1~0.2
|
||
float w_noise; // 噪声自适应 推荐:0.05~0.1
|
||
|
||
// 时间参数
|
||
uint32_t debounce_ms; // 去抖时间(ms) 推荐:30~50
|
||
uint32_t hold_ms; // 存在保持时间(ms)推荐:2000~5000
|
||
|
||
// 自适应参数
|
||
float noise_floor_R; // ΔR 噪声本底(滚动 1s RMS)
|
||
float noise_floor_L; // ΔL 噪声本底
|
||
} TriggerParam_t;
|
||
|
||
// ============================================================
|
||
// 触发判决状态机
|
||
// ============================================================
|
||
typedef enum {
|
||
STATE_IDLE, // 空闲
|
||
STATE_PRE_TRIGGER, // 预触发(去抖确认中)
|
||
STATE_TRIGGERED, // 已触发(有车)
|
||
STATE_HOLD, // 保持(存在定时器)
|
||
} TriggerState_t;
|
||
|
||
typedef struct {
|
||
TriggerState_t state;
|
||
TriggerParam_t param;
|
||
|
||
// 去抖计数器
|
||
uint32_t debounce_count;
|
||
|
||
// 存在保持定时器
|
||
uint32_t hold_remaining; // 剩余保持时间(ms)
|
||
|
||
// 输出
|
||
uint8_t car_present; // 当前是否有车
|
||
uint32_t car_count; // 累计车辆计数
|
||
|
||
// 噪声估计窗口
|
||
float noise_buffer[100]; // 100 点循环缓冲
|
||
uint8_t noise_idx;
|
||
} TriggerEngine_t;
|
||
|
||
// ============================================================
|
||
// 核心触发判决(每 10ms 执行一次)
|
||
// ============================================================
|
||
void Trigger_Process(TriggerEngine_t *te,
|
||
float dR, // 相对基线的 ΔR
|
||
float dL, // 相对基线的 ΔL
|
||
float dRdt, // ΔR 变化率(Ω/s)
|
||
float dLdt // ΔL 变化率(μH/s)
|
||
) {
|
||
// Step 1: 更新噪声本底(仅在无车时)
|
||
if (te->state == STATE_IDLE) {
|
||
te->noise_buffer[te->noise_idx] = fmaxf(fabsf(dR), fabsf(dL));
|
||
te->noise_idx = (te->noise_idx + 1) % 100;
|
||
|
||
// 每 100 点更新一次 RMS 噪声
|
||
if (te->noise_idx == 0) {
|
||
float sum_sq = 0;
|
||
for (int i = 0; i < 100; i++)
|
||
sum_sq += te->noise_buffer[i] * te->noise_buffer[i];
|
||
te->param.noise_floor_R = sqrtf(sum_sq / 100.0f);
|
||
te->param.noise_floor_L = te->param.noise_floor_R; // 近似
|
||
}
|
||
}
|
||
|
||
// Step 2: 计算触发置信度
|
||
float conf;
|
||
switch (te->state) {
|
||
|
||
case STATE_IDLE:
|
||
// 模糊逻辑触发判决
|
||
conf = te->param.w_dR * (dR / te->param.dR_trigger)
|
||
+ te->param.w_dL * (dL / te->param.dL_trigger)
|
||
+ te->param.w_dRdt * (fmaxf(dRdt, dLdt) / te->param.dRdt_trigger)
|
||
- te->param.w_noise * (te->param.noise_floor_R / te->param.dR_trigger);
|
||
|
||
// 归一化触发条件
|
||
if (conf > 1.0f) {
|
||
te->debounce_count++;
|
||
if (te->debounce_count >= te->param.debounce_ms / 10) {
|
||
// 去抖确认 → 进入触发状态
|
||
te->state = STATE_TRIGGERED;
|
||
te->debounce_count = 0;
|
||
te->car_present = 1;
|
||
te->car_count++;
|
||
te->hold_remaining = te->param.hold_ms;
|
||
}
|
||
} else {
|
||
te->debounce_count = 0; // 重置去抖
|
||
}
|
||
break;
|
||
|
||
case STATE_TRIGGERED:
|
||
// 有车持续中
|
||
te->hold_remaining = te->param.hold_ms; // 持续刷新保持定时器
|
||
|
||
// 释放条件:两个特征都低于释放阈值
|
||
if (dR < te->param.dR_release && dL < te->param.dL_release) {
|
||
te->debounce_count++;
|
||
if (te->debounce_count >= te->param.debounce_ms / 10) {
|
||
// 确认释放 → 进入保持状态
|
||
te->state = STATE_HOLD;
|
||
te->debounce_count = 0;
|
||
}
|
||
} else {
|
||
te->debounce_count = 0;
|
||
}
|
||
break;
|
||
|
||
case STATE_HOLD:
|
||
// 存在保持状态(覆盖车底缝隙)
|
||
te->hold_remaining -= 10; // 每 10ms 递减
|
||
|
||
// 如果信号重新出现(车辆还在)
|
||
if (dR > te->param.dR_trigger || dL > te->param.dL_trigger) {
|
||
te->state = STATE_TRIGGERED;
|
||
te->hold_remaining = te->param.hold_ms;
|
||
break;
|
||
}
|
||
|
||
// 如果保持定时器超时
|
||
if (te->hold_remaining <= 0) {
|
||
te->state = STATE_IDLE;
|
||
te->car_present = 0;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
```
|
||
|
||
### 7.2 车速估算与谷底检测
|
||
|
||
```c
|
||
// ============================================================
|
||
// 车速估算(基于同一线圈前/后沿时差)
|
||
// ============================================================
|
||
typedef struct {
|
||
float rise_time; // 上升沿 10%~90% 时间(ms)
|
||
float fall_time; // 下降沿 90%~10% 时间(ms)
|
||
float peak_dR; // 本次车辆 ΔR 峰值
|
||
float peak_dL; // 本次车辆 ΔL 峰值
|
||
float speed_est; // 估计车速(km/h)
|
||
} VehicleProfile_t;
|
||
|
||
VehicleProfile_t vprof;
|
||
|
||
// 在触发上升/下降沿时调用
|
||
void OnTriggerRising(void) {
|
||
vprof.rise_time_start = current_time_ms;
|
||
}
|
||
|
||
void OnTriggerFullyOn(void) {
|
||
vprof.rise_time = current_time_ms - vprof.rise_time_start;
|
||
}
|
||
|
||
void OnTriggerLeaving(void) {
|
||
vprof.fall_time_start = current_time_ms;
|
||
}
|
||
|
||
void OnTriggerFullyOff(void) {
|
||
vprof.fall_time = current_time_ms - vprof.fall_time_start;
|
||
|
||
// 车速估计(基于已知线圈长度和上升沿时间)
|
||
// 假设车辆有效耦合长度 ≈ 线圈长度 + 轴距/2
|
||
float effective_len = coil_length + wheelbase_est / 2.0f;
|
||
vprof.speed_est = effective_len / vprof.rise_time * 3.6f; // m/s → km/h
|
||
}
|
||
|
||
// ============================================================
|
||
// 谷底检测(前车/后车区分)
|
||
// ============================================================
|
||
typedef enum {
|
||
GAP_NONE, // 无间隙
|
||
GAP_SMALL, // 底盘间隙(属于同一辆车)
|
||
GAP_VALLEY, // 车缝(两车之间)
|
||
} GapType_t;
|
||
|
||
GapType_t DetectGap(float dR, float dL) {
|
||
static float dR_prev = 0, dL_prev = 0;
|
||
static uint32_t valley_ms = 0;
|
||
static uint8_t in_valley = 0;
|
||
|
||
// 微分判断是否下降
|
||
float dR_dot = dR - dR_prev;
|
||
float dL_dot = dL - dL_prev;
|
||
dR_prev = dR;
|
||
dL_prev = dL;
|
||
|
||
// 检测谷底:dR_dot 从负转正(由降到升)
|
||
if (!in_valley && dR_dot < 0 && dR < dR_trigger * 0.5f) {
|
||
in_valley = 1;
|
||
valley_ms = 0;
|
||
}
|
||
|
||
if (in_valley) {
|
||
valley_ms += 10; // 每 10ms
|
||
|
||
// 信号回升 → 谷底结束
|
||
if (dR_dot > 0 && dR > dR_trigger * 0.3f) {
|
||
in_valley = 0;
|
||
|
||
if (valley_ms > 200) { // 车缝 > 200ms
|
||
return GAP_VALLEY; // 判为前后两车
|
||
} else {
|
||
return GAP_SMALL; // 判为底盘间隙
|
||
}
|
||
}
|
||
|
||
// 谷底持续太深 → 车辆离开
|
||
if (dR < dR_release && valley_ms > 500) {
|
||
in_valley = 0;
|
||
return GAP_VALLEY;
|
||
}
|
||
}
|
||
|
||
return GAP_NONE;
|
||
}
|
||
```
|
||
|
||
### 7.3 IQ 平面可视化输出(调试用)
|
||
|
||
开发调试阶段,可将 IQ 轨迹输出到串口:
|
||
|
||
```
|
||
每 10ms 输出一行 CSV:
|
||
t_ms, dR_mOhm, dL_uH
|
||
|
||
用串口工具接收后可直接绘图:
|
||
Excel、Python matplotlib、SerialPlot 等
|
||
```
|
||
|
||
**典型波形解读:**
|
||
|
||
```
|
||
纯钢制车通过的 IQ 轨迹:
|
||
|
||
dR (mΩ) │ · ← 前轮
|
||
15 │ · ← 钢底盘主体
|
||
10 │ ·
|
||
5 │ ·
|
||
0 └──────────────────
|
||
0 0.5 1.0 1.5 2.0 dL (μH)
|
||
|
||
全铝制车通过的 IQ 轨迹:
|
||
|
||
dR (mΩ) │
|
||
30 │ · ← 铝底盘主体
|
||
20 │ ·
|
||
10 │
|
||
0 └──────────────────
|
||
0 0.2 0.4 0.6 0.8 dL (μH)
|
||
|
||
钢铝混合车通过的 IQ 轨迹(典型对角线分布):
|
||
|
||
dR (mΩ) │
|
||
25 │ · ← 铝蒙皮 + 钢骨架
|
||
20 │ ·
|
||
15 │ ·
|
||
10 │ ·
|
||
5 │
|
||
0 └──────────────────
|
||
0 0.5 1.0 1.5 2.0 dL (μH)
|
||
```
|
||
|
||
---
|
||
|
||
## 八、纯 MCU 数字 IQ 解调方案(备选,零硬件解调器成本)
|
||
|
||
如果不用 AD8302,可直接用 MCU 内置 ADC 采样信号,在软件中做数字 IQ 解调。
|
||
|
||
### 8.1 采样要求
|
||
|
||
```
|
||
系统参数:
|
||
激励频率:f₀ = 120kHz
|
||
ADC 采样率:f_s = 960kHz(8 倍过采样,每个周期 8 个点)
|
||
采样方式:同步触发(TIM 与 DDS 同步)
|
||
|
||
同时采集两个通道(参考 + 测量):
|
||
ADC1: 参考信号 v_ref(t)
|
||
ADC2: 测量信号 v_meas(t)
|
||
|
||
每个激励周期采集 8 点,连续采集 N 个周期:
|
||
数据块大小:8 × N 点(N 通常取 50~100)
|
||
```
|
||
|
||
### 8.2 数字 IQ 解调算法
|
||
|
||
```c
|
||
// ============================================================
|
||
// 数字 IQ 解调
|
||
// 频率 f₀ = 120kHz, f_s = 960kHz
|
||
// 每个周期 8 个点,窗口长度 100 个周期 = 800 个采样点
|
||
// ============================================================
|
||
|
||
#define F0 120000.0f // 激励频率 (Hz)
|
||
#define FS 960000.0f // 采样率 (Hz)
|
||
#define N_CYCLES 100 // 窗口周期数
|
||
#define N_SAMPS (N_CYCLES * 8) // 总采样点数
|
||
|
||
// 预计算的正交参考表(在 init 中计算一次)
|
||
float cos_table[N_SAMPS];
|
||
float sin_table[N_SAMPS];
|
||
|
||
void Init_DigitalDemod(void) {
|
||
for (int i = 0; i < N_SAMPS; i++) {
|
||
float t = (float)i / FS;
|
||
cos_table[i] = cosf(2.0f * PI * F0 * t);
|
||
sin_table[i] = sinf(2.0f * PI * F0 * t);
|
||
}
|
||
}
|
||
|
||
// 数字 IQ 解调:输入两个通道的原始采样数据
|
||
void DigitalDemod(
|
||
float *v_ref, // 参考通道采样数组 [N_SAMPS]
|
||
float *v_meas, // 测量通道采样数组 [N_SAMPS]
|
||
float *I_out, // 输出:I 分量(同相)
|
||
float *Q_out // 输出:Q 分量(正交)
|
||
) {
|
||
float I_ref = 0, Q_ref = 0;
|
||
float I_meas = 0, Q_meas = 0;
|
||
|
||
for (int i = 0; i < N_SAMPS; i++) {
|
||
// 参考通道
|
||
I_ref += v_ref[i] * cos_table[i];
|
||
Q_ref += v_ref[i] * sin_table[i];
|
||
|
||
// 测量通道
|
||
I_meas += v_meas[i] * cos_table[i];
|
||
Q_meas += v_meas[i] * sin_table[i];
|
||
}
|
||
|
||
// 归一化(除以窗口长度)
|
||
float inv_N = 1.0f / (float)N_SAMPS;
|
||
I_ref *= inv_N; Q_ref *= inv_N;
|
||
I_meas *= inv_N; Q_meas *= inv_N;
|
||
|
||
// 计算相对幅度比和相位差
|
||
float mag_ref = sqrtf(I_ref * I_ref + Q_ref * Q_ref);
|
||
float mag_meas = sqrtf(I_meas * I_meas + Q_meas * Q_meas);
|
||
float phase_ref = atan2f(Q_ref, I_ref);
|
||
float phase_meas = atan2f(Q_meas, I_meas);
|
||
|
||
// 输出
|
||
*I_out = mag_meas / mag_ref; // 归一化幅度
|
||
*Q_out = phase_meas - phase_ref; // 相位差
|
||
}
|
||
|
||
// 注意:
|
||
// 数字 IQ 方法的刷新率 = f₀ / N_CYCLES = 120kHz / 100 = 1.2kHz
|
||
// 可调 N_CYCLES 平衡 SNR 和响应速度
|
||
// N_CYCLES = 100 → 1.2kHz, SNR 高, 响应慢
|
||
// N_CYCLES = 20 → 6kHz, SNR 低, 响应快
|
||
```
|
||
|
||
### 8.3 AD8302 vs 纯MCU 方案对比
|
||
|
||
| 对比项 | AD8302 方案 | 纯 MCU 数字 IQ |
|
||
|--------|------------|---------------|
|
||
| BOM 成本增量 | +¥8~12 | 零 |
|
||
| ΔR 分辨率 | 0.01~0.03Ω | 0.02~0.05Ω |
|
||
| 刷新率 | 1kHz | 0.2~6kHz(可调) |
|
||
| MCU 算力占用 | 低(~5% CPU) | 中高(~20~40% CPU) |
|
||
| 抗混叠要求 | 外置 LPF | 外置抗混叠 LPF(更关键) |
|
||
| 开发难度 | 中(硬件+标定) | 高(采样时序+实时性) |
|
||
| 调试便利性 | 直观(示波器看 V_MAG/PHS) | 黑盒(全在代码里) |
|
||
|
||
**推荐:** 量产产品首选 AD8302 方案,开发验证阶段可用纯 MCU 方案快速原型。
|
||
|
||
---
|
||
|
||
## 九、调试与诊断接口
|
||
|
||
### 9.1 实时数据输出(UART)
|
||
|
||
```c
|
||
// 调试输出格式(CSV,115200bps)
|
||
// 每 10ms 输出一行:
|
||
|
||
void DebugOutput(float dR_mOhm, float dL_uH,
|
||
float dR_baseline, float dL_baseline,
|
||
uint8_t car_state, float temp_C) {
|
||
printf("%lu,%0.2f,%0.3f,%0.2f,%0.3f,%d,%0.1f\n",
|
||
HAL_GetTick(),
|
||
dR_mOhm, // ΔR 单位 mΩ
|
||
dL_uH, // ΔL 单位 μH
|
||
dR_baseline, // 基线 ΔR 单位 mΩ
|
||
dL_baseline, // 基线 ΔL 单位 μH
|
||
car_state, // 0=无车 1=有车
|
||
temp_C // 温度 °C
|
||
);
|
||
}
|
||
```
|
||
|
||
### 9.2 AD8302 自检
|
||
|
||
上电后检查 AD8302 是否正常工作:
|
||
|
||
```c
|
||
uint8_t AD8302_SelfTest(void) {
|
||
// 1. 检查 V_MAG 和 V_PHS 是否在 0~1.8V 范围内
|
||
float V_MAG = ReadAD8302_MAG();
|
||
float V_PHS = ReadAD8302_PHS();
|
||
|
||
if (V_MAG < 0.1f || V_MAG > 1.7f) return 0; // 异常
|
||
if (V_PHS < 0.1f || V_PHS > 1.7f) return 0; // 异常
|
||
|
||
// 2. 检查 VREF 输出是否为 1.8V ±5%
|
||
float VREF = ReadAD8302_VREF();
|
||
if (VREF < 1.71f || VREF > 1.89f) return 0;
|
||
|
||
// 3. 计算瞬间信噪比
|
||
float mag_stddev = EstimateNoise(V_MAG, 100); // 100 点
|
||
if (mag_stddev > 0.01f) return 0; // 噪声过大
|
||
|
||
return 1; // 自检通过
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 十、性能验证与调参指引
|
||
|
||
### 10.1 现场调试步骤
|
||
|
||
```
|
||
Step 1:空载标定
|
||
- 线圈周围无金属
|
||
- 采集 V_MAG₀, V_PHS₀
|
||
- 计算噪声本底 σ_MAG, σ_PHS
|
||
|
||
Step 2:模拟车辆(铁板测试)
|
||
- 1m×1m 钢板置于线圈正上方
|
||
- 记录 ΔR、ΔL 变化量
|
||
- 验证触发判决是否正常工作
|
||
|
||
Step 3:铝板测试
|
||
- 1m×1m 铝板(3~5mm厚)
|
||
- 验证 ΔR 信号是否足够大
|
||
- 调整模糊权重使铝板可靠触发
|
||
|
||
Step 4:实车测试
|
||
- 纯钢、钢铝混合、全铝车辆各 10 次通过
|
||
- 统计触发成功率和响应时间
|
||
|
||
Step 5:环境测试
|
||
- 高温/低温(恒温箱)
|
||
- 喷水模拟雨天
|
||
- 验证基线和补偿算法
|
||
```
|
||
|
||
### 10.2 参数调优速查
|
||
|
||
| 现象 | 可能原因 | 调整方向 |
|
||
|------|---------|---------|
|
||
| 铝车漏触发 | ΔR 阈值太高 | 降低 dR_trigger(如 0.015→0.008Ω) |
|
||
| 温漂误触发 | 基线跟踪太慢 | 增大 alpha_slow |
|
||
| 雨天后频繁误触发 | 积水漂移 | 增大上升沿速率阈值 |
|
||
| 车辆离开后长时间保持"有车" | 存在定时器太长 | 减小 hold_ms |
|
||
| 跟车计数不足 | 车缝谷底判断太严 | 减小 T_gap_min |
|
||
| 高海拔频繁异常触发 | 匝间放电 | 降低激励电压 / 增强绝缘 |
|
||
|
||
---
|
||
|
||
## 文档结束
|