☰
锂离子电池不同SOC下的宽带阻抗谱Matlab仿真
2026/10/19 5:28:02 网站建设 项目流程

做电池的人十有八九都遇到过这种困惑:同一颗电芯,放电到不同电量和刚充满电时,动态表现完全像两颗电池。想知道内部发生了什么,电化学阻抗谱(EIS)是目前最直观、信息量最丰富的手段之一。今天要聊的这个小项目,就是用Matlab计算不同充电状态(SOC)下锂离子电池的宽带电化学阻抗谱,从零开始仿真阻抗实部、虚部、模值、相位的变化规律,并且把完整代码放出来。适合正在做BMS开发、电池参数辨识、SOC估算,或者写论文需要仿真数据的朋友,尤其是那些不想天天泡在电化学工作站上,想先从模型层面把阻抗响应悟透的人。

EIS这个东西,说难不难,说简单也不简单。它本质上就是把电池当成一个复杂的电路网络,通过扫描频率来看它对交流小信号的响应。低频看扩散,中频看电荷转移,高频看欧姆接触,每一个频段都对应电池内部一个具体的物理化学过程。而SOC不同,这些过程的强度就不同,反映到阻抗谱上是整条曲线的形态变化。这个项目要做的,就是把这种变化用等效电路模型定量算出来,并且可视化。

内容整体设计与思路拆解

1.1 为什么非要“扫SOC”不可

锂离子电池的充放电过程,本质上是锂离子在正负极之间来回嵌入和脱嵌。SOC低的时候,负极石墨里锂都快被抽光了,正极材料里的锂也少,这时候锂离子嵌入/脱出的驱动力不足,电荷转移电阻会明显变大。SOC高的时候,特别是接近满充的末端,电极表面锂浓度接近饱和,容易出现极化,阻抗又会有一个快速抬升的趋势。中间平台区的阻抗相对稳定,变化幅度小。

这种“SOC-阻抗”的关联性在实际工程里非常重要。比如做SOC估算的时候,单纯靠开路电压(OCV)拟合,曲线平台区太平坦,电压法测不准;如果在模型中引入一个随SOC变化的阻抗响应,辨识精度就能提上去。又比如快充策略制定时,如果能预判当前SOC下电池的电荷转移电阻有多大,就能在阻抗增大时主动降流,避免负极析锂。再比如电池健康状态(SOH)诊断,很多老化的核心表现就是SEI膜增厚导致欧姆阻抗和界面阻抗上升,但这种上升在不同的SOC下表现不一样,不把SOC维度扫全,很容易误判。所以这个项目选择“不同充电状态”作为变量,不是随意定的,是实际需求驱动的。

1.2 宽频带到底“宽”在哪里

“宽带”这个词,略微有点唬人。放在EIS语境里,它指的是频率范围要跨足够的数量级,把不同时间常数的物理过程都覆盖到。高频段(kHz到百kHz)对应的是电解液离子传导和集流体接触电阻,响应非常快;中频段(Hz到kHz)对应电荷转移和双电层充放电,时间常数在毫秒到秒的量级;低频段(mHz到Hz)对应锂离子在固相中的扩散,响应很慢,要扫很久。

我在这套代码里把频率范围定在0.01 Hz到100 kHz,跨越7个数量级。上限取100 kHz,是因为再往上,寄生电感和电缆电容等干扰开始主导,对电池本体研究意义不大;下限取0.01 Hz,是因为更低频率的测量耗时太长,而且电池在低频下可能会有明显的非线性响应,仿真计算也会面临数值稳定性问题。这个频率范围是工程上的一个常用折中区间。

1.3 方案选型与工具选择

做这类仿真,可选的工具其实不少:Python的科学计算栈、Simulink、甚至直接用电子表格硬算。我最终选了Matlab纯脚本实现,理由有三条。

第一,阻抗计算本质是复数数组运算,Matlab对向量化的复数运算支持极好,代码写起来和数学表达式几乎一一对应,不容易出错。第二,EIS结果的可视化要求比较高,Nyquist图要保持纵横比一致,Bode图要对数坐标,Matlab的绘图函数改起来非常顺手。第三,后续如果需要扩展成参数拟合、蒙特卡洛分析、甚至和实测数据做对比,Matlab的优化工具箱和曲线拟合工具都是现成的。至于为什么不直接用Simulink搭电路仿真,那是因为等效电路模型的阻抗解析表达式可以直接推导,用不到那么重的建模手段,脚本反而更轻、更透明。

