ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Simulink光伏阵列故障仿真:构建带标签样本库的实用指南

Simulink光伏阵列故障仿真:构建带标签样本库的实用指南

实际做光伏阵列故障诊断算法的时候,我遇到的最大瓶颈往往不是算法本身,而是样本数据。想训练一个能分辨阴影遮挡、组件老化、热斑失效的模型,手头却没有多少带标签的故障曲线。真实电站里不可能为了收集数据去主动制造故障,就算找一片遮挡做测试,太阳角度一变、云飘过来,数据又不可复现。后来我把整套工作搬到Simulink仿真环境里,用光伏阵列常见故障仿真模型来生成可控、可复现、带精确标签的故障数据,才把这件事跑顺。这篇文章不只讲模型怎么搭,更多是把我做故障注入、特征提取和调试时踩过的坑总结出来,给正在做或者准备做同类工作的朋友一条能直接走的路。

1. 为什么用Simulink做光伏阵列故障仿真

1.1 实测故障数据的三重困境

先说成本。我曾经给一个电站运维团队做诊断算法原型支持,想收集真实故障样本,发现几乎没有可操作性。主动制造故障要停机,停机就要损失发电量,运维负责人根本不愿意配合;组件老化、接线盒腐蚀这类故障,不等到真正发生你也不知道它哪天出现;就算用实验室的一块小板子做阴影实验,和现场大阵列的接线方式、组件失配情况也差得很远。这是第一重困境:成本高、周期长、规模小。

第二重困境是数据不可重复。同样是阴影遮挡,上午十点和下午三点,太阳高度角不同,遮挡边界在组件上的投影位置完全不同,I-V曲线形状差异非常大。同一个遮挡物,在固定支架和跟踪支架上的表现也不一样。现场采集到的曲线通常是多种因素叠加的结果:老化的组件正好碰上局部遮阴,MPPT又刚好在动态调整,测出来的曲线根本分不清哪个现象是因、哪个是果。这种数据拿去给算法打标签,标签本身就不干净。

第三重困境是异常工况的覆盖范围。真实故障是小概率事件,一个电站一年也碰不到几次典型故障,想把所有故障类型、所有严重程度、所有温度辐照组合都攒齐,几乎不可能。仿真的价值就在这里:它可以用很小的成本生成几千条带精确标签的故障曲线,覆盖从-10℃到70℃的温度范围、从100 W/m²到1000 W/m²的辐照范围,还能精确控制故障发生的时间和位置。这不是为了替代实测,而是为了给算法提供结构化的训练样本和验证样本。

1.2 这套模型适合谁用

根据我的经验,用到这套故障仿真模型的场景大致有三类:

使用者主要诉求仿真模型能提供的价值
光伏诊断算法开发者需要带标签故障样本训练分类器批量生成阴影、老化、热斑等故障曲线,并准确记录故障标签
电站运维平台/告警系统开发者验证告警阈值和诊断规则在仿真中注入故障,观察告警是否准确触发、有无误报
高校/培训机构学员快速理解光伏故障机理搭建可交互的Simulink Demo,直观看到I-V/P-V曲线变化

如果你是做MPPT算法研究的,这套模型同样有用,因为阴影和失配产生的多峰P-V曲线,正是各种全局寻优算法要解决的经典场景。后面我会专门提到怎么用模型生成多峰曲线。

1.3 为什么选Simulink而不是自己写程序

自己用Python或MATLAB脚本写一个五参数光伏模型并不难,难的是后续扩展。故障仿真的本质是拓扑仿真:同样的组件,今天串成5串并联,明天改成3串,后天又要在某条支路加旁路二极管,纯手写代码会把大量时间耗在电路结构维护上。Simulink里拖模块、复制子系统、加开关,改拓扑非常直观。

更关键的是,Simulink里的PV Array模块已经内置了光伏电池的五参数方程求解,你只需要填写厂家datasheet上的STC参数和组件内部电池串数,模块会自动反算内部参数。而且模型可以继续向下游扩展:接变换器、接MPPT算法、接逆变器模型,把故障仿真从组件级一直延伸到系统级。这对做控制器算法验证的人特别有价值。

2. 基线模型这样搭:从光伏组件到整阵列

2.1 模型里的电池到底在算什么

先说结论:Simulink里的PV模型核心是单二极管五参数等效电路。它的方程可以写成:

