做电力系统调度优化这几年,类似的题目几乎每个学期都会在我这边出现一次:“考虑源荷两侧不确定性的含风电电力系统低碳调度(Matlab代码实现)”。如果你也正在做这类研究,大概率是卡在这几个地方:不确定性怎么建模才不算拍脑袋、低碳目标怎么进目标函数、Matlab代码搭到什么程度才算真的跑通。这篇文章就来把这些事一件件拆开讲,围绕风电、负荷双侧预测误差,给出一套可以用课程设计、论文复现和个人项目练习的调度建模与求解框架。适合正在做电力系统优化方向的学生,也适合想从确定性调度转向不确定性调度的从业者。
我默认你已经会基本Matlab和优化求解器,但就算你只有一点基础,按下面的顺序走,也能跑出第一版结果。这个选题的核心价值不在于“代码文件名有多漂亮”,而在于你能不能回答三个问题:不确定集或场景怎么生成、碳排放成本怎么量化、调度结果如何在成本和低碳之间取得平衡。把这三点打通,剩下的就是搭模型和调参的过程。
1. 场景与调度思路:为什么要把“不确定性”和“低碳”放在一起
1.1 风电接入之后,调度为什么变难了
早期电力系统调度最怕的两个字叫“预测”。好在负荷预测误差相对较小,调度员只要留出旋转备用,基本就能保安全。但是风电大规模接入后,这个平衡被打破了。风机出力由自然风速决定,且存在明显的反调峰特性:夜晚负荷低谷时风速往往偏高,白天负荷高峰时风速往往偏低。这个“反着来”的特性,直接导致原有的确定性调度思路不再适用。
更麻烦的是,风电预测的误差通常比负荷预测误差大一个量级。风速稍变,风功率可能从50%出力掉到20%出力。如果你只按预测值的单点情况安排机组计划,实际运行时就会面临两大类问题:要么出力不足,需要紧急开启高成本机组或者切负荷;要么出力过剩,被迫弃风。任何一类,都意味着经济性和安全性同时受损。
所以现在的主流做法,是把风电出力和负荷水平都看成带概率分布的随机量,而不是固定的一组数。调度计划需要在不确定性进来之前就做出来,还要保证在所有“可能的世界”里都尽量不越界、不亏电、不浪费。这就是“考虑源荷两侧不确定性”这句话背后的真实工程需求。
1.2 源荷两侧不确定性,拆开来看到底是什么
“源侧不确定性”主要指风电出力预测误差,也可以扩展到光伏出力或水电来水波动,但本题里关注点就是风电。风电预测误差本质上来源于数值天气预报的不准确,所以模型里很有代表性的处理方式是:先预测风速,再转换成功率,并给预测功率附上一个误差分布。这个分布可以用正态分布近似,也可以根据历史数据拟合出更贴合实际的偏态分布。
“荷侧不确定性”指负荷预测误差,主要受天气、产业结构、用户行为等因素影响。在区域电网尺度上,负荷预测误差通常服从平均值为零的正态分布,标准差大约为预测负荷的1%~3%。这部分误差单独看不严重,但当荷侧误差和风电误差同时出现时,会在某些时段形成双向叠加,极大地压迫系统可调度空间。
这里必须记住一个关键点:不能把源和荷两侧不确定性分开做两次独立备用,因为二者的偏差可能正相关、负相关或完全不相关。更稳妥的做法是,把二者作为独立随机变量同时抽样,生成联合场景,才能真正反映最恶劣情形。这也是很多毕业设计里只搞了个风电场景、却把负荷当成固定值拍进去,最后结果显得很“假”的原因。
1.3 低碳调度到底改变了什么
传统经济调度只看发电成本,目标是让火电机组经济运行。但含风电的低碳调度,目标函数里多了一块“碳排放量”或“碳排放成本”。在双碳目标大背景下,碳排放不再只是环保报告里的一个数字,而是可以直接折算成成本的约束条件。
低碳调度一般有三种落地方式。第一种最简单,把碳排放量作为软目标加入目标函数,用价格系数折算成成本。第二种是设置碳配额,系统碳排超出配额时需要用碳交易市场购买额度,购买成本进入目标函数。第三种更严格,把碳排放作为硬约束,限制调度周期内的总排放上限。实际论文和工程项目里最常见的是前两种的结合:目标函数里含碳交易成本,同时保留排放上限约束。
风电的低碳价值在这个模型里非常直接:风电机组几乎零碳排放,在有风电接入的情况下,系统可以通过调整火电出力来减少总碳排,从而少买碳排放配额。这部分“碳减排收益”如果能在目标函数中显式体现,低碳调度就不再只是口号,而是一个可以被优化算法自动权衡的经济信号。
2. 不确定性建模与场景生成:这是调度计算的地基
2.1 风电功率的不确定性建模
风电功率不是一个可以直接用正态分布描述的物理量,因为它由风速经过三次方关系转换而来。比较经典的流程是先用Weibull分布描述风速的随机性,再通过风机的功率曲线转换得到功率场景。式中Weibull分布的形状参数和尺度参数需要根据当地风场历史风速拟合,如果没数据,取形状参数k等于2左右、尺度参数c按平均风速换算,也经常能搞出一版可用结果。
如果你手头的数据是历史风功率,不走风速曲线这条路,也可以直接对预测误差建模。比如将实际风功率视为“预测值+误差”,误差项用均值为零的截断正态分布描述,并限制在风电场装机容量上。这种方法实践起来更省事,也更贴近调度系统里“预测—修正—再计划”的实际运行逻辑。
我个人在做Matlab代码时习惯双轨验证:先按风速Weibull分布生成一组样本,再按预测误差正态分布生成一组样本,两者交叉检验。原因很简单——如果只用误差模型,结果会偏向“预测值附近抖动”,缺少对极端风速的覆盖;如果只看风速分布,又可能忽略预测系统本身偏差带来的持续性影响。
2.2 负荷预测误差建模与双侧场景构造
负荷预测误差相对成熟,普遍假设为均值为零的正态分布,标准差取预测负荷的1%~3%。在建模时可写为:
[ P_L^s = P_L^{forecast} + \varepsilon_L^s, \quad \varepsilon_L^s \sim N(0, \sigma_L^2) ]
其中负荷场景的数量要与风电场景保持一致,这样才能配对成联合时序场景。生成联合场景时,建议先分别生成每个时段的风电功率和负荷样本,再把同一时段的样本配成一对。这样做的好处是,调度模型可以识别同一时刻源荷偏差的真实叠加,而不是让每个不确定量各自独立演进。
在实际代码里,很多人会直接把负荷预测值当成固定数据,只在风功率上加扰动。这样程序确实更快,但不是严谨的“源荷双侧不确定性”。如果审稿人或答辩老师看到你的模型里负荷全是单点,大概率会直接点出问题。既然题目已经明确写了“源荷两侧”,至少要让负荷也带上误差带,哪怕只用最基础的正态分布场景,整个模型的完整度也会立刻上一个档次。
2.3 场景生成之后必须做的事:场景削减
蒙特卡洛抽样能轻松生成几千个场景,但你不可能把几千个场景全丢进优化模型里。一是变量规模爆炸,求解时间不可接受;二是许多场景之间相似度极高,把它们一起放进模型只会增加约束却不会增加信息含量。因此需要做场景削减,从原始场景集中挑出有代表性的少量场景,代表整个概率空间。
常用方法有概率距离聚类法(K-means)、快速前向选择法和启发式后向削减法。按我的使用习惯,基于K-means聚类的方式性价比最高:先随机生成1000个原始场景,然后聚类成10~20个典型场景,每个场景保留其聚类中心,并统计该类样本占比作为概率。这样既覆盖代表性波动,又控制模型规模。
如果你用的是YALMIP,场景数量建议控制在10~20个之间。低于5个,不确定性描述会失真;高于30个,求解时间会明显拉长,尤其在考虑机组启停时。这个数量不是越高越好,而是要让目标价值随场景数增加趋于稳定且可解释。
2.4 场景法的局限性和鲁棒思路补充
场景法本质是“用部分代表全域”,所以它无法保证覆盖所有极端情况。一个更稳健的选择是引入鲁棒优化:不指定每个随机量的精确概率分布,只定义不确定集,比如盒式不确定集或椭球不确定集,寻找在最坏情况下仍然可行的调度方案。
但鲁棒优化也有自己的问题,就是结果偏保守,经济性差。折中的方案是分布鲁棒优化,用矩信息约束不确定性分布集合,既比场景法更稳健,又比纯鲁棒更经济。这个方向稍深一些,不建议零基础直接上。如果你做的题目是课程设计或普通期刊论文,场景法完全够用,且更容易在答辩时解释“场景是怎么来的、概率是多少”。
我个人在写含水火的调度项目时,会把场景法作为基准方案,罗列出各场景下的出力细节;然后追加一个“极端场景”或“最坏负荷/风电组合场景”来验证模型韧性。这样一个基准案加一个压力测试,结论说服力会强很多。
3. 低碳经济调度模型:从目标函数到约束条件
3.1 目标函数:运行成本与碳排放如何加权
含风电的低碳调度,目标函数至少包含两大部分:常规机组运行成本和碳排放相关成本。常规机组运行成本可用二次函数表示,即每台火电机组出力对应的煤耗成本。风电成本通常取一个很小的运行维护系数,甚至简化为零,但弃风惩罚成本不能没有,否则风功率会被无低成本地完全消纳,这在工程上不真实。
碳排放成本可以有两种写法。第一种是简单的碳税式成本,单位碳排放乘以碳价;第二种是更接近电力现货市场的配额交易式,即系统总排放量减去免费配额后的差额乘以碳交易价格。后者更符合国内碳市场的设计逻辑,也能更好地展示风电的减排价值,所以稳妥起见我建议用配额交易模型。
于是目标函数可以写为:
[ \min \quad \sum_{t} \sum_{i} \big[ C_i(P_{i,t}) + C_{CO2} \cdot (E_i(P_{i,t}) - A_i) \big] + C_{wind} \cdot P_{wind,t} + C_{curtail} \cdot P_{curtail,t} ]
如果你考虑两阶段随机调度,目标函数里还会多一项“各个场景下的期望调整成本”:包括火电机组增减出力成本、弃风惩罚和失负荷惩罚。这一项是场景法建模的灵魂,它让模型能提前为“未来可能发生的偏差”预留可调节空间。
3.2 系统约束:功率平衡、出力区间、爬坡与旋转备用
调度模型跑不跑得通,约束的完整性比目标函数更关键。最基础的四个约束必须写清楚:
- 功率平衡约束:任意时刻,常规机组出力加上风电出力,等于负荷需求加上网络损耗,如果计及弃风和切负荷,还要把这两个松弛变量放入平衡方程。
- 机组出力上下限约束:每台火电机组出力不能超过其技术最小出力和最大出力。
- 机组爬坡约束:相邻时段之间,机组出力变化量不能超过最大爬坡速率。这是风电大波动场景下的关键约束,很多论文复现者忘了它,结果调度结果里机组出力“跳变”,一看就不符合实际。
- 系统旋转备用约束:由于风电和负荷存在预测误差,系统必须具备上调和下调备用容量。备用约束在整个模型中承担“安全兜底”的角色,不能省略。
如果在随机规划框架下,功率平衡和爬坡约束还应当分“日前计划”和“实时调整”两层。第一阶段定机组基准出力,第二阶段允许机组在各场景下做小范围增减,但调整量受爬坡和剩余容量限制。这么一拆,模型才真正具备“面对不确定性仍能调整”的能力。
3.3 碳排放约束与碳交易机制的具体写法
碳交易机制在调度模型中通常拆成三步。第一步,计算每台火电机组的碳排放量,一般用出力的一次或二次函数表示,单位是tCO2/MWh;第二步,给定系统免费碳排放配额,这个配额通常按历史排放水平或机组类型设定;第三步,计算总排放量减去总配额,差值进入碳交易市场。若为正则买碳,若为负则可出售碳配额获得收益。
我建议在Matlab代码中把碳配额设置为一个可调参数而非固定常数。这样做的好处是,你可以快速测试不同配额水平对调度结果的影响。比如把配额调低,就会看到风电消纳量上升、火电出力下降,这是非常有说服力的敏感性分析图表。
硬性的总排放量上限也可以做进约束里,作为碳交易成本之外的第二道闸门。逻辑很简单:如果碳交易价格太低,目标函数可能为了省钱而多排碳,此时硬约束就能防止碳排突破红线。实际实现时两条腿走路最稳。
3.4 为什么说这种调度可以体现“低碳”
简单算一笔账假设一台100MW火电的碳排放强度约为0.8tCO2/MWh,一台50MW风电场在相同时段出力30MW,这部分出力替代火电后,每小时直接减少约24吨碳排放。如果碳市场价格为50元/吨,则每小时减排收益约1200元。这个数字放在日调度里,就是一天接近三万元的经济信号。
低碳调度模型的本质,就是把这类减排信号从“感觉上应该做”变成“优化器自动算出来应该做”。风电场不出力的时候,火电承担基荷,碳排自然高;风电场出力充足时,火电主动压低出力,甚至保留更多负备用空间吸纳风电。这个行为不是靠人工指令,而是靠碳价格传导进目标函数后,由优化自动寻优得到的结果。
所以低碳调度和传统经济调度最大的差别就在于:传统调度中风电只是替代火电的免费礼物,而低碳调度中,风电是一个能够减少碳配额支出并获得经济收益的主动资源。这一点想在论文里讲清楚,直接画一张“碳价升高时风电消纳量上升”的敏感性图就够了。
4. Matlab实现要点:从建模到求解器配置
4.1 总体代码架构设计
我建议把整个Matlab工程按数据、场景生成、模型构建、求解、后处理分模块组织,不要把所有东西塞进一个脚本里。工程结构大致如下:
wind_lowcarbon_dispatch/ |-- data/ % 负荷、风电预测曲线、机组参数 |-- scenario/ % 场景生成与削减 |-- model/ % YALMIP建模主函数 |-- solve/ % 求解器配置与优化执行 |-- plot/ % 结果可视化 |-- main.m % 主程序入口这个结构的价值在于:你不必为了改一个机组参数而把整段代码翻一遍。模块化还有一个好处,就是方便做多方案对比。比如只改碳配额,跑的仍然是同一套模型函数,自然就能画出配额变化对结果影响的曲线。
主程序无非就是三步:读数据、生成场景、调用模型求解。写代码时优先保证能跑通,再考虑优化运行时间。不要一上来就套花哨的加速技巧,否则代码报错时你根本不知道是建模问题还是加速逻辑问题。
4.2 YALMIP建模核心代码示例
我用YALMIP实现两阶段随机低碳调度模型的简化版。核心大致如下:
% 参数初始化已省略:ngen, Nt, Ns, Pmax, Pmin, ramp, ... % 场景输入: Pwind_s(Nt, Ns), Pload_s(Nt, Ns), prob_s(1, Ns) P = sdpvar(ngen, Nt, 'full'); % 第一阶段日前出力计划 r_up = sdpvar(ngen, Nt, 'full'); % 上调备用容量 r_down = sdpvar(ngen, Nt, 'full'); % 下调备用容量 % 第二阶段场景调整量 deltaP = sdpvar(ngen, Nt, Ns, 'full'); curtail = sdpvar(Nt, Ns, 'full'); % 弃风量 shed = sdpvar(Nt, Ns, 'full'); % 切负荷量 Constraints = []; % 第一阶段约束 for t = 1:Nt Constraints = [Constraints, sum(P(:,t)) + sum(Pwind_forecast(:,t)) == Pload_forecast(:,t)]; Constraints = [Constraints, Pmin <= P(:,t) <= Pmax]; if t > 1 Constraints = [Constraints, -ramp <= P(:,t) - P(:,t-1) <= ramp]; end end第二阶段约束可写成循环场景内的再调度平衡公式,这里最关键的是每台机组的调整量与其备用的关系:
for t = 1:Nt for s = 1:Ns Constraints = [Constraints, deltaP(:,t,s) <= r_up(:,t)]; Constraints = [Constraints, deltaP(:,t,s) >= -r_down(:,t)]; Constraints = [Constraints, Pmin <= P(:,t) + deltaP(:,t,s) <= Pmax]; end end目标函数里除了发电和碳成本,再按场景期望加权加入调整补偿、弃风和切负荷惩罚,最后调用optimize求解:
Objective = base_cost + co2_cost + sum(prob_s .* expected_adjust_cost); ops = sdpsettings('solver', 'gurobi', 'verbose', 2); [diagnostic, solution] = optimize(Constraints, Objective, ops);这只是一个小骨架,真正落地还需要把时段数、场景数搭好,尤其注意deltaP这个三维变量的索引方向。我见过很多次报错就是因为sdpvar的维度顺序和循环下标不一致。
4.3 求解器选择与参数设置
Matlab下求解混合整数线性规划或二次规划,第一选择是Gurobi或CPLEX。如果学校没有商业求解器授权,可以用YALMIP自带的sedumi或sdpt3处理连续问题,但遇到整数变量就会吃力。更轻量的替代方案是免费的SCIP或HiGHS,但要在Matlab里接入会稍微折腾。
调求解器参数也是关键。建议优先设置两个参数:一个是TimeLimit,另一个是MIPGap。对普通课程设计,MIPGap设置在1%到5%之间就足够,硬怼到0.1%会把求解时间拉得非常长。我的习惯是先用3%的gap快速出一版结果,观察变量分布有没有明显不合常理的数值,再收紧到1%跑最终版本。
如果模型里没有整数变量,只是一个线性规划或二次规划,Gurobi通常几十秒内就能解决。但只要你加入了机组启停变量,问题就变成混合整数规划,场景数稍微一多就会指数级增长。所以想控制求解时间,最有效的办法不是买更贵的机器,而是削减场景数和机组数。
4.4 结果输出与可视化:别把图只画给自己看
调度结果的输出不能只看目标函数值。至少应该画三张图:第一张是各时段机组出力堆叠图,含风电出力曲线;第二张是各场景下的平衡偏差,即负荷需求与发电出力的差值,观察是否有系统性缺口;第三张是碳排放量随碳配额或碳价变化的敏感度曲线。
堆叠图要能清楚的看出“风电大发时段,火电出力被压下来”的效果;如果任意时段出现风电出力大幅超出系统消纳能力,堆叠图会表现出弃风或减出力,这往往是约束设计是否合理的信号。
在论文或报告中,强烈建议加上“无碳约束、含碳价、含碳配额+碳交易”三个方案的结果对比。三个点一对比,读者一眼就能看到低碳约束如何改变调度决策,比你写几段文字都管用。后处理代码尽量用stackedbar或area,不然图会一眼难尽。
5. 调试实录:那些年踩过的坑
5.1 场景数到底取多少合适
我第一版代码用的是5个场景,求解飞快,但风电出力偏差窄得离谱,系统备用需求被严重低估;改成50个场景后结果更稳定了,但求解时间翻了十倍不止。后来用快速前向场景削减,从2000个原始样本里聚出15个场景,再结合概率权重来替代等概率场景,效果立刻变好。
如果你不确定自己的场景数是否合理,做一次收敛性测试:分别用5、10、20、30个场景跑同一组参数,画出目标函数值随场景数变化的曲线。当目标值在某个数量之后不再明显变化时,就说明这个场景数已经足够。别小看这个测试,它在答辩时几乎等同于一个方法验证环节。
5.2 求解无解或一直超时
无解不是罕见的问题。常见的无解原因有三个:一是功率平衡约束和负荷、风电场景不匹配,比如忘了在平衡方程里加弃风和切负荷变量;二是爬坡约束过严,机组从一个出力水平跃变到另一个出力水平超过了限值;三是备用约束写死之后,系统没有足够容量支撑最恶劣场景。
排查时先检查模型的可解性。把目标函数改成常数,也就是只求可行性,如果仍然无解,说明约束本身不成立。接着按变量逐一宽松:先放开最恶劣场景,再放开备用容量,最后放开爬坡约束,看问题出在哪一层。这个方法我试过无数次,每次都能快速定位到具体约束。
5.3 低碳约束过紧导致结果失真
碳配额设得太低时,模型会进入一个很尴尬的境地:为了不超碳排,火电只能压到最小出力,但如果负荷高峰远超风电+最小火电出力之和,模型就必须大量切负荷。此时目标函数里虽然碳排达标,系统却牺牲了可靠性。这种结果看着像低碳了,实际上是伪低碳,不值得表扬。
保障可靠性的办法是给失负荷加高额惩罚成本,同时在结果分析里单独列出“失负荷电量”指标。如果失负荷超过总负荷的某一比例,说明碳约束过于激进,需要回退配额或者增加储能/联络线等灵活性资源,而不是让模型硬冲。
5.4 实用避坑清单速查
| 问题现象 | 常见原因 | 处理办法 |
|---|---|---|
| 目标函数值太大且不稳定 | 场景概率未归一化 | 检查prob_s求和是否为1 |
| 风电曲线没有引起火电变化 | 模型中风电成本设为零且无消纳约束 | 加入弃风惩罚或风电最小消纳比例 |
| 优化结果出现机组跳变 | 缺少爬坡约束 | 在日前计划中加入爬坡限制 |
| 碳排始终等于配额上限 | 碳配额约束过紧或目标函数未加碳价 | 调整配额,或检查碳交易成本公式 |
| 求解特别慢 | 场景数过多或含整数启停变量 | 做场景削减,设置合理MIPGap |
| 结果图很难看出低碳效果 | 没有做方案对比 | 至少跑出无碳价/有碳价两版结果 |
这个表查起来很方便,基本覆盖了大多数复现时容易遇到的坑。调试过程中保持一个习惯:每次只改动一个变量,翻看结果变化,而不是把参数一口气全改掉。这样就算出问题,你也能知道是谁导致的。
6. 从“能跑”到“能讲”:一些个人体会
代码能跑通只是第一步,能把结果讲明白才是另一道坎。我见过太多人拿着一个算例结果,面对“为什么在这个时段弃风”这类问题时说不清楚。根子在于,他们用的模型都没有把弃风成本写成显式变量,也没有按场景统计弃风时段分布,当然无从解释。
我的建议是,哪怕导师没有要求,也把弃风电量、碳排放总量、供电可靠性三个指标单独做统计。不要只输出目标值。三个指标列一列,风电的正面作用和系统的紧张程度都一目了然。答辩或汇报时,这些数字就是你的论据。
实际做项目时还有一个体会,就是碳价对调度结果的真实影响区间往往不像你想象得那么大。碳价从30元/吨改到80元/吨,碳排总量可能只降几个百分点,除非你同时把配额也收紧。所以低碳调度不能只靠碳价调节,还是要配合同步提高风电消纳能力、增加备用资源和灵活调节能力,这个组合拳才是工程落地最有效的路径。
如果你后续想扩展,比较推荐往“分布鲁棒优化+低碳调度”方向走,把风电出力的矩信息放进调度模型,或者把储能系统也纳入作为灵活性资源。前者偏理论,后者更偏工程。两者都能让现在这套Matlab代码变得更完整、更有说法力。跑通当前模型之后,在这些方向上迭代下去,会很快进入能独立做方案的状态。