嵌入式面试真题第 07 题:电源纹波影响 ADC 电量采样的软件滤波

在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
11
12
flowchart LR
A[电池/分压网络] --> B[ADC 原始采样]
P[DC-DC/PWM/PA/射频负载] -->|周期纹波与瞬态压降| B
V[内部 Vref/Bandgap] --> C[参考电压修正]
B --> D[同步采样与过采样]
D --> E[异常点剔除]
E --> F[均值/中值/低通/陷波]
C --> F
L[负载、温度、充放电状态] --> G[负载感知补偿]
F --> G
G --> H[电压慢变量估计]
H --> I[SOC 映射、滞回与 UI 状态机]

问题

硬件工程师告诉你,当前版图的电源纹波太大,可能会影响 ADC 电池电量采样精度。作为软件工程师,在硬件已经无法改版的前提下,你能通过哪些纯软件或数字滤波手段,最大限度地剔除这部分周期性电源噪声?

除了列出滤波算法,还需要说明:如何判断噪声来自 ADC 参考电压、模拟输入、电源地弹、DC-DC/PWM 开关、功放负载还是射频发射;采样时刻和采样率应如何选择;过采样、中值、均值、低通和陷波分别适合什么场景;如何处理负载引起的真实电压跌落;如何避免电量百分比抖动;以及如何在 MCU、DMA 和 RTOS 环境下落地实现并验证效果。

回答

结论:软件不能把已经污染、削顶或混叠后的 ADC 信号恢复成“真实无噪声电压”,也不能替代缺失的去耦、电源隔离、参考源和模拟前端设计。但软件可以把问题拆成“采样避噪、参考修正、稳健去异常、周期噪声抑制、负载状态补偿、慢变量估计、显示状态机”七层处理,从而显著降低周期纹波、偶发尖峰和负载瞬态对电量估算的影响。

最有效的方案通常不是单独使用某个滤波器,而是组合链路:先用定时器或 PWM 事件做同步采样,在开关噪声较小的相位窗口内通过 ADC+DMA 采集 16~64 点;再做饱和检查、去极值或中值/Hampel 异常点剔除;随后进行分组平均或一阶 IIR 低通;如果确认存在稳定且落入采样带宽内的固定频率纹波,再增加窄带陷波;同时采内部 Vref/Bandgap 修正 VDDA 波动;最后根据功放、无线发射、背光、马达、充电和温度状态做负载感知补偿,并通过充放电状态机、滞回、限速和时间确认更新电量百分比。

电池电量是秒级到分钟级变化的慢变量,因此不应让单次 ADC 原始值直接驱动 UI。软件真正需要估计的是“在已知温度和负载条件下的电池稳态电压或等效开路电压”,而不是某一个瞬间的端电压。

总体处理架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
flowchart TD
A[ADC Timer Trigger] --> B[ADC + DMA Raw Buffer]
B --> C{原始数据合法?}
C -->|越界/饱和/通道异常| X[记录诊断并丢弃窗口]
C -->|合法| D[同步相位筛选或整周期分组]
D --> E[中值/Hampel/去极值]
E --> F[均值/Trimmed Mean]
F --> G[VDDA/Vref 修正]
G --> H{是否存在稳定窄带纹波?}
H -->|是| I[Notch/Biquad]
H -->|否| J[跳过窄带滤波]
I --> K[IIR/FIR 慢低通]
J --> K
K --> L[负载与温度补偿]
L --> M[OCV/SOC 映射]
M --> N[充放电状态机 + 滞回 + 更新限速]
N --> O[电量百分比/UI/告警]

这一架构刻意把“原始采样值”“滤波电压”“补偿后电压”“最终 SOC”分成不同层级。工程上必须分别保存和观测这些量,否则当电量显示异常时,很难判断是 ADC 采样、滤波参数、参考电压、负载补偿还是 SOC 曲线的问题。

建议至少保留以下诊断量:

诊断量 含义 主要用途
adc_raw_min/max/mean 一个采样窗口内的原始统计值 判断饱和、尖峰、纹波幅度和 ADC 稳定性。
vdda_mv 通过内部参考估算的模拟电源电压 判断 ADC 参考是否跟随电源波动。
vbat_instant_mv 窗口稳健统计后的瞬时电池电压 判断短时负载和周期噪声。
vbat_filtered_mv 低通后的慢变量 作为负载补偿输入或 UI 基础值。
vbat_ocv_est_mv 估算的等效开路电压 映射 SOC。
load_state 当前负载状态 解释瞬态压降并选择不同参数。
filter_reject_count 被判定为异常点的数量 监测噪声恶化、算法过严或采样窗口不合理。
soc_display 最终显示电量 验证状态机、滞回和限速。

先判断“纹波”到底进入了哪一条路径

在写滤波器之前,必须先建立噪声路径模型。示波器上看到电源有纹波,并不等价于 ADC 结果中的误差只来自电源引脚。对于 MCU 内置 SAR ADC,常见耦合路径至少包括模拟输入、参考电压、模拟电源、地、电阻分压网络、采样保持电容充电、数字开关噪声和负载导致的真实电池端电压变化。

1
2
3
4
5
6
7
8
9
10
11
flowchart LR
BAT[电池端电压] --> DIV[电阻分压/RC]
DIV --> AIN[ADC 模拟输入]
SW[DC-DC/PWM 开关节点] -->|电场/磁场/地阻抗耦合| AIN
SW --> AVDD[AVDD/VDDA]
GND[大电流回流/地弹] --> AIN
GND --> AGND[ADC 地参考]
AVDD --> REF[ADC 参考电压]
REF --> ADC[ADC 转换核心]
AIN --> ADC
CPU[CPU/DMA/GPIO/RF 数字活动] -->|基底/电源耦合| ADC

可以把一次采样近似写成:

1
2
3
4
5
6
7
x[n] = Vbat_true[n]
+ eripple[n]
+ eload[n]
+ eref[n]
+ espike[n]
+ equant[n]
+ eoffset[n]

其中:

  • Vbat_true[n] 是真实电池端电压,它本身会随 SOC、温度、充放电电流和电芯内阻变化。
  • eripple[n] 是开关电源、PWM、功放或数字活动形成的周期干扰。
  • eload[n] 是真实负载电流引起的端电压跌落,不应简单当成噪声全部滤掉。
  • eref[n] 是 ADC 参考电压或 VDDA 波动导致的比例误差。
  • espike[n] 是开关边沿、射频发射、ESD、继电器或 GPIO 群切换产生的孤立尖峰。
  • equant[n] 是量化噪声及转换器本底噪声。
  • eoffset[n] 是偏置、增益、温漂、分压电阻误差和校准残差。