I = Iph - Io × [exp((V + I × Rs) / (n × Vt)) - 1] - (V + I × Rs) / Rsh

其中Vt = kT/q,大约等于0.0257V(25℃),n是二极管理想因子,Iph是光生电流,Io是反向饱和电流,Rs是串联电阻,Rsh是并联电阻。Iph随辐照强度近似线性变化,Io随温度指数上升,Rs体现的是电极接触电阻、栅线电阻和体电阻,Rsh体现的是电池边缘漏电。这四个参数加上理想因子,就是光伏仿真里常说的五参数。

理解这个方程很重要,因为后面做老化故障注入,本质就是改Rs、Rsh、Iph这几个参数。PV Array模块帮我们做了方程求解,但参数的含义必须清楚。填参数时,模块会根据你给的Isc、Voc、Vmp、Imp、Ns自动反算出内部五参数,不需要手动迭代拟合。

2.2 用厂家参数填出一个标准组件

这里我以一个典型的250W多晶组件为例,参数如下表:

参数数值说明
Pmax250W标准测试条件(STC)下最大功率
Voc45.2V开路电压
Isc9.08A短路电流
Vmp36.3V最大功率点电压
Imp8.48A最大功率点电流
Ns60组件内部串联电池片数
α_Isc+0.05% / ℃短路电流温度系数
β_Voc-0.32% / ℃开路电压温度系数

在Simulink中,从库浏览器进入 Simscape > Electrical > Specialized Power Systems > Renewable Energy,拖出 PV Array 模块。双击模块,在Module parameters里填表里的厂家参数,然后在Array data里设置串并联结构。比如我常用的是每串20块、4串并联,那么阵列标称功率就是20×4×250W = 20kW,开路电压约904V,短路电流约36.3A。

模块面板里会显示反算出来的Io、Rs、Rsh等内部参数。建议截图留档,因为后面做老化仿真时,这些值就是你要改的基准。

2.3 环境输入和输出端口怎么接

PV Array模块的输入端口有两个,分别是辐照度Ir(W/m²)和电池温度T(℃),都是物理信号口。验证基线时,直接用Constant模块分别给1000和25。注意,这里的T是电池板温度,不是环境温度,很多人第一次都会搞错。

输出侧,模块提供电气端子+和-,以及V、I、P测量端口。电气端子必须接负载或者可控源,空着会让求解器发散。V、I、P可以直接接Scope查看,也可以用To Workspace模块把数据导入工作区,方便后续做批量处理和特征提取。不同MATLAB版本的端口位置略有差异,但基本流程是一样的。

2.4 用受控电压源扫出整条I-V曲线

故障诊断需要用到整条I-V曲线,而不是某个工作点的数据。最简单的方法是在PV阵列输出端接一个Controlled Voltage Source,让电压从0线性升到略高于开路电压,同时记录电流和功率。电压扫描信号可以用Signal Builder或From Workspace生成。

这里给一段生成扫描信号的脚本:

V_oc = 904; % 阵列开路电压 V_vec = linspace(0, V_oc * 1.05, 3001); % 扫描电压,3001个点 t_vec = linspace(0, 20, 3001); % 仿真时长20秒 scan_data = [t_vec; V_vec]'; save('v_scan.mat', 'scan_data');

然后在模型里用From Workspace读取scan_data,作为Controlled Voltage Source的电压指令。仿真完成后,把V、I、P记录到工作区,画出来就能得到完整的I-V和P-V曲线。基线验证的标准是:在STC条件下,仿真得到的Pmax应该在20kW左右,与理论值误差小于1%。如果偏得太多,先检查辐照度是不是设成1000、电池温度是不是设成25,再检查组件参数有没有填错。

3. 五类典型故障的注入方法与I-V/P-V特征

3.1 局部阴影遮挡:拆子串加旁路二极管

PV Array模块的Ir和T输入只能是标量,所以如果你想把一块板遮住、其它板不遮,就不能用一个大模块直接做。正确做法是把阵列拆成若干子串,每个子串用独立的PV Array模块,给不同的辐照输入,然后在子串两端并联一个Diode模块模拟旁路二极管。

举个具体例子:一个组串20块组件,正常时Ir=1000、T=25。要模拟局部遮挡,把它拆成三段:正常段10块组件、轻度遮挡段5块组件(Ir=600)、重度遮挡段5块组件(Ir=300)。三段串联,每段并联一个旁路二极管。仿真后你会看到非常经典的故障特征:I-V曲线出现明显的台阶,P-V曲线出现两个甚至三个局部峰值。

