☰
配电网可靠性评估中序贯蒙特卡洛模拟的原理与Matlab实现
2026/10/7 4:25:33 网站建设 项目流程

1. 从"为什么选序贯蒙特卡洛"说起:解析法搞不定的场景,它反而擅长

配电网可靠性评估这个方向,理论书籍里讲得最多的是解析法。故障模式影响分析、最小割集、网络拓扑法,这些方法在小型辐射状配电网里确实够用,手算都能出结果。可一旦网络规模上去了、分布式电源接进来了、分段开关联络开关的动作策略变复杂了,解析法的建模复杂度会急剧膨胀——每个开关动作策略都要写一遍条件概率表达式,每个孤岛运行场景都要重新推导最小割集,代码写了一堆,最后结果还不一定准。

这时候就该蒙特卡洛模拟上场了。蒙特卡洛思路很朴素:把元件故障看成随机过程,用随机抽样驱动系统状态变化,跑足够多年份,然后统计停电事件的平均表现。它不追求穷举所有系统状态,而是用大量随机样本来逼近真实概率分布,天然能处理复杂拓扑、复杂控制策略,甚至非指数分布也照单全收。

蒙特卡洛内部还分两支:序贯和非序贯。非序贯法(状态抽样法)是直接对每个元件抽一个运行/停运状态,组合成系统状态再做评估,不需要时间维度,优点是计算快,缺点是它隐含了一个假设——各元件的状态是独立且同时发生的,这跟配电网的物理过程不太吻合。实际上配电网发生故障后有一个完整的时序过程:故障发生、保护动作、隔离故障、开关切换转供、修复恢复。非序贯法很难细致刻画这些步骤对用户停电时间的影响。

序贯蒙特卡洛模拟法则不一样,它按时间轴一步步推演,每个元件都生成一段连续的生命周期——正常运行一段时间、故障停运一段时间、再修复投运,如此循环。然后把这些元件生命周期叠加在同一个时间轴上,系统层面就能看到一个完整的停电事件从发生到恢复的全过程。这就是序贯法最大的价值:它把时间顺序和状态转移逻辑都保留下来了,能直接回答"用户平均一年断电几次、平均每次断电多久"这类工程问题。

我在实际做配电网可靠性仿真时,基本默认用序贯法。本文就把这套方法的原理、Matlab代码架构、指标统计细节和工程坑一次性讲清楚,适合正在做配电网规划、可靠性评估、分布式电源并网影响分析的工程师和研究生参考。

2. 序贯模拟的心跳:元件时序状态生成与事件步进机制

2.1 两状态可靠性模型与状态持续时间抽样

配电网元件的运行状态可以抽象为最简单的两状态模型:正常运行(运行态)和故障停运(停运态)。长期统计下来,元件从运行态进入故障态的平均时间间隔就是平均无故障工作时间MTTF,从故障态恢复到运行态的平均时间就是平均修复时间MTTR。

这里有一个非常关键的转换:教科书上元件故障率通常给的是"次/年",比如某条架空线路故障率λ=0.25次/年。在Matlab仿真里,我们把时间单位统一成小时,那么故障率就是λ_hour = 0.25 / 8760 ≈ 2.85e-5次/小时。同样,修复率μ = 1 / MTTR,MTTR单位是小时,修复率单位就是次/小时。

然后我们用指数分布抽样来确定每个状态持续的时间。指数分布有个经典性质:如果随机变量服从均值为1/λ的指数分布,那么用均匀随机数U通过反变换法就能抽出一个持续时间:

[ T = -\frac{1}{\lambda} \ln(U) ]

其中U是(0,1)区间均匀分布的随机数。运行状态持续时间的期望是MTTF,故障状态持续时间的期望是MTTR。这个公式是整个序贯模拟的基石,每个元件在每个状态下的停留时间都由它产生。

2.2 事件步进法:为什么不用固定时间步长扫描

刚开始写这套代码时,我陷入过一个误区:用小时作为固定步长,逐小时扫描系统状态,仿真1000年就是876万个时间点,元件一多,内存和CPU都吃不消,而且大部分时间系统什么都没发生,纯属浪费。