这几类误差的统计特征不同,不能只靠“加一个平均滤波”统一处理。

推荐的定位顺序

  1. 同时记录 ADC 原始码、内部 Vref 码、负载状态和触发相位。
  2. 用示波器分别测量电池分压输入、VDDA/VREF、ADC 地附近和主要开关节点。
  3. 对 ADC 原始序列做时域波形和频谱分析,确认主频、谐波、尖峰密度和低频包络。
  4. 改变采样率或采样相位,观察干扰频率是否发生混叠或幅值变化。
  5. 分别关闭 PWM、功放、无线发射、背光和充电,做 A/B 对比。
  6. 用已知稳定电压源替代电池分压输入,判断问题位于输入源还是 ADC 参考/电源路径。
  7. 对比固定相位采样、整周期均匀采样和自由运行采样的结果。

典型现象与判断

现象 更可能的原因 软件侧验证办法
ADC 原始值与内部 Vref 码反向同步波动 VDDA/VREF 变化导致比例误差 同时采 Vref 并换算 VDDA,修正前后比较。
ADC 波形与 PWM 周期严格同步 PWM/DC-DC 周期耦合 改触发相位或关 PWM 对比。
只在 PA 大音量、RF 发射或马达启动时下跌 真实负载压降叠加地弹 结合负载状态和电流数据判断恢复时间。
少量极端高低码、其余样本稳定 开关尖峰、通道切换或 ADC 采样未稳定 中值/Hampel 有效;延长采样时间并丢弃首样本。
改变采样率后低频拍频发生变化 高频纹波混叠到低频 重新选择采样率、模拟 RC 或同步触发。
不同 ADC 通道之间互相串扰 高源阻抗、采样保持电容未充分充电 增大采样时间,通道切换后丢弃首样本。

采样策略比滤波器更优先

周期性噪声最有效的处理往往发生在采样之前。若采样时刻总落在开关边沿附近,后端再复杂的滤波器也只能处理已经被污染的数据;如果采样频率与纹波频率形成不利的相位关系,还可能把高频纹波混叠为难以滤除的低频漂移。

同步采样:在“安静相位”取样

如果噪声源是可控的 PWM、DC-DC、音频 Class-D、LED 调光或系统周期任务,应尽量使用硬件事件链:定时器/PWM 比较事件触发 ADC,ADC 通过 DMA 搬运结果,CPU 只处理完成后的数据窗口。这样可减小 RTOS 调度和中断延迟造成的采样相位抖动。

1
2
3
4
5
6
7
8
9
10
11
12
13
sequenceDiagram
participant PWM as PWM/DC-DC 周期
participant TIM as Timer Compare
participant ADC as ADC
participant DMA as DMA
participant Task as Filter Task

PWM->>PWM: 开关边沿/高噪声区
PWM->>TIM: 进入稳定平台
TIM->>ADC: 硬件触发转换
ADC->>DMA: 写入原始样本
DMA->>Task: 半满/满缓冲事件
Task->>Task: 稳健统计、Vref 修正和低通

同步采样有两种目标,不能混淆:

  • 若目标是获得“每个开关周期中噪声最小的瞬时值”,应固定在安静相位采样。
  • 若目标是获得“一个完整纹波周期的平均电压”,应在一个或多个完整周期内均匀布点并平均。

固定相位采样并不一定等于平均电池电压。例如纹波是近似三角波,固定在谷值附近会形成稳定但有偏的结果。因此需要先定义测量目标,再选择相位。

整周期相干平均

已知纹波频率 fripple 时,可以在整数个纹波周期内采样并平均。理想情况下,周期干扰在完整周期上的正负分量相互抵消。设每周期采 N 点,累计 M 个完整周期,则窗口长度应满足:

1
2
Fs = N * fripple
Window = M * N samples

工程上允许存在频率误差,但窗口越长,对频率漂移越敏感。若 DC-DC 采用 PFM、跳脉冲或扩频模式,纹波不再是严格稳定的单频信号,此时固定周期相干平均效果会下降,应改用相位窗口、稳健统计和宽带低通组合。

避免混叠

若实际纹波频率为 fripple,采样率为 Fs,则采样后可能观察到的混叠频率近似为:

1
falias = |fripple - k * Fs|

其中 k 取使 falias 落入 [0, Fs/2] 的整数。一个 500 kHz 的开关纹波,可能因为低速采样而表现成几赫兹甚至更低的“电池缓慢波动”。这种伪低频一旦与 SOC 变化频段重叠,就很难靠普通低通区分。

因此应优先采用以下手段:

  1. 用 PWM/定时器硬件同步,避免随机相位混叠。
  2. 提高原始采样率,使纹波频率和谐波可被识别,再数字低通和抽取。
  3. 让采样窗口覆盖整数周期。
  4. 如果硬件已有输入 RC,确认其截止频率与采样策略匹配。
  5. 不要让低速周期采样恰好锁定在纹波的固定坏相位。

ADC 采样时间与高阻分压

电池检测常用较大的分压电阻以降低静态功耗,这会提高 ADC 输入源阻抗。SAR ADC 内部采样保持电容需要在有限采样时间内通过外部等效电阻充电;若采样时间过短,会出现增益误差、通道串扰和首样本偏差。软件可采取:

  • 选择更长的 ADC sample time。
  • 唤醒分压或切换通道后等待 RC 稳定。
  • 通道切换后丢弃第一次转换。
  • 连续采两次或多次,使用后续样本。
  • 先采低阻参考通道再采高阻通道时,检查残余电荷影响。

这类误差不是数字滤波能完全消除的;滤波只能让偏差更稳定,不能自动消除系统性偏差。

各类滤波算法如何选

过采样与平均

过采样是最基础的方法:在电池电压变化远慢于 ADC 采样速率时,短时间内采集多点并求平均,可以降低零均值、近似不相关噪声的方差。对于独立同分布噪声,N 点平均后的标准差理论上约降为原来的 1/sqrt(N)

1
y[k] = (x[kN] + x[kN+1] + ... + x[kN+N-1]) / N

但必须注意:

  • 固定相位的确定性纹波不会因为重复采相同相位而消失。
  • 相关噪声、低频漂移和参考电压变化不会按 1/sqrt(N) 理想下降。
  • 增加采样次数提高的是噪声统计意义上的有效分辨率,不会自动修正增益、偏置、参考误差和电阻容差。
  • 当输入噪声小到不足以跨越 ADC 量化码时,简单过采样也未必增加有效位数。