等效电路模型是整套仿真的核心。这里我选用了经典的Randles类改进电路,拓扑结构为:欧姆电阻R0,串联一个电荷转移电阻Rct与恒相位元件CPE的并联支路,再串联一个Warburg扩散阻抗W。这个拓扑是锂离子电池阻抗建模最常用的基础结构,既能覆盖主要的物理过程,又不至于复杂到参数辨识困难。

核心细节解析与实操要点

2.1 等效电路模型各元件的物理映射

电路拓扑确定之后,关键是每个元件要和电池内部的实际过程对上号。

R0是欧姆阻抗,主要来源是电解液离子电导、隔膜、集流体与电极界面的接触电阻。它的特点是频率无关,在Nyquist图上表现为高频段与实轴的交点。Rct是电荷转移电阻,描述的是锂离子在电极/电解液界面发生电化学反应时克服激活势垒的难易程度。这个电阻直接与SOC、温度、界面SEI膜状态挂钩,是诊断电池健康状态的重要指标。

CPE是恒相位元件,替代理想电容来描述双电层。因为电极表面不是光滑的,孔隙率高、表面粗糙、电流分布不均,这些都导致实际的双电层行为偏离理想电容,表现为“弥散效应”。在数学上,CPE阻抗表达式是Z = 1 / (Y0·(jω)^α),其中α取值在0到1之间,α越接近1,CPE越接近纯电容;α偏离1的程度,可以直观理解成电极表面的粗糙度或不均匀性。Warburg阻抗描述锂离子在固相颗粒内部的半无限扩散过程,它在低频段表现为一条与实轴夹角45°的直线,斜率反映扩散阻力。

2.2 CPE参数与Warburg阻抗的计算要点

这里有两个特别容易踩坑的细节,必须单独拎出来说。

CPE的阻抗表达式里,Y0的量纲不是简单的法拉第量纲,而是依赖于α的值,单位是F·s^(α-1)。很多新手在设置参数时只看数值,不看量纲,结果代进去之后阻抗量级彻底乱掉。实际工程中,Y0通常在mS级别的数值范围内,代码里我将其量级设置在10的负3次方到负4次方之间,这样计算出的时间常数才能落在合理的阻抗谱频段。

Warburg阻抗也有两个版本:半无限扩散和有限层扩散。半无限扩散假设扩散层厚度远大于离子扩散长度,阻抗表达式是Zw = σ·(1−j)/√ω,在Nyquist图上就是标准的45°直线。有限层扩散则假设扩散层有边界,低频段会出现一个弧型的向上弯曲。对于常规商业化锂离子电池的阻抗谱测量频段,半无限扩散模型在大多数情况下足够使用,所以代码里采用半无限模型。如果你测量的是微型电极或者是固态电解质体系,那可能要考虑有限层模型,这是后话。

2.3 SOC如何驱动模型参数变化

等效电路定了,下面就是把元件参数写成SOC的函数。这一步是整个仿真“活”起来的关键。参数随SOC的变化关系,不是纯数学拟合凭空捏造的,它是从实际电化学机理和大量实验中提炼出的规律。

Rct和SOC的关系,通常是指数衰减的形式。低SOC时,锂离子浓度低,界面电荷转移困难,Rct很大;随着SOC上升,浓度升高,反应活性增强,Rct迅速下降并趋于平缓。接近满充时,由于电极表面浓度极化和反应动力学的限制,Rct可能出现小幅回升。代码中我用指数函数加常数项来模拟这个基本走势,虽然没有实测数据那么精确,但趋势是符合电化学规律的。

Warburg系数σ同样和SOC强相关。锂离子在固相中的扩散系数本身就与嵌锂浓度有关,低SOC下浓度较低,扩散驱动力不足,σ偏大;高SOC下浓度较高,扩散路径上的锂离子浓度梯度变小,σ有所下降。R0受SOC影响相对较弱,但随着SOC升高,电解液中的锂盐浓度和电导率会有小幅变化,所以代码里也给了它一个轻微的正向线性修正。

2.4 参数取值合理性

仿真不是随便扔几个数进去算出来就完事,参数的物理量级必须落在合理区间,否则结果没有参考意义。

