☰
基于Simulink的液流电池储能系统充放电仿真全解析
2026/10/2 3:54:49 网站建设 项目流程

做液流电池储能系统仿真这件事,很多人一开始是奔着“搭个电池模型,拉一条充放电曲线”去的。我当年第一次用 Simulink 搭全钒液流电池(VRB)储能系统时,也以为只要把电池开路电压、内阻放进去就能完事,结果跑出来的电压曲线平台不明显,效率算出来甚至超过 100%,后来才发现是电流方向定义和 SOC 初始值一起出了问题。这篇内容我会从建模思路、电化学参数整定、Simulink 模型搭建到常见坑位排查,完整记录一遍基于 Simulink 的液流电池储能系统充放电特性仿真过程,适合正在做储能系统仿真、电池建模、BMS 策略验证,或者在准备课程设计与横向课题项目的朋友参考。跟着这个流程走,你可以搭出一个既能复现液流电池平台电压、又能评估充放电效率,还方便后续扩展控制策略的系统级仿真模型。

1. 项目定位与模型边界

1.1 为什么液流电池的仿真思路和锂电池不一样

液流电池和锂电池在结构上有本质区别。锂电池的电化学反应发生在电极材料内部,容量和功率是绑在一起的,模型里通常要处理 SOC、温度、老化、内阻变化这些非线性因素。而液流电池的正负极活性物质储存在外部电解液储罐里,电堆只负责离子交换和电化学反应,容量由电解液体积和浓度决定,功率由电堆面积和电流密度决定,两者可以独立设计。

这个差异直接影响仿真建模的取舍。液流电池里最值得关注的是“荷电状态对应的开路电压平台”,以及“泵耗、流量对系统效率和功率响应的影响”。如果你一上来就按锂电池的 Thevenin 模型思路,忽略电解液流量而只做 SOC-OCV 查表,其实也能复现端电压曲线,但那样就丢失了液流电池“能量和功率解耦”的物理特点。

在做这个仿真项目时,我给自己的定位是:做一个系统级模型,目标是能复现恒流充放电过程中电压平台、SOC 变化曲线、充放电能量与效率,同时为后续的功率控制、SOC 估算算法留好接口。模型边界设定为“电堆等效电路 + SOC 积分 + 简单泵耗补偿”,不涉及流场三维 CFD,也不做电堆内部电流密度分布的详细计算。这个选择不是因为复杂模型不好,而是系统级仿真要的是计算速度和参数可标定,一个包含流体力学细节的模型在 Simulink 里跑起来非常慢,而且大量参数在小规模试验台上根本测不出来。

1.2 系统级模型应该包含哪些物理量

一个能反映充放电特性的液流电池模型,至少需要包含下面几个物理量:

  • 开路电压E_ocv,它随 SOC 变化;
  • 欧姆内阻R_0,主要来自质子交换膜、电极接触电阻和电解液电导率;
  • 极化特性,用一阶 RC 网络模拟电荷转移和传质过程;
  • 端电压输出表达式,充电时是E_ocv + I*R_internal方向,放电时相反;
  • SOC 计算方法,这里用安时积分法作为基础;
  • 泵耗功率,因为是液流电池的特性项,建议至少用一个简化公式。

模型内部物理量之间的关系其实比较简单,难点在于参数怎么定、方向怎么约定、模型在什么求解器条件下稳定。后面我会逐步展开。

2. 建模前要搞清楚的电化学与参数基础

2.1 全钒液流电池的半电池反应与开路电压

全钒液流电池是目前液流电池里商业化程度最高的技术路线,正负极都是钒的不同价态离子。充电时正极发生二价钒到三价钒? 严格来说,正极是四价钒VO2+氧化到五价钒VO2+,负极是三价钒V3+还原到二价钒V2+。放电时方向反过来。电堆的开路电压范围通常落在 1.2V 到 1.6V 之间,中间存在一段比较明显的电压平台,这也是液流电池“适合长时间充放电储能”的重要原因。

开路电压和电解液中各价态钒离子的浓度直接相关,可以用能斯特方程表达。不过在实际工程仿真里,我不会去解全浓度场方程,那需要知道电解液体积、流量、反应速率常数、膜渗透率等参数,不缺数据时没必要硬解。更实用的做法是采用简化的 SOC-OCV 关系:

E_ocv = E_0 + K * ln(SOC / (1 - SOC))