经典过采样理论常用“每增加 1 bit 有效分辨率,需要约 4 倍采样数”的近似,即增加 m bit 需要约 4^m 个样本。这一结论有前提:噪声应足以打散量化误差、样本之间不能高度相关,且后续要进行低通和抽取。不能把它当成所有 MCU ADC 都能无条件实现的精度承诺。

去最大最小值平均

对 16~64 点短窗口,去除若干最大值和最小值后求平均,是 MCU 上成本低、效果稳定的工程方法。它对少量正负尖峰比普通均值更稳健,又比完整排序的中值更能保留多点平均带来的降噪能力。

例如 32 点窗口可去除最大 2 点和最小 2 点,再对剩余 28 点求平均:

1
trimmed_mean = sum(sorted(x)[2:30]) / 28

实现时不一定需要全排序。若只去一个最大和一个最小,只需单次遍历统计 sum/min/max;若去多个极值,可使用小顶堆、大顶堆,或在样本数较小的情况下直接插入排序。

适用场景:

  • 窗口内大多数样本正常,仅少量毛刺。
  • MCU 算力有限,不希望运行复杂滤波。
  • 既希望抑制尖峰,又希望降低随机噪声。

不适用场景:

  • 纹波占据整个窗口且具有稳定周期。
  • 异常点比例过高。
  • 真实负载突变导致整段数据都发生跃迁。

中值滤波

中值滤波将窗口排序后取中间值:

1
y[n] = median{x[n-k], ..., x[n], ..., x[n+k]}

它对孤立脉冲和毛刺非常有效,边沿保持能力也优于均值滤波。常用窗口为 3、5、7 或 9 点。窗口过大将增加延迟,并把真实的电压阶跃压平。

推荐用法不是“只做大窗口中值”,而是先用 3 点或 5 点中值去尖峰,再进入均值或一阶 IIR:

1
Raw -> Median(3/5) -> Mean/Trimmed Mean -> IIR

Hampel 异常点检测

Hampel 滤波比简单中值更适合需要保留原值并输出异常标志的场景。它使用窗口中位数 m 和中位数绝对偏差 MAD

1
2
MAD = median(|xi - m|)
threshold = K * 1.4826 * MAD

|x[n] - m| > threshold 时,把该点标为异常,可替换成中位数或直接丢弃。系数 1.4826 用于在高斯分布假设下把 MAD 标度到标准差量级。嵌入式实现中不必追求统计学形式完全精确,可以固定窗口和阈值,并对 MAD=0 做保护。

Hampel 的优势是能输出 reject_count,便于诊断噪声是否突然恶化,而不是无声地把所有异常都平滑掉。

滑动平均与分组平均

长度为 N 的滑动平均:

1
y[n] = (x[n] + x[n-1] + ... + x[n-N+1]) / N

可用运行和优化:

1
2
sum[n] = sum[n-1] + x[n] - x[n-N]
y[n] = sum[n] / N

优点是实现简单、直流增益为 1、对高频噪声有明确抑制。缺点是需要保存窗口、产生约 (N-1)/2 样本群延迟,并在特定频率上形成梳状零点。若纹波频率恰好与窗口长度不匹配,抑制可能不稳定。

对电池电量采样,常见做法是“快速窗口分组平均 + 慢速 IIR”,而不是一个极大的滑动平均窗口:快速窗口负责原始噪声,慢速 IIR 负责 UI 稳定性。这样参数更容易解释和调试。

一阶 IIR 低通

一阶 IIR 是电量估计中最实用的滤波器之一:

1
y[n] = y[n-1] + alpha * (x[n] - y[n-1])

其中 alpha 越小,滤波越平滑、响应越慢。若采样周期为 Ts,希望近似时间常数为 tau,可取:

1
alpha = 1 - exp(-Ts / tau)

Ts << tau 时,可近似为:

1
alpha ≈ Ts / tau

例如每 100 ms 得到一个稳健电压值,希望时间常数约 5 s,则 alpha ≈ 0.02。固定点实现可把 alpha 表示为 Q15 或选成 1/2^k

1
y += (x - y) >> k

一阶 IIR 适合电池这种慢变量,但应根据状态动态调整时间常数:

状态 建议时间常数 原因
正常放电、轻载 5~20 s 抑制慢纹波和 UI 抖动。
重载瞬态 10~60 s 或暂缓更新 避免瞬时压降被误认为 SOC 快速下降。
充电开始/结束 2~10 s 并配合状态确认 需要响应状态变化,但避免充电纹波。
低电告警 可使用独立快速支路 不能因 UI 慢滤波而延误保护。
休眠唤醒 先快速重建,再切慢滤波 避免沿用过期历史值。

保护阈值和 UI 电量应使用不同滤波链。低压关机、安全保护需要较快、可证明的响应;UI 则允许更慢、更平滑。

FIR 低通

FIR 具有线性相位和天然稳定性,适合采样率固定、频率指标明确、算力允许的系统。可以用窗函数法设计低通,在高采样率下抑制开关纹波后再抽取。但对普通电池电量显示,FIR 往往不是首选,因为:

  • 需要较多系数和乘加运算。
  • 截止频率很低时,阶数可能较高。
  • 对频率漂移的窄带纹波不一定比同步采样更高效。
  • UI 电量并不要求严格线性相位。

FIR 更适合作为“高采样率 ADC 数据的抗混叠数字滤波 + 抽取”模块,而不是单纯为显示平滑堆叠大量 tap。

固定频率陷波

当频谱确认存在稳定的窄带周期干扰,并且该干扰未被混叠成不可辨识的低频时,可以使用二阶 IIR 陷波器。常见传递函数为:

1
2
3
             1 - 2*cos(w0)*z^-1 + z^-2
H(z) = --------------------------------------
1 - 2*r*cos(w0)*z^-1 + r^2*z^-2

其中:

1
2
w0 = 2*pi*f0/Fs
0 < r < 1

零点放在单位圆的 ±w0 处形成陷波,极点半径 r 控制带宽;r 越接近 1,陷波越窄,但定点量化和频率漂移敏感度也越高。对应差分方程为:

1
2
3
4
5
y[n] = x[n]
- 2*cos(w0)*x[n-1]
+ x[n-2]
+ 2*r*cos(w0)*y[n-1]
- r^2*y[n-2]

实际实现时通常需要做直流增益归一化、系数量化和内部累加位宽保护。

陷波适用条件:

  • 纹波主频稳定,采样率稳定。
  • 频率落在可数字处理的带宽内。
  • 频谱中有明显窄峰。
  • 不希望用很低的低通截止频率牺牲响应速度。