事件步进法完全绕开了这个浪费。它的思路是:只记录状态改变的时刻。每个元件生成一串"事件对",每个事件对包含状态持续时间、转移后状态。整个系统层面,把所有元件的事件列表合并起来,按时间排序,只在有事件发生的时刻去检查系统状态、提取故障影响,其他时间直接跳过。这样仿真1000年的年度序列,实际需要处理的事件数大约是"总元件数 × 1000年 × 每年平均故障数",量级小得多。

比如一条馈线年故障率0.2次,20条馈线,每年系统级故障事件也就是个位数的量级,1000年也就几千个事件。这个数量级在Matlab里处理是非常轻松的。

2.3 元件状态序列生成的Matlab代码骨架

我贴一段核心的状态序列生成函数,做法是为每个元件生成状态持续时间序列,返回一个二维数组,第一列是状态转移时刻(小时),第二列是转移后的状态。运行态用0表示,故障态用1表示。

function evts = gen_component_events(lambda_year, MTTR, sim_hours, comp_id) % 输入: % lambda_year : 元件年故障率(次/年) % MTTR : 平均修复时间(小时) % sim_hours : 仿真总时长(小时) % comp_id : 元件编号(用于调试) % 输出: % evts : N×3数组, 每行=[转移时刻(小时), 状态, 持续时间(小时)] lambda_h = lambda_year / 8760; % 折算成小时故障率 mu = 1 / MTTR; % 修复率(次/小时) t = 0; state = 0; % 初始为运行态 evts = zeros(10000, 3); % 预分配, 实际够用 n = 0; while t < sim_hours if state == 0 % 正常运行, 抽样正常运行持续时间 d = -log(rand) / lambda_h; % 期望 MTTF = 1/lambda_h state = 1; else % 故障状态, 抽样修复时间 d = -log(rand) / mu; % 期望 MTTR = 1/mu = MTTR state = 0; end t = t + d; if t > sim_hours break; end n = n + 1; evts(n, :) = [t, state, d]; end evts = evts(1:n, :); end

这段代码有几个容易踩坑的地方。一是初始状态,第一个持续时间从t=0开始抽取,默认元件初始是运行态,这在长期仿真里偏差很小,但如果你只仿真一年,这个初始假设会影响第一年的指标。二是预分配数组大小,千万养成预分配习惯,不然在循环里动态增长数组会慢到怀疑人生。三是事件序列是"在一个元件内部按时间排好序",但不同元件之间的事件需要后续合并排序。

2.4 故障事件的提取逻辑

有了每个元件的状态转换序列,系统级别的工作就清楚多了:把全部元件的事件按照发生时刻合并,生成一个统一的系统时间线。在每一个有元件状态发生变化的时刻,当前的"系统状态向量"就改变了——某个元件故障了或者恢复了。我们把这个时刻记下来,同时记录哪些元件处于故障态,持续到哪个时刻。

一个典型故障事件的完整生命周期是这样的:馈线某段在t时刻故障,保护装置动作,故障段隔离,非故障段通过联络开关恢复供电;修复人员到场,修复故障段,系统恢复到正常拓扑。在序贯模拟中,这个生命周期压缩成两个关键时间点:故障开始时刻和故障修复时刻,两者之间的区间就是系统处于异常状态的时段。

对于简单辐射状拓扑,如果配电网没有太复杂的开关重构策略,故障的后果评估可以按每条馈线单独处理:故障元件所在支路下游负荷点停电时间等于修复时间,上游负荷点停电时间可取0(如果有备用电源自动切换则另说)。这就是序贯法和非序贯法最大的不同——解析法需要你对着拓扑逐条算,序贯法只需要你按时间逐事件判断。

3. 主循环设计:从元件事件到负荷点停电时间的累积统计

3.1 系统仿真主循环的Matlab框架

主循环是整个程序的中枢。我的做法是分三层:外层循环跑仿真年份,中层循环遍历每个系统故障事件,内层循环遍历所有负荷点评估该事件的影响。

下图是主框架的核心伪代码结构(不涉及具体数据格式):

