简介:面向电力系统规划、调度与运营人员的蒙特卡洛仿真模拟负荷出力预测资料包,以概率统计视角讲解负荷场景建模思路,覆盖历史数据收集、模型构建、随机抽样、结果分析与优化验证等完整流程。压缩包共7个文件,大小仅23KB,以4个Matlab脚本(.m)为核心,另有自动保存文件(.asv)和一张负荷曲线图(.fig);脚本包含负荷曲线生成、参数提取等关键函数,结构清晰,便于直接运行、二次改造或嵌入教学演示。已有550人学习,适合需要掌握蒙特卡洛模拟预测方法、快速搭建负荷出力仿真模型的电力研究者与工程师。借助配套图形与代码,可直观理解负荷出力的概率分布与不确定性范围,减少重复编程,辅助完成负荷预测、风险评估及调度决策中的定量分析。
1. 蒙特卡洛仿真模拟电力负荷出力,到底在解决什么问题
调度评审会上最常见的争论是「预测曲线之外,该留多少余量」。确定性负荷预测只给一条最可能的曲线,但实际出力受气温预报偏差、用户侧随机行为、分布式电源波动影响,一条线回答不了「最坏到多少」。过去靠预测值加固定安全系数,系数拍小了出风险,拍大了被质疑浪费。
蒙特卡洛仿真模拟电力负荷出力,就是把负荷当成服从概率分布的随机过程,用上千次抽样生成同样多的可能出力曲线,再对场景集求 P50、P95、最大缺口,把「不确定性」变成可计算、可回测的定量输入,直接支撑容量规划、储能配置和调度策略评估。适合做负荷预测、微网和储能设计的人,也适合要向业务方解释余量依据的技术人员。
2. 负荷出力随机模型怎么定:分布选取、误差尺度与时序相关
2.1 预测误差不是白噪声,先分清来源再建模
确定性负荷预测的误差来源大致三类:气象预报偏差,空调负荷占比高的地区尤其明显;用户侧行为,电动车充电、商业排产、节假日安排都在其列;系统事件,比如设备检修和临时限电。这三类来源有一个共同特征:连续时段同向偏差,不是独立的高斯白噪声。把预测误差当白噪声直接叠加在曲线上,生成的场景集每个时段都在高频抖动,相邻时段的出力变化量远超真实系统。这个「假爬坡」会污染所有与时序相关的评估指标,后面算储能切换次数、机组爬坡校验时会一起失真。
我一般在建模前先把误差按来源分类统计,用过去 6-12 个月的实际负荷与同期预测值做残差分析:先看残差均值是否显著非零,判断有无系统偏差;再看标准差随负荷水平怎么变,决定用比例误差还是绝对误差;最后算一阶自相关系数,决定要不要上时序模型。残差均值明显非零时,问题出在预测模型本身,先修预测,不要让一个带偏置的随机模型去掩盖它。
2.2 正态、t 分布还是 GEV:按评估对象选尾部
最常见的设定是正态误差:
P(t) = P_fc(t) + ε(t), ε(t) ~ N(0, (σ% · P_fc(t))²)
σ% 是相对误差标准差,日前预测一般取 3%-5%,日内滚动预测取 1.5%-3%。误差强度取成与负荷水平成正比,是因为峰荷时段空调和生产负荷占比高,绝对预测偏差天然更大;用固定绝对误差会低估峰荷时段的波动、高估谷荷时段的波动,包络形状整体失真。
正态分布在均值附近表现好,短板在尾部偏薄。评估对象是 P95 以内的常规容量与电价风险时,正态加合理 σ% 足够;一旦涉及保供、限电这类极端场景评估,我会换 t 分布(自由度 5 左右),或在历史数据超过 3 年时对每时段极端负荷直接拟合广义极值分布(GEV)。注意厚尾是双刃剑:t(ν=4) 的 99% 分位比正态高出不少,规划结果明显偏保守,容量成本上升,这个取舍要让决策者知道。另外要区分本主题与材料、化学领域的动力学蒙特卡洛——KMC 按事件速率推进系统状态,属于动态演化;电力负荷出力仿真用的是静态统计蒙特卡洛,每轮抽样产生一个独立场景,场景之间不共享演化轨道。
2.3 用 AR(1) 把相邻时段的误差相关性装进去
独立抽样下各时段 ε(t) 互不相关,场景曲线爬坡率被系统性夸大,储能充放电切换次数、机组爬坡校验这类指标的结论会跟着失真。简单且有效的做法是 AR(1):
ε(t) = φ·ε(t-1) + η(t), η(t) ~ N(0, (σ%·P_fc(t))²·(1-φ²))
φ 是一阶自相关系数,取 0.6-0.9:日前预测误差持续性强取高值,日内滚动预测取低值。η 的方差乘 (1-φ²),是为了让 ε(t) 的边际方差保持在 (σ%·P_fc(t))²——φ 只改变误差的时间结构,不改变整体波动幅度。参数起步值参考下表,落地前用你自己的残差统计复核:
| 场景 | 分布 | σ% 建议 | φ 建议 | 选择理由 |
|---|---|---|---|---|
| 日前预测、工商业主导 | 正态 | 3-5% | 0.7-0.9 | 误差主要由温度预报偏差驱动,持续性强 |
| 日内滚动预测(4 小时内) | 正态 | 1.5-3% | 0.5-0.7 | 有实测值持续校正,误差衰减快 |
| 极端天气 / 保供评估 | t(ν=5) | 5-8% | 0.8 左右 | 需要厚尾覆盖尖峰风险 |
| 高比例分布式光伏 / 充电桩 | t(ν=4) 或混合分布 | 6-10% | 0.6-0.8 | 用户侧随机行为多,误差常呈双峰 |
σ% 的标定窗口不要太短:一个月数据标出来的 σ% 波动很大,换个月份结论就变。我一般用 60-90 天滚动窗口,每天重算一次;φ 用残差的一阶自相关直接估计,不用手调。
import numpy as np rng = np.random.default_rng(7) T = 96 # 15 分钟一个点,一天 96 点 phi = 0.8 # AR(1) 自相关系数 sigma_ratio = 0.04 # 相对误差标准差 4% def base_load(t): # 典型双峰负荷曲线,单位 MW,t 为 0..95 morning = 220 * np.exp(-((t - 34) / 5.0) ** 2) evening = 260 * np.exp(-((t - 76) / 4.5) ** 2) return 180 + morning + evening P_fc = base_load(np.arange(T)) eta_scale = sigma_ratio * P_fc * np.sqrt(1.0 - phi * phi) e = np.zeros(T) e[0] = rng.normal(0, sigma_ratio * P_fc[0]) for t in range(1, T): e[t] = phi * e[t-1] + rng.normal(0, eta_scale[t])eta_scale 每个时段都不同,因为误差尺度跟着预测负荷走;e[0] 直接用当前负荷的 σ% 抽,AR 过程在前几个点完成预热,对 96 点曲线影响可忽略。φ=0 时退化为独立抽样,相当于一个开关参数,方便你做对照实验验证相关性到底影响了哪些指标。
3. 用 Python 跑通蒙特卡洛负荷出力仿真的最小脚本
3.1 生成 N 条负荷出力场景的完整代码
下面的脚本可以直接存成 .py 文件运行。P_fc 是确定性预测曲线,先用双峰负荷形状示意,实际项目里换成你系统的负荷预测输出即可,其余逻辑不用动。
import numpy as np def generate_scenarios(n_scen, P_fc, sigma_ratio, phi, seed=42): rng = np.random.default_rng(seed) # 固定 seed,保证可复现 T = len(P_fc) eta_scale = sigma_ratio * P_fc * np.sqrt(1.0 - phi * phi) scenarios = np.zeros((n_scen, T)) for i in range(n_scen): e = np.zeros(T) e[0] = rng.normal(0, sigma_ratio * P_fc[0]) for t in range(1, T): e[t] = phi * e[t-1] + rng.normal(0, eta_scale[t]) scenarios[i] = P_fc + e # 预测曲线 + 相关误差 return scenarios P_fc = base_load(np.arange(96)) S = generate_scenarios(5000, P_fc, sigma_ratio=0.04, phi=0.8, seed=42) print(S.shape) # (5000, 96)参数说明:n_scen 是场景数,5000 是起步值,做 P95 评估建议提到 10000 以上;sigma_ratio 对应上一章的相对误差标准差;phi 是 AR(1) 系数,控制相邻时段误差的平滑程度;seed 固定后同事能复现出同一个结果集,排错和评审都靠它。逐时段循环是 Python 里最直接的写法,5000 个场景 × 96 点在一台普通笔记本上毫秒级跑完,没有性能压力。
提示:场景集生成后先另存为 .npy 文件,后续所有分位数计算、回测脚本都从同一个文件读数据,避免每次运行重新抽样的随机差异干扰对比。
3.2 从场景集提取 P50 / P05 / P95 包络
p50 = np.percentile(S, 50, axis=0) p05 = np.percentile(S, 5, axis=0) p95 = np.percentile(S, 95, axis=0) import matplotlib.pyplot as plt t = np.arange(96) plt.figure(figsize=(10, 5)) plt.plot(t, P_fc, "k-", lw=2, label="deterministic forecast") plt.fill_between(t, p05, p95, alpha=0.3, label="5%-95% envelope") plt.legend() plt.xlabel("period index (15 min)") plt.ylabel("load (MW)")关键在 axis=0:它表示对 5000 个场景在「同一时段」上求分位数,输出 96 个时段各自的分布分位点。容易犯的错是把 axis 省掉,对整个场景矩阵求一个全局分位数,得到的是一条没有时间意义的直线。P05 到 P95 之间是 90% 置信带宽,含义是:若模型假设成立,真实负荷每个时段约有 90% 概率落在区间内。峰荷时段带宽明显大于谷荷时段,这正是比例误差的效果;如果你画出来的包络上下等宽,说明误差尺度设成了固定值,要检查模型。
3.3 输出里最容易读错的两个口径
第一个是「逐时段 P95」与「日峰值 P95」。逐时段 P95 是每个 15 分钟点各自的 95% 分位值;日峰值 P95 要先取每条场景一天的最大负荷、再对这 N 个最大值求 P95,两者数值通常差几个 MW。容量规划用的是后者,包络图展示的是前者,报告里写混了会被审计质疑。
第二个是 P95 不是「最大负荷」。P95 的含义是:在模型假设下,该时段有 5% 的概率被超过。设计容量若直接取逐时段 P95 再叠加,通常偏保守;更合理的做法是跑完优化模型后,统计违反约束的场景占比是否在可接受范围。第 5 章的覆盖率回测就是用来检验这个占比与假设是否一致的。
4. 仿真次数怎么定:标准误、拉丁超立方与三个常见坑
4.1 用分块法估计 P95 的标准误
蒙特卡洛算法的基础结论是估计量的标准误与 1/√N 成正比:N 从 1 万提到 4 万,标准误只缩一半。均值类指标收敛快,分位数(尤其 P95、P99)收敛慢,因为尾部样本密度低。工程上不靠感觉定 N,用分块法做收敛判断:
def quantile_block_se(S, q=95, block_size=1000): n_blocks = S.shape[0] // block_size est = np.array([ np.percentile(S[b*block_size:(b+1)*block_size], q, axis=0) for b in range(n_blocks) ]) return est.std(axis=0, ddof=1) / np.sqrt(n_blocks) se = quantile_block_se(S, q=95) print(f"P95 分块标准误最大 {se.max():.2f} MW")做法是把 N 个场景切成 M 块,每块独立算 P95,再对 M 个结果求标准误。判断准则:如果标准误最大值小于你的精度要求(比如 1 MW),N 够用;否则成倍增加 N 重跑。分块的前提是块间独立,生成时用固定 seed 且每次取不同的随机序列即可满足。这个方法对 P50 同样适用,只是 P50 的标准误通常远小于 P95,两者分别评估,别用一个 N 糊弄所有分位数。
注意:分块标准误反映的是「抽样噪声」,不是模型误差。模型选错分布导致的风险,分块法看不出来,要靠第 5 章的覆盖率回测发现。
4.2 拉丁超立方抽样:同样的 N 换更稳的尾部
朴素蒙特卡洛的随机数大量堆在分布中部,P95 附近的样本偏少;拉丁超立方(LHS)把每时段的分布轴等分成 N 层,每层恰好抽一个样本,尾部覆盖更均匀。对带 AR(1) 的负荷出力仿真,我用轻量版 LHS:对每时段的驱动噪声做独立分层,再逐场景过滤波器:
from scipy.stats import norm def generate_scenarios_lhs(n_scen, P_fc, sigma_ratio, phi, seed=1): rng = np.random.default_rng(seed) T = len(P_fc) eta_scale = sigma_ratio * P_fc * np.sqrt(1.0 - phi * phi) # 每时段做 N 层分层抽样,再随机打乱行序,避免时段间出现假相关 U = (np.arange(n_scen).reshape(-1, 1) + rng.random((n_scen, T))) / n_scen for t in range(T): U[:, t] = U[rng.permutation(n_scen), t] eta = norm.ppf(U) * eta_scale scenarios = np.zeros((n_scen, T)) for i in range(n_scen): e = np.empty(T) e[0] = eta[i, 0] / np.sqrt(1.0 - phi * phi) # 稳态起点 for t in range(1, T): e[t] = phi * e[t-1] + eta[i, t] scenarios[i] = P_fc + e return scenarios S_lhs = generate_scenarios_lhs(5000, P_fc, sigma_ratio=0.04, phi=0.8, seed=1)U 的构造保证每个时段、每一层恰好被抽一次,norm.ppf 把均匀分层映射到正态分位点;逐列随机打乱是为了避免各时段层序完全一致带来的虚假相关性。与朴素蒙特卡洛同 N 相比,LHS 的 P95 标准误一般能降 20%-40%,计算时间几乎不变。需要严格保持时段间秩相关结构的场景,再上 Iman-Conover 方法做相关校正,普通项目不必引入。
4.3 三个常见坑:seed、N 太小、独立抽样丢相关性
第一个坑是生成时没固定 seed。蒙特卡洛结果自带随机性,不固定 seed 意味着同事复现不出你的 P95,评审时没法核对。所有生产脚本我都会把 seed 作为显式参数传入并记录在结果文件名里。
第二个坑是 N 太小却报 P99。N=1000 时 P99 只有约 10 个样本落在尾部区间,估计量的标准误可能大到你不敢相信。报 P95 起步 N=10000,报 P99 建议 N=50000 以上,并配合 4.1 的分块标准误一起给出。
第三个坑是把独立抽样当通用解法。φ 从 0.8 改成 0,相邻时段误差符号随机翻转,场景集爬坡率分布完全失真;用于储能评估时,充放电切换次数会比相关模型高出数倍。判断标准很简单:评估指标里只要含「变化量」「切换次数」「连续持续时间」这类时间结构量,就必须用带 φ 的模型。
5. 场景集怎么用:容量校验、储能评估与覆盖率回测
5.1 日峰值口径与逐时段口径的差别
做容量规划时,先用日峰值口径看总缺口:
peak_demand = S.max(axis=1) # 每条场景的日最大负荷 cap_needed = np.percentile(peak_demand, 95) print(f"95% 场景覆盖所需容量: {cap_needed:.1f} MW")axis=1 表示沿时间轴取每条场景一天的最大值,得到 N 个日峰值,再对这 N 个值求 P95。这条结果回答的是「配多大容量能覆盖 95% 的模拟日」,与逐时段 P95 包络语义不同。储能评估则要保留时间结构:把场景集喂给充放电策略模拟器,统计充放电深度、切换次数、未满足电量,再用分位数汇总。此时 4.3 强调的时序相关性直接影响结论,不能省。
5.2 覆盖率回测:验证模型假设的唯一靠谱办法
模型能不能进决策,最终看历史回测。对过去 K 天,每天用当日可得的历史信息滚动生成当日 P05/P95 包络,检查实际负荷落进包络的天数占比,与理论值 90% 对比:
| 回测结果 | 可能原因 | 处理动作 |
|---|---|---|
| 实测覆盖率 87%-93% | 模型与数据匹配 | 维持 σ%、φ 不变 |
| 实测覆盖率明显低于 90% | σ% 偏小或分布尾部太薄 | 按残差重标定 σ%,必要时换 t 分布 |
| 实测覆盖率明显高于 90% | σ% 偏大,余量过度 | 收窄 σ%,节约容量成本 |
| 覆盖率随月份漂移 | 标定窗口含跨季数据 | 缩短到 60-90 天滚动窗口 |
我一般至少回测 60 天,把每天重跑的成本压到毫秒级后,整套「标定-仿真-回测-再标定」闭环可以在批处理脚本里自动化,每次发布预测模型前跑一遍覆盖率报告。当实测覆盖率系统性偏低时,优先修 σ% 而不是直接换厚尾分布——多数情况是误差尺度标小了,而不是形状选错;只有确认残差本身的峰度明显大于 3 时,才值得迁移到 t 分布。
本文还有配套的精品资源,点击获取