陷波不适用条件:

  • DC-DC 使用 PFM、跳脉冲或扩频,频率大幅漂移。
  • 干扰是宽带噪声或随机尖峰。
  • 采样率太低,纹波已混叠到接近直流。
  • 真实电池变化和干扰频率重叠。

不要仅凭“硬件说有 500 kHz 纹波”就在 10 Hz 的采样序列上设计 500 kHz 陷波。数字滤波器只能处理采样后实际存在于离散频谱中的频率;若 500 kHz 已混叠为 1 Hz,必须围绕 1 Hz 现象重新分析,但此时也可能误伤真实电池变化。

自适应陷波

若纹波频率缓慢漂移,可用频率估计加自适应陷波,例如基于 Goertzel/短时 FFT 估计主峰,再更新 w0。但在低成本 MCU 的电量采样里,自适应陷波通常不是第一选择,原因是参数稳定性、误锁定和验证成本较高。优先顺序应是:

  1. 硬件同步触发。
  2. 整周期平均或相位窗口。
  3. 稳健统计和低通。
  4. 固定陷波。
  5. 最后才考虑自适应陷波。

内部 Vref/Bandgap 修正

如果 ADC 参考电压来自 VDDA,而 VDDA 本身被 DC-DC 纹波或负载拉动,同一个电池分压输入会得到不同 ADC 码。很多 MCU 提供内部 bandgap/Vref 通道以及出厂校准值,可通过采内部参考估算当前 VDDA,再修正外部通道。

通用关系为:

1
2
ADC_code = Vin / Vref_actual * ADC_full_scale
Vin = ADC_code / ADC_full_scale * Vref_actual

若内部参考名义电压为 Vbg,其 ADC 码为 Code_bg

1
Vref_actual ≈ Vbg * ADC_full_scale / Code_bg

电池经分压比 Kdiv = (Rtop + Rbottom) / Rbottom 后:

1
Vbat ≈ Code_bat / ADC_full_scale * Vref_actual * Kdiv

对带出厂校准常量的 MCU,应使用芯片参考手册给出的公式和校准电压条件,不要把某一系列 MCU 的常量直接复制到另一系列。还要注意:内部参考本身有容差、温漂和启动稳定时间,它适合修正 VDDA 的相对变化,但不等于高精度外部基准。

推荐采样顺序:

  • 同一时间窗口内采电池通道和 Vref 通道。
  • Vref 通道启用后等待稳定。
  • 对 Vref 也做稳健平均,但不要过度延迟,使其无法跟踪同一窗口的 VDDA 波动。
  • Code_bg 设置合理范围,防止除零或异常值造成电压计算爆炸。
  • 使用 64 位中间量避免整数乘法溢出。

如果 ADC 输入和参考电压受到完全相同的比例变化,某些比例式测量可能天然抵消;但电池分压通常是绝对电压测量,仍需明确参考电压来源和公式。

负载感知补偿:不要把真实压降当噪声

播放大音量、低频增强、马达启动、背光升亮、蓝牙/Wi-Fi 发射或 CPU 突发运算时,电池端电压会因为电芯内阻、连接器、电源路径阻抗和保护器件压降而真实下降。简单低通只能把这一下降“延迟显示”,却不能判断它是 SOC 下降还是负载造成的暂态。

最简单的电池等效模型可写成:

1
Vterminal = OCV(SOC, T) - Iload * R0 - Vrc

其中:

  • OCV 是与 SOC 和温度相关的开路电压。
  • Iload * R0 是瞬时欧姆压降。
  • Vrc 是电化学极化形成的慢恢复分量。

若有电流传感器,可做一阶补偿:

1
OCV_est = Vterminal + Iload * R0_est

R0_est 可随温度、SOC 和老化程度查表。若没有电流传感器,可以使用负载状态估算:

1
Vcomp = Vfiltered + DeltaV(load_state, volume, tx_power, temperature)

例如在功放大音量或 RF 发射窗口内,暂缓 UI 电量下降;负载结束后等待恢复窗口,再使用恢复后的电压更新 SOC。这里的“暂缓”不能影响真正的欠压保护,保护路径仍需依据更快的端电压和持续时间判定。

推荐的双路径设计

1
2
3
4
5
6
7
8
9
flowchart LR
A[ADC 原始电压] --> B[快速稳健滤波]
B --> C[保护路径]
C --> D[欠压告警/限功率/关机]

B --> E[慢速低通]
L[负载/温度/充电状态] --> F[补偿与 OCV 估计]
E --> F
F --> G[SOC 与 UI 路径]

快速保护路径关注“端电压是否持续危险”;慢速 UI 路径关注“用户可理解的剩余电量”。两者不能共用同一组极慢滤波参数。

电量百分比状态机

从电压映射到电量百分比时,必须引入状态机、滞回和更新限速。否则即使电压只波动几十毫伏,也可能在 SOC 曲线陡峭区造成多个百分点跳变。

1
2
3
4
5
6
7
8
9
10
11
12
stateDiagram-v2
[*] --> Init
Init --> Idle: 采样稳定
Idle --> Discharge: 放电确认
Idle --> Charge: 充电确认
Discharge --> HeavyLoad: 大负载
HeavyLoad --> Discharge: 负载解除并恢复
Discharge --> LowBattery: 低压持续满足
Charge --> FullConfirm: 满电条件满足
FullConfirm --> Charge: 条件退出
Charge --> Idle: 充电器移除
LowBattery --> Shutdown: 保护条件满足

状态机需要解决的问题

  1. 充电时电压抬升,不能直接按放电 OCV 曲线映射。
  2. 大负载下电压瞬降,不能立即降低大量电量。
  3. 负载解除后电压回弹,不能让电量反复上涨。
  4. 满电和低电需要持续时间确认,避免单点触发。
  5. UI 百分比应限制每次变化幅度和最短更新时间。
  6. 充电状态下通常只允许电量不下降,放电状态下通常只允许电量不无故上升,但需为重估和校准留出受控例外。
  7. 休眠很久后重新唤醒,应允许根据稳定电压重新锚定,而不是死守旧值。

滞回示例

假设 20% 对应某个补偿电压阈值 V20,可设置:

1
2
3
从 21% 降到 20%:V <= V20_down 持续 Tdown
从 20% 升到 21%:V >= V20_up 持续 Tup
V20_up > V20_down

同一阈值上下使用不同边界,避免在边界附近来回跳变。

更新限速示例

  • 正常放电:每 30~120 s 最多下降 1%。
  • 充电:根据充电阶段每 10~60 s 最多上升 1%。
  • 重载期间:冻结 UI 或只允许非常慢的下降。
  • 低压持续:允许快速下降并触发告警,但必须与保护路径一致。
  • 开机初始化:收集数秒稳定数据后一次性设置初值。