这个形式和能斯特方程在数学上同构,因为 SOC 本质上是离子浓度的归一化表示。需要注意的是,当 SOC 等于 0 或 1 时,对数项会直接产生无穷大数值,所以模型里 SOC 一定要加饱和限幅。我在模型里初始 SOC 设为 0.1,上限 0.95,下限 0.05,既是出于数值安全,也符合工程上液流电池避免过度充放的运行要求。

这里要解释一个重要概念:E_0是标准平衡电位,不是开路电压平台中心。全钒液流电池单片电池的E_0约为 1.4V,实际工作平台会根据电解液浓度、温度和 SOC 范围上下波动。K 值是一个与温度、电子转移数、离子活度系数相关的等效系数,我通常在 0.02 到 0.08 之间标定。

2.2 从电堆参数到仿真参数的换算

仿真模型里的参数不能凭空拍脑袋,必须和实际系统对应。我以一套 10kW/40kWh 的全钒液流电池储能系统为参考。假设电堆由 100 节单电池串联,单节平均电压约 1.4V,电堆额定电压 140V。10kW 功率在 140V 下对应额定电流约 71.4A,我按 75A 设置最大工作电流,方便计算。

容量方面,40kWh 是能量,不是 Ah。如果放电平均电压取 135V,那么对应容量大约40000Wh / 135V ≈ 296Ah。这个数字和直接用“额定电压 140V 除以 40kWh”得到的 285Ah 有差距,原因是电池在放电过程中端电压不是恒定 140V。仿真里我建议把额定容量Q_Ah定在 280 到 300 之间,具体取多少,取决于你参考的是充电还是放电工况。

内阻参数我也给出一个典型参考量级:欧姆内阻R_0对 100 节电堆而言约 0.1Ω 到 0.5Ω,换算到单节是毫欧级别。我在仿真模型里把整个电堆等效成一个整体,所以R_0 = 0.15Ω左右比较合理。极化电阻R_1比R_0大一些,通常取 0.2Ω 到 0.5Ω,极化电容C_1非常大,因为电化学双电层和传质过程的时间常数很长,我取 500F 到 2000F 之间。

2.3 用安时积分表达SOC

SOC 在液流电池里可以理解为电解液“剩余可用电荷量”的百分比。仿真里最通用的做法是安时积分:

SOC(t) = SOC(0) + (1 / (3600 * Q_Ah)) * ∫ I_bat dt

这里I_bat的方向需要明确。我约定:充电电流为正,放电电流为负,积分后 SOC 在充电时上升,放电时下降。很多第一次搭模型的人会在这一步犯错,因为有些人习惯把放电电流定义为正,导致 SOC 曲线反向,效率也变成负值。

为什么除以 3600?因为Q_Ah的单位是安时,而仿真时间单位是秒,A·s 要换算成 A·h 需要除以 3600。这个细节虽然简单,却是最容易出 bug 的地方。Simulink 里可以直接用一个Integrator模块实现积分,输出端加Saturation模块限幅。要注意Saturation放在积分器后面会改变积分器的内部状态,严格来说如果 SOC 顶到上限,需要把积分输入掐掉,否则积累误差会导致 SOC 状态和实际值不一致。工程上可以先接受限幅,因为多数仿真场景不会长时间深充深放。

3. Simulink 模型搭建实操

3.1 顶层架构:先把信号流画出来

我不建议打开 Simulink 就拖模块,先想清楚信号流向。模型顶层分为四个部分:模式控制、电流指令生成、电池电堆模型、SOC 与数据观测。

模式控制负责定义“这段仿真时间在充电还是放电”。最简单的方式是使用Clock、Compare To Constant和Multiport Switch组合。比如:0 到 3600 秒充电,3600 到 3700 秒静置,3700 到 7300 秒放电。用Clock模块输出时间,接两个比较器,再通过Multiport Switch选择+50、0、-50三个电流指令。这个方案比 Stateflow 直观,适合新手;如果后续要切换恒功率模式,再用 Stateflow 或 MATLAB Function 也不迟。

电池电堆模型是核心,内部再把“OCV 计算”、“RC 极化环节”、“端电压合成”拆成独立子系统。这样做的好处是后续改 SOC-OCV 关系时不用动整个模型,只改 OCV 计算子系统即可。

数据观测部分统一使用Scope加To Workspace输出到工作区。很多人习惯直接用 Scope 看曲线,但仿真结束后 Scope 波形不方便定量分析。我每次都会加一个To Workspace,输出变量名设为simData,这样后处理画图、计算效率都很方便。

3.2 用基础模块搭等效电路模型