% 仿真参数 sim_years = 1000; % 仿真年份数 sim_hours = sim_years * 8760; n_components = length(components); % 元件数 n_loadpoints = length(loadpoints); % 负荷点数 % 预分配累计变量 total_outage_count = 0; % 累计停电次数 total_outage_duration = 0; % 累计停电时长(小时) total_energy_not_supplied = 0; % 累计失电量(kWh) % 生成所有元件的状态序列 comp_events = cell(n_components, 1); for i = 1:n_components comp_events{i} = gen_component_events(... components(i).lambda, components(i).MTTR, sim_hours, i); end % 合并事件到全局时间轴 sys_time_line = merge_events(comp_events); % 遍历每个系统事件 for e = 1:size(sys_time_line, 1) t_event = sys_time_line(e, 1); comp_id = sys_time_line(e, 2); state = sys_time_line(e, 3); % 如果是元件从运行转故障, 触发故障事件分析 if state == 1 % 寻找该元件故障影响的负荷点集合 affected_lps = find_downstream_loadpoints(comp_id); % 对每个受影响负荷点, 累积故障次数和故障时长 for lp = affected_lps outage_duration = compute_outage_duration(comp_id, lp, t_event, sys_time_line); total_outage_count = total_outage_count + 1; total_outage_duration = total_outage_duration + outage_duration; % 如果该负荷点有功率数据, 统计失电量 total_energy_not_supplied = total_energy_not_supplied + ... loadpoints(lp).average_power * outage_duration; end end end

实际跑起来,这个主循环的复杂度远远低于"年逐小时扫描",因为系统事件数远少于时间点数。我测试过一个101节点的配电网(典型IEEE RBTS算例),30个元件,仿真1万年的系统事件大约是3万个,Matlab里主循环几秒钟就跑完了。

3.2 故障上游与下游负荷点如何区分

故障影响分析是序贯法的核心功夫。一个元件故障,对整个配电网的用户停电影响取决于网络拓扑和开关布置。简单辐射状网络下:

  • 故障点下游的所有负荷点全部停电,停电时长 = 故障修复时间。
  • 故障点上游的负荷点,如果配电网没有联络开关或其他电源支撑,也会停电,但停电时长通常等于故障隔离时间(通常很短,假定保护装置能快速切除)。
  • 如果有联络开关且负荷可以转供到相邻馈线,上游负荷点在倒闸操作完成后即恢复供电。

实现上,需要把网络拓扑存成邻接矩阵或节点-支路关联表。对于逐事件的遍历,如果每次事件都重新做一次拓扑遍历,效率堪忧。我通常的做法是预先计算每个支路故障时影响的下游负荷点列表,存成一个稀疏关联矩阵,这样在主循环里查表就行。

假设网络有n_b条支路和n_l个负荷点,构造一个n_b×n_l的稀疏矩阵downstream_map,如果支路b故障导致负荷点l停电,则downstream_map(b,l)=1。这个矩阵用深度优先搜索一次生成,后续查效率极高。

3.3 一年指标与多年指标的转换

序贯法的仿真结果是"多年累加值",最后要折算成年均值。比如仿真了1000年,累计停电次数是3500次,那系统年平均停电次数就是3.5次。累计停电时长同样折算成年均值。

实际输出时,我的做法是每一年的年度指标都统计一版,这样可以画出年度指标随年份变化的曲线,直观看到收敛过程。具体实现是在主循环里记录每个事件发生所在的"仿真的第几年",按年分组累加。这个记录并不额外增加计算负担。

4. 可靠性指标计算的细节:公式、单位与Matlab实现

4.1 负荷点指标先算清楚

配电网可靠性指标的层次分为负荷点级和系统级。负荷点级的三个基础指标是:

  • 平均故障率λ(次/年):该负荷点每年平均经历多少次停电。
  • 平均停电持续时间r(小时/次):该负荷点每次停电平均持续多久。
  • 年平均停电时间U(小时/年):该负荷点每年平均累计停电多久。

三者满足 U = λ × r。在序贯仿真中,分别统计这个负荷点的停电次数和停电时长,除以仿真年数即可。

