基于Matlab的电力市场竞争建模:从线性规划到市场出清价格分析

📅 2026/8/27 10:44:38
基于Matlab的电力市场竞争建模:从线性规划到市场出清价格分析
1. 从现实问题到数学模型能源市场的核心博弈如果你关注过电力市场的价格波动或者听说过“碳交易”、“绿证”这些名词那你已经站在了能源市场竞争的门口。这个领域远不止是发电厂和电网的物理连接其背后是一套极其复杂、由规则、策略和预测交织而成的经济系统。我最初接触能源市场建模是为了解决一个很实际的问题一个拥有风电和储能的小型能源聚合商如何在波动的现货市场和辅助服务市场中制定报价策略以实现收益最大化这个问题把我直接推向了数学建模的深水区。简单来说能源市场竞争模型就是用数学语言来刻画和模拟市场中各个参与者发电商、售电商、大用户、储能运营商等的决策行为及其相互作用最终预测市场出清价格、交易量以及各方的收益。它的核心价值在于能将现实中充满不确定性的博弈转化为计算机可以运算和推演的“沙盘”从而为投资决策、风险管理和政策评估提供定量依据。无论是准备数学建模竞赛的学生还是能源行业的从业者掌握这套从现实抽象到模型再从模型反馈到现实的思维与实践能力都至关重要。从网络热词可以看出Matlab是这一领域最主流的工具之一其强大的矩阵运算、优化工具箱和仿真能力非常适合处理能源市场模型中的高维优化和随机模拟问题。而“数学建模”本身就是连接问题与解决方案的桥梁。本文将从一个实战案例出发拆解构建一个简化但完整的电力现货市场竞争模型的全过程涵盖问题定义、模型建立、Matlab求解以及结果分析。我们会避开复杂的理论推导聚焦于“如何动手做出来”并分享那些在课本和论文里不会写的实操细节与踩坑经验。2. 案例定义一个简化的电力日前现货市场我们构建一个高度简化但骨架清晰的案例一个区域电力日前现货市场。所谓“日前市场”指的是在实际运行日的前一天各发电商提交次日每个交易时段比如每小时一个时段的发电报价曲线市场运营机构根据全网负荷预测和所有报价以社会福利最大化为目标或购电成本最小化通过优化计算确定每个时段的出清价格和中标电量。2.1 市场参与者与规则设定假设市场中有3家发电商G1 G2 G3和1个代表全体用户的购电侧。市场运营一个交易日分为24个时段T1到24。发电商成本每个发电商i都有一个线性成本函数即发电成本与发电量成正比。这是最常见的简化。设其成本函数为C_i(q) a_i * q b_i其中q为发电量a_i为边际成本元/MWhb_i为固定成本系数。在实际建模中a_i就是其报价的关键参考。报价策略在日前市场发电商需要向交易中心提交一条“报价曲线”即在不同价格下愿意提供的电量。在我们的简化模型中我们假设发电商采用“按边际成本报价”的策略这是经济学理论中的完全竞争假设但实际中可能存在策略性报价。因此其报价曲线实际上就是其边际成本曲线价格p a_i。机组约束每个发电商有最大出力Pmax_i和最小出力Pmin_i限制。此外还有爬坡率约束即相邻时段发电量的变化不能超过Ramp_iMW/h。这是电力系统物理特性的体现也是模型复杂性的重要来源。负荷需求每个时段t有一个预测的刚性负荷D_tMW即用户必须满足的用电量。需求侧在此简化模型中被视为价格不敏感。2.2 数学模型构建一个线性规划问题基于以上设定市场出清的目标是满足所有时段负荷需求的前提下最小化总的发电成本即最大化社会福利因为需求固定。这直接形成了一个线性规划问题。决策变量q_{i,t}—— 发电商i在时段t的中标发电量MW。目标函数最小化总发电成本。Minimize: Σ_{t1}^{24} Σ_{i1}^{3} (a_i * q_{i,t})注意线性成本函数中的固定成本b_i在短期运行决策中不产生影响因此目标函数只包含边际成本部分。约束条件功率平衡约束每个时段总发电量等于总负荷。Σ_{i1}^{3} q_{i,t} D_t, for all t发电机组出力上下限约束Pmin_i q_{i,t} Pmax_i, for all i, t爬坡率约束-Ramp_i q_{i,t} - q_{i, t-1} Ramp_i, for all i, t2非负约束q_{i,t} 0市场出清价格p_t由优化问题的对偶变量决定。具体来说每个时段功率平衡约束对应的拉格朗日乘子影子价格就是该时段的边际出清价格。这意味着p_t等于满足最后一单位负荷所需的最低边际成本。在Matlab中使用linprog等求解器时可以直接获取这个对偶变量。注意这里做了一个关键简化即假设发电商按真实边际成本报价。现实中发电商可能进行策略性报价以获取更高利润这需要用到博弈论模型如纳什均衡复杂度会指数级上升。本案例旨在搭建最基础的分析框架。3. Matlab实战从数据到出清结果接下来我们使用Matlab将上述模型实现。整个过程包括数据准备、模型构建、求解和结果分析。3.1 数据准备与参数定义我们首先在Matlab中定义所有参数。为了更贴近现实我们可以构造一个具有典型日负荷曲线特征的需求数据。%% 1. 定义发电商参数 num_gen 3; % 3个发电商 num_hours 24; % 24小时 % 发电商参数矩阵: [边际成本a(元/MWh), 最小出力Pmin(MW), 最大出力Pmax(MW), 爬坡率Ramp(MW/h)] gen_data [ 200, 50, 300, 100; % G1: 成本较高但调节能力强 150, 100, 500, 150; % G2: 成本中等主力机组 100, 200, 800, 80; % G3: 成本最低但爬坡较慢可能是燃煤机组 ]; a gen_data(:, 1); Pmin gen_data(:, 2); Pmax gen_data(:, 3); Ramp gen_data(:, 4); %% 2. 定义日负荷曲线 (模拟一个典型日) base_load 1000; % 基础负荷 MW peak_ratio 1.5; % 峰值比例 % 构造一个双峰曲线早高峰和晚高峰 hour (1:num_hours); D base_load * (1 0.3*sin(2*pi*(hour-6)/24) 0.2*sin(2*pi*(hour-18)/24)); D D * (1 (peak_ratio-1)*(hour12 | hour20)/2); % 在12点和20点制造尖峰 D round(D); % 取整 % 可视化负荷曲线 figure; plot(hour, D, b-o, LineWidth, 1.5); xlabel(时间 (小时)); ylabel(负荷需求 (MW)); title(日前市场预测负荷曲线); grid on; xlim([1, 24]);3.2 构建线性规划模型这是最核心的一步。我们需要将目标函数和约束条件转化为线性规划的标准形式min f*x, s.t. A*x b, Aeq*x beq, lb x ub。%% 3. 构建线性规划问题 % 决策变量 x 是一个列向量按 [q_{1,1}, q_{2,1}, q_{3,1}, q_{1,2}, ..., q_{3,24}] 排列 % 即先排所有发电商在时段1的量再排时段2以此类推。 num_vars num_gen * num_hours; % 决策变量总数 % 目标函数系数 f: 最小化总边际成本 f []; for t 1:num_hours f [f; a]; % 每个时段每个发电商的边际成本构成目标系数 end % 等式约束 (功率平衡): Aeq * x beq % 每个时段有一个等式约束 Aeq zeros(num_hours, num_vars); beq D; % 等式约束的右侧就是负荷需求 for t 1:num_hours col_start (t-1)*num_gen 1; col_end t*num_gen; Aeq(t, col_start:col_end) 1; % 对应该时段所有发电商发电量系数为1 end % 不等式约束 (爬坡率): A * x b % 每个发电商在每个相邻时段间有2个爬坡约束上坡和下坡 num_ramp_constraints num_gen * (num_hours - 1) * 2; A zeros(num_ramp_constraints, num_vars); b zeros(num_ramp_constraints, 1); constraint_idx 1; for i 1:num_gen for t 2:num_hours % 变量索引: 发电商i在时段t的变量位置 idx_t (t-1)*num_gen i; idx_t_1 (t-2)*num_gen i; % 上坡约束: q_{i,t} - q_{i, t-1} Ramp_i A(constraint_idx, idx_t) 1; A(constraint_idx, idx_t_1) -1; b(constraint_idx) Ramp(i); constraint_idx constraint_idx 1; % 下坡约束: -q_{i,t} q_{i, t-1} Ramp_i (等价于 q_{i, t-1} - q_{i,t} Ramp_i) A(constraint_idx, idx_t) -1; A(constraint_idx, idx_t_1) 1; b(constraint_idx) Ramp(i); constraint_idx constraint_idx 1; end end % 变量上下界约束 lb x ub lb zeros(num_vars, 1); ub zeros(num_vars, 1); for t 1:num_hours for i 1:num_gen var_idx (t-1)*num_gen i; lb(var_idx) Pmin(i); ub(var_idx) Pmax(i); end end3.3 模型求解与结果提取使用Matlab的linprog求解并获取对偶变量影子价格作为市场出清价格。%% 4. 求解线性规划 options optimoptions(linprog, Display, iter, Algorithm, dual-simplex); [x, fval, exitflag, output, lambda] linprog(f, A, b, Aeq, beq, lb, ub, options); if exitflag 0 disp(优化求解成功); total_cost fval; disp([最小总发电成本为, num2str(total_cost), 元]); else error(优化求解失败); end %% 5. 整理并分析结果 % 5.1 提取各机组出力计划 schedule reshape(x, [num_gen, num_hours]); % 转置使行是时间列是机组 disp(各发电商24小时出力计划 (MW):); disp(array2table(schedule, VariableNames, {G1, G2, G3}, RowNames, strcat(T, string(1:24)))); % 5.2 获取市场出清价格 (影子价格) % lambda.eqlin 对应等式约束 Aeq*x beq 的对偶变量即每个时段的边际价格 market_price lambda.eqlin; % 单位元/MWh disp(市场24小时出清价格 (元/MWh):); disp(array2table(market_price, VariableNames, {价格}, RowNames, strcat(T, string(1:24)))); % 5.3 计算各发电商总收入与利润 % 收入 出清价格 * 中标电量 revenue zeros(num_gen, 1); for i 1:num_gen revenue(i) sum(market_price .* schedule(:, i)); end % 成本 边际成本 * 中标电量 (忽略固定成本) cost a .* sum(schedule, 1); profit revenue - cost; disp(各发电商收入、成本与利润分析 (元):); result_table table(a, sum(schedule, 1), revenue, cost, profit, ... VariableNames, {边际成本, 总发电量, 总收入, 总变动成本, 总利润}, ... RowNames, {G1, G2, G3}); disp(result_table);4. 结果深度解读与市场行为分析运行上述代码后我们会得到一份完整的市场出清报告。真正的价值不在于这几个数字而在于对其背后经济学和物理学意义的解读。4.1 出清价格曲线与负荷曲线的关系通常出清价格曲线会紧随负荷曲线波动。在负荷高峰时段为了满足额外的电力需求调度中心必须调用边际成本更高的发电机组例如我们的G1从而导致整个系统的边际成本即出清价格上涨。反之在负荷低谷时可能只需要成本最低的基荷机组G3运行价格就会走低。我们可以绘制对比图来直观展示%% 可视化负荷、出清价格与机组出力堆叠图 figure; subplot(3,1,1); plot(hour, D, k-^, LineWidth, 1.5); ylabel(负荷 (MW)); title(负荷与出清价格); grid on; xlim([1,24]); yyaxis right; plot(hour, market_price, r-s, LineWidth, 1.5); ylabel(价格 (元/MWh)); legend(负荷需求, 出清价格, Location, northwest); subplot(3,1,2); area(hour, schedule); ylabel(出力 (MW)); title(发电商出力计划 (堆叠图)); legend(G1 (高成本), G2 (中成本), G3 (低成本), Location, best); grid on; xlim([1,24]); subplot(3,1,3); bar(hour, schedule, stacked); ylabel(出力 (MW)); title(发电商出力计划 (柱状图)); xlabel(时间 (小时)); grid on; xlim([1,24]);通过这幅图你可以清晰地看到价格驱动在价格高的时段通常是所有机组都接近满发或者高成本机组G1被大量调用。机组组合低成本机组G3作为基荷几乎全天满发。中成本机组G2承担主要的负荷跟踪任务。高成本机组G1仅在高峰时段启用扮演“调峰”角色。爬坡约束的影响仔细观察G2和G1在负荷快速上升时段如早上8点的出力曲线。它们可能无法瞬间达到经济调度理论上的最优值因为受到了Ramp参数的限制。这是物理约束影响经济结果的典型体现。4.2 利润分析背后的市场力查看利润表你可能会发现一个有趣的现象边际成本最低的机组G3利润可能不是最高的。这是因为在边际定价机制下所有中标机组都按统一的市场出清价格结算。G3虽然成本低但它在低谷时段发电此时市场价格也低。而G1虽然成本高但它只在价格极高的尖峰时段发电短短几小时的高价电量可能带来可观的利润。这引出了“市场力”的概念。如果一个发电商拥有在关键时刻如负荷高峰、网络阻塞时影响价格的能力它就可能行使市场力通过物理持留故意少报可用容量或经济持留报高价来推高价格。我们的简化模型假设按成本报价没有考虑策略行为。若要模拟市场力需要引入博弈论让每个发电商以利润最大化为目标进行报价优化这通常需要用到均衡模型或智能代理模拟。4.3 灵敏度分析与“如果”情景模型的强大之处在于可以进行“如果…会怎样”的分析。例如如果G3机组突然故障最小出力升为0只需修改Pmin(3) 0并重新求解观察价格和机组组合的变化。你会发现价格整体上涨G2和G1的工作点大幅增加。如果引入一个边际成本为180元/MWh的新燃气机组在gen_data中添加一行并相应调整所有矩阵维度可以看到它对G1的替代作用。如果负荷预测出现偏差可以用另一组负荷数据D_actual代替原来的D但依然用基于原预测D计算出的机组计划schedule来检查是否满足实际负荷这可以模拟预测误差带来的平衡风险。在Matlab中这些都可以通过简单的参数修改和循环求解快速实现让你对市场动态有更深刻的理解。5. 模型扩展与进阶思考基础模型搭建完成后我们可以向其中添加更多现实世界的复杂性使其更加强大。5.1 引入网络约束与阻塞管理真实的电力市场是运行在物理电网上的。输电线路有容量限制。当低成本机组的电力无法全部输送到负荷中心时就会发生“阻塞”。这时市场需要引入“节点边际价格”LMP。在模型中这需要在每个节点增加功率平衡约束并为每条线路增加传输容量约束。目标函数不变但求解后不同节点的出清价格LMP会不同其差值反映了阻塞成本。Matlab的优化工具箱同样可以处理这类问题只是约束矩阵A和Aeq会变得更加庞大。5.2 从确定性到随机优化我们的模型是确定性的假设负荷预测D_t是精确已知的。实际上负荷和可再生能源如风电、光伏出力具有很强的不确定性。这就需要引入随机规划或鲁棒优化。例如在日前决策时我们考虑多个可能的负荷场景高、中、低每个场景有一个发生概率。目标函数变为最小化“期望总成本”约束条件则需要满足所有场景或具有一定置信度。Matlab的优化工具箱支持此类问题的建模但求解难度和计算量会大大增加。5.3 从单次出清到多市场耦合电力市场不仅包括日前市场还有实时平衡市场、调频辅助服务市场等。一个完整的模型可能需要模拟发电商在多市场间的联合报价策略。例如一个储能电站可以在日前市场卖电同时在实时市场买电来充电通过价差套利。这需要构建一个多时段、多市场的决策模型其核心是动态规划或混合整数线性规划MILP以处理储能的充放电状态和容量约束。5.4 从优化到仿真代理建模与博弈当我们需要研究发电商的策略性行为时优化模型就不够了需要用到基于代理的建模Agent-Based Modeling, ABM。我们可以为每个发电商编写一个“代理”程序赋予其成本、容量、策略规则如基于历史价格的报价学习算法。将这些代理置于一个模拟的市场环境中让他们相互博弈观察长期下来会形成什么样的价格格局和市场结构。Matlab虽然也能做但Python的Mesa、NetLogo等ABM专用平台或AnyLogic等仿真软件可能更便捷。6. 实战中的避坑指南与经验之谈最后分享一些在构建和求解此类模型时容易踩的坑这些在教科书和论文里往往一笔带过。6.1 数据准备与单位一致性这是最基础也最容易出错的一步。模型中所有参数的单位必须一致。例如成本a_i的单位是元/MWh出力q_{i,t}的单位是MW时间间隔是1小时。那么总成本a_i * q_{i,t}的单位就是 (元/MWh) * MW * 1h 元是正确的。如果你的时间间隔是15分钟0.25小时那么就需要仔细处理。爬坡率Ramp_i的单位是MW/h意味着每小时的变化量在15分钟时段模型中约束应写为|q_{t} - q_{t-1}| Ramp_i * 0.25。我曾因为忽略时间尺度导致爬坡约束形同虚设结果出现了机组出力在相邻时段跳变数百兆瓦的不现实情况。6.2 模型无解与可行性分析当你兴冲冲地跑完代码却得到“No feasible solution found”时不要慌张。这通常意味着约束条件之间存在矛盾。常见原因有总供给能力不足所有机组的Pmax之和小于某个时段的负荷D_t。检查数据。爬坡能力不足负荷在相邻时段变化太快即使机组总容量够也来不及爬坡满足。例如低谷时只有G3运行高峰时需要G1快速顶上但G1的Pmin可能高于0且从0到Pmin也需要爬坡时间。这时需要检查Pmin和初始状态我们模型假设从零开始但实际需要考虑机组初始出力。最小出力之和过大所有机组的Pmin之和大于某个时段的低谷负荷D_t导致无法下调。调试技巧逐步放松约束来定位问题。例如先注释掉爬坡约束和最小出力约束看模型是否能解。如果能再逐一加回约束找到导致无解的那一条。也可以尝试使用linprog的‘diagnostics’选项获取更详细的 infeasibility 报告。6.3 对偶变量影子价格的理解与提取影子价格是理解市场出清的关键。在Matlab中lambda.eqlin对应等式约束lambda.ineqlin对应不等式约束A*x blambda.lower和lambda.upper对应变量上下界。对于我们的功率平衡约束lambda.eqlin就是系统边际成本。但要注意如果某个约束是松的非紧约束其对应的影子价格可能为0。例如在非高峰时段输电线路容量约束可能不生效其影子价格就为0意味着增加一单位传输容量不会改变目标函数值。6.4 计算性能与模型尺度我们的案例只有3台机组、24时段求解瞬间完成。但当机组数上百、时段数达到8760全年小时并且考虑网络约束成百上千个节点和线路时问题规模会急剧膨胀。此时模型构建效率使用循环来组装大型稀疏矩阵Aeq和A会非常慢。应使用Matlab的稀疏矩阵构造函数sparse直接构建坐标格式i, j, v的矩阵效率可提升百倍。求解器选择对于超大规模线性规划问题linprog的内点法‘interior-point’通常比单纯形法‘dual-simplex’有更好的性能。对于混合整数规划如包含启停决策需要使用intlinprog。问题分解对于随机规划等超大规模问题可能需要用到Benders分解、Dantzig-Wolfe分解等算法这些在Matlab中需要自己实现或调用专门的优化库如YALMIP、CVX。构建能源市场竞争模型就像在计算机里搭建一个微缩的经济生态系统。从最简单的成本最小化出发逐步加入物理约束、不确定性、策略行为模型的复杂度和真实感也随之提升。这个过程不仅锻炼数学建模和编程能力更能培养一种系统性的能源市场思维。当你看到自己构建的模型成功模拟出价格尖峰或者解释了一个复杂的市场现象时那种成就感是无可替代的。最关键的是通过这个可运行的Matlab案例框架你可以任意修改参数、增减约束亲自去探索“如果”背后的答案这才是建模学习中最有价值的部分。