这些数值不能直接照搬,应由电池容量、产品功耗、用户体验和安全策略共同确定。

推荐的组合滤波流程

面试回答可以给出一条可落地的默认链路:

1
2
3
4
5
6
7
8
9
10
11
Timer/PWM 硬件触发
-> ADC DMA 采集 32 点电池通道 + 8 点 Vref 通道
-> 检查 ADC 饱和、Vref 合法性和 DMA 完整性
-> 3 点中值或 Hampel 去除尖峰
-> 32 点去两端极值平均
-> 使用 Vref 修正为 mV
-> 可选固定频率二阶陷波
-> 一阶 IIR,正常 tau=8s,重载 tau=30s
-> 负载/温度补偿得到 OCV_est
-> OCV-SOC 查表与插值
-> 充放电状态机、滞回和 UI 更新限速

这条链路并不是越多级越好。每一级都应有明确目标和可观察效果;如果无法说明某一级抑制的噪声类型,就不应盲目加入。

参数如何量化

原始采样率

采样率应同时考虑:

  • 纹波频率和谐波。
  • ADC 转换时间和源阻抗。
  • DMA 带宽与 CPU 处理预算。
  • 期望的更新周期。
  • 是否需要相位同步或整周期平均。

若只做 UI 电量,每秒更新 1~10 次已经足够,但原始 ADC 可以在每次更新窗口内以更高频率突发采样。例如 UI 每 100 ms 更新一次,每次由定时器在 2 ms 内采 32 点。这样既能做稳健统计,也不必长期占用 ADC。

窗口长度

窗口长度需要覆盖期望抑制的噪声,同时不能让真实状态变化延迟过大。可按以下思路设置:

1
N_window >= Fs_raw * T_cover

其中 T_cover 至少覆盖若干个纹波周期或一次典型干扰突发。若噪声源是 1 kHz PWM,原始采样率 16 kHz,32 点窗口覆盖 2 ms,即 2 个 PWM 周期;如果希望覆盖 8 个周期,应增加到 128 点或调整采样布局。

IIR 时间常数

时间常数应从产品允许的响应时间反推,而不是凭感觉设置。假设每 100 ms 更新一次,希望电压阶跃在约 15 s 内接近稳定,可先选择 tau=5 s,因为一阶系统约在 3*tau 后达到最终值的 95%。随后通过实测重载恢复和 UI 体验调整。

陷波半径

二阶陷波的 r 决定带宽。r 过小会形成较宽抑制带,可能影响有用信号;r 过大则对频率漂移敏感。固定点 MCU 上还要评估:

  • Q 格式系数量化后极点是否仍在单位圆内。
  • 内部累加器是否溢出。
  • 初始状态是否导致启动瞬态。
  • 采样率漂移是否使陷波中心偏移。

建议使用离线脚本生成系数和频响,再把定点系数固化到代码,并用单元测试验证稳定性。

MCU/RTOS 落地架构

1
2
3
4
5
6
7
8
9
10
flowchart LR
T[Timer/PWM Trigger] --> ADC[ADC Scan]
ADC --> DMA[DMA Ping-Pong Buffer]
DMA --> ISR[Half/Full ISR]
ISR --> Q[Event/Queue]
Q --> FT[Filter Task]
FT --> DIAG[Raw/Filter Diagnostics]
FT --> EST[Battery Estimator]
EST --> UI[UI Task]
EST --> PM[Power Manager]

线程与中断职责

  • 定时器/PWM:生成确定相位的硬件触发。
  • ADC:按固定通道序列扫描电池、Vref、温度等通道。
  • DMA:把数据写入 ping-pong 缓冲区,避免 CPU 逐样本中断。
  • ADC/DMA ISR:只确认缓冲区、记录时间戳并投递事件,不执行排序、浮点计算或日志打印。
  • Filter Task:做窗口统计、参考修正、陷波和低通。
  • Battery Estimator:做负载/温度补偿、SOC 映射和状态机。
  • UI/Power Manager:分别消费显示电量和保护状态。

数据一致性

DMA 缓冲区应避免在 CPU 处理时被覆盖。常见方案:

  • 双缓冲/ping-pong。
  • 半传输和全传输中断分别处理两个半区。
  • ISR 只传递缓冲区索引,不复制大量数据。
  • 如果处理任务可能跟不上,记录 overrun 并丢弃旧窗口,不要悄悄读取正在写入的数据。
  • 原始数据、滤波状态和 SOC 状态使用明确的所有权或短临界区保护。

C 语言实现示例

下面示例展示“去一个最大值和最小值平均 + 一阶 IIR + 状态化参数”的最小实现。实际项目仍需增加 ADC 通道交织解析、Vref 校准、单位换算、异常统计和平台适配。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

/**
* @brief Runtime state of the battery voltage filter.
*/
typedef struct
{
int32_t filtered_mv_q8; /**< Filtered battery voltage in mV, Q8 format. */
bool initialized; /**< Indicates whether the IIR state is initialized. */
} battery_filter_t;

/**
* @brief Calculate a trimmed mean by removing one minimum and one maximum sample.
*
* @param samples Pointer to the ADC sample array.
* @param count Number of samples in the array, must be at least 3.
* @param mean Output mean value after removing one minimum and one maximum sample.
* @return true if the result is valid; otherwise false.
*/
static bool adc_trimmed_mean_u16(const uint16_t *samples, size_t count, uint32_t *mean)
{
uint64_t sum = 0U;
uint16_t min_value;
uint16_t max_value;
size_t i;

if ((samples == NULL) || (mean == NULL) || (count < 3U))
{
return false;
}

min_value = samples[0];
max_value = samples[0];

for (i = 0U; i < count; ++i)
{
const uint16_t value = samples[i];

sum += value;
if (value < min_value)
{
min_value = value;
}
if (value > max_value)
{
max_value = value;
}
}

sum -= min_value;
sum -= max_value;
*mean = (uint32_t)(sum / (count - 2U));
return true;
}

/**
* @brief Update a first-order IIR filter using a power-of-two coefficient.
*
* The transfer equation is y += (x - y) / 2^shift. A larger shift produces
* a slower response and stronger smoothing.
*
* @param filter Pointer to the filter state.
* @param input_mv New input voltage in millivolts.
* @param shift IIR coefficient shift, valid range is 1 to 15.
* @return Filtered voltage in millivolts.
*/
static int32_t battery_iir_update(battery_filter_t *filter, int32_t input_mv, uint8_t shift)
{
const int32_t input_q8 = input_mv << 8;

if ((filter == NULL) || (shift == 0U) || (shift > 15U))
{
return input_mv;
}

if (!filter->initialized)
{
filter->filtered_mv_q8 = input_q8;
filter->initialized = true;
}
else
{
filter->filtered_mv_q8 += (input_q8 - filter->filtered_mv_q8) >> shift;
}

return (filter->filtered_mv_q8 + 128) >> 8;
}

