做锂电池仿真的人,十有八九都绕不过等效电路模型这条路。而在这条路上,二阶RC模型基本上是大家在工程实践中最常落地的折中方案——它比一阶模型多描述了一个极化过程,精度上了一个台阶;又比三阶、四阶模型少了大量参数辨识的麻烦,算起来也快。这篇文章我就把我的Simulink搭建思路、参数获取方法、常踩的坑和排查经验一次性梳理出来,给正在做BMS算法验证、电池状态估计或者整车能量管理仿真的朋友做个参考。
1. 为什么是二阶RC模型而不是一阶或三阶
1.1 等效电路模型的本质
锂电池的等效电路模型,说白了就是用电阻、电容这些基本电路元件,去近似描述电池内部的电化学行为。你不需要把正负极材料、电解液浓度分布、锂离子扩散过程全建出来,只需要抓住外部端电压对电流激励的响应特性,就可以用这个模型去估算SOC、SOP、SOH这些关键状态。实际做BMS算法开发的时候,绝大多数人用的都是这种模型,因为它计算量小,还容易在单片机或者嵌入式平台上跑起来。
一阶RC模型在数学上就是一阶系统,端电压对电流阶跃的响应就是一条单指数的曲线。但真实的锂电池,在电流突变之后电压的响应是明显分为两个阶段的:先是一个几秒到几十秒的快速变化,这个对应电化学极化;然后是一个几十秒到几百秒的缓慢变化,这个对应浓差极化。单个RC网络只能拟合其中一个阶段,强行用一阶模型去拟合完整的动态响应,必然出现较大误差。
1.2 一阶、二阶、三阶RC模型的对比
二阶RC模型就是把两个时间常数不同的RC网络串联起来,让它们分别去描述快速极化和慢速极化两个过程。我在实际项目中做过对比,同样的HPPC测试数据下,一阶模型的端电压拟合误差通常在30到80毫伏之间,而二阶模型能压到10毫伏以内。三阶模型理论上还能再提高一点精度,但对参数辨识的要求高了很多,而且容易出现过拟合——你会发现辨识出来的三组RC参数中有两组时间常数非常接近,这时候第三个RC网络的存在意义就不大了,反而增加了计算负担。
| 模型类型 | 动态响应描述能力 | 端电压拟合精度 | 参数辨识难度 | 计算开销 |
|---|---|---|---|---|
| 一阶RC | 只覆盖单一时间常数 | 较差,高倍率工况下误差大 | 低 | 很低 |
| 二阶RC | 区分快/慢两个极化过程 | 好,误差10mV以内 | 中等 | 低 |
| 三阶RC | 能覆盖更多频率段 | 提升有限,容易过拟合 | 高 | 中等 |
所以在实际项目中,只要不是做非常高精度的电化学边界条件分析,二阶RC模型就是性价比最高的选择。
1.3 这个模型适合干什么
二阶RC模型在Simulink里搭建起来之后,可以做的方向非常多:你可以用它做SOC估算算法的验证——比如卡尔曼滤波的观测器设计;也可以用它做电池在特定工况下的温升估算,配合热模型一起跑;还可以把它集成到整车模型里,做能量管理策略的仿真验证。我现在自己更多是把二阶RC模型作为电池状态估计的核心被控对象模型,在这个基础上做观测器、自适应滤波算法,效果很稳定。
2. 建模前的准备工作:参数从哪来
2.1 HPPC测试与参数辨识思路
搭建二阶RC模型之前,你得先有电池的参数。这些参数不是查手册查出来的,而是要从实验数据里辨识出来。目前行业里最常用的就是HPPC混合脉冲功率特性测试。测试方法不复杂,在电池SOC的每个点(一般是每10% SOC取一个点)上,先给一个固定倍率的放电脉冲,比如1C或者2C,持续10秒,然后静置足够长的时间让电压恢复稳定,再给一个同样倍率的充电脉冲,再静置。整个过程中记录电流和端电压数据,后面参数辨识全靠这份数据。
很多新手会犯一个错误:脉冲之后静置时间不够长,电压还没来得及恢复到开路电压就进入下一个工况了。这样辨识出来的OCV是偏的,后面所有RC参数也都会跟着错。我一般建议静置时间至少要在30分钟以上,条件允许的话做到1小时,宁可多花时间也要拿到准确的OCV。
2.2 参数分离的物理逻辑
拿到HPPC数据之后,怎么把R0、R1、C1、R2、C2这几个参数分离出来?核心逻辑是利用时间尺度的差异。
电流撤掉的那一刻,欧姆内阻R0引起的压降是瞬间消失的——这是唯一一个没有时间延迟的分量。所以你在电流跳变的那个点上,端电压的突变量除以电流跳变量,就是R0。比如放电电流从0跳到10A,端电压瞬间掉了60mV,那R0大概是6毫欧。
接下来是快速极化的RC网络R1C1,它对应的是电压在几十秒内逐渐恢复的那段。把电压恢复曲线减去R0的瞬态跳变,剩下的部分去拟合两个指数衰减项。时间常数小的那一项就是R1C1,时间常数大的那一项就是R2C2。这里需要用最小二乘或者非线性拟合工具来处理,Matlab的curve fitting toolbox就能干这个活。
我把这个过程再捋一下:
- 在电流阶跃瞬间,通过电压突变幅度计算R0;
- 在静置阶段,提取电压恢复曲线;
- 用双指数函数对电压恢复曲线做拟合,得到两个时间常数及对应的幅值;
- 根据时间常数和幅值反算R1、C1、R2、C2;
- 对每个SOC点重复上述过程,得到参数随SOC变化的曲线。
最后你会得到一组随SOC变化的参数表:R0(SOC)、R1(SOC)、C1(SOC)、R2(SOC)、C2(SOC),以及OCV(SOC)曲线。这些查表数据就是接下来Simulink模型的输入。
2.3 温度的影响要不要考虑
锂离子电池的参数对温度非常敏感,尤其是低温环境下,内阻会成倍上升。如果你的模型只做常温下的估算,那参数表可以只标定25℃即可;但如果你要覆盖更宽的温度范围,最好在-20℃、0℃、25℃、45℃几个温度点都做一遍HPPC,然后把参数表扩展成二维的,SOC和温度作为两个查询维度。
做整车仿真的时候温度的影响尤其明显,冬天低温状态下的SOC估算误差经常就是因为忽略了温度对RC参数的影响。我在早期的项目里吃过这个亏,后来老老实实补做了几个温度点的标定,模型精度就上来了。
3. Simulink模型搭建全流程
3.1 顶层层级设计
在Simulink里搭模型,我习惯先把顶层结构设计好,再逐级往下细化。顶层的输入是电流,输出是端电压和SOC。电流信号从外面给进来,正值为放电,负值为充电。如果后面要跑工况仿真,这个电流输入可以直接接一个From Workspace模块读取工况数据。
这个模型本身的输出,端电压和SOC,你要接Scope去看波形,也可以往上层传到整车模型里作为电池包的响应。
3.2 底层子模块划分
我把模型分成了这么几个子模块:
SOC计算模块:用安时积分法计算SOC。输入是电流,经过一个增益1/Q(Q是电池容量,单位Ah转成As要乘3600)后进入积分器,积分结果再除以额定容量得到SOC,最后加初始SOC的偏移量。要注意积分器的初值设置成初始SOC对应的数值,或者把SOC计算单独做成初始条件可配置的形式。
OCV查表模块:用Lookup Table模块,输入SOC,输出对应的开路电压OCV。这个查表数据就是之前HPPC辨识出来的OCV-SOC曲线。
欧姆内阻模块:电流乘R0(SOC)得到瞬时压降。因为R0是SOC的函数,所以查表模块的输出要跟电流做乘法。
两个RC网络模块:每个RC网络用下面的微分方程表示:
code复制Urc_dot = (I - Urc / R) / C
在Simulink里就是减法和除法先算,得到的结果进积分器,积分器的输出就是Urc。再把Urc通过一个增益1/R反馈回去和电流I相减。我通常直接用积分器模块,方便同时拿到Urc作为输出。
端电压聚合模块:端电压等于OCV减去欧姆压降,再减去两个RC网络的电压:
code复制Ut = OCV(SOC) - I * R0(SOC) - U1 - U2
这里把R0的查表和电压聚合放在一起也行,但我喜欢单独做,因为后面要接入观测器的时候,这些中间变量都要能从模型里引出来。
3.3 关于代数环的处理
用Simulink搭这类模型,最容易碰到的问题就是代数环。当你把电流作为输入查R0表,再查OCV表,这两个模块的输出都取决于当前仿真步的输入,而SOC又通过积分器反馈过来,一不留神就会形成代数环。
处理代数环的办法有好几种:最简单的是在反馈回路上加一个Memory模块或者Unit Delay模块,把上一步的SOC值用到这一步的查表里。这样模型就变成了离散近似,但仿真速度会快很多,精度损失可以忽略。我平时用离散求解器的时候,更倾向于用Unit Delay,因为它清除了代数环同时还保持了模型的因果性。
还有一个办法是用Simscape的物理建模环境,物理网络求解器会自动处理这类代数约束,不需要你手动干预。但Simscape的电池模型参数是封装好的,自定义起来反而麻烦,不如自己用基础模块搭出来的灵活。
3.4 完整Simulink结构示例
把上面的模块组织起来,一个标准的二阶RC模型的信号流大概是这样:
电流I进入模型,同时流向三条路径。
第一条路径,电流通过增益1/Q进积分器得到SOC,SOC作为查询条件到OCV查表模块得到开路电压,同时也作为R0、R1、C1、R2、C2查表模块的查询条件。
第二条路径,电流经过R0查表输出的电阻值相乘,得到欧姆压降I*R0。
第三条路径,电流分别进入两个RC网络子模块。每个子模块内部,电流和反馈回来的Urc/a求差,乘以1/C再积分,得到Urc。这个Urc既作为本模块输出,又通过1/R反馈回去参与下一时刻的计算。
最后在汇总点上:OCV - I*R0 - U1 - U2,得到端电压Ut。
3.5 仿真参数设置
求解器我一般选ode45,默认的变步长就行。如果你的模型里用了Unit Delay来处理代数环,那必须用离散求解器,步长可以设成跟后面控制器一样的采样时间,比如0.1秒。这样也更贴近实际BMS的运行环境。
仿真时间要根据你的工况数据长度来定。如果跑UDDS工况,一个循环大概1400秒,那仿真时间就设成1400秒。要注意的是,电流数据的时间轴必须跟仿真时间轴对齐,否则数据插值会出现偏差。
4. 仿真验证与参数配套
4.1 验证数据怎么选
模型搭完了,参数也装了,接下来最关键的一步就是验证。很多新手容易犯的错误是拿标定用的同一份数据来验证模型,这完全说明不了问题。标定数据和验证数据必须分开。
我常用的验证工况有这么几种:第一种是多倍率混合脉冲,跟HPPC类似但幅值和时长都改了,用来验证模型对动态工况的响应能力;第二种是恒流放电工况,用1C持续放电到底,看端电压曲线和中途的静置回复是否对得上;第三种是动态工况,比如UDDS或者WLTC这种实际道路工况的功率谱,用来验证模型在真实使用场景下的表现。
4.2 精度指标怎么算
验证的时候,我一般把仿真端电压和实测端电压画在同一张图上,肉眼看一个大概趋势,更重要的是用数值指标去量化。最常用的是均方根误差RMSE和最大绝对误差。我的经验是,常温下二阶RC模型在动态工况上的RMSE能控制在20mV以内,峰值误差控制在50mV以内,就算合格。如果超过这个范围,大概率是参数辨识某个环节出了问题,或者是温度影响没有处理好。
再补充一个细节:验证的电流激励范围最好覆盖你参数辨识时候的电流范围。如果你的HPPC只做到1C,然后拿去验证3C的工况,那模型误差突然变大也正常,因为大电流下的极化行为不是简单线性外推能描述的。
4.3 参数不匹配时怎么调
如果验证下来发现电压误差偏大,不要一上来就去调Simulink模型,先回头看看参数有没有问题。
一种常见现象是:验证曲线在放电初期就明显偏离,持续到中后段才慢慢重合。这种情况八成是R0偏大或者偏小了,因为R0只影响瞬态压降的大小。
另一种常见现象是:大电流脉冲结束后的恢复阶段,仿真电压回弹的速度和实测对不上。这就涉及到RC时间常数了。如果回弹太快,说明时间常数偏小;回弹太慢,说明偏大。你可以单独调R1C1和R2C2两组参数,去匹配电压恢复曲线的前段和后段。
我在实际项目中就用过这个办法,把两个时间常数分别做了一次微小调整,误差就降了一半多。但要注意一点:手动调参本质上是在补救辨识环节的疏漏,最可靠的还是回到数据端,重新做脉冲测试,确保静置时间足够、电流测量精度够高。
4.4 参数表在Simulink中的组织方式
参数辨识完之后,你会得到一张参数表,每个SOC点对应一组R0、R1、C1、R2、C2,还有OCV。在Simulink里我习惯用结构体或者Matlab表格(table)存起来,然后在模型的初始化回调里用load加载,再用Lookup Table模块的断点数据指向这些变量。
这样做的最大好处是:换一批电池数据的时候,只要把参数表替换掉,模型不用动任何一根线。我后面做不同电芯型号的仿真时,这一套模型直接复用,只换参数,效率和可靠性都有保障。
5. 常见问题与排查技巧实录
5.1 代数环错误
不管用什么版本的Simulink,搭这种带反馈的电路模型,代数环问题几乎是必然会遇到的。当你看到类似“Algebraic state in block ... failed to solve”的报错信息,说明Simulink在某个步长内解不出这个代数约束。
解决的办法前面提过,加Memory模块或者Unit Delay切断代数环。但我还要提醒一下:加完之后模型的精度和稳定性都取决于仿真步长,步长设得太大,查表数据的阶跃变化会带来明显的锯齿失真。一个折中的做法是把查表模块的插值方法设成平滑的,或者用离散积分器配合小步长。
5.2 仿真结果发散
有一次我搭完模型,一跑就发散,波形直接飞掉了。排查了半天,发现是积分器的初始条件没有设置好。当SOC计算模块里的积分器初值是0,而实际上电池的初始SOC是80%,那么前几秒的OCV查表值会突然跳变,导致电压输出出现一个巨大的尖峰,加上反馈作用,仿真就崩了。
解决办法很简单:把SOC积分器的初始条件设置成实际初始SOC对应的数值。另外一个常见原因是求解器最大步长设置过大,遇到快速变化的电流信号时,状态更新跟不上,数值就发散了。把最大步长改小,比如0.01秒,往往就能解决。
5.3 查表的边界问题
Lookup Table模块在查询范围之外的输入会外推,这个外推值可能非常离谱。你如果仿真覆盖的SOC范围到了0到100%,那两端的查表外推可能产生负SOC或者超过100%的SOC,OCV查出来的值也不可靠。
我一般是把SOC限幅在0到1之间,在SOC计算模块的积分器输出端加一个Saturation模块。这一步很重要,它既防止了查表外推导致的异常结果,也让后面的观测器设计更稳定。不过要注意,加限幅之后SOC计算的积分存在饱和损失,如果需要精确的安时积分结果,最好在限幅之外保留一个真实的累计电量信号。
5.4 模型和实测长时间漂移
如果你跑的是长时仿真,比如几个小时甚至一整天的工况循环,你可能会发现模型端电压和实测电压的偏差越来越大。这通常是SOC估算误差累积的结果。安时积分法本身没有校正机制,电流测量有偏置,积分时间一长必然漂移。
在模型验证阶段,我会允许这个漂移存在,因为它本身反映了安时积分的固有缺陷。但如果你要在这个模型基础上做算法验证,我建议SOC计算模块改成闭环结构,用电压误差去修正SOC,比如加一个PI校正项,模拟BMS里常用的SOC估算逻辑。这样仿真结果和实车表现更接近,验证出来的算法才可信。
5.5 Simscape Battery工具箱要不要用
Matlab现在推的Simscape Battery工具箱确实很方便,模型都封装好了,自带参数辨识、热模型、老化模型。但我发现很多实际项目里还是更多人选择自己用基础模块搭二阶RC模型,为什么?因为自定义模型的透明度高,所有中间变量都能拉出来调试,方便接入自研的观测器算法。Simscape的模型是物理域连接,信号流和BMS算法之间需要额外的传感器模块来转换,反而增加了复杂度。
我的建议是:如果只是想快速验证一个想法,Simscape Battery两条命令就能出一个电池模型;如果目标是做算法验证和嵌入式代码生成,那自己搭的二阶RC模型更灵活,后面还能配合Embedded Coder直接生成C代码,跑在ECU或者快速原型平台上。
6. 结尾
踩过几次坑之后,我现在搭二阶RC模型的速度快了很多,模板也不怎么变,主要精力都花在参数标定和数据验证上。在这里想提醒大家一句话:模型本身的数学结构很简单,真正决定模型好不好用的永远是参数的准确性和验证的严格性。如果你在搭模型的过程中发现仿真结果和实测对不上,先别急着怀疑代码,先回去看看数据。另外一个小技巧:参数表尽量用结构体变量存在MATLAB的base workspace里,在Simulink的Model Explorer中一次性配置好所有Lookup Table接口,后面维护起来会省很多时间。希望这篇分享能帮你在锂电池建模的路上少走点弯路。