为什么会出现台阶?因为当外部电流大于被遮挡子串的短路电流时,该子串的旁路二极管会导通,把被遮挡的电池短路掉,相当于这一段从串联电路中被“旁路”了,整个阵列的输出电压就少了一段;当外部电压继续升高,电流降低到小于被遮挡子串的短路电流时,旁路二极管截止,这一串又重新参与串联工作,电压突变。这个突变点,正是诊断算法识别阴影遮挡可以利用的关键特征。

3.2 组件老化与性能衰减:改参数而不改环境

老化是渐变故障,它的物理本质是光生电流下降、内部串联电阻增加、漏电流变大。反映到模型参数上,就是Isc下降、Rs增大、Rsh减小。和阴影遮挡完全不同,老化不会改变辐照输入,所以不需要拆模型。

实际操作时,我倾向于把关键参数做成MATLAB变量,方便批量扫老化工况。例如:

aging_rate = 10; % 老化工况,10% isc_aged = 9.08 * (1 - aging_rate / 100); rs_aged = rs_base * (1 + aging_rate / 15); % 串联电阻随老化上升

把模块里的Isc和Rs分别改成这些变量后跑仿真。老化的I-V特征很直观:整条曲线整体下移,Isc下降,Pmax下降,但不会出现台阶,P-V曲线也只有一个峰值。这是和阴影故障最明显的差别。

老化和阴影经常同时出现在真实电站里,诊断时不能只看Pmax减少,还要看有没有台阶、Voc有没有变化。做故障样本库的时候,两类样本必须都覆盖,而且可以再加组合故障样本:老化的组件正好被遮挡,曲线会又低又带台阶。

3.3 热斑故障:阵列级模型的近似做法

热斑的完整物理过程很复杂:某个电池被遮挡或损坏后,它变成负载消耗其他电池产生的能量,温度急剧升高,最终可能烧毁封装。要在仿真里精确复现这个过程,需要电池级的电-热耦合模型,普通阵列级PV模型做不到这个分辨率。

工程上的近似做法是:把疑似发生热斑的组件单独拆出来,给它一个很低的辐照输入(比如150 W/m²),同时把它的温度设得很高(比如85℃)。这样做能在电气层面复现热斑对整串输出的影响:该串短路电流明显低于其他串,I-V曲线出现类似阴影的台阶,而且由于电池温度高,该子串重新导通时的开路电压也更低,台阶位置和普通阴影有细微差别。

这里必须提醒一句:这种近似手段只能用于研究热斑对阵列输出的影响,不能用来判断热斑会不会烧毁组件、温度会不会蔓延。真要研究热斑安全性,得用专门的热仿真或实验,别把电气模型的结论直接套到安全评估上。

3.4 开路、短路和旁路二极管失效

开路故障的注入方式最容易理解:在组串中间加一个Breaker或者直接在模型里断开。我一般用Breaker,这样可以在仿真时间轴上控制故障发生时刻。开路现象很干净:故障串电流变为0,如果并联阵列里有一串断掉,阵列总Isc会按比例下降。比如4串并联断掉1串,Isc大约降为原来的3/4,Voc基本不变。

组件输出端短路的注入需要小心。我的做法是在被短路组件两端并联一个由开关控制的0.01Ω小电阻,故障信号到来时闭合小电阻。短路后该组件的输出电压被钳到接近0,整串输出电流畸变,曲线非常难看。仿真这种工况时求解器容易发散,建议在模拟电路里加一个小的稳定电容。

旁路二极管失效有两种情况,特征完全不同。断路失效意味着局部遮挡时没有旁路保护,被遮挡段被迫承受反向电压,I-V曲线会出现反向弯曲的异常特征;但因为普通PV模块没有内置反向击穿模型,这个只能做定性观察。短路失效更简单,被旁路的那段组件永久不参与发电,阵列Voc和Vmp会明显下降一段,P-V曲线一般只有一个峰但峰值点电压偏低。

3.5 组串失配:多分支不同参数模拟工程现实

组串失配在真实电站非常常见:新旧组件混装、不同批次组件混用、东西朝向辐照不同,都会造成并联支路参数不一致。这种故障的注入方式是把并联阵列的每条支路都独立建模,给不同支路设置不同的Isc、Voc或者不同的辐照曲线。