Vref 修正的整数计算框架

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**
* @brief Convert battery ADC code to millivolts using an internal reference channel.
*
* @param battery_code Averaged ADC code of the battery divider channel.
* @param vref_code Averaged ADC code of the internal reference channel.
* @param adc_full_scale ADC full-scale code, for example 4095 for a 12-bit ADC.
* @param vref_cal_mv Calibrated internal reference voltage in millivolts.
* @param divider_num Battery divider ratio numerator.
* @param divider_den Battery divider ratio denominator.
* @param battery_mv Output battery voltage in millivolts.
* @return true if the conversion is valid; otherwise false.
*/
static bool battery_code_to_mv(uint32_t battery_code,
uint32_t vref_code,
uint32_t adc_full_scale,
uint32_t vref_cal_mv,
uint32_t divider_num,
uint32_t divider_den,
uint32_t *battery_mv)
{
uint64_t vdda_mv;
uint64_t value;

if ((vref_code == 0U) || (adc_full_scale == 0U) || (divider_den == 0U) || (battery_mv == NULL))
{
return false;
}

vdda_mv = (uint64_t)vref_cal_mv * adc_full_scale / vref_code;
value = (uint64_t)battery_code * vdda_mv * divider_num;
value /= (uint64_t)adc_full_scale * divider_den;

if (value > UINT32_MAX)
{
return false;
}

*battery_mv = (uint32_t)value;
return true;
}

注意:上面把内部参考简化为“已知校准电压”。具体 MCU 可能给的是某个校准供电电压下的原始 ADC 校准码,公式会不同,必须以芯片参考手册为准。

二阶陷波状态结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* @brief Fixed-point biquad filter coefficients and delay states.
*/
typedef struct
{
int32_t b0_q30;
int32_t b1_q30;
int32_t b2_q30;
int32_t a1_q30;
int32_t a2_q30;
int32_t x1;
int32_t x2;
int32_t y1;
int32_t y2;
} biquad_q30_t;

陷波器实现需要根据输入范围选择状态和累加器位宽。若 ADC 电压已经换算为毫伏,内部乘加建议至少使用 64 位。系数符号约定必须与设计工具一致,避免把差分方程中的 -a1*y1-a2*y2 和存储系数符号搞反。

推荐配置结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* @brief Configurable parameters for battery sampling and estimation.
*/
typedef struct
{
uint16_t raw_window_size; /**< Number of raw ADC samples per processing window. */
uint16_t reject_limit; /**< Maximum allowed rejected samples per window. */
uint8_t normal_iir_shift; /**< IIR shift used during normal load. */
uint8_t heavy_load_iir_shift; /**< IIR shift used during heavy load. */
uint16_t ui_update_period_ms; /**< Minimum interval between UI percentage updates. */
uint16_t recovery_delay_ms; /**< Delay after a heavy-load event before SOC refresh. */
uint16_t low_voltage_hold_ms; /**< Required low-voltage persistence before an alarm. */
uint16_t vref_min_code; /**< Minimum valid internal-reference ADC code. */
uint16_t vref_max_code; /**< Maximum valid internal-reference ADC code. */
} battery_estimator_config_t;

将参数集中配置有两个好处:一是便于不同硬件版本和电池型号复用;二是可以在测试报告中明确记录每次实验使用的参数,避免“改了几个魔法数但无法复现”。

如何选择一套默认方案

在没有完整频谱数据、但需要先交付一个可靠版本时,可以采用保守方案:

模块 默认建议 说明
ADC 触发 定时器硬件触发 避免 RTOS 抖动。
原始窗口 32 点 在算力、延迟和稳健性之间折中。
异常处理 去 1~2 个最大/最小值,或 Median(5) 抑制尖峰。
Vref 每个电池窗口同步采样 修正参考变化。
快速输出 每 100 ms 一次 供保护和诊断。
UI IIR tau=5~15 s 具体由产品体验调参。
重载策略 冻结或 tau=20~60 s 等待端电压恢复。
陷波 默认关闭 只有频谱证据充分时启用。
SOC 分充电/放电曲线,带温度和滞回 避免单一电压线性映射。
保护 独立快速路径和持续时间确认 不受 UI 慢滤波拖延。

测试与验证流程

软件滤波的效果必须通过可重复实验验证,不能只看 UI“似乎更稳定”。建议建立原始数据回放和自动化指标。

1
2
3
4
5
6
7
8
9
10
flowchart TD
A[定义测试工况] --> B[同步采集示波器与 ADC Raw]
B --> C[记录负载/PWM/RF/温度/充电状态]
C --> D[离线频谱与时域分析]
D --> E[选择采样相位和滤波参数]
E --> F[嵌入式在线实现]
F --> G[A/B 对比与故障注入]
G --> H{指标满足?}
H -->|否| D
H -->|是| I[固化参数和回归测试]

基础测试工况

  1. 稳定直流源代替电池,系统轻载。
  2. 稳定直流源,PWM/DC-DC 全功能运行。
  3. 电池供电,功放从静音切到最大负载。
  4. 蓝牙/Wi-Fi 周期发射和连续发射。
  5. 背光、马达、LED、振动器等负载阶跃。
  6. 充电器插入、恒流、恒压和充满阶段。
  7. 高低温环境下的静置与负载测试。
  8. 不同 SOC 区间,尤其是电压平台区和低电陡降区。
  9. 休眠唤醒、ADC 重新初始化和通道切换。
  10. 故意改变采样率和相位,验证混叠假设。

需要记录的数据

  • ADC 原始样本及时间戳。
  • PWM/定时器相位或触发编号。
  • 内部 Vref 原始码。
  • 电池分压通道原始码。
  • 负载状态、音量、RF 发射功率、CPU 频点。
  • 充电状态和充电电流。
  • 温度。
  • 各级滤波输出。
  • 最终 SOC 和 UI 百分比。
  • 欠压告警、限功率和关机事件。

评价指标