4.2 系统级指标与公式

系统级指标在负荷点指标基础上,用用户数或负荷量加权聚合。常用的几个:

指标公式单位含义
SAIFIΣ(用户停电次数) / 总用户数次/(用户·年)系统平均停电频率
SAIDIΣ(用户停电时长) / 总用户数小时/(用户·年)系统平均停电持续时间
CAIDISAIDI / SAIFI小时/次用户每次停电的平均持续时间
ASAI(用户总供电小时 - 用户停电小时) / 用户总供电小时无供电可用率
ENSΣ(每次停电的失电量)kWh/年年总失电量
AENSENS / 总用户数kWh/(用户·年)平均每个用户失电量

计算时最容易被忽略的是SAIFI和SAIDI分母的分歧。SAIFI本质是"平均每个用户每年停几次电",分母是用户总数;但如果某个负荷点的用户数多,它在分子里的加权就更大。Matlab中实现时,需要给每个负荷点配置用户数n_user和平均功率。代码可以这样写:

% 某负荷点 lp 的统计 SAIFI_num = SAIFI_num + outage_count(lp) * n_user(lp); SAIFI_den = SAIFI_den + n_user(lp); SAIDI_num = SAIDI_num + outage_duration(lp) * n_user(lp); SAIDI_den = SAIDI_den + n_user(lp); % 仿真结束后 SAIFI = SAIFI_num / SAIFI_den / sim_years; SAIDI = SAIDI_num / SAIDI_den / sim_years; CAIDI = SAIDI / SAIFI; ASAI = (sim_years * 8760 - total_outage_duration / n_user_total) / (sim_years * 8760);

注意ENS统计中功率的处理。如果做年度时序仿真,每个小时的负荷是变化的,那失电量应该积分每个停电时段内的实时负荷;如果简化为平均负荷,那ENS就是平均功率乘以停电时长。教科书算例一般给恒定负荷,平均功率处理就够了。但工程上要评估配电网可靠性对用户的影响,建议至少用日负荷曲线,因为故障高发期和负荷高峰期的错位会严重影响ENS数值。

4.3 故障隔离与转供恢复的时长建模

有一个细节值得展开:负荷点停电时长并不永远等于元件的修复时间。

  • 对于故障点上游的负荷点,如果保护装置动作后立即恢复上游供电,停电时长可能只有几秒到几分钟,这部分在序贯模拟中往往被简化建模为"故障隔离时间"。
  • 如果上游负荷点没有备用电源,停电时长就要算到修复完成。
  • 如果有联络开关且转供容量足够,上游负荷点停电时长 = 倒闸操作时间;由于下游负荷点不能转供,停电时长 = 修复时间。

所以每个负荷点的停电时长分布其实是混合的。我在程序里用一个"恢复策略配置矩阵"来描述:对每条支路、每个负荷点,给一个恢复方式标识(0表示等待修复,1表示通过分段开关隔离后立即恢复,2表示通过联络开关转供恢复),以及对应的恢复操作所需时间。这种表驱动的做法,后续修改策略时只需要改数据表,不用改主循环代码。

顺带说一句,很多教材里讲序贯法只做简单的"支路故障,下游停电"两分法,这个在论文里够用,但工程上真要对具体的配电网做评估,两分法会高估停电时长——实际配电网大量故障通过分段开关隔离和联络开关转供,在1小时内就能恢复非故障段供电,这部分差别直接体现在SAIDI指标上,数值差20%很正常。

5. 收敛性判断与计算加速:仿真多少年才算数

5.1 为什么不能指定"跑1000年就完事"

序贯蒙特卡洛的收敛性判断是新手最容易糊弄过去的地方。有人直接写个sim_years=1000,跑完就宣称得到结果,这是不对的。不同网络的可靠性水平差异巨大,有的每年平均故障次数高、方差大,有的系统极少停电,1000年的样本可能都不够格。

标准的做法是用相对方差系数β来判断。以系统年平均停电频率SAIFI为例,假设仿真产生的年停电频率样本有m年的记录,样本均值(\hat{\lambda})和样本方差(S^2)可以算出,那么:

