ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于STM32的铅酸电池内阻测试仪:交流注入法实现与优化

基于STM32的铅酸电池内阻测试仪:交流注入法实现与优化 从一块退役的12V 7Ah铅酸电瓶说起。我手头这块电池空载电压12.6V看着挺正常可一旦带上一个55W的卤素灯泡电压直接砸穿到9V以下灯光昏暗发红。用万用表测电压永远测不出问题这就是铅酸电池典型的虚电压状态——内阻已经升高到一个离谱的程度但表面电压依旧漂亮。于是就有了做一台STM32铅酸蓄电池内阻测试仪的想法用交流阻抗法把这块电池的真实健康状况量化出来。这台仪器的核心目标很简单通过向电池注入一个已知频率和幅度的微小交流电流然后测量电池两端产生的交流电压响应计算出电池的交流内阻。整个项目跑下来我最大的感受是——硬件电路决定测量下限而数据采集与数字处理的优化程度直接决定了这台仪器是玩具还是工具。这篇内容我就把完整的原理拆解、硬件链路设计、以及我在数据采集优化上踩过的坑和最终方案全部梳理出来希望对想做类似电化学阻抗测量或精密微弱信号检测的朋友有实际参考价值。1. 为什么是交流阻抗法铅酸电池的体检指标与测量原理先聊一个基础问题为什么铅酸电池的健康状态要看内阻而不是看电压铅酸电池内部由正负极板、隔板、电解液和汇流排构成。随着充放电循环次数的增加极板会发生硫化、活性物质脱落、失水干涸极栅腐蚀也会加重。这些老化现象的共同结果就是——电池内部的离子导通路径受阻、电子导通路径恶化宏观表现就是欧姆内阻增加。当电池内阻从出厂时的几毫欧升高到数十毫欧时即使电压还正常它的大电流放电能力、充电接受能力都已经严重退化了。所以内阻是比电压更早、更可靠的健康预警指标。测量内阻有两种主流方法我直接对比一下你会明白为什么选交流法而不是直流放电法对比维度直流放电法交流注入法本项目采用原理对电池施加固定大电流测量电压跌落按欧姆定律计算内阻注入已知频率的小幅交流电流测量交流电压响应计算阻抗是否消耗电池能量消耗需要电池处于静置或特定SOC状态基本不消耗测量过程等效于微弱的交流信号叠加对电池状态的影响大电流会扰动电池极化状态测量前后状态不一致扰动极小可以认为是无损测量测量重复性受放电深度、温度影响大固定频率下重复性较好能否在线测量难需要断开负载容易可设计成在线监测从工程实现和测量意图来看交流注入法的核心优势在于无损、快速、可重复。那么交流注入法的物理基础是什么对铅酸电池施加一个正弦交流电流 (I_{ac}(t) I_m \sin(\omega t))电池两端会产生一个同频率的交流电压响应 (U_{ac}(t) U_m \sin(\omega t \theta))。这里 (\theta) 是相位差。电池的复阻抗是[ Z \frac{U_{ac}}{I_{ac}} \frac{U_m}{I_m} e^{j\theta} |Z| \cos\theta j|Z| \sin\theta ]其中实部 (R |Z|\cos\theta) 是等效串联电阻虚部 (X |Z|\sin\theta) 是电抗部分。对于铅酸电池在1kHz左右的测量频率下虚部占比很小所以 (|Z|) 可以近似看作欧姆内阻。不过这里必须说清楚一个点电池不是纯电阻它是一个复杂的电化学系统。铅酸电池的等效电路可以简化为(R_{ohm})欧姆内阻电解液、极板、连接件电阻纯电阻特性与频率无关(R_{ct})电荷转移电阻反映电化学反应阻力(C_{dl})双电层电容极板与电解液界面的电容效应(Z_w)Warburg阻抗反映离子扩散过程低频段明显如果频率太低比如1Hz以下(R_{ct}) 和 (Z_w) 的贡献会很大测出来的阻抗包含大量极化阻抗成分这受温度、SOC影响非常明显重复性差。如果频率太高比如10kHz以上测量回路中的分布电感和电缆电容会引入误差。而1kHz左右是一个行业约定俗成的频率因为这个频率下 (C_{dl}) 相当于短路(Z_w) 的影响也很小测出来的结果更接近真实的欧姆内阻重复性最好也方便和市售内阻仪的数据做对比。2. 硬件链路设计从恒流源到仪表放大器的信号调理原理搞清楚了硬件上的第一件事就是设计一个稳定的交流恒流源。这个模块的性能直接决定了整个系统的测量下限也是我在项目初期反复修改的地方。2.1 激励源方案如何生成一个说一不二的交流电流要注入电流我最初的方案是STM32的DAC直接输出正弦波再经过一个运放做的Howland恒流源。但实际测试下来直接用DAC直出有个问题——DAC的输出范围是0~3.3V叠加了直流偏置虽然可以通过隔直电容去掉但后续要多一级调理而且驱动能力不足。最终我采用的方案是STM32DAC低通滤波V/I变换电路。STM32的DAC输出一个由查表法生成的正弦波频率1kHz幅值通过软件可调。DAC输出后首先过一个二阶低通滤波器把DAC固有输出中的高频台阶和量化噪声滤掉让波形更干净。然后经过一个由运放搭建的Howland电流源实现V/I变换把电压信号转换为恒流输出。这里有一个非常关键的细节Howland电流源的负载调整率取决于电阻匹配精度。原理上只要 (R1/R2 R3/R4)输出电流就和负载无关。但实际电阻是有误差的哪怕是0.1%精度的电阻在负载阻抗变化时电流也会有几毫安的波动。我最初用普通1%精度的贴片电阻结果在接上不同内阻的电池时恒流源的输出电流确实发生了可测的变化这直接影响测量结果的一致性。解决办法有两个一个是选用0.1%精度的精密电阻网络另一个是在软件里做电流校准。我两者都做了——硬件上换用精密电阻软件上在每次测量前先通过一个标准电阻通道做满量程校准双保险。注入电流幅值的选择也需要权衡。太大会扰动电池的极化状态太小则信噪比不足。经过实测铅酸电池在1kHz下的内阻一般在1mΩ~30mΩ之间好的新电池在5mΩ以下寿命末期的电池可能超过50mΩ我设定恒流源的输出幅值为100mA这样在10mΩ的电池上只能产生1mV的电压信号。这个幅值既不会明显扰动电池状态又能在后端调理放大后获得可测量的信号。提示电流源的输出通道上必须串联一个隔直电容。铅酸电池两端有直流电压如果不隔直直流电压会直接灌进恒流源输出级轻则导致输出异常重则烧毁运放。隔直电容选型时把截止频率放在10Hz左右既能隔掉直流又不影响1kHz的交流激励。2.2 前端检测与放大从毫伏级信号里抢救出有用的信息这是整个项目中信号链路最难处理的一段。想象一下这个场景电池两端叠着12V的直流电压上面挂着一个1kHz、幅度1mV左右的微弱交流电压信号。如果直接把电池两端接进ADC12V的直流分量会瞬间让ADC饱和1mV的交流信号连ADC的最小分辨率都不够。所以必须做三件事隔直、放大、滤波。隔直是第一步。我用了两个串联的隔直电容把直流阻隔掉只保留1kHz的交流分量。离开电池之后这个信号就可以放心地做后续放大处理了。放大环节我选了仪表放大器AD620。之所以不用普通运放是因为普通运放做差分放大时两个输入阻抗不可能完全对称共模抑制比在高频下会明显下降。电池两端的1kHz信号上叠加的共模干扰幅度远远大于有用的差模信号如果共模抑制比不够一个看似微小的共模干扰就可能完全淹没差模信号。仪表放大器的对称差分输入结构能把共模抑制比做到90dB以上配合隔直措施信号的纯度才有保证。放大倍数我设定为100倍。1mV的输入放大后变成100mV正好落在STM32 ADC转换量程的合适区间。如果信号太大比如电池内阻较大软件里可以通过DAC降低激励电流来调整。放大之后的信号还要过一个带通滤波器中心频率选在1kHz带宽设为±150Hz左右。这个滤波的目的不是做精细选频——那是软件算法后面的事——而是先把带外的噪声能量削减一部分防止后端ADC在采样时发生混叠。2.3 四线开尔文接法排除导线和接触电阻的干扰最后必须强调的是测量接法。测低内阻时普通的双线接法是完全不可行的。假设测试线的本身电阻是50mΩ电池内阻是5mΩ用万用表的两根表笔测到的电压会同时包含电池压降和导线压降测出来的内阻比真实值大了一个数量级。所以必须用四线开尔文接法两根线通电流激励另外两根线单独测电压。电流线和电压检测线在电池端分开电压检测线上几乎没有电流流过导线上的压降就不会引入测量误差。夹子的四个端子要使用镀金的独立开尔文夹头确保电流端和电压端的接触位置尽量靠近电池极柱本体减小接触电阻的影响。3. 数据采集ADC采样策略与同步触发的工程实现硬件信号链路准备好之后真正的技术重头戏才刚开始——怎么把模拟信号准确、稳定地变成数字量并且在算法层把淹没在噪声里的微弱信号提取出来。这部分的优化工作前后花了将近两周时间也是让我收获最大的一段。3.1 为什么直接采ADC算幅值不靠谱最开始我抱着先跑通再优化的想法直接让STM32的ADC以30kHz的采样率连续采样采个一秒钟的数据然后在程序里找这个正弦波的最大值和最小值相减除以2当作幅值再除以电流和增益系数算内阻。实测结果非常差。读数跳动大一次测量和下一次测量之间能差到20%以上而且只要附近有人开关灯、电机启停读数就乱跳。问题出在哪峰值检测的精度依赖于单点采样是否恰好落在正弦波的顶点上。30kHz采样率采1kHz的正弦波每个周期只有30个采样点正弦波峰值附近的采样点可能偏离真实顶点好几个百分点。再加上随机噪声会叠加在每个采样点上单个采样点的噪声可以直接贡献到峰值估计里信噪比很差。说白了峰值检测方法只利用了采样序列中极少的信息量大部分采样点都被浪费了。3.2 用DFT单点提取得到干净的幅值正确的思路是对采样序列做离散傅里叶变换DFT但不是做全频段的FFT而是只计算激励频率1kHz对应的那个频点的幅值和相位。这就是单点DFT或数字相敏检波DPSD的核心思想。设ADC采样序列为 (x[n])采样频率为 (f_s)采样点数为 (N)激励频率为 (f_0)那么 (f_0) 频点的DFT分量为[ X(f_0) \sum_{n0}^{N-1} x[n] \cdot e^{-j2\pi f_0 n / f_s} ]把指数项展开成三角函数[ X(f_0) \sum_{n0}^{N-1} x[n] \cdot \cos(2\pi f_0 n / f_s) - j \sum_{n0}^{N-1} x[n] \cdot \sin(2\pi f_0 n / f_s) ]这个 (X(f_0)) 的幅度就是信号在1kHz频点的幅值相位就是信号相对于采样起始点的相位。为什么这个方案能抗噪声关键在于DFT是一种相关运算。它实际上是在检验采样序列中有多少成分与1kHz正弦/余弦模板相关。随机噪声、工频干扰及其谐波在1kHz频点上的投影都很小。特别是当采样长度包含整数个激励周期时DFT相当于一个完美的窄带滤波器带外频率成分的贡献被极大抑制。但DFT单点提取有一个前提采样频率必须与激励频率精确同步。如果两者不同步会产生频谱泄漏能量会从1kHz频点泄漏到邻近频点测出来的幅值就会周期性波动。这就是为什么我放弃了简单的定时器自由运行采样转而使用同步触发方案。3.3 定时器触发的同步采样方案硬件上我使用STM32的TIM1产生1kHz的PWM脉冲同步触发DAC在DMA模式下更新正弦波数据同时TIM1的另一个通道产生的触发信号送给ADC的外部触发引脚让ADC在每个正弦波周期的固定相位开始采集。这样采样序列与激励信号之间保持严格的相位关系不需要在软件里做相位对齐。ADC采样率我最终定为64kHz即每个信号周期采64个点总共采1024个点对应16个信号周期。为什么选这个配置我简单算一下64kHz采样率对1kHz信号的每个周期有64个采样点正弦波幅值测量的量化误差已经小到可以忽略1024个采样点对应16个整数周期DFT的频率分辨率为 (64k / 1024 62.5) Hz1kHz正好在整数频点上没有频谱泄漏总采样时间为16ms测量速度快在电池极化状态还没有明显变化时数据已经采集完毕在代码结构上ADC使用DMA双缓冲模式。一块缓冲区在采集数据的同时另一块缓冲区的数据交给CPU做DFT运算。这样数据采集和数据处理可以并行进行单次测量的处理时间不超过2ms整机的测量周期可以做到几十毫秒一次完全能满足在线监测的需求。3.4 意料之外的混叠问题不加抗混叠滤波器的代价这里必须复盘一个踩过的坑。最初我把硬件带通滤波器去掉了直接让ADC采样放大后的信号想着反正软件里做DFT能够选频。结果就是——测量结果在某些工况下会出现偶发性的剧烈跳变。后来用示波器对比放大后的信号和ADC采样数据才发现问题出在高频噪声混叠。AD620放大后的信号里除了1kHz的有用信号还混杂了大量高频噪声比如开关电源的开关噪声几十kHz到几MHz、无线电干扰等。按照奈奎斯特定理这些超过ADC采样率一半32kHz的高频分量会被折叠回低频段混进1kHz附近而DFT无法分辨混叠进来的假频率分量。所以硬件上的抗混叠滤波器不是可有可无的装饰品而是采样系统正确工作的前提。我重新加回了硬件带通滤波器问题解决。4. 信号处理优化从能测到测准的关键算法调整数据采集上同步了之后测出来的内阻读数已经比较稳定了但离精准测量还有距离。仔细分析误差来源后我在算法层面又做了三轮优化。4.1 过采样与求平均把12位ADC的等效分辨率撑上去STM32内置ADC是12位分辨率用来测量1mV左右的信号虽然放大过但量化噪声和随机噪声依然存在。有没有办法在不更换高分辨率ADC芯片的前提下提升有效分辨率有的就是过采样与求平均技术。简单说如果对同一个稳定的信号重复采样 (4^m) 次并求平均等效分辨率可以提升 (m) 位。原理在于量化噪声可以被视为近似均匀分布的随机噪声多次采样求平均可以把这类噪声的功率压低。当然这个技术有一个前提——信号本身必须是带限的且采样之间必须存在足够的随机抖动或噪声注入否则所有采样值都一样求平均没有意义。我实测下来当采样率从64kHz提升到256kHz每个周期256个点再做4点平均等效分辨率从约12位提升到了约14位测量重复性有了肉眼可见的改善。这里的代价是采样数据的计算量变大好在STM32F4系列带FPU和DSP指令在单点DFT场景下只需要算2个频点256kHz采样率下1024个点的DFT运算成本完全可承受。4.2 相位无关的幅值修正用I/Q两路分量消除相位误差在早期的同步采样方案中我发现不同温度下测得的同一块电池的内阻值存在约2%~3%的偏差。最开始怀疑是温度影响电池内阻本身的真实变化但我用恒温箱对比后发现温度引起的真实内阻变化没有这么大。反复排查后我终于定位到了问题AD620的相频特性、隔直电容的相位偏移、带通滤波器的群延迟都会随温度发生微小变化。这些相位偏移虽然不影响信号幅值但如果算法中把采样序列与固定相位的正弦/余弦模板做相关运算相位偏移会导致幅值估计偏低——具体偏移量等于 (\cos\theta)(\theta) 是相位偏移角。当 (\theta) 变化几度时幅值误差就有百分之几的变化。解决思路很巧妙不直接计算幅值而是把DFT结果分解为同相分量I和正交分量Q这其实就是DFT结果的实部和虚部[ I \sum_{n0}^{N-1} x[n] \cos(2\pi f_0 n / f_s) ][ Q \sum_{n0}^{N-1} x[n] \sin(2\pi f_0 n / f_s) ]信号幅值为[ U_m 2\sqrt{I^2 Q^2} / N ]因为 (\sqrt{I^2 Q^2}) 与信号和采样模板之间的相位差无关这样无论相位怎么漂移幅值估计都保持准确。这个改动看起来只改了一行代码但效果立竿见影——温度变化下的测量偏差从2%以上降到0.3%以内。4.3 限幅滤波与中值滤波的配合电池在测量过程中偶尔会遭遇外部干扰脉冲比如感性负载的启停瞬间会通过空间耦合到测量电缆上。DFT对随机噪声有很好的抑制作用但这种大幅值的窄脉冲干扰会让DFT结果产生可见的跳变。我在DFT计算之前加入了一层预处理对ADC采样序列先做一次限幅判断如果某个采样点的值与前一个采样点的差值超过预设阈值比如超过信号最大可能变化速率的1.5倍就判定该点为干扰点用相邻点的均值替换。这个操作成本极低但对偶发脉冲干扰的抑制效果非常显著。同时在最终的内阻计算层面我又做了一次五取中值的中值滤波——连续测量五次取中间值作为最终结果。在测量周期只有几十毫秒的前提下五次测量带来的时间延迟完全不影响使用体验。4.4 关于电缆电容和接触电阻的校准问题还有一个容易被忽视的误差来源测量电缆的分布电容和残余电感。在1kHz频率下电缆电容的容抗非常大1m电缆几十pF的电容容抗都在兆欧级别影响可以忽略。但电缆电感和开尔文夹头的残余电感会在1kHz下产生微小的感抗分量与电池内阻叠加后使读数略微偏高。校准方法是把开尔文夹头短接不接电池测到的阻抗就是系统本底阻抗。软件里把每次测量的阻抗减去这个本底值即可完成校准。注意本底阻抗随温度会有微小变化如果追求极致精度可以在系统开机自检时自动执行一次短接校准。5. 系统实现与实测STM32工程架构与测量结果分析算法方案确定后落地到STM32工程时还有一些工程化的细节需要处理这里一并分享。5.1 软件架构与任务划分我在STM32F407上实现了这个系统软件架构划分为几个层次定时器/DAC驱动层负责生成1kHz正弦波激励通过DMA循环输出波形表ADC/DMA采集层负责同步触发采样、DMA搬运、双缓冲区切换信号处理层负责DFT计算、幅值解算、滤波和温度补偿应用层负责校准、数据显示、按键处理、通信和存储这种分层的好处是每层都可以独立测试。我在调试阶段给信号处理层写了一个模拟数据注入接口——直接把一组已知幅值的正弦波数据喂给处理层验证算法正确性。这样就不需要每次都接真实电池来排查软件逻辑问题了。5.2 关键处理环节的代码示例正弦波表的生成和DFT计算是其中最核心的两个函数。正弦波表我用了预先生成的查找表#define SINE_TABLE_SIZE 64 #define ADC_BUF_SIZE 1024 // 在初始化时生成1kHz正弦波查找表16位DAC0x0000-0x0FFF uint16_t sin_table[SINE_TABLE_SIZE]; uint32_t table_init_done 0; void sine_table_init(void) { for (uint16_t i 0; i SINE_TABLE_SIZE; i) { double phase 2.0 * 3.14159265358979 * i / SINE_TABLE_SIZE; // DAC为12位幅值取中间4000个码留出偏置余量 sin_table[i] (uint16_t)(2048 1700 * sin(phase)); } table_init_done 1; }DFT函数使用逐点累加的方式避免对整个采样数组做二次寻址typedef struct { float real; float imag; float mag; float phase; } dft_result_t; #define FS 256000.0f #define F0 1000.0f #define N 1024 // 预先计算好的正弦/余弦因子每采样一个点更新一次角度 void compute_dft(const uint16_t *adc_buf, uint16_t *ref_cos, uint16_t *ref_sin, uint16_t adc_offset, dft_result_t *out) { float sum_i 0.0f; float sum_q 0.0f; float scale 2.0f / N; for (uint16_t n 0; n N; n) { float x (float)adc_buf[n] - (float)adc_offset; float angle 2.0f * 3.14159265f * F0 * n / FS; sum_i x * cosf(angle); sum_q x * sinf(angle); } sum_i * scale; sum_q * scale; out-real sum_i; out-imag sum_q; out-mag sqrtf(sum_i * sum_i sum_q * sum_q); out-phase atan2f(sum_q, sum_i); }不过我想特别说明一下在实际工程里我并没有在DFT的每个采样点都调用cosf/sinf——这会消耗大量CPU周期。更好的做法是预先算好1024个点的正余弦因子存到Flash里DFT计算时直接查表累加。上面的代码是为了让算法逻辑更直观实际部署时务必做查表优化。5.3 实测数据不同状态电池的内阻对比我用这块项目板分别测了三块电池效果非常直观电池状态标称容量实测内阻1kHz带载压降观察新电池未循环12V 7Ah8.7 mΩ55W灯泡下电压跌幅约0.4V循环300次后的电池12V 7Ah15.2 mΩ55W灯泡下电压跌幅约0.8V报废电池电压虚高12V 7Ah42.6 mΩ55W灯泡下电压跌落到9V以下可以看到内阻读数和带载表现高度吻合。而且三次重复测量的标准差都在0.3mΩ以内说明系统测量重复性完全能够满足日常检测需要。5.4 和市售内阻仪对比的校准结果为了确保测量数据的可信度我还借了一台市售的专用铅酸电池内阻仪做了对比。在同一天、相同温度条件下分别用两台仪器测同一组电池电池编号市售仪器读数mΩ本机读数mΩ偏差19.19.40.3214.815.10.3340.941.70.8偏差基本在3%以内且偏差方向一致本机读数偏高说明存在一个系统性的正偏差。进一步排查后我推测是激励电流的相位噪声和系统本底阻抗校准不够精确导致的通过调整开尔文夹头短接校准流程后偏差进一步缩小到2%以内。这个精度对电池健康评估来说已经足够了——毕竟内阻值本身在电池老化过程中可能是倍增式变化的2%的误差对判断电池是否该换了几乎没有影响。6. 避坑总结与后续扩展方向整个项目从原理验证到样机稳定运行花了大约三周业余时间。回头梳理一下最值得拿出来分享的踩坑经验和优化要点有这么几点。第一交流和直流分量必须彻底分离。无论是隔直电容的位置、容值还是仪表放大器的选型都要围绕在强直流背景下测量微弱交流这个核心矛盾来设计。任何一级的直流泄漏都会让后级放大提前饱和。第二同步采样是DFT选频有效的前提。如果采样时钟和激励信号不同步DFT单点提取的优势会大打折扣。定时器同时触发DAC波形更新和ADC采样是最简洁可靠的同步方案。第三抗混叠滤波不能少。我在这上面栽过跟头后来才理解软件咋算也救不了硬件采错的数据这句话。DFT选频只能处理带内信号带外噪声如果已经混叠到带内神仙算法也滤不掉。第四相位变化对幅值测量的影响远比想象中大。用I/Q正交分量解算幅值把相位漂移从误差链路里剔除是提升测量精度性价比最高的一步。关于后续扩展我目前有几个明确的方向一是把激励频率做成可配置的支持从0.1Hz到10kHz的扫频测量这样不只能测内阻还能测电化学阻抗谱EIS电池模型辨识的维度会丰富很多二是加入温度传感器补偿——铅酸电池的内阻对温度非常敏感典型温度系数约3%/10°C如果要做到跨季节对比数据温度补偿必不可少三是考虑把激励电流从固定100mA改为自适应——根据电池内阻的初步测量结果动态调整电流幅值让信噪比在全量程范围内都保持最优。最后分享一个实际使用中的小技巧测量铅酸电池内阻时尽量在电池静置半小时以上再测至少也要静置几分钟因为刚充完电或刚大电流放电后的电池内部极化还没有完全消退此时测出的内阻会明显偏离稳态值。我自己就是在项目调试初期发现同一块电池上午和下午测的数据对不上排查了半天才意识到是电池状态不一致导致的而不是仪器的问题。这一点对于任何想复现这个项目的朋友都值得注意。
返回列表