光伏阵列常见故障仿真模型附Simulink仿真
这几年做光伏系统仿真研究,被问得最多的问题不是MPPT算法怎么写,而是“光伏阵列的故障到底怎么在Simulink里搭出来”。学校里教的通常只有正常工况下的光伏电池模型,然而实际工程里,真正让运维人员和科研团队头疼的是遮阴、老化、开路、短路这些异常状态。今天这篇文章,我就把我在Simulink里搭建光伏阵列常见故障仿真模型的完整思路、建模步骤、参数设置和踩坑记录整理出来,希望能帮到正在做光伏故障诊断、MPPT算法验证或者微电网保护研究的朋友。
这套模型适合谁?一是刚接触光伏仿真、想用Simulink复现I-V、P-V特性曲线的初学者;二是需要验证故障检测算法、但又不想在室外实际组串上做破坏性测试的研究人员;三是做课程设计或毕业设计的同学——只要把故障注入模块的参数改一改,就能快速得到一组有研究价值的仿真数据。先说明一下,我这里的做法偏“半物理建模”,也就是以光伏电池的五参数单二极管模型为核心,结合外围开关、信号源和测量模块来模拟各种故障,而不是直接调用Simscape电气库里的PV Array模块。原因后面会详细说。
1. 故障仿真为什么值得做:从光伏电站的真实痛点说起
1.1 光伏阵列在实际运行中会遇到的故障类型
光伏电站不是装了就能稳定发电二十五年的,阵列长期暴露在户外,遭受的故障类型比很多人想象中复杂。从工程运维的统计数据来看,光伏阵列的故障主要集中在以下几类:
部分遮阴(Partial Shading):这是最常见、也最容易在仿真里复现的故障。云朵飘过、树叶遮挡、鸟类排泄物、灰尘积聚,都会导致同一串中不同组件的接收辐照度不一致。遮阴的麻烦之处在于它不光让发电量下降,还会导致被遮组件反向偏置,局部发热形成热斑效应,严重时直接烧毁电池片。
组件老化与性能衰减(Degradation):光伏组件的输出功率会随着使用年限增加而下降,主要是串联电阻变大、光生电流变小。老化的组件与新组件混在同一串里,会让整串输出特性变差,甚至出现失配损失。
组件开路(Open Circuit):连接器脱落、焊带断裂、接线盒故障,都会造成组件开路。开路故障的特征非常明显——整个串联支路的电流直接变为零,但对并联的其他支路影响相对较小。
组件短路(Short Circuit):旁路二极管击穿、电池片内部短路,都会导致组件输出异常。短路的组件相当于把一小段电压“吃”掉了,串内其他组件要被迫“多出力”去补电压缺口,同样会造成失配。
接地故障(Ground Fault):直流侧电缆绝缘破损,正极或负极对地漏电。这个故障在仿真里模拟相对复杂,因为涉及接地阻抗和保护装置的动作逻辑,通常放在系统保护层面去研究。
1.2 为什么选择用Simulink做故障仿真
做光伏故障研究,路径无非三条:室外实测、硬件在环试验、纯软件仿真。室外实测最真实,但要等故障出现、要搭测试平台,成本高、周期长,而且有些故障(比如电池片短路)根本没法在不破坏组件的前提下制造。硬件在环试验精度高,但需要dSPACE、RT-LAB这类实时仿真设备,门槛也不低。
纯软件仿真,尤其是Simulink里的模型仿真,是性价比最高的方案。Simulink的优势在于它把电气电路建模和控制系统建模放在同一个环境里,既能搭出光伏阵列的电气模型,又能直接挂MPPT控制器、故障诊断算法做闭环验证。尤其是后面想做基于数据驱动的故障检测(比如用神经网络识别I-V曲线特征),Simulink里仿出来的故障数据可以直接导出训练集,衔接非常顺。
这里我也想提醒一下:很多人以为直接在Simscape Electrical库里拖一个PV Array模块出来就能做故障仿真,这个想法对一半。PV Array模块做正常工况和遮阴工况没问题,但想细致模拟“某一块组件内部短路”“某一串的隔离二极管反接”“老化导致串联电阻增大”这类局部故障,它就不够灵活了。所以我更推荐的做法是用自定义函数封装光伏电池模型,再通过外部信号去控制故障参数的变化。
1.3 这套模型能解决什么问题
我给这套故障仿真模型总结了三层价值:
- 第一层,教学演示:把不同故障下的I-V曲线和P-V曲线画出来,直观展示“遮阴出现多峰”“开路电流为零”“短路电压塌陷”等现象,比口头讲一百遍都管用。
- 第二层,算法验证:故障诊断算法的本质是“从电信号特征反推故障类型和位置”,仿真模型就是算法验证的试验场。你可以批量生成各种故障工况的数据集,验证分类器的准确率。
- 第三层,系统设计:在做光伏电站的电气设计、保护定值整定时,用故障仿真模型去模拟极端工况,评估断路器、熔断器、逆变器保护策略是否合理,比凭经验估算要靠谱得多。
2. 建模的核心思路:基于五参数单二极管模型的自定义封装
2.1 为什么要用单二极管模型而不是直接拖模块
光伏电池的数学模型,搞电力电子的人应该不陌生。最常用的是单二极管五参数模型,它用五个参数(光生电流Iph、反向饱和电流I0、串联电阻Rs、并联电阻Rsh、理想因子n)来描述电池的输出特性。公式长这样:
I = Iph - I0 × [exp(q × (V + I × Rs) / (n × k × T)) - 1] - (V + I × Rs) / Rsh
这个公式看着唬人,其实拆开理解就三部分:光生电流减去二极管结电流减去漏电流。其中q是电子电荷量(1.60217646×10⁻¹⁹ C),k是玻尔兹曼常数(1.3806503×10⁻²³ J/K),T是电池温度(单位K)。
在Simulink里实现这个模型,有三种常见路子:
| 实现方式 | 优点 | 缺点 |
|---|---|---|
| Simscape PV Array模块 | 直接可用,内置详细参数 | 故障注入不灵活,难以模拟局部老化、内部短路 |
| 纯Simulink数学模块搭建 | 原理透明,适合教学 | 模型臃肿,改参数麻烦,容易出错 |
| MATLAB Function + 电气接口(本文推荐) | 灵活度高,参数可外部化,故障注入方便 | 需要自己写迭代求解,封装有一定门槛 |
我选择的是第三种。核心思路是:把五参数模型的求解逻辑写成一个MATLAB Function,输入是电压V、辐照度G、温度T,输出是电流I。然后在Simulink里通过一个受控电流源把函数输出的电流注入到电路中,形成电气回路。
2.2 五参数怎么推算出来
标准工况(STC:辐照度1000 W/m²,温度25°C)下,制造商数据手册会给出四个关键参数:短路电流Isc_ref、开路电压Voc_ref、最大功率点电流Impp_ref、最大功率点电压Vmpp_ref。再加上电池串联数Ns,就可以反推五参数。
这里给一个实际算例,以常见的250W多晶硅组件为例:
| 参数 | 数值 |
|---|---|
| 短路电流Isc_ref | 8.21 A |
| 开路电压Voc_ref | 37.2 V |
| 最大功率点电流Impp_ref | 7.83 A |
| 最大功率点电压Vmpp_ref | 31.9 V |
| 电池串联数Ns | 60 |
推算思路是通过四条I-V曲线上的已知点联立方程求解。先估算理想因子n(通常在1到1.5之间,晶体硅电池一般取1.2),然后利用开路电压点(I=0, V=Voc)和短路电流点(V=0, I=Isc)分别列方程,加上最大功率点的电压和电流关系dP/dV=0,就能把Iph、I0、Rs、Rsh解出来。这个求解过程用MATLAB的fsolve函数可以很方便地实现。
2.3 非标准工况下的参数修正
这是故障仿真模型能不能用、准不准的关键。辐照度和温度变化后,Iph和I0都会跟随变化,常用的修正公式是:
Iph = (G / G_ref) × [Iph_ref + Ki × (T - T_ref)],其中Ki是短路电流温度系数,通常取0.0032 A/K。
I0 = I0_ref × (T / T_ref)³ × exp(q × Eg / (n × k) × (1/T_ref - 1/T)),其中Eg是硅的带隙能量,约1.12 eV。
也就是说,温度升高时,短路电流略微增大、开路电压明显降低;辐照度降低时,短路电流近似成比例下降,开路电压下降幅度较小。这个特征直接决定了我后面做部分遮阴仿真时的波形形态——遮阴组件的Isc降低了,但Voc可能只掉一点点,于是整串的I-V曲线会出现“阶梯”。
3. 故障仿真模型的具体搭建与实现
3.1 系统框架和顶层拓扑
我搭建的仿真模型结构分三层:
底部是光伏阵列电气主回路,由多个“光伏组串模块”并联组成。每个组串模块内部,多块组件串联,每块组件都做成一个独立的子系统,这样就能对任意一块组件单独注入故障。
中间层是故障注入控制层,用Step信号、Pulse Generator和手动开关来控制故障发生的时间和类型。
最高层是测量与数据采集层,用电压表、电流表采集数据,一路接到示波器看实时波形,一路通过To Workspace导出到MATLAB工作区做离线分析。
以一个小型阵列为例:阵列由3个组串并联,每个组串6块组件串联,单块组件参数用上面250W的算例。整体阵列的额定功率约4.5kW,开路电压223.2V,短路电流24.63A。这个规模既跑得快,又能清楚看到串间和串内的故障交互效应。
3.2 正常组件子系统的搭建步骤
正常组件子系统是最基本的单元,All in建模大概分五步:
第一步,写MATLAB Function。新建一个MATLAB Function模块,命名为PV_Cell,输入是组件端电压V_pv、辐照度G、温度T,输出是电流I_pv。函数体内部就是用五参数模型方程迭代求电流。注意这里有个关键细节:组件的I-V关系是隐式方程(电流I既出现在左边,又出现在右边的电压项I×Rs里),所以需要用牛顿-拉夫逊迭代或者fzero求解,不能直接写成显式表达式。
核心代码思路如下(简化版):
function I = PV_Cell(V, G, T) % 输入参数 T_ref = 298.15; G_ref = 1000; % 五参数(从数据手册推算得到) Iph_ref = 8.26; I0_ref = 5.1e-10; Rs = 0.28; Rsh = 310; n = 1.2; Ns = 60; q = 1.60217646e-19; k = 1.3806503e-23; % 温度修正 Iph = (G / G_ref) * (Iph_ref + 0.0032 * (T - T_ref)); I0 = I0_ref * (T / T_ref)^3 * exp(q * 1.12 / (n * k) * (1/T_ref - 1/T)); % 迭代求解电流 Vt = n * k * T * Ns / q; f = @(i) Iph - I0 * (exp((V + i * Rs) / Vt) - 1) - (V + i * Rs) / Rsh - i; I = fzero(f, Iph); end第二步,做电气接口封装。在子系统内部放一个Controlled Current Source(受控电流源),把MATLAB Function输出的电流I接到电流源的输入端。受控电流源串一个电压测量模块组成“电流注入+电压反馈”的结构,这样函数需要组件端电压V作为输入,电流源又向电路注入电流,就形成了闭环。
第三步,参数外部化。把辐照度G和温度T从子系统外部引入。我的做法是在子系统上开两个输入端口,连接到外面的Constant模块或者Signal Builder。这样改故障值不用进子系统内部改代码。
第四步,加旁路二极管。单块组件要并联一个二极管来模拟旁路保护,这个很关键。旁路二极管的作用是当组件被遮阴或其他组件“压着”时,电流绕过这块组件继续流动,防止热斑。实现上用Simscape Electrical里的Diode模块即可,参数设成正向压降0.7V、导通电阻0.01Ω。
第五步,封装成子系统并测试。单独跑一个组件,输入辐照度1000W/m²、温度25°C,电压从0扫到40V,确认I-V曲线和P-V曲线符合数据手册,最大功率在250W左右。这一关过了,后面阵列层面的仿真才有意义。
3.3 各类故障的Simulink实现方式
这是这篇文章的核心干货部分。我把五种常见故障在Simulink里的具体实现方法整理成下面的表格:
| 故障类型 | Simulink实现方法 | 关键参数设置 |
|---|---|---|
| 部分遮阴 | 直接修改目标组件的辐照度输入值,比如从1000改成400 | 遮阴组件数、遮阴程度通过Constant或Step控制 |
| 组件老化 | 修改MATLAB Function里的Rs、Iph参数,让短路电流下降、串联电阻增大 | Rs从0.28提高到0.6,Iph按衰减比例下调 |
| 组件开路 | 在组件输出端串一个Breaker(断路器)模块,设置断开时刻 | Breaker初始状态闭合,在故障时间点断开 |
| 组件短路 | 用一个低阻值电阻+开关并联在组件两端,模拟内部短路 | 短路电阻取0.01Ω,开关在故障时刻闭合 |
| 组串隔离二极管失效 | 在组串正极加一个二极管,用开关控制其通断 | 二极管反向击穿时相当于短路,用一个Breaker并联 |
这里我把遮阴故障多说两句。做部分遮阴仿真时,有三种配置方式:一是手动改每组件的辐照度Constant值,适合静态分析;二是用Signal Builder画辐照度随时间变化的曲线,适合看动态遮阴过程;三是在工作区定义数组变量,Simulink里用Selector模块读取指定元素,适合批量扫参数。我实测下来,批量扫参场景下第三种最省事。比如你要看“3块组件遮阴、遮阴比例从10%到90%变化时阵列最大功率的轨迹”,用Selector读数组就能自动跑完所有工况,不需要一个一个手改。
开路故障的实现细节要特别注意:Breaker模块断开时,如果串联支路上有电流,会触发数值突变,仿真器容易出现代数环或刚性报错。解决办法是在断开的支路两端并联一个高阻值电阻(比如1MΩ),让电流有一个泄放路径,避免在断开瞬间电流从额定值跳变到零导致的数值震荡。
3.4 故障控制与工况切换逻辑
为了让一个模型能连续模拟“正常→故障A→故障B→恢复”的完整序列,我的做法是设计了故障模式选择端口。具体思路是:
在一串组件之外加一个故障配置向量,向量元素定义每块组件的故障代号,比如0代表正常、1代表遮阴、2代表老化、3代表开路、4代表短路。把这个向量作为数组输入到模型里,通过MATLAB Function内部的switch逻辑,根据故障代号去调用不同的参数组合。
这样做的最大好处是,你只需要在工作区改一个数组,整串的故障状态就全变了。做故障诊断算法验证的时候,用一个循环语句遍历几十种故障组合,自动跑完并把结果存下来,效率非常高。
4. 典型故障下的仿真结果与诊断特征
4.1 正常工况与部分遮阴工况的对比
先跑一个基准工况:3串并联,每串6块组件,辐照度统一1000W/m²,温度25°C。仿真时间1秒,用电压扫描法(从开路电压往下扫)或者直接让MPPT控制器跟踪,看阵列的输出。
正常工况下,阵列的I-V曲线是平滑的单台阶,P-V曲线只有一个峰值点,最大功率约4.4kW(控制器、线路损耗有一些折减)。
然后做部分遮阴:第一串的2、3号组件辐照度降到400W/m²,其余组件保持1000W/m²。这时候看I-V曲线,会看到明显的“阶梯”:第一串由于有两块组件被遮阴,该串电流受限到遮阴组件对应的水平,P-V曲线变成双峰。第一个峰出现在被遮组件的短路电流对应的高电压区,第二个峰出现在低电压高电流区。
这个现象的原理很简单:被遮阴的组件因为旁路二极管导通而被“旁路”了,串内电流被迫绕过它们,但绕过后会损失一块组件电压。于是整个串联支路在电流大于遮阴组件Isc时变成“少了一块”的组串,开路电压相应减少。
这个简化版表格可以直接拿去写报告:
| 工况 | 组串1辐照度 | 最大功率 | P-V曲线形态 | I-V曲线特征 |
|---|---|---|---|---|
| 全部正常 | 6×1000 W/m² | 4.40 kW | 单峰 | 平滑矩形 |
| 轻度遮阴 | 4×1000 + 2×800 | 3.95 kW | 单峰偏移 | 单阶梯 |
| 重度遮阴 | 4×1000 + 2×400 | 3.10 kW | 双峰 | 明显双阶梯 |
4.2 开路故障与短路故障的波形特征
开路故障:在t=0.5s时,将第一串中第3块组件的Breaker断开。波形上看,第一串的输出电流瞬间从正常工作点的约7.5A掉到几乎零,整串退出工作。因为另外两串还在发电,阵列总输出电压基本维持不变(开路的那一段等于从串联链路里被剔除了),总电流从约22A降到约15A。P-V曲线单峰还在,但峰值明显降低。
这里有一个容易忽略的点:组串开路后,隔离二极管可以防止其他串的电流反向灌入故障串。如果没有隔离二极管,故障串会变成一个负载,消耗其他串发出来的功率,问题就不只是少了一串这么简单了。
短路故障:在t=0.5s时,第二串中第4块组件由正常切换到短路状态(开关闭合,低阻电阻旁路)。波形上看,第二串的总电压下降了该组件正常时的输出值约32V,但电流路径没有断,所以第二串还是能输出电流,只是该串的最大功率明显下降。如果旁路的是整串而不是单块组件,那就能看到该串输出电压几乎为零,相当于一个低阻负载挂在了母线上,这个工况比单块组件短路严重得多。
4.3 老化故障模拟的特殊之处
老化故障和其他故障不一样,它是渐变过程,不是突变过程。所以仿真时不能用Step信号,而是用斜坡信号或者老化时间函数去改Rs和Iph。我的做法是把老化程度定义为一个“老化系数”,默认1.0,随时间缓慢下降,比如每年下降0.5%。在MATLAB Function内部,各项参数乘以老化系数,就能模拟出长期运行后的性能衰减。
老化组件的I-V特征:短路电流全线下降,开路电压也有下降但幅度较小,曲线“膝盖”位置(最大功率点附近的拐角)变得更圆滑,填充因子下降。这个特征在做故障诊断时很有用,因为遮阴导致的I-V曲线变化是局部的(只在特定电流区间出现凹陷),而老化导致的I-V曲线变化是整体性的,两种故障在特征空间里区分度很高。
4.4 故障特征如何用于诊断算法
做完故障仿真,最有价值的产出是把故障特征量化成诊断判据。以我的经验,下面这几个指标对故障识别最有效:
- 归一化填充因子:最大功率/(Voc×Isc),正常组件在0.7到0.8之间。老化组件明显降低,遮阴组件可能变化不大但曲线形态异常。
- I-V曲线的一阶导数形态:正常曲线导数平滑单调,遮阴曲线的导数会出现间断点,间断点的位置就是遮阴组件的短路电流对应值。用导数间断点数量可以推断遮阴的组数。
- 工作点偏移量:如果阵列接的是固定电压的母线(比如直接带DC-DC变换器),故障后阵列工作点会偏离原最大功率点。这个偏移量可以用作MPPT适应性评估的指标。
把这些特征写到故障诊断算法里,就形成了完整的“仿真→特征提取→分类器训练”链条。我之前试过用SVM对仿真的故障数据进行分类,在五类故障上的识别准确率能做到95%以上,数据来源就是这套故障仿真模型。
5. Simulink实操中常见的报错与避坑技巧
5.1 代数环与数值发散问题
用MATLAB Function建模最头疼的问题就是代数环。原因是组件端电压V和输出电流I在电路拓扑里相互依赖:电压测量值作为函数输入,函数输出电流又决定电压值。Simulink在求解这种代数约束时,如果模型里又叠加了开关器件的逻辑切换,很容易出现“cannot solve algebraic loop”的报错。
我的处理办法有两个:一是给电流测量回路加一个高带宽的受控电压源做“缓冲”,相当于在环路里插入一个物理惯性;二是在反馈通道串一个Memory模块或Unit Delay,人为打破代数依赖。注意加了延迟会牺牲一点仿真精度,所以延迟模块尽量放在采样率已经足够高的支路上,不要把采样延迟加到主功率回路上。
5.2 故障切换瞬间的步长与求解器设置
故障注入本质上是切换电路拓扑,仿真器在切换瞬间可能因为状态不连续导致步长骤减,仿真时间被无限拉长。我在做开断故障时就被这个问题折磨过。
解决组合拳如下:
| 设置项 | 推荐值 | 说明 |
|---|---|---|
| 求解器类型 | 变步长 ode23tb | 对刚性系统稳定性好,切换瞬间不容易崩 |
| 最大步长 | 1e-5 s | 强制限制步长,避免大跨越导致漏掉切换瞬态 |
| 断路器弧电阻 | 并联1MΩ电阻 | 给开路瞬间的电流一个泄放路径 |
| 故障切换时间 | 至少错开0.01s | 不要在同一个仿真时刻同时触发多个断路器 |
5.3 数据导出与批处理跑工况
跑完仿真要分析数据,通常用To Workspace模块把电压、电流、功率导出。这里有一个容易踩的坑:To Workspace默认保存格式是Timeseries,在MATLAB里做运算要先转换成double数组。更高效的做法是在模块参数里把Save format设为“Array”,然后输出变量名去统一读取。
批量跑工况时,我习惯把仿真时间设成一个很短的时长(比如0.2秒,只够系统稳定),然后用Simulink的sim()函数在for循环里反复调用。循环前先写好工况参数矩阵,每次循环通过set_param修改Constant模块的值或者加载不同的工作区变量,最后把每次仿真的特征数据存进一个cell数组。
伪代码逻辑给大家参考:
% 工况参数矩阵 cases = [1000 1000; 1000 400; 1000 200]; % 每行是两块组件的辐照度 for i = 1:size(cases,1) G1 = cases(i,1); G2 = cases(i,2); simOut = sim('PV_Fault_Model', 'StopTime', '0.2'); % 提取特征 v = simOut.v_array.Data; i = simOut.i_array.Data; p = v .* i; Pmax(i) = max(p); end5.4 组件参数不匹配时的阵列级误差
最后说一个很隐蔽的问题:很多人把组件参数当成恒定值,直接用数据手册里的名义值建了模型就跑。但实际组件在出厂时有正公差/负公差,同一串里的组件Isc和Voc本身就有偏差,更不用说温度不均匀导致的差异。如果仿真模型里所有组件参数一模一样,那阵列级仿真结果会比实际电站“理想”很多,故障特征会被掩盖。
所以做工程级仿真时,我给每块组件的参数加一个微小的随机扰动,一般用±2%以内的均匀分布随机数。这样仿真出来的阵列特性更接近真实电站,诊断算法的鲁棒性验证也更可信。
这套模型我在好几个项目里都用过,从教学演示到算法验证都够用。每次新搭一个故障工况,我都会先跑一遍正常工况做基线,确认各项指标和数据手册对得上再继续。仿真这件事,最怕的就是模型的基线不对,后面所有的故障分析都是在错误的基础上叠加错误。建好基线、做好参数外部化、把故障注入逻辑封装清楚,这套模型就能反复用,而不是每次换个故障就要推倒重来。