在 Simulink 里搭电池等效电路有两种路线:一种是用 Simscape Electrical 里的 Battery 模块,另一种是用基础 Simulink 模块手动搭方程。这里我选择基础模块,原因是液流电池不像锂电池那样有现成电池模块参数,手动搭方程可以完全控制每一个物理量,也方便把 SOC 积分、Nernst 方程、泵耗等环节全部串起来。

具体搭建过程是这样:

第一步,建立 SOC 积分。用一个Constant模块输出当前电流指令,经过增益1/(3600*Q_Ah)后进Integrator,初始值设 0.1,输出接Saturation限幅到[0.05, 0.95]。

第二步,建立 OCV 生成模块。我使用MATLAB Function模块,核心代码是:

function Eocv = calcOCV(SOC) E0 = 1.4; K = 0.05; SOC = max(0.03, min(0.97, SOC)); Eocv = E0 + K * log(SOC / (1 - SOC)); end

限幅之后再取对数是为了避免 NaN。你也可以用Lookup Table实现 SOC-OCV 曲线,数据来自实验标定,效果更真实。两者我都试过,查表法在 SOC 两端更可控,Nernst 表达式更能体现电压陡变的物理趋势,看个人喜好。

第三步,建立极化环节。极化电压微分方程为:

dV_p / dt = I_bat / C_1 - V_p / (R_1 * C_1)

用Integrator实现,输入接I_bat/C_1 - V_p/(R_1*C_1)。这个方程表示极化电压随时间的变化率,稳态时极化电压等于I_bat * R_1。之所以用积分器而不是直接用I_bat * R_1,是因为电池极化不是瞬时响应,而是一个渐进过程,这个“滞后”特性对系统动态仿真非常重要。

第四步,合成端电压:

V_terminal = E_ocv - I_bat * R_0 - V_p

需要注意符号。充电时电流为正,I_bat*R0和V_p都是正数,端电压高于开路电压;放电时电流为负,端电压等于开路电压减去内部压降。如果符号反了,充电电压反而低于开路电压,结果就完全错了。

最后加一个电流传感器和功率计算环节。电流直接用经过增益的指令信号即可,功率用V_terminal * I_bat计算。液流电池的一个特点是电解液由泵驱动,所以系统功耗里有泵耗这一块。简化模型里我把泵耗设为一个随电解液流量变化的常数,比如 0.8kW,后期如果要精细建模,可以用流量和扬程的三次方关系做。

3.3 用参数脚本驱动模型,不要硬编码

我在模型里坚持一个原则:所有参数不直接填数字在模块参数框里,而是定义一个 MATLAB 结构体params,放在基础工作区,模块参数框引用params.R0、params.R1这样的表达式。这样做的最大好处是批量仿真的时不用逐个改模块,写个脚本循环改params.R0然后反复调用sim()就行。

参数脚本我通常会这么写:

% 液流电池仿真参数 params.E0 = 1.4; % 标准平衡电位,V params.K = 0.05; % Nernst 等效系数,V params.R0 = 0.15; % 欧姆内阻,Ohm params.R1 = 0.25; % 极化电阻,Ohm params.C1 = 1000; % 极化电容,F params.Q_Ah = 285; % 额定容量,Ah params.SOC_init = 0.1; % 初始SOC params.SOC_min = 0.05; % SOC下限 params.SOC_max = 0.95; % SOC上限 params.pump_kW = 0.8; % 泵耗功率,kW

模型里的Integrator初始值写成params.SOC_init,Saturation上下限写成params.SOC_min和params.SOC_max。这样整个模型的可配置性非常高。

仿真时长我通常设在 7300 秒左右,对应“充满再放空”一个完整周期。使用simOut = sim('VRB_Model.slx')的方式跑仿真,仿真结束后从simOut里取波形数据做后处理。Scope可以实时看趋势,但真正的效率计算必须在后处理脚本里完成。

4. 充放电特性仿真结果与参数影响

4.1 恒流充放电的曲线特征

一个合格的液流电池充放电模型,跑完完整周期后应该在 Scope 上看到三组关键曲线:SOC 随时间线性变化、端电压呈“平台+两端陡变”形状、充放电切换瞬间电压有跳变。

SOC 曲线比较好理解,恒流充放电下它是线性的。从初始 0.1 开始,充电时以每秒约50A / (285Ah*3600s)的速度上升,到 0.95 大约需要0.85 * 285Ah / 50A = 4.845 小时。如果你的仿真跑出来 SOC 上升速度不符合这个计算值,基本可以确定是增益系数或者电流方向的问题。