[ \beta = \frac{S / \sqrt{m}}{\hat{\lambda}} ]

β反映的是估计值的相对精度。通常要求β小于等于5%左右才认为结果可接受。如果β太高,就继续增加仿真年份,直到满足精度为止。

在代码里做动态收敛判定的逻辑大概是:

beta = 1; sim_year = 0; results = []; while beta > 0.05 && sim_year < MAX_SIM_YEARS % 继续追加仿真N年(比如每次+100年) sim_year = sim_year + BLOCK_YEARS; % 跑一个区块的仿真, 得到该区块的年指标 block_result = simulate_block(BLOCK_YEARS); results = [results; block_result]; lambda_hat = mean(results); S2 = var(results); beta = sqrt(S2 / length(results)) / lambda_hat; end

注意相似但重要的点:可靠性指标的方差天然比较大,尤其像SAIFI这种低频事件,如果系统平均停电频率只有0.5次/年,那逐年数据的方差很容易就超过均值,要达到β<5%,仿真年份可能要到数万年级别。这是序贯法的客观代价,跑之前要有心理准备。

5.2 方差缩减的实用手段

如果仿真真的需要天文数字般的时间,就需要方差缩减技术。评价序贯蒙特卡洛的方差缩减方法,这里我实际用过的、有效又不引入太大复杂度的有三种。

第一种是公共随机数(对偶抽样)。跑完一组随机种子后,把种子倒过来再跑一遍,两次结果取平均。这样能抵消一部分随机涨落对系统指标的影响。实现非常容易,代价是计算时间翻倍,但方差缩减效果通常在30%左右,性价比不错。

第二种是控制变量法。找一个与目标指标相关性高的辅助量,比如"系统元件总故障次数",它的期望可以从元件可靠性参数直接解析算出。用模拟得到的辅助量均值与理论值的偏差去修正目标量的估计,可以把主指标的重大随机波动削掉一部分。这个方法在配电网这样做过很多次,对ENS这类受负荷随机性影响大的指标尤其有效。

第三种是分层抽样。把仿真按"无故障年"和"有故障年"分层,对无故障年的比例单独估计,对有故障年的年份单独统计指标。其实就是把每年指标拆成"是否停电"和"停电时长"两个独立事件分别估计。代码稍复杂,但可以显著减小SAIFI估计的方差。

5.3 计算加速的其他思路

除了方差缩减,代码层面还有两个加速点。第一是用Matlab的稀疏矩阵和向量化运算来批量处理负荷点评估。主循环内层的负荷点评估最容易成为瓶颈,如果downstream_map预计算成稀疏矩阵,影响评估可以直接做逻辑索引运算。

第二是采用并行仿真。Matlab的parfor对这类场景天然友好——把仿真年份分成若干块,每块在不同worker上独立跑,最后汇总。我实测过在12核机器上可以做到近线性的加速比。如果只是单机跑,Simulation Years上到50000年,串行可能要跑数十分钟甚至更久,并行之后能缩小到几分钟级别。

6. 工程实现中最容易翻车的细节:四个坑和逐个绕坑方案

6.1 坑一:仿真起始瞬态偏差

序贯法仿真一开始,所有元件都从正常状态出发,这会导致最初几年的故障率低于稳态值——因为元件刚投运"新"的,没有累积自然老化的故障风险。虽然指数分布无记忆性在理论上避免了老化效应,但如果只仿真100年,前面几年的偏差会对整体指标产生可察觉的影响。

绕坑方法其实很简单:一是把仿真开始的"暖机"阶段去掉,丢弃前5%~10%年份的统计结果;二是在生成元件事件序列时,让第一个状态持续时间在"中间截断"而非从完整分布抽样。我习惯用第一种,操作透明且容易解释,尤其在论文里描述仿真方法时,直接写明"丢弃前50年作为启动瞬态"就交代清楚了。

6.2 坑二:元件故障率单位换算错误

这个坑我早期踩得特别深。教材给元件可靠性参数常常是"故障率0.1次/年、平均修复时间5小时/次",看起来很简单。但如果你把修复率μ直接写成5,或者把故障率λ直接除以8760又四舍五入,在模拟里跑出来的MTBF和MTTR就完全不对了。