指标 定义 用途
峰峰值 Vpp 稳态窗口最大值减最小值 观察纹波和毛刺。
标准差/RMS 稳态噪声统计 比较滤波前后随机噪声。
最大绝对误差 相对高精度表或稳定电源 验证绝对精度。
95/99 百分位误差 排除单点偶然性 评价长尾。
阶跃响应时间 负载或输入变化后的收敛时间 检查滤波是否过慢。
过冲/欠冲 阶跃后的最大偏离 检查 IIR/FIR/陷波瞬态。
异常拒绝率 被丢弃样本占比 判断阈值是否合理。
UI 跳变次数 固定工况下百分比变化次数 评价用户体验。
保护触发延迟 欠压到保护动作的时间 验证安全路径。

建议验收条件示例

以下仅为测试模板,实际阈值应由产品定义:

  • 稳定输入下,滤波后电压标准差低于指定值。
  • PWM/RF/功放开启前后,补偿后稳态误差不超过目标范围。
  • 单次尖峰不引起 UI 百分比变化。
  • 重载持续时间较短时,UI 不出现多百分点瞬降。
  • 真实电池电压持续下降时,UI 在规定时间内跟随。
  • 欠压保护不因慢滤波延迟超过安全上限。
  • ADC/Vref 异常时输出明确故障,不使用无效数据更新 SOC。

常见错误方案

只做大窗口平均

问题:若采样始终落在相同噪声相位,平均只会得到稳定的错误值;若负载跌落持续整个窗口,平均也无法判断它是噪声还是真实压降。

改进:先优化触发相位,再用稳健统计和负载状态解释。

盲目增加 IIR 平滑时间

问题:UI 看起来稳定,但真正的低电状态被延迟,甚至影响关机保护。

改进:保护路径和 UI 路径分离,分别定义响应时间。

看到固定开关频率就直接加陷波

问题:实际采样序列中的频率可能已经混叠,或 DC-DC 处于变频/扩频模式,固定陷波中心无效。

改进:先记录原始数据并做频谱分析,再决定是否启用陷波。

不采内部 Vref

问题:参考电压波动会被误判为输入电压波动,任何后级滤波都只能平滑比例误差。

改进:在同一窗口采 Vref 并使用芯片校准公式修正。

把负载压降全部滤掉

问题:大电流下的端电压下降是真实物理现象,可能反映电池内阻增大或供电能力不足。完全隐藏会错过低温、老化和接触不良风险。

改进:UI 可以补偿和暂缓,保护路径仍需监控真实端电压及持续时间。

直接用电压线性映射 SOC

问题:多数电池的 OCV-SOC 关系非线性,并受温度、充放电滞回、负载和老化影响。

改进:使用经过电池测试得到的分段查表,区分充电/放电曲线,必要时结合库仑计量。

在 ISR 里完成全部滤波和日志

问题:排序、浮点、FFT 或日志打印可能拉长中断,影响采样确定性并引入新的系统抖动。

改进:ISR 只投递缓冲区事件,复杂处理放在线程中。

算法选择决策表

噪声特征 首选方法 辅助方法 不建议的首选
与 PWM/DC-DC 相位固定 硬件同步采样 整周期平均、低通 仅靠大窗口平均。
少量孤立尖峰 Median/Hampel/Trimmed Mean 输入合法性检查 窄带陷波。
宽带随机噪声 过采样、均值、FIR/IIR 降采样 只去极值。
稳定窄带纹波 同步采样或 Notch 低通 超大中值窗口。
频率漂移/扩频纹波 同步窗口、宽带低通 自适应估计 过窄固定 Notch。
VDDA/Vref 波动 内部参考修正 稳健平均 Vref 仅平滑电池通道。
大负载真实压降 负载感知补偿/OCV 估计 状态机和恢复等待 把所有下降都当异常点。
UI 百分比抖动 滞回、限速、状态机 慢 IIR 直接显示单次 ADC。

频域分析与参数辨识

只看均值和峰峰值,往往无法区分随机噪声、固定纹波、拍频和负载包络。建议在开发阶段保留一条原始数据导出通道,把 ADC 原始码、Vref 码、触发计数和负载标志输出到文件,再在 PC 上进行频谱和相关性分析。

采集长度与频率分辨率

对长度为 N、采样率为 Fs 的数据做离散傅里叶变换,频率分辨率约为:

1
Delta_f = Fs / N

例如 Fs=8 kHzN=8192 时,频率分辨率约为 0.9766 Hz。如果只采 128 点,虽然可以快速观察波形,但很难准确区分相邻的低频拍频。采集长度应覆盖多个干扰周期和完整的负载工作周期。

窗函数与谱泄漏

当采样窗口不包含整数个信号周期时,FFT 会出现谱泄漏。开发分析时可使用 Hann 窗等窗函数观察主频和谐波,但窗函数会改变幅值,需要做相应校正。对于算法上线,不一定需要在 MCU 上做 FFT;频谱分析主要用于确定采样相位、采样率、陷波中心和低通截止频率。

相干性与相关分析

如果可以同时记录 PWM 周期计数、RF 发射标志或音频帧边界,可计算 ADC 数据与这些控制信号的相关性。若 ADC 波动在负载事件后具有固定延迟和恢复曲线,更可能是供电路径压降;若每次都集中在开关边沿附近,更可能是耦合尖峰或地弹。

推荐做以下对比:

对比实验 观察重点 可得结论
固定相位采样 vs 随机相位采样 均值、Vpp 和频谱主峰 判断周期噪声是否与触发相位相关。
同步采 Vref 前后 修正前后的电压波动 判断参考电压波动占比。
轻载 vs 重载 阶跃幅度和恢复时间 估算供电路径等效内阻和 RC 特征。
不同采样率 低频峰位置是否移动 判断是否存在混叠。
关闭某个噪声源 对应频谱峰是否消失 建立噪声源与观测结果的因果关系。

估算等效内阻

若能获得负载阶跃前后的电流和端电压,可粗略估算瞬时等效内阻:

1
R0_est = -(V_after - V_before) / (I_after - I_before)

应选取负载变化后很短时间内的电压差,减少慢极化分量影响。估算结果需要按温度和 SOC 分桶,并限制异常值。该参数可用于负载补偿,也可用于判断电池老化、连接器接触不良或保护 MOS 阻抗上升。

典型场景设计实例

场景一:500 kHz DC-DC,ADC 每 100 ms 读取一次

如果软件任务每 100 ms 直接启动一次 ADC,且启动时刻受 RTOS 调度影响,500 kHz 纹波会以随机相位进入采样。单点结果可能在纹波上下沿之间跳动,若任务周期与开关控制存在弱同步,还可能形成低频拍频。