端电压曲线是重点。充电过程中,即使 OCV 在一个平台区间内缓慢上涨,端电压也始终高于 OCV,高出来的部分是欧姆压降和极化压降。随着 SOC 接近 0.9 以上,能斯特效应加剧,电压会快速翘尾上升。放电过程则相反,端电压低于 OCV,SOC 降到低位时快速下跌。这个“低 SOC 电压跳水”现象在实际储能系统里非常重要,是 BMS 设定充放电截止条件的主要依据。

切换瞬间的电压跳变来自I*R0的瞬变。因为 RC 极化环节存在惯性,所以充电刚切到放电时,电压会先经历一个快速跳变,再缓慢“爬”到放电极化稳定值。这个动态过程在锂电池模型里叫“回弹电压”,在液流电池里同样存在。如果你的模型里看不到这个回弹,说明 RC 环节可能建得不对,或者C_1取值太小。

4.2 倍率、内阻与效率的灵敏度分析

充放电特性的核心指标之一是效率。系统中的能量效率可以定义为:

η = 放电能量 / 充电能量

用一个后处理脚本直接对V * I在时间轴上积分,注意只积累有效充放电区间,静置段要排除。

我用这个模型做过一组倍率扫描实验:电流分别取 25A、50A、75A,对应约 0.09C、0.18C、0.26C 倍率。仿真结果和我预想的一致:电流越大,欧姆损耗和极化损耗越大,能量效率越低。比如 25A 时效率能到 88% 左右,75A 时掉到 80% 附近。这组数据非常“液流电池”——它的效率对充放电倍率很敏感,不适合做短时大功率冲击,更适合做数小时级的能量型应用。

再看内阻灵敏度。在 140V 系统里,R0从 0.15Ω 增加到 0.30Ω,75A 电流下欧姆压降会增加75 * 0.15 = 11.25V,占系统电压约 8%。这个比例相当可观,直接反映在充放电能量效率上。所以在实际系统运维中,膜内阻增大是导致液流电池效率下降的最常见原因之一,仿真模型可以很好地量化这个过程。

如果你想高效地做这些参数扫描,不要手动改一个跑一次,应该用脚本循环。

4.3 泵损和系统净效率

液流电池的泵耗是它和锂电池非常不一样的地方。储能系统对外输出的净功率 = 电池放电功率 - 泵耗功率,充电时电网输入功率 = 电池吸收功率 + 泵耗功率。泵耗虽然不参与电化学反应,但它持续存在,特别是长时间储能场景下,泵耗占比会直接影响系统度电成本。

在我的简化模型里,泵耗取固定 0.8kW。10kW 系统放一个小时,理想电池放电能量 10kWh,但实际对外净输出只有 9.2kWh,净效率直接降了 8 个百分点。如果想把泵耗建得更细一点,可以用:

P_pump = k_flow * Q_flow^3

其中Q_flow是电解液体积流量,k_flow是管路和泵的效率系数。这个立方关系意味着流量降一点,泵耗会大幅下降,但流量降低又会影响浓度和传质,导致极化内阻增加。这是一个典型的多物理场权衡问题,系统级模型里完全可以用一个查表模块把它加进来。

5. 仿真过程中常见的坑与排查实录

5.1 Bus Selector 没有可选信号

这是 Simulink 使用中非常典型的场景。有人从总线里拉了个Bus Selector,双击一看,左侧列表里什么都没有,显示“没有可选信号”。第一次遇到这个问题,你会以为是总线没给到模块,但通常原因只有一个:总线上的信号没有“名字”,或者总线对象在模型初始化前没有被加载。

解决方法是:回到来源总线,确认进入Bus Creator的每根信号线上都已经用信号标签命名,而且总线数据在端口上被正确传递。更稳健的办法是在 MATLAB 里定义Simulink.Bus对象,然后在Bus Creator的属性里选择该总线对象作为输出数据类型。这样做的好处是信号名、数据类型都在对象里定义清楚,Bus Selector能稳定识别。

我在这个液流电池模型中确实用到过总线,把E_ocv、V_p、V_terminal、SOC打包成一条总线,统一送入观测模块。有一次改动模型后,Bus Selector又变成空白,最后发现是模型PreLoadFcn回调里忘记加载BusesDef.m脚本。所以我的建议是:只要用了总线对象,就必须把总线定义脚本挂到模型回调里,否则换个 MATLAB 版本或者别人打开模型,都会出问题。如果你不想碰这些复杂性,干脆用普通的信号连线,别用总线,这个坑就自然绕开了。

5.2 仿真一开始就发散或数值刚性

这个项目里最让我头疼的数值问题是“仿真刚开始就跑出 NaN,或者电压曲线疯狂振荡”。排查之后,原因通常是三类。