以一颗容量约2 Ah的软包电池为例,R0的典型量级在10到20毫欧之间,代码里设在0.012欧姆附近,是合理的。Rct的量级在几毫欧到几十毫欧之间,低SOC时可以到50毫欧以上,高SOC时降到几毫欧,代码里的指数函数范围大致覆盖了这个区间。CPE的Y0在mS级,α在0.8左右,这是多孔电极常见的弥散指数范围。Warburg的σ系数,量级大约在10的负4次方量级,具体数值和电极厚度、扩散系数有关。如果算出来的Nyquist图高频半圆半径大得离谱,或者低频扩散尾短得看不见,优先怀疑是某个参数的物理量级设置出了问题。

Matlab代码实现与运行流程

3.1 主脚本结构与计算流程

整套代码的架构非常清晰,按“参数初始化、SOC循环、阻抗计算、结果绘图”四段式组织。先设定SOC扫描向量和频率向量,然后对每一个SOC点分别计算等效电路的总复阻抗,最后统一绘图。这种方式逻辑直观,即使没看过代码的人,拿到手也能顺着走。

频率向量用logspace(-2, 5, 600)生成,即在0.01 Hz到100 kHz之间取600个对数均匀分布的点。取600个点的原因是画Nyquist图时要有足够密的点才能把半圆的弧度画平滑,尤其是低频扩散尾和半圆相接的位置,点数太少会出现折线段感。

SOC扫描数组用的是0.1:0.1:1.0,从10%扫到100%,每隔10%取一个点,一共10条阻抗曲线。这个间隔密度用来观察趋势是够的,如果你想看细节,改成0.05:0.05:1.0,脚本会自动多算9条,完全不用改其他代码。

3.2 核心计算代码逐段解析

下面是整个计算过程的核心代码块,我加了比较详细的注释。