推荐改法:

  1. 使用定时器在每 100 ms 打开一次突发采样窗口。
  2. 窗口内以较高采样率采 32~64 点,或由 DC-DC/PWM 同步事件触发。
  3. 选择稳定平台相位,避开开关边沿和大电流切换时刻。
  4. 采样电池通道和内部 Vref。
  5. 对窗口做去极值平均,再输出一个 100 ms 级瞬时值。
  6. UI 支路再用 5~15 s 时间常数的一阶 IIR。

如果 ADC 和定时器资源无法达到足够高的原始采样率,应优先找“安静相位”,而不是尝试在低速数据上恢复 500 kHz 波形。

场景二:Class-D 功放引起音量相关电压波动

Class-D 或大功率音频负载的电流与音频包络相关,既有高频开关成分,也有低频真实负载变化。单纯陷波只能处理某个固定载波,无法消除音乐包络引起的端电压下降。

推荐把问题分成两层:

  • 高频开关噪声:通过同步采样、短窗口稳健平均和输入带宽限制处理。
  • 音频包络负载:根据音量、限幅器增益、功放使能和估计输出功率做状态补偿;负载解除后等待电压恢复再更新 SOC。

如果系统可以获得音频 RMS、峰值或功放电流估计,可用作负载模型输入。若只能知道“功放开启/关闭”,至少建立轻载、普通播放和大音量三个状态,分别设置补偿量和恢复延时。

场景三:蓝牙或 Wi-Fi 周期发射造成尖峰

无线发射常表现为短时大电流脉冲,ADC 数据中可能出现少量低值尖峰。如果尖峰占窗口样本比例很低,Median(3/5)、Hampel 或去极值平均通常有效。如果发射持续时间占窗口大部分,则应结合射频调度标志改变采样时刻,或在发射结束后的稳定窗口采样。

对射频事件不能无限期冻结电量更新。应设置最大冻结时间;若设备长时间持续发射,仍需基于滤波后的端电压和负载补偿更新 SOC,并确保保护路径持续工作。

场景四:充电纹波与电量显示上升

充电时,充电器开关纹波、恒流/恒压阶段转换和充电终止脉冲都可能影响 ADC。充电状态下端电压通常高于静置 OCV,不能直接套用放电曲线。建议:

  • 独立配置充电状态滤波参数。
  • 结合充电 IC 的状态脚、充电电流和终止条件。
  • 充电过程中使用单独的 SOC 更新策略,必要时以库仑计量为主。
  • “100%”需要满足电压、电流和持续时间条件,不能因瞬时高压直接置满。
  • 充电器拔出后等待表面电荷和端电压回落,再切换回放电估算。

校准、量产与长期漂移

滤波只改善重复性,不等于校准。若分压电阻误差、ADC 增益误差和参考电压误差形成固定偏差,输出可以非常平滑但始终不准确。因此量产前应建立最少一到两点电压校准流程。

一点校准

在已知输入电压 Vcal 下测得软件计算值 Vmeas,得到比例修正:

1
2
Gain = Vcal / Vmeas
Vcorrected = Vmeas * Gain

一点校准主要修正增益误差,不能同时区分偏置。

两点校准

在两个已知电压 V1V2 下测得 M1M2,建立线性模型:

1
2
3
Vcorrected = A * M + B
A = (V2 - V1) / (M2 - M1)
B = V1 - A * M1

校准系数应带版本号、CRC 和有效范围,存储在可靠的非易失区域。启动时校验失败,应回退到默认系数并记录诊断,而不是继续使用随机数据。

温度与批次差异

电阻温漂、内部参考温漂、电芯内阻和 OCV-SOC 曲线都会随温度变化。高低温测试不应只验证滤波是否稳定,还要验证:

  • 同一真实电压在不同温度下的测量偏差。
  • 负载压降和恢复时间随温度的变化。
  • SOC 映射是否需要温度分区。
  • 低温大负载时是否会误触发低电或关机。
  • 不同 PCB 批次、ADC 芯片和电阻容差下的分布。

在线健康监测

量产版本可增加轻量诊断:

  • Vref 码超范围次数。
  • ADC 饱和次数。
  • 单窗口异常样本比例。
  • 估算内阻突增。
  • 负载解除后的恢复时间异常。
  • 校准数据 CRC 错误。

这些指标有助于区分“算法参数不合适”和“硬件已出现故障”。当异常比例持续过高时,应降低对电量百分比的置信度,必要时显示保守值或提示维护,而不是继续输出看似平滑但不可信的结果。

工程调参顺序

建议严格按以下顺序调参,避免多个参数同时变化导致无法归因:

  1. 固定硬件工况,确认 ADC sample time、通道顺序和触发时刻正确。
  2. 确认 Vref 修正公式和校准常量正确。
  3. 只启用原始窗口统计,选择样本数和异常点策略。
  4. 做采样相位扫描,寻找稳定相位或确定整周期平均方式。
  5. 记录频谱,决定是否需要陷波。
  6. 调整快速路径的噪声与响应折中。
  7. 再调整 UI IIR 时间常数、滞回和限速。
  8. 最后加入负载补偿、温度补偿和 SOC 曲线。
  9. 在所有工况下回归保护延迟,防止 UI 优化影响安全动作。
  10. 固化参数、数据集和验收阈值,纳入持续回归。

每一步都应保留 A/B 数据。没有原始数据和参数版本记录的“滤波调优”通常不可复现,也无法判断后续硬件变更是否使旧参数失效。

面试中应如何组织回答

可以按以下顺序作答:

  1. 先说明边界:软件不能消除硬件纹波本身,只能降低其对采样和 SOC 估算的影响。
  2. 先定位噪声路径:输入、Vref、VDDA、地弹、开关耦合、量化噪声和真实负载压降。
  3. 优先优化采样:定时器/PWM 同步触发、安静相位、整周期平均、提高采样率并避免混叠。
  4. 做稳健预处理:中值、Hampel、去极值平均,剔除尖峰和异常窗口。
  5. 做参考修正:同步采内部 Vref/Bandgap,修正 VDDA 波动。
  6. 根据频谱选滤波器:宽带噪声用均值/FIR/IIR,稳定窄带纹波才用陷波。
  7. 区分真实负载压降:结合电流、功放、RF、温度和充电状态估算 OCV。
  8. UI 使用慢变量:SOC 查表、滞回、更新限速和充放电状态机。
  9. 保护路径独立:欠压保护不能被 UI 慢滤波拖延。
  10. 最后给出验证方法:记录 ADC Raw、Vref、相位和负载状态,做频谱、阶跃和 A/B 测试。

一句话概括:先通过同步采样和参考修正减少“采错”,再用稳健统计、低通或陷波减少“噪声”,最后用负载模型和状态机避免把真实瞬态误判为电量变化。

参考资料