第一类是 SOC 初始值导致的 NaN。SOC 初始为 0 时,log(SOC/(1-SOC))会得到-Inf,再经过加法就变成 NaN。这个问题的根治办法是 SOC 进 OCV 函数之前先限幅,以及初始值设为 0.1,我给过代码。

第二类是代数环。如果端电压计算和电流指令计算之间直接互相依赖,比如V = f(I),I = g(V),Simulink 在交点处解不出数值,就会报代数环或者振荡。解决方法是把电流指令独立出来,不要让它反馈影响自身;如果需要闭环控制,则通过控制器状态量打破直接代数路径。

第三类是求解器步长问题。RC 环节的时间常数是R1 * C1 = 0.25 * 1000 = 250 秒,按道理不算刚性,但模型中如果有逻辑切换(充放电切换、开关事件),ode45 可能会在某些切换点附近自适步长失败。我统一换用ode15s,并设置最大步长不超过 10 秒。这样处理之后,整个 7300 秒仿真运行很稳定,没有出现非物理振荡。

5.3 效率异常和SOC方向问题

效率算出来超过 100%,这是另一个我见过很多次的“新手现象”。问题多半不是模型公式错了,而是充放电循环初始 SOC 和结束 SOC 不一致。举例来说,你从 SOC 0.1 开始充电到 0.9,然后放电到 0.2,放电过程中放出去的能量大于充电过程中充进去的能量,这在数学上完全可能,但这部分“多出来”的能量来自 SOC 的初始差异,不能直接当成效率。

合理做法是让初始 SOC 和结束 SOC 基本一致,或者在后处理时先把充电能量和放电能量按净 SOC 变化修正。我通常会在后处理脚本里加一段判断:对比循环前后 SOC 差值,如果差值超过 0.01,就提示“该循环未完全对称,效率结果仅供参考”。

另外还有一个方向问题。缩放到 100 节电池时,如果某个增益把电流单位搞错,放电 1 小时 SOC 下降量会偏离理论值。判断方法很简单:充电时 SOC 必须单调上升,放电时必须单调下降,如果方向反了,去检查I_bat的符号约定,而不要先怀疑积分器配置。

6. 从仿真结果到工程落地

6.1 用脚本批量扫描参数并自动出图

模型搭到能稳定跑完一个周期之后,我建议立刻做一套“批量仿真 + 自动出图”的脚本。这个习惯能极大提升参数敏感性分析的效率。脚本核心逻辑很简单:

% 参数扫描示例 R0_list = [0.1, 0.2, 0.3, 0.4]; for i = 1:length(R0_list) params.R0 = R0_list(i); simOut = sim('VRB_Model.slx'); % 从simOut提取波形,计算效率并保存 efficiency(i) = calcEfficiency(simOut); end plot(R0_list, efficiency, '-o'); xlabel('欧姆内阻 R0 / Ohm'); ylabel('能量效率 / %');

有了这套脚本,你可以在几分钟内回答“内阻增加 0.05Ω 效率损失多少”“倍率翻倍后电压平台变化多大”这类工程问题。仿真的价值不只是画一条曲线,而是帮你在设计阶段就建立起对关键参数的量化认知。

6.2 模型能扩展成什么

这个基础模型后续扩展空间很大。我个人做过几个方向,都验证可行。一是加一个温度模块,把电解液温度作为状态量,影响内阻和能斯特方程参数,可以模拟高温天气下系统效率变化。二是把泵耗模型从常数改为流量反馈控制,观察“降低流量提升净效率但增加极化损耗”的博弈。三是接入 BMS 控制逻辑,把 SOC 估算从安时积分换成扩展卡尔曼滤波,用这个模型生成仿真观测数据来验证算法。四是配合电力电子变换器模型,把电池模型放到直流母线仿真环境里,验证恒功率充放电控制。

如果你有代码生成需求,比如要用 Embedded Coder 把 SOC 估计算法部署到控制器,这个模型在搭建时就该避免使用MATLAB Function里的动态内存分配和文件读写,尽量用定长数组和基础运算模块。很多人在做控制算法时才发现模型“不适合生成代码”,只好推倒重来,这个代价比想象中大。

最后再分享一个我个人的习惯:仿真结束不要急着关模型,直接用 MATLAB 脚本把simData里的电压、电流、SOC 曲线画出来,和上一次结果放一起对比。这个“每调一次参数就看一次前后对比”的做法,比任何调试技巧都更能帮你找问题。做仿真和做实验有一点很像——数据不会骗人,但前提是你得把它看得足够仔细。

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

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

立即咨询