我常用的做法是:4条并联支路中,3条支路用全新的250W组件参数,第4条支路用老化10%的参数。仿真结果会出现和局部阴影类似的多峰P-V曲线,但根因完全不同:并联阵列的电压被高电压支路钳制,低电压支路电流受阻甚至被反偏。这时候要看每条支路单独的电流,哪条支路电流特别小甚至为0,就是被钳制的支路。给每条支路加一个电流测量点,是诊断失配问题的关键手段。

4. 把故障特征变成可用的诊断接口

4.1 从曲线里提取哪些特征

故障仿真的最终目的,是把“曲线异常”翻译成“故障类型和位置”。我一般从I-V/P-V曲线上提取这几类特征:

  • 基本电气量:Isc、Voc、Pmax、Vmpp、Impp。这几个量直接反映故障严重程度。
  • 填充因子FF = Pmax / (Voc × Isc)。FF下降一般意味着曲线形状劣化,老化、失配都会让它变化。
  • 台阶位置和数量:通过计算dV/dI的突变点定位。台阶数对应被旁路的子串组数,台阶的电流水平对应被遮挡部分的短路电流。
  • Isc附近的斜率:反映并联电阻Rsh的大小,Rsh下降时这段斜率更明显。
  • Voc附近的斜率:反映串联电阻Rs,老化时这段曲线会更“塌”。

整理成表格会更清楚:

故障类型I-V/P-V最明显特征需要重点看的参数
局部阴影I-V台阶、P-V多峰台阶电流水平、峰值数量
组件老化无台阶,整体下移Isc、Pmax、FF
热斑类似阴影但Isc更低台阶位置、该段Voc偏移
组串开路Isc按比例下降Isc、Voc
支路失配多峰、支路电流不均各支路电流、峰值数量

4.2 批量仿真与样本落盘

做故障诊断算法,不能只跑一两条曲线。我会用一个总控脚本循环跑不同故障模型,把V、I、P和故障标签一起存成mat文件。伪代码类似:

fault_cases = {'normal','shadow','aging','hotspot','open'}; for idx = 1:length(fault_cases) set_param([mdl '/FaultSelector'], 'Value', num2str(idx)); out = sim(mdl, 'StopTime', '20'); V = out.V.Data; I = out.I.Data; P = V .* I; save(sprintf('sample_%s.mat', fault_cases{idx}), 'V', 'I', 'P'); end

需要提醒的是,记录数据的同时一定要把环境参数、故障类型、故障严重度、注入位置一起存进变量,否则后期分析时数据没有标签,等于白跑。仿真结束前再用脚本自动抽特征值,生成一个特征表,后面训练分类器时直接用特征表,不需要每次重新跑Simulink。

4.3 加噪声:缩小仿真和实测的差距

直接用干净仿真曲线训练算法,拿到现场数据上通常效果不好,原因是仿真曲线太“完美”了。真实传感器有量化误差、噪声、温漂,MPPT过程还会引入动态纹波。解决方法是给曲线叠加噪声。

在Simulink模型里,最简单的方式是在V/I信号通路上加Band-Limited White Noise模块。噪声幅值建议按量程折算:电流量程50A时,叠加0.3A量级的噪声;电压量程1000V时,叠加5V量级。这组数只作起点,最好根据你的实际传感器精度来调。如果不想改模型,也可以在后处理阶段用MATLAB直接加噪声,例如:

I_noisy = I + 0.3 * randn(size(I));

加噪声最大的价值是让分类器学到“趋势特征”而不是“精确数值”,到了实测数据上才有泛化能力。我自己的经验是,训练集里混合一部分干净样本和一部分带噪样本,比全部用带噪样本效果更稳定。

4.4 用时间线剧本一次跑完多故障

很多人做故障仿真是每个故障单独建一个模型,模型文件越来越多,管理很乱。我更推荐在模型里做一个故障选择子系统:用Signal Builder或者Repeating Sequence生成一个整数信号,0代表正常、1代表阴影、2代表老化、3代表热斑,然后在子系统内部用Multiport Switch去切换不同的辐照、温度输入或组件参数。