% ============================================= % 不同SOC下锂离子电池宽带阻抗谱仿真 % 等效电路:R0 + (Rct // CPE) + Warburg % ============================================= clear; clc; close all; % 1. SOC扫描范围:10% ~ 100%,步长10% SOC_list = 0.1:0.1:1.0; % 2. 宽带频率范围:0.01 Hz ~ 100 kHz freq = logspace(-2, 5, 600); omega = 2 * pi * freq; s = 1i * omega; % 3. 预分配存储数组 Z_all = zeros(length(SOC_list), length(freq)); absZ_all = zeros(length(SOC_list), length(freq)); phaseZ_all = zeros(length(SOC_list), length(freq)); % 4. 对每个SOC计算阻抗 for k = 1:length(SOC_list) soc = SOC_list(k); % 4.1 欧姆电阻:随SOC微幅增加 R0 = 0.012 + 0.003 * soc; % 4.2 电荷转移电阻:SOC越低,Rct越大 Rct = 0.035 * exp(-4.5 * soc) + 0.004; % 4.3 CPE参数:Y0随SOC增大,alpha在0.8附近 Y0 = (0.8 + 0.6 * soc) * 1e-3; alpha = 0.80 + 0.06 * soc; % 4.4 Warburg系数:SOC越低扩散阻抗越大 sigma_w = 8e-4 * (1.05 - soc); % 4.5 各支路阻抗计算 Z_ohm = R0 * ones(size(s)); % 欧姆阻抗 Z_cpe = 1 ./ (Y0 * s.^alpha); % CPE阻抗 Z_rc = (Rct .* Z_cpe) ./ (Rct + Z_cpe); % Rct与CPE并联 Z_warburg = sigma_w ./ sqrt(omega) .* (1 - 1i); % 半无限扩散 Z_all(k, :) = Z_ohm + Z_rc + Z_warburg; % 4.6 提取阻抗模值、相位、实部和虚部 absZ_all(k, :) = abs(Z_all(k, :)); phaseZ_all(k, :) = angle(Z_all(k, :)) * 180 / pi; end

这段代码里最核心的坑在CPE的幂运算。Matlab的s.^alpha对复数数组进行幂运算时,底层采用的是复变函数的多值分支,结果可能和你预想的角度范围有差异,但它本身符合数学定义。真正要注意的是Y0的量级,如果Y0设置成纯电容的数值比如10的负6次方量级,算出来的CPE阻抗会在兆欧量级,Nyquist图直接飞出去。

Warburg项有一点要特别提醒:表达式里我用的是1 - 1i,在Matlab里虚数单位写作1i,不要写i。虽然在Matlab里两者默认都能用,但在复杂的复数运算中,i有被当作变量覆盖的风险,会导致计算结果完全错乱。这种问题排查起来非常痛苦,所以代码里一律用1i。

3.3 可视化输出与图形解释

计算完成后,绘图部分是理解结果的窗口。最常用的两个图是Bode图和Nyquist图。Bode图展示阻抗模值和相位随频率的变化,适合看频段特征;Nyquist图展示实部与虚部的关系,适合看等效电路各个过程的“形状”。

下面的绘图代码把不同SOC的结果用颜色渐变区分开,生成论文里常见的颜色渐变曲线组。

% ========== 图1:Bode模值图 ========== figure('Position', [100, 100, 700, 500]); cmap = parula(length(SOC_list)); hold on; for k = 1:length(SOC_list) loglog(freq, absZ_all(k, :), 'Color', cmap(k, :), 'LineWidth', 1.5); end hold off; xlabel('频率 f (Hz)', 'FontSize', 12); ylabel('阻抗模值 |Z| (Ω)', 'FontSize', 12); title('不同SOC下阻抗模值Bode图'); legendCell = cellstr(num2str(SOC_list', 'SOC=%.0f%%')); legend(legendCell, 'Location', 'northeast', 'FontSize', 9); grid on; % ========== 图2:Nyquist图 ========== figure('Position', [850, 100, 700, 500]); hold on; for k = 1:length(SOC_list) reZ = real(Z_all(k, :)); imZ = -imag(Z_all(k, :)); % 虚部取负,符合EIS惯例 plot(reZ, imZ, 'Color', cmap(k, :), 'LineWidth', 1.5); end hold off; xlabel('实部 Re(Z) (Ω)', 'FontSize', 12); ylabel('虚部 -Im(Z) (Ω)', 'FontSize', 12); title('不同SOC下的Nyquist图'); axis equal; % 保持纵横比一致,避免圆弧变形 legend(legendCell, 'Location', 'northeast', 'FontSize', 9); grid on;

画Nyquist图时axis equal非常重要。如果不加这一行,Matlab默认会把坐标轴拉伸,明明是一个圆弧,硬生生会被画成椭圆,甚至看起来像一条直线,非常误导人。很多新手第一次画Nyquist图觉得“哪里不对”,十有八九就是栽在这个地方。

3.4 结果长什么样才算合理

跑完这段代码,你会在Nyquist图上看到10条形状类似但尺度不同的曲线:高频起点落在实轴0.012附近,中频是一个圆弧,圆弧的直径对应Rct,低频末端拖着一条45度斜向上的扩散尾巴。随着SOC从10%增加到100%,圆弧直径明显缩小,说明Rct在减小,这与低SOC下锂离子嵌入困难、界面电荷转移阻抗大的机理完全一致。与此同时,低频扩散尾的长度也在变化,SOC越低尾巴越长,说明扩散阻抗越大。

Bode图上,阻抗模值曲线在高频段会趋于平稳,数值等于R0;进入中频段后开始抬升,形成一个平台再下降或进入扩散区;低频段再次抬升。不同SOC的曲线在低频段的分层最明显,这个现象和实际电池测试的结果是对得上的。如果你算出来的图出现高频半圆直径不随SOC变化,或者扩散尾巴完全消失,先回头检查Rct与sigma_w的SOC依赖公式。

常见问题与排查技巧实录

4.1 Nyquist图高频半圆“塌了”或“挤成一团”

这是最常遇到的问题。半圆挤成一团,多半是因为Rct和CPE构成的时间常数正好落在你取的频率间隔中间,导致采样点太少,圆弧看起来不完整。解决思路有两个:一是把频率向量加密,把600改成1000甚至1500;二是重新设计Rct和Y0的数值量级,让时间常数τ=Rct·Y0^(1/alpha)落在你关注的频段内。实际操作中我通常先用代码画出半圆的峰值频率,再反过来调参数,效率更高。

另一种情况是半圆“塌了”,即看起来像一条压扁的弧。这时基本可以断定是CPE里的α设置得太小,比如小于0.6,导致圆弧压得很平。α的物理含义是弥散程度,商用锂离子电池通常在0.75到0.9之间,如果仿真里你想模拟一个老化很严重的电池,可以把α调低到0.7以下,但如果是为了对标新鲜电芯,α保持在0.8以上更合理。

4.2 低频频段曲线出现锯齿或跳变

低频出现锯齿,尤其是扩散尾巴附近,通常有两个原因。第一个是omega在某些频点接近零导致数值问题,但0.01 Hz的下限不会触发。真正常见的原因是s.^alpha在低频小角度情况下的数值精度问题,因为复数的幅角很小,幂运算会放大数值误差。解决办法是改用双精度默认数据类型,并且在计算时先用double(omega)强制转换。第二个原因是绘图时坐标轴的自动缩放把低频段的微小起伏放大了,可以在yline或xlim上手动限制坐标范围,避免极端值干扰视觉。

4.3 参数初值设置与收敛心态

如果这个代码后续被用来做实测数据拟合,你会遇到一个绕不开的问题:等效电路参数初值给多少合适。我的经验是,先用仿真代码跑一遍,把不同SOC下的参数走势记下来,这些仿真值本身就是实测拟合的最强先验初值。比如实测低SOC点的Nyquist圆弧直径大约0.04欧姆,那Rct初值就填0.04左右,再去跑迭代优化,收敛速度和稳定性会好很多。千万不要把初值随便设成1欧姆、1e-6法拉这种拍脑袋的数,优化算法很容易跑进局部极小值出不来,那时候你会以为是算法不行,其实只是初值太离谱。

4.4 仿真与实测对不上的常见原因

仿真模型毕竟是理想化的,和实测数据有偏差很正常。最常见的原因是忽略了接触阻抗和电缆分布电感,这些在高频段影响很大,实测Nyquist图高频起点往往会往上偏移,而仿真里没有这个分量。第二个常见原因是实际电池的扩散阻抗不完全是半无限行为,在极低频段会看到扩散尾部向上弯曲,这是有限层扩散的特征。如果对标的实测数据有这样的弯曲,你需要在模型里把Warburg替换成有限层扩散阻抗,表达式变成Zw=(σ/√(jω))·coth(√(jωT)),T是扩散时间常数。这个改进不难,但效果很直观。

实操心得与扩展方向

5.1 从仿真走向实测的完整链路

这个项目做完,我自己的体会是它不是一个孤立的仿真Demo,而是一条完整研究链路的起点。仿真代码算出来的阻抗谱,可以先用来回答“SOC变化会让阻抗谱怎么动”这个定性问题,然后再拿着电化学工作站去测真实的电池,测出来的数据反过来标定模型的参数。仿真和实测互相印证,形成闭环,你的等效电路模型才会越来越可信。

有一个细节值得分享:在做阻抗实测时,SOC的标定方式会影响结果。同样标称50% SOC,通过静置电压标定和通过累计电量标定,实际状态可能有几个百分点的偏差,而阻抗谱尤其是Rct对SOC的微小差异很敏感。所以你在用仿真结果和实测对比时,一定要明确SOC的标定方法,否则你会看到数据偏移,但根本说不清是模型问题还是SOC定义问题。

5.2 模型如何扩展成更实用的工程工具

如果你后面想把这个代码用到更真实的场景,有几个升级方向可以参考。

温度维度加入后,Rct、扩散系数和电解液电导率都服从Arrhenius关系,低温下Rct成倍增长。和SOC叠加起来就是SOC-温度-阻抗三维响应面,这对BMS的低温快充策略设计特别有用。老化维度加入后,SEI膜增厚导致R0上升,活性物质损失导致Rct曲线整体上移,这些变化趋势都可以建模。更进一步,那种利用阻抗谱特征和SOC/老化状态的机器学习模型,训练数据里就需要大量全SOC范围的阻抗仿真谱作为初始样本,这个代码正好可以做数据生成器。

5.3 最后说一个常被忽略的小问题

在整套代码里,R0对SOC的依赖我用了线性修正,量级很小。但在真实电池里,R0受温度影响远大于受SOC影响。这种做法是有意的:它让代码具备SOC维度的丰富变化,又不会过度复杂化。但如果你要把这个模型用到实车BMS上,千万别忘了温度和SOC耦合对R0的影响,最好做一个二维查表,而不是用单一变量公式。仿真模型的价值在于帮你建立直觉,工程落地的准确性要靠实测数据来不断修正和验证。这是我在这个项目里最深刻的体会。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询