日前-日内两阶段优化调度与多参数敏感性分析:基于Matlab的完整实现思路
电力系统优化调度这块,做研究或者写论文的朋友应该都绕不开“日前-日内两阶段”这个框架。它不是什么花哨的新概念,但在实际工程和学术研究里确实非常实用——简单说就是把调度决策拆成“提前一天做计划”和“当天做修正”两步:日前阶段根据预测数据制定机组启停和基础出力计划,日内阶段再根据更新的预测和实时状态做滚动修正,应对风光出力波动和负荷变化。
这篇文章我基于自己跑过的一个Matlab项目,专门讲清楚三件事:两阶段优化调度的框架怎么搭、双变量(日前/日内)怎么衔接、以及对电价、光伏、风电、负荷这四个关键参数做独立敏感性分析的方法和代码实现。做完这套分析,你能回答一个很重要的问题:到底哪个参数的不确定性对系统运行成本影响最大?这个结论对后续的鲁棒优化、随机优化研究都很有参考价值。
不管你是在写毕业论文、发期刊论文,还是做实际工程预研,这篇文都能给你一套可以直接改来用的思路和代码骨架。
1. 日前-日内两阶段优化调度的整体设计思路
1.1 为什么要拆成“两阶段”而不是一次优化到位
先说个直观的问题:为什么不能直接做一个全时段的优化,把未来24小时的决策一次性算完?
答案很简单——预测不确定性。光伏和风电的出力预测误差在提前24小时的尺度上可能达到20%到30%,负荷预测相对好一些,但也有波动。如果你提前一天就把所有发电机组的出力精确到某个值,第二天实际风光一波动,你可能面临弃风弃光或者切负荷的尴尬局面,成本损失非常大。
两阶段策略的思路是:把决策分成分层级的。日前阶段做的是“慢决策”——决定哪些机组开机、启停状态是什么、基础出力计划是多少,这些决策一旦定了,短时间内不方便改(机组启停有最小运行/最小停机时间约束,启停本身也有成本)。日内阶段做的是“快决策”——在机组已经开起来的前提下,用滚动优化去修正出力分配,吸收预测误差,同时储能、可调负荷这些灵活资源也在日内参与调节。
用一个类比来理解:出行前你会提前规划好大致的路线和出发时间(日前计划),但真正开车上路时,你会根据实时导航信息调整车速、变道甚至临时绕路(日内修正)。两阶段调度就是这个逻辑。
1.2 日前调度与日内调度的衔接逻辑
两个阶段不是互相孤立的,日内阶段的优化需要以日前阶段的决策结果作为输入和边界条件。具体来说:
- 日前阶段输出:机组启停状态(0/1变量)、各时段机组基础出力、储能充放电的日前计划、与主网的交互功率计划。
- 日内阶段输入:日前确定的启停状态(不能再变)、修正后的超短期预测数据(光伏、风电、负荷)、日前出力计划作为参考基准。
- 日内阶段输出:各机组出力的修正量(在日前计划附近调整)、储能的实时充放电功率、切负荷/弃风的调整量。
日内优化通常在滚动时间窗口下进行,比如每15分钟滚动一次,每次优化未来4小时,但只执行第一个时段的决策——这是模型预测控制(MPC)思想在电力调度里的典型应用。这样既保证了决策的全局性(有日前计划托底),又能及时响应短期波动。
1.3 目标函数怎么设计
目标函数是整个优化的核心,通常是最小化系统总运行成本,但在两阶段框架下,目标函数的构成会不太一样。
日前阶段的目标函数一般包括:
- 机组启停成本(开机成本、停机成本)
- 机组燃料成本(通常用出力的二次函数或分段线性函数近似)
- 弃风弃光惩罚成本(如果允许弃风弃光的话)
- 失负荷惩罚成本(如果允许切负荷的话)
- 与主网交互购电成本
日内阶段的目标函数则偏向于“修正”:在满足系统平衡的前提下,最小化出力调整带来的额外成本,同时尽量接近日前计划(防止机组出力和日前偏差太大导致运行风险)。
我实际用的目标函数是这样写的(简化形式):
min Σ [ 启停成本 + 燃料成本 + 弃风惩罚 + 失负荷惩罚 + 购电成本 ]等价地,在Matlab中用linprog或intlinprog求解时,目标函数会写成向量形式f'*x,把每个决策变量的成本系数按顺序拼到f向量里。
2. 四个关键参数的建模与处理
2.1 电价参数:外部市场的“晴雨表”
电价在这里指的是系统与外部主网交互时的购售电价格(或者微电网场景下的分时电价)。在并网型微电网或园区综合能源系统中,电价直接决定了“自己发电还是买电”的经济性选择。
电价模型通常采用分时电价曲线,用一天24小时(或96个15分钟时段)的价格序列表示。在敏感性分析中,电价的变化通常用一个比例系数来缩放:比如基础电价乘以0.8、1.0、1.2,观察调度结果如何变化。
关键点:电价对调度决策的影响是全局性的。电价上升时,系统会倾向于多发自有电力、储能更多地在高电价时段放电;电价下降时则相反。而且在两阶段框架下,日前阶段就对电价有完整预期,所以电价的影响发生在日前计划层面,日内阶段更多是接收电价的实际值。
2.2 光伏参数:出力的“蓝天模式”
光伏出力建模主要考虑光照强度和环境温度。简化模型中,光伏出力正比于光照强度,同时受温度影响有小幅修正。实际调度中,光伏预测曲线通过历史数据和天气预报获得,通常给出24小时的出力标幺值曲线,再乘以装机容量得到实际功率。
我用的光伏出力模型:
P_pv(t) = η_pv * S(t) * A * (1 - β * (T_c(t) - 25))其中S(t)是光照强度,A是光伏板面积,η_pv是光电转换效率,T_c是电池板温度,β是温度系数。
在敏感性分析中,光伏的变化通常用光照强度的缩放来模拟——比如将光照曲线整体乘一个0.8到1.2的系数——也可以直接对光伏出力曲线的峰值拉伸或压缩。
光伏出力的特点是有明显的时段性(白天高、夜间零)和随机波动性(云层遮挡导致短时骤降),所以在日内阶段,光伏预测误差是调度修正的主要压力来源之一。
2.3 风电参数:出力波动最大的不确定性源
风电出力建模通常基于风速-功率曲线。典型的风机功率曲线可以用分段函数描述:切入风速以下出力为零,额定风速到切出风速之间出力恒定,中间区间近似线性或三次方关系。
简化模型中,风力发电被认为与风速的三次方成正比(在切入风速和额定风速之间):
P_wt(t) = 0.5 * ρ * A * Cp * v(t)^3在实际调度代码中,风电通常也是直接给出一条预测出力曲线。风电出力的特点是夜间可能比白天更大(由于大气边界层变化),并且波动性强、反调峰特性明显——这会给系统调峰带来压力。
敏感性分析中,风电参数的变化同样用比例系数缩放出力曲线,或者通过改变风速序列(比如整体加或减一个偏移量)来观察影响。
2.4 负荷参数:必须满足的“硬约束”
负荷参数是所有调度模型中最“不能商量”的约束——电力系统的运行目标是平衡,负荷需求在大多数模型中是刚性约束,不像风光具有可调性(除非引入需求响应)。建模时,负荷用24小时曲线表示,包含峰值、谷值和典型的工作日/休息日模式。
敏感性分析中,负荷的变化通常有两种方式:
- 整体缩放:将负荷曲线整体乘以系数,模拟负荷水平的上升或下降。
- 峰值迁移:改变峰值时段,模拟不同用电结构。
从实际经验来看,负荷水平的上升通常会推高总运行成本,并且可能导致部分时段需要增加购电或者启动更多机组,而负荷的峰值时段变化则会影响机组的启停策略。
3. 敏感性分析的方案设计:为什么“独立”是关键
3.1 敏感性分析的三种常用方法
在做参数敏感性分析前,先明确方法。业内常用的有三类:
- 单因子扰动法(OAT):每次只改变一个参数,其他参数保持基准值。这是“独立敏感性分析”最常用的实现方式,简单直观,容易解释。
- 全局敏感性分析:同时变化所有参数,用Monte Carlo采样或Sobol分解等方法评估各参数的独立贡献和交互作用。精度高,但计算量大,结果解释也更复杂。
- 边界分析法:直接考察参数在极端取值下的系统行为,一般用于鲁棒性验证。
标题里强调的是“分别进行独立的敏感性分析”,所以采用第一种方法——单因子扰动法——最合适。每次只动一个参数,其他三个保持基准值,这样量化出来的影响就是该参数独立贡献的近似。
3.2 参数变化范围的设定
敏感性分析的一个关键问题是:参数变化范围怎么定。
我的做法和参考:
- 基准工况:先设置一组基准预测数据(电价、光伏、风电、负荷),跑一次两阶段优化,得到基准总成本。
- 变化范围:每个参数在基准值的正负20%范围内变化,步长取5%或10%。即每个参数取5到9个不同水平,做独立敏感性分析。
- 变化方式:对于电价和负荷,直接缩放价格序列和负荷序列;对于光伏和风电,缩放出力曲线。
选择20%的原因是:在实际电力系统中,日前预测误差的合理范围大致就是这个水平。如果变化范围太小,看不出效果,太大则偏离工程实际太远。
3.3 敏感性指标怎么计算
敏感性分析不只是看成本变化值,还需要一个归一化指标来比较不同参数的影响大小。常用指标是两个:
- 成本变化率(绝对敏感性):参数变化x%时,总成本变化了多少金额或百分比。
ΔC / ΔP = (C(P+ΔP) - C(P)) / ΔP- 弹性系数(相对敏感性):参数变化1%时,成本变化百分之几。
E = (ΔC / C0) / (ΔP / P0)弹性系数的好处是可以横向比较——因为每个参数变化的基准不同(电价基准可能是0.8元/kWh,光伏基准可能是2000kW),直接比较绝对变化量没有意义,弹性系数通过归一化消除了这个影响。
我在项目中用两个指标一起分析:绝对变化量看影响规模,弹性系数看敏感程度。
4. Matlab实现:从约束搭建到敏感性分析代码
4.1 数据准备与参数初始化
代码的第一步是准备好所有输入数据。我习惯把所有数据集中放在一个结构体里,方便统一管理和传递。
%% 数据准备 % 定义基础数据结构 params = struct(); params.H = 24; % 调度周期(小时) params.dt = 1; % 时间间隔(小时) % 负荷曲线(基准场景,单位:kW) params.load = [ ... 24小时负荷数据 ... ]; % 光伏出力曲线(标幺值,基准场景) params.pv = [ ... 24小时光伏出力标幺值 ... ]; params.pv_capacity = 2000; % 光伏装机容量(kW) % 风电出力曲线(标幺值,基准场景) params.wt = [ ... 24小时风电出力标幺值 ... ]; params.wt_capacity = 1500; % 风电装机容量(kW) % 分时电价(单位:元/kWh) params.price = [ ... 24小时电价数据 ... ]; % 机组参数 params.gen = struct(); params.gen.num = 3; % 火电机组数量 params.gen.Pmin = [100, 50, 30]; % 最小出力(kW) params.gen.Pmax = [500, 300, 150];% 最大出力(kW) params.gen.a = [0.02, 0.03, 0.04];% 燃料成本二次系数 params.gen.b = [15, 18, 20]; % 燃料成本一次系数 params.gen.c = [50, 40, 30]; % 燃料成本常数项 params.gen.startup_cost = [200, 150, 100]; % 启动成本 % 储能参数 params.ess = struct(); params.ess.capacity = 500; % 储能容量(kWh) params.ess.Pmax = 100; % 最大充放电功率(kW) params.ess.eta_c = 0.95; % 充电效率 params.ess.eta_d = 0.95; % 放电效率 params.ess.soc_min = 0.1; % 最低SOC params.ess.soc_max = 0.9; % 最高SOC params.ess.soc_init = 0.5; % 初始SOC4.2 日前-日内两阶段优化调度的核心代码逻辑
这里我用一个关键设计:将两阶段优化拆成两个顺序调用的函数。每个函数内部用linprog或intlinprog求解。先说日前部分。
日前阶段要做的核心约束包括:
- 功率平衡约束
- 机组出力上下限约束
- 机组爬坡约束
- 储能充放电约束和SOC递推约束
- 旋转备用约束(可选)
由于机组启停是整型变量,日前阶段用intlinprog求解。这是完整的日前调度函数结构:
function [schedule_day, cost_day] = day_ahead_optimization(params) % 日前优化调度 % 决策变量顺序: % x(1:H) : 机组1出力 % x(H+1:2H) : 机组2出力 % x(2H+1:3H) : 机组3出力 % x(3H+1:4H) : 储能充电功率 % x(4H+1:5H) : 储能放电功率 % x(5H+1:6H) : 机组1启停状态(0/1) % x(6H+1:7H) : 机组2启停状态(0/1) % x(7H+1:8H) : 机组3启停状态(0/1) % x(8H+1:9H) : 购电功率 % x(9H+1:10H) : 弃风功率 % x(10H+1:11H) : 弃光功率 H = params.H; n_gen = params.gen.num; % 决策变量总数 n_vars = 4*H + 3*H + 3*H + H + H + H; % 实际上是 13*H % --- 构建目标函数系数 --- f = zeros(n_vars, 1); % 机组燃料成本(用分段线性近似,二次成本转线性) % 这里为了简化,按线性成本处理,实际中可用分段线性化 for g = 1:n_gen idx = (g-1)*H + 1 : g*H; f(idx) = params.gen.b(g); % 线性成本系数 end % 储能充放电成本(设一个小值防止同时充放电) idx_ch = 3*n_gen*H + 1 : 3*n_gen*H + H; idx_dis = 3*n_gen*H + H + 1 : 3*n_gen*H + 2*H; f(idx_ch) = 0.01; f(idx_dis) = 0.01; % 弃风、弃光惩罚成本(取较大值) idx_curtail_wt = (3*n_gen + 2)*H + 1 : (3*n_gen + 2)*H + H; idx_curtail_pv = (3*n_gen + 3)*H + 1 : (3*n_gen + 3)*H + H; f(idx_curtail_wt) = 50; % 元/kWh,惩罚值 f(idx_curtail_pv) = 80; % 购电成本(使用分时电价) idx_grid = (3*n_gen + 3)*H + H + 1 : (3*n_gen + 3)*H + 2*H; f(idx_grid) = params.price; % --- 构建约束矩阵 --- % 这里用稀疏矩阵减少内存占用 Aeq = []; % 等式约束(功率平衡等) beq = []; A = []; % 不等式约束 b = []; % 功率平衡约束 % 总出力 + 储能放电 + 购电 = 负荷 + 储能充电 + 弃风 + 弃光 % 对每个时段 t 建立等式约束 for t = 1:H row = zeros(1, n_vars); for g = 1:n_gen row((g-1)*H + t) = 1; % 机组出力 end row(3*n_gen*H + H + t) = 1; % 储能放电 row(3*n_gen*H + t) = -1; % 储能充电(负荷侧) row((3*n_gen+3)*H + 2*H + t) = -1; % 购电(电源侧,注意符号) row((3*n_gen+2)*H + t) = 1; % 弃风(负荷侧) row((3*n_gen+3)*H + t) = 1; % 弃光(负荷侧) % 等式右边:负荷 + 光伏 + 风电的净需求(注意符号安排) Aeq = [Aeq; row]; beq = [beq; params.load(t)]; end % 注:上式约束表示 机组出力+储能放电+弃风+弃光 = 负荷+储能充电+购电 % 具体符号安排需根据决策变量顺序调整,这里以说明逻辑为主 % 机组出力上下限约束 for g = 1:n_gen for t = 1:H % x((g-1)*H+t) - Pmax(g)*u(g,t) <= 0 % x((g-1)*H+t) - Pmin(g)*u(g,t) >= 0 u_idx = (3*n_gen + 3 + g - 1)*H + t; row1 = zeros(1, n_vars); row1((g-1)*H + t) = 1; row1(u_idx) = -params.gen.Pmax(g); A = [A; row1]; b = [b; 0]; row2 = zeros(1, n_vars); row2((g-1)*H + t) = 1; row2(u_idx) = -params.gen.Pmin(g); A = [A; -row2]; % 注意符号转换 b = [b; 0]; end end % 储能SOC约束 soc = zeros(H+1, 1); soc(1) = params.ess.soc_init * params.ess.capacity; for t = 1:H % SOC(t+1) = SOC(t) + eta_c*Pch(t) - Pdis(t)/eta_d % 用线性约束表示,把SOC变量消去后写入约束矩阵 % 具体为 SOC上限和下限约束 end % 设置线性规划参数 options = optimoptions('intlinprog', 'Display', 'off'); % 定义整数变量(启停状态对应的索引) intcon = []; for g = 1:n_gen intcon = [intcon, (3*n_gen + 3 + g - 1)*H + 1 : (3*n_gen + 3 + g - 1)*H + H]; end % 求解 lb = zeros(n_vars, 1); ub = [repmat(params.gen.Pmax(1), H, 1); repmat(params.gen.Pmax(2), H, 1); repmat(params.gen.Pmax(3), H, 1); repmat(params.ess.Pmax, H, 1); % Pch repmat(params.ess.Pmax, H, 1); % Pdis ones(3*H, 1); % 启停状态 repmat(5000, H, 1); % 购电上限 repmat(params.wt_capacity, H, 1); % 弃风上限 repmat(params.pv_capacity, H, 1)]; % 弃光上限 [x_opt, cost_day] = intlinprog(f, intcon, A, b, Aeq, beq, lb, ub, options); % 结果提取... schedule_day = x_opt; end日内阶段的核心逻辑类似,但有几个关键差别:启停状态固定为日前优化结果、出力上下限在日前计划附近收缩、时间窗口滚动。这里把日内函数的结构也列出来:
function [schedule_intra, cost_intra] = intraday_optimization(params, schedule_day, horizon) % 日内滚动优化 % 输入: % params - 系统参数 % schedule_day - 日前优化结果 % horizon - 滚动优化窗口(小时数) % 提取日前启停计划(固定不变) u_day = schedule_day(...); % 提取启停状态 % 决策变量: % 机组出力修正量(ΔP,相对日前计划) % 储能充放电功率 % 购电功率修正 % 弃风弃光量 % 目标函数:最小化修正成本 + 弃风弃光惩罚 % 约束条件: % 1. 功率平衡(用超短期预测数据) % 2. 机组出力在日前计划附近可调(上下限为日前出力±爬坡能力) % 3. 启停状态固定不变 % 4. 储能SOC约束同上 % 滚动求解: % for t = 1:num_steps % 优化未来horizon时段的决策 % 只执行第一个时段的决策 % 更新系统状态和预测数据 % end % 返回日内调度结果和实际运行成本 end4.3 敏感性分析的独立循环实现
这一部分是题目的重点——四个参数分别独立分析。核心设计思想是:一个基准场景 + 多个扰动场景 + 独立循环。
我用一个统一的函数接口来实现这个逻辑:
function results = sensitivity_analysis(params_base) % 敏感性分析主函数 % 参数变化范围和步长 factor_range = 0.8:0.05:1.2; % 变化范围:80% ~ 120%,步长5% % 初始化结果结构体 results.price = zeros(length(factor_range), 1); results.pv = zeros(length(factor_range), 1); results.wt = zeros(length(factor_range), 1); results.load = zeros(length(factor_range), 1); % 基准场景成本 [~, cost_base] = day_ahead_optimization(params_base); results.base_cost = cost_base; % --- 电价敏感性分析 --- for i = 1:length(factor_range) params_temp = params_base; params_temp.price = params_base.price * factor_range(i); [~, cost] = day_ahead_optimization(params_temp); results.price(i) = cost / cost_base; % 归一化成本 end % --- 光伏敏感性分析 --- for i = 1:length(factor_range) params_temp = params_base; params_temp.pv = params_base.pv * factor_range(i); [~, cost] = day_ahead_optimization(params_temp); results.pv(i) = cost / cost_base; end % --- 风电敏感性分析 --- for i = 1:length(factor_range) params_temp = params_base; params_temp.wt = params_base.wt * factor_range(i); [~, cost] = day_ahead_optimization(params_temp); results.wt(i) = cost / cost_base; end % --- 负荷敏感性分析 --- for i = 1:length(factor_range) params_temp = params_base; params_temp.load = params_base.load * factor_range(i); [~, cost] = day_ahead_optimization(params_temp); results.load(i) = cost / cost_base; end end注意几个关键设计点:
第一,这里以日前阶段优化成本作为敏感性分析的主要输出。原因是日前成本是全时段统筹的结果,日内阶段只是在日前计划基础上的修正,主要反映的是预测误差的应对能力,而不是参数水平变化的直接影响。如果要做完整的“两阶段敏感性”,应该把日内滚动优化的累计成本也加起来——这可以在函数里加一个日内成本输出的扩展来实现,计算量会增加不少。
第二,每个参数独立变化时,其他三个参数保持在基准值。这样才能保证成本变化纯粹是由目标参数引起的,不会混入交互效应。
第三,成本做了归一化处理,除以基准成本得到比值。这样四条敏感性曲线就能画在同一张图上直接比较相对变化的陡峭程度。
4.4 结果可视化与图表绘制
画图是整个分析里非常重要的一环,图表的表达直接决定了你结论的说服力。
我建议至少画三种图:
第一种:四条敏感性曲线叠在一张图上(主图)
figure('Color', 'w', 'Position', [100, 100, 800, 500]); plot(factor_range, results.price, 'r-o', 'LineWidth', 1.8); hold on; plot(factor_range, results.pv, 'g-s', 'LineWidth', 1.8); plot(factor_range, results.wt, 'b-^', 'LineWidth', 1.8); plot(factor_range, results.load, 'm-d', 'LineWidth', 1.8); xlabel('参数变化系数'); ylabel('归一化运行成本'); legend('电价', '光伏', '风电', '负荷', 'Location', 'best'); grid on; set(gca, 'FontSize', 12, 'FontName', 'Times New Roman');第二种:弹性系数柱状图
把每个参数在±10%变化时计算出的弹性系数画成柱状图,一目了然看出哪个参数最“敏感”。
第三种:典型时段的调度对比图
选出成本变化最大的参数场景(比如电价下降20%),把该场景下的机组出力、储能SOC与基准场景画在一起对比,直观展示参数变化如何改变调度策略。
5. 常见问题与排查技巧实录
5.1 intlinprog求解速度慢怎么办
这是跑两阶段优化最常见的痛点。我遇到过的情况:机组数量一多、时段一多,intlinprog求解时间呈指数增长,跑一个敏感性分析(4个参数×9个水平=36次优化),可能要等几个小时。
我的解决思路:
- 优先用稀疏矩阵表达约束,大大减少内存占用和求解时间。
- 如果机组只有两三台,可以试试把启停变量用穷举或启发式方法预判,把整数规划退化为线性规划。例如先固定一个合理的启停序列,然后用linprog求解,这样速度会快很多。
- 设置合理的求解精度和时间上限:optimoptions('intlinprog', 'LPPreprocess', 'basic', 'MaxTime', 60),实用主义最重要,不需要追求全局最优的极致证明。
5.2 功率平衡约束怎么检查
这是最“阴”的bug来源——约束矩阵写错一个符号或者索引偏移,结果完全不靠谱但你很难发现。
我的经验是写一个独立的验证函数,把优化结果代回约束条件检查是否满足,误差超过1e-6就报警。尤其是功率平衡约束,每一时段的总发电和总负荷必须严格相等(在允许弃风弃光的模型中,“相等”指平衡方程成立,弃风弃光是松弛变量,使得实际供给可以小于需求)。
5.3 敏感性曲线出现非单调变化
理论上,负荷水平上升成本一定上升,光伏或风电出力上升成本一定下降(因为替代了昂贵的购电或燃料)。但实际跑出来可能会出现成本非单调变化的情况,尤其是在考虑机组启停和储能的情况下——光伏出力小幅上升时成本没有变化(因为正好卡在某一台机组的最小技术出力上),再继续上升到某个阈值后成本突然跳变(机组从运行状态退出)。
这不是bug,而是调度问题的内在特性。敏感性分析报告里应该对这种“分段平台”现象做解释,说明经济调度中的整数决策导致了非线性响应。
5.4 储能SOC初始化对结果的影响
储能的初始SOC值对日前调度结果影响很大。如果初始SOC设置为0.9(高电量),系统在第一个时段就不需要充电,可以更从容地应对高峰;如果初始SOC是0.1,那么储能第一个时段就必须充电,可能加剧系统负担。
在敏感性分析中,所有场景使用相同的初始SOC是必须做到的——否则不同场景之间的成本差异就无法归因于目标参数的变化。代码里要把params.ess.soc_init放在基准参数里,不在敏感性循环中修改。
5.5 电价敏感性分析的特殊误区
电价敏感性分析的误区在于:有些朋友把电价和负荷同步缩放,这就完全破坏了“独立”的前提。电价改变时,负荷预测保持完全不变——这是最理想的独立分析条件,但实际上负荷和电价在真实系统中是有相关性的(电价高时负荷通常高)。如果你想进一步研究关联影响,那就应该走相关性分析或全局敏感性分析的路线,而不是独立敏感性分析。
另外,购电价格的改变会影响购电功率的决策,但如果电价变得过高,模型会宁愿启停机组也不想买电,这时候购电功率会降到接近零——这是正确的调度行为,不要当成bug。
6. 结果分析:如何解读敏感性分析数据
6.1 四个参数影响程度的横向对比
以我跑过的典型算例来看,结论大致是这样的(具体数值取决于你的系统参数,但规律有普适性):
| 参数 | 变化范围 | 总成本变化幅度 | 弹性系数 | 敏感程度排序 |
|---|---|---|---|---|
| 电价 | ±20% | ±8%~12% | 0.4~0.6 | 较高 |
| 负荷 | ±20% | ±10%~15% | 0.5~0.75 | 最高 |
| 光伏 | ±20% | ±3%~6% | 0.15~0.3 | 中等 |
| 风电 | ±20% | ±2%~5% | 0.1~0.25 | 较低 |
负荷的弹性系数通常最高——因为负荷是刚性约束,负荷上升时系统必须增加发电或者购电,成本大概率直接增加。而风电和光伏作为“零边际成本”电源,它们出力的变化更多是替代了原有的火电出力或外购电,所以对成本的影响相对温和。
6.2 为什么风电的光伏的敏感性曲线可能不对称
参数增加10%带来的成本下降幅度,往往不等于参数减少10%带来的成本上升幅度。原因在于系统运行的“强约束”并不是对称的:负荷上升时可能触发新的机组启停,而负荷下降时停机的节省成本可能不如启停成本本身。
这种不对称性本身就是研究结论的一部分,在论文或报告中应该点出来,说明系统在参数上行和下行方向上的调节弹性不同。
6.3 结论的工程应用价值
做完敏感性分析,最直接的输出是回答这几个问题:
- 哪个预测参数的精度最需要提升?如果负荷弹性系数最大,那么负荷预测做准比提高光伏预测精度带来的经济效益更大。
- 哪些时段的调度决策对不确定性最敏感?可以在敏感性分析中加一个“分时段成本变化”的输出,找出风险最高的时段,针对性地配置备用容量。
- 储能应该在哪个方向投入才能最大化价值?如果电价敏感性高,说明峰谷价差套利空间大;如果光伏敏感性高,说明储能的消纳价值更突出。
7. 扩展方向与后续改进
跑完基础版本之后,还有几个很好的扩展方向:
- 从日前到日内:将日内滚动优化的累计成本纳入敏感性指标。这样可以得到更完整的“两阶段敏感性”结论,代价是计算量翻倍,因为你每个参数场景都要跑一次滚动优化,而不是只跑一次日前优化。
- 引入预测误差的随机性:确定性敏感性分析只考虑了参数水平变化,如果你的场景里需要模拟预测误差的影响,可以给曲线加随机扰动,然后用Monte Carlo方法做统计输出,计算期望成本和风险(比如CVaR)。
- 加入碳交易成本或需求响应:如果模型中加入碳价或需求侧弹性负荷,敏感性分析的参数就不仅限于这四项,但分析框架完全一样——改一个参数、跑一遍优化、记录成本。
从我个人踩过坑的经验来看,有一点想特别提醒:敏感性分析的结论强烈依赖基准场景的选择。如果你取的基准光伏出力曲线是阴天数据,那么光伏参数变化的影响会被低估;如果你取的基准负荷曲线是平缓的夜间数据,负荷变化的影响也会被压低。所以在报告结论之前,至少用2-3套不同的基准场景做交叉验证,看看敏感性排序是否一致。这一点在写论文时非常加分——审稿人通常会注意到这个问题,而你提前做了就是亮点。
Matlab这套代码的骨架相对简洁,实际工程中你大概率需要扩展更多的设备和约束条件,但两阶段架构和独立性分析的框架是可以复用的。先从一套简化模型跑通全流程,再逐步加复杂度,这是最稳妥的路线。