这样一次仿真可以输出正常→阴影→老化→热斑的连续时间线,数据里自带时段标签。切换瞬间求解器容易抖,我建议在切换点附近加一个0.5秒的斜坡过渡,不要直接用阶跃。这个小技巧可以大幅降低求解器收敛失败的概率,也让数据更接近真实故障的渐变过程。

5. 仿真中容易翻车的细节与工程化建议

5.1 求解器发散:不是模型错了,是数值问题

Simulink里故障模型最常报的错就是“Nonlinear solver: failed to converge”,尤其当我把多块PV组件串联、再并上旁路二极管和开关之后。第一反应不要觉得模型搭错,大概率是数值问题。

我的处理顺序是:第一步在网络里加Solver Configuration;第二步把仿真最大步长限制在0.01秒以内;第三步把电压扫描时间拉长;第四步,如果还发散,就在容易电压突变的节点并一个小电容,比如10μF,用来稳定节点电压。真实逆变器输入侧本来就有电容,仿真里加它不算作弊,反而更接近实际。有一回我把20块组件直接串起来扫电压,加了个小电容之后立刻就稳了,这个经验可以少走很多弯路。

5.2 扫描范围和步长直接影响曲线质量

扫描电压范围我建议取0到1.05倍的Voc,再高没有物理意义,阵列会被强行拉到反向工作区,电流变负、P-V曲线乱套。扫描步长要足够细,至少2000个点,否则台阶和峰值细节会被吃掉。我见过有人用50个点扫描,明明有双峰,画出来却只有一个峰,还以为是模型问题,其实是采样太粗。

如果做MPPT寻优算法测试,我更推荐用慢速锯齿波而不是单次斜坡,这样可以在一个仿真时长内反复扫描多次,观察算法在不同工作点之间的切换行为。锯齿波的下降沿也要留足够时间,让电路回到稳态。

5.3 温度输入错误的隐蔽误差

温度是PV仿真里最容易埋雷的参数。PV Array模块的那个T是电池温度,不是气象站的环境温度。如果你直接把环境温度25℃填进去,在标准工况下误差还不明显,但到高温或低温场景,Voc偏差会放大到几个百分点。做热斑仿真时更是如此,热斑温度是局部电池温度,可能超过100℃,这时候用环境温度显然不对。

没有实验数据时可以按经验公式粗估电池温度:T_cell≈T_amb+0.03×Ir。这只是工程粗估,不是精确值,但至少比直接把环境温度当电池温度靠谱。正式实验时还是要用红外测温仪或背板温度传感器。

5.4 批量并行跑模型的文件锁问题

批量仿真跑多了之后,我踩过一个很具体的坑:用parfor并行跑同一个Simulink模型,MATLAB会报模型文件被占用,因为多个worker在同时加载同一个slx文件。解决办法有两种:一是把模型内部改成Model Reference,让每个worker独立实例化;二是简单粗暴,在循环里复制一个临时模型副本再跑:

newModel = tempname; copyfile('pv_fault_model.slx', [newModel '.slx']); load_system(newModel); out = sim(newModel, 'StopTime', '20'); close_system(newModel, 0);

另外,如果一批仿真只有参数变化,拓扑不变,优先用Fast Restart模式,避免每次从头初始化整个模型,速度能快好几倍。

5.5 热斑仿真结果的适用范围要讲清楚

最后再强调一次模型边界。我这里说的热斑仿真,是在电气输出层面复现“热斑会让I-V/P-V曲线变成什么样”,它服务于故障诊断和算法验证。如果你想研究热斑本身的温度场分布、封装材料是否被烧穿、组件会不会起火,这套阵列级模型完全不够用,必须回到电池级电-热耦合仿真或者做真机实验。把电气仿真结论乱用在安全评估上,是会出大问题的。

5.6 分享一个我的固定流程

每次开始故障注入之前,我都要先跑一遍STC基线。如果基线Pmax偏差超过1%,先修模型,再谈故障样本。原因很简单:在一个有系统误差的基线上叠加故障,生成的所有样本都带着同样的偏差,训练出来的诊断模型到真实数据上表现会很差。这个习惯帮我避免过无数次返工。

我还会在存档数据时,把辐照、温度、故障类型、故障注入位置、严重度全部写进文件名或数据结构里。仿真跑完那一刻不存在“以后再补标签”这回事,时间一长你自己都记不清那条曲线当时是怎么生成的。数据自描述,是故障仿真工程化的第一步,也是最后一步。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进