一个例子:某条馈线λ=0.25次/年,MTTR=4小时。换算后λ_hour应该精确写成0.25/8760=2.8539e-5次/小时。如果你偷懒用0.25/8760=2.85e-5,看似只差一点点,但仿真10000年后,总故障次数的误差能放大到约140次,SAIFI直接偏差5%。修复率μ=1/4=0.25次/小时,这个不能换算错。我建议在代码里用注释明确标注每个变量的单位,并且在仿真结束后做一个校验:模拟出的每年元件故障总数应该接近Σλ,如果偏差超过5%,先回去查单位。

6.3 坑三:负荷点用户数与节点的对应关系

可靠性指标公式看起来简单,但"用户数"这个数据在实践中非常容易搞错。IEEE标准算例里通常给出每个负荷节点的用户数,比如节点2有120户。但在实际工程数据中,你拿到的可能是"该台区年售电量",或者"配变容量",需要自行折算成用户数或负荷值。

我在程序里建议的数据结构是:每个负荷点一个结构体,包含total_user、average_power、load_curve参数。即使算例里所有节点都按1个用户处理,也要保留这个字段,因为后续做敏感性分析、评价"哪个节点对SAIFI贡献最大"时必须用得上。另一个常见问题是把"节点数"当成"用户数"去算SAIFI,得到的数值会大得离谱,这类错误在论文审稿时比较好抓,但在自己调试过程中也很难发现,建议写个断言:SAIFI数值应该在0~50范围内,超出就报警。

6.4 坑四:只统计了"停电开始"却没统计"停电结束"

这个坑最隐蔽。事件步进法扫描过程中,一个故障在t时刻发生,影响某些负荷点。如果你只在这个时刻把停电次数和停电时长加上去,忽略了故障修复时刻的系统状态更新,就会造成同一故障事件对不同负荷点"重复停电"或"漏停"的问题。

严谨的做法是:每个故障事件不仅要记录发生时刻,还要在事件列表里找到对应的修复时刻。对于每个受影响负荷点,在故障发生时刻记录停电开始,在修复时刻记录停电结束,中间的时间段才是真正的停电时长。如果中间有倒闸操作恢复供电的负荷点,应该在恢复时刻关闭停电记录。为了减少这类逻辑错误,我会用两个累计器分别记录"停电次数"和"停电总时长",前者在故障发生时递增,后者在恢复时递增。这个区分在代码结构上也更清晰,方便后续添加复杂的恢复策略。

7. 工程落地中的经验与建议

最后分享几点实操层面的体会。

第一,序贯蒙特卡洛做配电网可靠性评估,代码实现有难度但不是核心难点。真正的难点在数据——网络的拓扑数据、每个元件的可靠性参数、每个开关的动作逻辑、负荷点的用户数和负荷曲线,这些数据的整理通常要占整个项目60%的时间。写代码之前先花大力气把数据表设计好,能节省后续大量调试时间。

第二,仿真程序写完后,先用一个极其简单的网络做验证。比如一个电源点带两个负荷点的辐射状网络,手算都能算出SAIFI和SAIDI,跑一遍仿真对一下数字,确认代码逻辑正确后再套用实际复杂网络。我每次改代码都重复这个流程,避免在错误逻辑上叠床架屋。

第三,结果的可视化对说服力很有帮助。收敛曲线(β随年份下降)、年停电次数分布直方图、各负荷点停电时长箱线图,这些图不仅能验证程序的收敛性,还能在汇报结果时直观展示"为什么认为这个指标可信"。Matlab生态里这个流程已经很成熟了,plot、histogram、boxplot三个函数就够用。

序贯蒙特卡洛的优势在于时间逻辑完整,能够精细刻画故障响应过程,这在含分布式电源、微网和复杂开关策略的现代配电网里几乎是不可替代的。把本文这套框架吃透,可靠性评估的代码实现就基本成型了,后续往任意方向扩展——加新能源模型、加储能策略、加负荷时序性——都只是在这个骨架上做增量开发的事。

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

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

立即咨询