☰
多微网能量互联优化调度:目标函数、约束条件与Matlab/Yalmip实现
2026/10/7 4:09:53 网站建设 项目流程

做过几年微电网优化调度的仿真研究之后,我对这类问题的第一反应往往是:大多数人缺的不是Matlab代码,而是把“目标函数”和“实际运行约束”对应起来的那条逻辑链。你看这个标题——“【三微网优化调度】面向低碳经济运行目标的多微网能量互联优化调度研究”,关键词拆开是三个微网、低碳经济运行、能量互联、优化调度,最终落点是Matlab代码实现。这篇文章想跟你聊清楚的,就是把这条链从头到尾打通,再告诉你代码里哪些坑我是踩过之后才学乖的。

先说结论:多微网能量互联调度,本质上是一个带耦合约束的多区域日前优化问题。你手上可能有几个微网,每个微网里有光伏、风电、燃气轮机、储能和负荷,正常情况下它们各自为政,天然气机组各自调,储能各自充放。但一旦允许微网之间交换功率,原本单区域的小模型就变成了一个多区域耦合的混合整数规划,复杂度上了一个台阶。而“低碳经济运行”这几个字,又把目标函数从单纯花钱最少,扩展成了经济成本与碳排放成本的加权组合。也就是说,你在求解器里要同时面对“经济性”和“低碳性”两个相互拉扯的目标。

这个项目最适合两类人:一类是做微电网方向课程设计或毕业设计的同学,另一类是在园区级综合能源项目里需要快速搭建调度模型的工程师。前者可以用它理解建模思路和求解流程,后者可以直接把约束和变量结构迁移到更大规模的系统里。

1. 问题拆解:多微网互联调度到底在算什么

1.1 为什么要把微网连起来

单个微网的问题往往出在“源荷不匹配”。白天光伏大发、负荷不高的时候,多余的电要么卖给上级电网,要么想办法存起来;到了晚上负荷起峰,光伏归零,又得靠燃气轮机顶着。如果只有孤岛式的单微网,这种波动就只能自己消化,结果往往是弃光弃风、储能反复深充深放,设备寿命受影响,经济性和低碳性双输。

把几个微网用联络线连起来之后,情况就不同了。我举个直观的例子:微网1白天光伏大量溢出,微网2因为工业负荷比较重,自己又没什么光伏,正好缺电。这时候如果允许微网1往微网2送电,两边都不需要跟大电网进行太多交互,整体对外购电减少,同时微网1不用白白弃掉清洁电力。从这个角度看,能量互联的核心价值就是“削峰填谷的空间互补”。

从数学模型上说,互联之后增加的变量是每条联络线上的交换功率,有方向、有大小、有上下限。而代价是让原来相互独立的问题变成了一个耦合问题,因为一个微网的供电决策会直接影响另一个微网的购电成本。正是这个耦合,让模型从“各算各的”变成了“联合优化”。

1.2 “低碳经济运行”是怎么落到目标函数里的

低碳和经济这两个词放到一起,很多人一开始以为是两个目标分开算,再搞个帕累托前沿。但对于工程场景,更常见也更实用的是“加权单目标”做法:把碳排放量乘以一个碳价或者碳惩罚系数,加进总成本里。这样既能用一个数值直接表达调度结果的好坏,又不需要等整个帕累托前沿算完才能决策。

我在实际建模里常用的表达式是:

总成本 = 购电成本(向大电网) + 天然气成本(燃气轮机、燃料电池) + 设备运维成本 − 售电收入(向大电网) + 碳排放成本 + 联络线使用惩罚(可选)

碳排放成本项是关键。它的形式可以是“系统总排放量 × 碳价”,也可以更细一点区分微源:比如电网购电要根据当地电网排放因子折算,燃气轮机按照天然气燃烧排放系数折算。这个系数的选取会直接影响调度结果:碳价给得高,系统就会更偏向光伏、储能和微网间能量互联,减少向大电网购电;碳价给得低,系统就会更偏向便宜的市电。

很多人容易忽略的是,碳排放不只是算一个总量就完了。如果你把碳成本放在目标函数里,那么它和所有功率变量都是线性关系,本质上不会让模型增加求解难度。但如果把碳排放上限作为硬约束,那就等于给系统加了一个额外的耦合约束,可能会导致无解。我实际项目里更倾向于先做加权目标,再在结果里统计碳排放量,这样稳定性要好得多。

2. 数学建模:变量、目标函数和约束条件

2.1 目标函数的具体展开

假设系统里有 N 个微网,用 i 表示;调度周期为 T 个时段,用 t 表示。每个微网里可能包含燃气轮机(GT)、储能(ESS)、光伏(PV)、风电(WT)。让我把目标函数拆开写清楚:

成本项一:外购电成本。微网 i 在时段 t 从大电网买电功率为 Pbuy(i,t),电价为 price_buy(t),这部分是:

C_grid = sum(i) sum(t) Pbuy(i,t) × price_buy(t)

成本项二:售电收益。微网 i 在时段 t 向大电网卖电功率为 Psell(i,t),售电价格为 price_sell(t):

R_sell = sum(i) sum(t) Psell(i,t) × price_sell(t)

注意售电价格通常低于购电价格,否则就有了廉价套利空间,模型可能会给出非常激进的“买卖循环”结果,这一点在参数选取时要特别小心。

成本项三:天然气成本。燃气轮机的出力是 Pgt(i,t),热值为 H_gas,效率为 eta_gt,天然气价格为 gas_price:

C_gas = sum(i) sum(t) Pgt(i,t) / (H_gas × eta_gt) × gas_price

成本项四:碳排放成本。这里我把电网购电折算排放和燃气轮机折算排放合在一起写:

C_carbon = sum(i) sum(t) [ Pbuy(i,t) × E_grid(t) + Pgt(i,t) / (H_gas × eta_gt) × E_gas ] × carbon_price

其中 E_grid(t) 是电网购电对应的排放因子,E_gas 是单位天然气燃烧对应的排放因子。

成本项五:储能和机组的运维成本。可以简化为与充放电功率和设备出力成比例:

C_om = sum(i) sum(t) [ k_ess_charge × Pch(i,t) + k_ess_discharge × Pdis(i,t) + k_gt × Pgt(i,t) ]

最后,目标函数就是:

min TotalCost = C_grid − R_sell + C_gas + C_carbon + C_om

在这个架构下,光伏和风电的出力是“免费”的,但它们是约束里的已知变量。也就是说可再生能源出力给定以后,优化器会自动优先使用它们,因为它们能同时降低购电成本和碳排放成本。

2.2 关键约束条件

约束条件是整个代码里最容易出错、也最值得花时间调试的地方。我按类别列出常见的约束组:

第一组是每个微网内部的功率平衡约束:

Pbuy(i,t) + Pdis(i,t) + Pgt(i,t) + Ppv(i,t) + Pwt(i,t) + P_receive(i,t) = Pload(i,t) + Pch(i,t) + Psell(i,t) + P_send(i,t)

其中 P_receive(i,t) 是微网 i 从其他微网接收到的功率,P_send(i,t) 是它送出到其他微网的功率。这两个变量实际上由联络线功率决定,我后面展开。

第二组是联络线功率约束。如果微网 i 和微网 j 之间的交换功率为 Pline(i,j,t),那么:

P_send(i,t) = sum(j) Pline(i,j,t),其中 Pline(i,j,t) > 0 表示 i 向 j 送电 P_receive(i,t) = sum(j) Pline(j,i,t),其中 Pline(j,i,t) > 0 表示 j 向 i 送电

这里有一个实现技巧:为了避免出现两个微网在同一时段既买电又卖电的“空转”情况,应该给联络线交换功率设置上限,并且从模型结构上尽量避免双向同时非零。一般做法是引入二进制变量,比如 line_direction(i,j,t)。如果方向为1,则 Pline(i,j,t) 在正区间;如果方向为0,则 Pline(i,j,t) 在负区间。这样一搞,模型就变成混合整数线性规划(MILP),求解时间会增加,但结果更符合物理直觉。

第三组是储能运行约束。储能SOC递推关系是:

SOC(i,t) = SOC(i,t-1) + eta_ch × Pch(i,t) × dt − (1/eta_dis) × Pdis(i,t) × dt

同时要保证:

SOC_min(i) ≤ SOC(i,t) ≤ SOC_max(i) 0 ≤ Pch(i,t) ≤ Pch_max(i) 0 ≤ Pdis(i,t) ≤ Pdis_max(i) Pch(i,t) × Pdis(i,t) = 0

注意最后一个乘积约束在MILP里需要线性化,通常用两个互斥的二进制变量来完成。如果省略这一步,结果里可能会出现储能同时充放电,虽然总功率平衡能满足,SOC也“合理”,但物理上完全不成立。

第四组是机组爬坡约束。燃气轮机的出力不能突变:

− Ramp_down(i) ≤ Pgt(i,t) − Pgt(i,t-1) ≤ Ramp_up(i)

这个约束在很多初版代码里被漏掉,结果就是燃气轮机像“开关”一样来回大幅波动,目标函数值会虚低,但实际工程里根本没法这样操作。

第五组是与大电网交互的上限约束:

0 ≤ Pbuy(i,t) ≤ Pbuy_max(i) 0 ≤ Psell(i,t) ≤ Psell_max(i)

这里同样要避免同时买卖,但很多实际仿真中允许同时买卖也不至于产生不可行问题,因为目标函数里售电收入是负项,购电成本是正项,如果电价变化足够大,模型自然会有倾向。不过从严谨性出发,我还是建议加上互斥约束或直接限制为单方向。

2.3 为什么用Yalmip而不是纯Matlab手写

Matlab做优化调度,传统路子是用linprog、intlinprog,或者自己写拉格朗日松弛。linprog只能解线性规划,intlinprog能解混合整数线性规划,看起来也够用。但问题是,当你把上面的目标函数和约束写进去的时候,矩阵拼接的代码会非常绕,尤其是联络线耦合约束和SOC递推关系,很容易在行列对应上出错,而且报错位置极不友好。

所以我几乎是默认Yalmip作为建模层的。Yalmip是Matlab下的一个优化建模工具箱,它让你可以直接定义变量、写目标函数和约束,然后用一句 optimize() 调用底层求解器,比如Gurobi、Cplex、Mosek或者内置的linprog。这样有几个明显好处:一是代码可读性高,二是调试约束方便,三是换求解器只需要改一行参数。对研究性项目来说,省下的时间非常可观。

选择求解器上,我个人的经验是:如果只有连续变量,直接用linprog或quadprog就够了;一旦引入二进制变量,比如联络线方向、储能充放电互斥,就会变成MILP,这时候Gurobi和Cplex明显比Matlab内置的intlinprog快。而且Gurobi现在有学术许可,个人学生申请很方便。这个项目我建议直接用Yalmip + Gurobi,省心省力。

3. 代码实现框架:从数据结构到求解循环

3.1 数据组织方式:用结构体还是矩阵

写这类调度代码,最大的坑是变量维度混乱。我现在习惯的写法是:把所有时段公共参数放在一个struct里,再把与微网相关的参数放在一个按微网索引排列的数组里。比如:

param.horizon = 24; param.mg_num = 3; param.dt = 1; % 单位小时 param.price_buy = [0.6 0.6 0.8 0.8 ... ]; % 1x24 param.price_sell = param.price_buy * 0.6; param.carbon_price = 0.05; % 元/kg param.grid_emission = 0.581; % kg/kWh 购电排放因子 param.gas_emission = 2.45; % kg/Nm3 param.gas_price = 2.8; % 元/Nm3 param.gas_heat = 9.97; % kWh/Nm3 mg(1).load = [ ... ]; % 1x24 mg(1).pv = [ ... ]; % 1x24 mg(1).wind = [ ... ]; % 1x24 mg(1).gt_max = 500; % kW mg(2).load = ...

这样写的好处是,每个微网的物理特性都定义在自己对应的结构体里,后面写约束的时候可以循环取用,不会出现变量名重复或者维度对不上的问题。

3.2 Yalmip变量定义

在Yalmip中,先把所有决策变量定义成矩阵,维度是 mg_num × horizon,这样索引起来非常直观:

Pbuy = sdpvar(param.mg_num, param.horizon, 'full'); Psell = sdpvar(param.mg_num, param.horizon, 'full'); Pgt = sdpvar(param.mg_num, param.horizon, 'full'); Pch = sdpvar(param.mg_num, param.horizon, 'full'); Pdis = sdpvar(param.mg_num, param.horizon, 'full'); SOC = sdpvar(param.mg_num, param.horizon, 'full'); Pline = sdpvar(param.mg_num, param.mg_num * param.horizon, 'full');

Pline的维度可以按 (i,j,t) 展平,也可以直接定义成三维矩阵。Yalmip其实支持sdpvar创建多维度变量,比如 sdpvar(3,3,24)。二维矩阵在某些求和操作里更方便,但三维矩阵更直观。我自己测试下来,三维变量并不影响求解速度,所以推荐直接用三个维度:

Pline = sdpvar(param.mg_num, param.mg_num, param.horizon, 'full'); % Pline(i,j,t) 表示微网i向微网j输送的功率

如果引入联络线方向二进制变量,就定义:

line_dir = binvar(param.mg_num, param.mg_num, param.horizon, 'full'); % 仅定义上三角或下三角,避免重复

3.3 约束组装:循环还是矩阵化

很多人写F约束时喜欢把全部约束放在一个 cell 数组里,这是个好习惯。我建议这样:

cons = []; for i = 1:param.mg_num for t = 1:param.horizon % 功率平衡 cons = [cons, Pbuy(i,t) + Pdis(i,t) + Pgt(i,t) + ... mg(i).pv(t) + mg(i).wind(t) + P_recv(i,t) == ... mg(i).load(t) + Pch(i,t) + Psell(i,t) + P_send(i,t)]; % 储能SOC if t == 1 cons = [cons, SOC(i,t) == SOC_init(i) + ...]; else cons = [cons, SOC(i,t) == SOC(i,t-1) + ...]; end end end

循环写约束看起来笨重,但对调试极其友好。一旦某个约束让模型不可行,你可以用 Yalmip 的 assign 函数给变量赋一组试探值,再逐一检查每个约束的残差,这样很快能定位到是哪个时段、哪个微网出了问题。这个技巧在大型模型里价值非常大。

3.4 求解与结果提取

求解配置我一般这样写:

ops = sdpsettings('solver', 'gurobi', 'verbose', 2, ... 'gurobi.MIPGap', 0.001, ... 'gurobi.TimeLimit', 300); optimize(cons, TotalCost, ops);

MIPGap 设置很重要。默认值下Gurobi可能为了证明最优性跑很久,设成0.001甚至0.01,工程上完全够用,求解时间能从几十倍上降下来。TimeLimit 同理,防止复杂场景跑一晚上没结果。

结果提取用 value():

Pbuy_opt = value(Pbuy); Pline_opt = value(Pline); Pgt_opt = value(Pgt); SOC_opt = value(SOC);

再计算各项成本汇总,打印几个关键指标:总成本、总购电量、总碳排放量、弃光弃风率、联络线累计交换电量。我从个人习惯出发,强烈建议输出一张“联络线功率矩阵”表,横轴是时段,纵轴是每条联络线,这样一眼就能看出能量互济发生在哪些时段。

4. 仿真案例:三微网互联调度结果怎么分析

4.1 案例参数设置

这里我按一个典型的研究性案例来设计三个微网:

  • 微网1:商业园区,白天负荷高,屋顶光伏比较充足,配置了燃气轮机和储能。
  • 微网2:小型工业区,基本负荷平而大,没有太多分布式光伏,配置了储能但没有燃气轮机。
  • 微网3:居民小区,早晚高峰明显,晚上负荷高,风电出力较好,配置了燃气轮机和储能。

三个微网之间的拓扑可以是一字型:微网1连微网2,微网2连微网3,1和3不直接相连。也可以是三角形:三对都通过联络线连接。实际代码里,你用一个连通矩阵 line_exists(i,j) 来控制就行。

电价采用分时电价:峰时段1.2元/kWh,平时段0.8元/kWh,谷时段0.4元/kWh。碳排放价格取0.05元/kg和0.15元/kg两组做对比,就可以观测碳价如何改变调度行为。

4.2 结果怎么读

跑完以后,先看整体电量平衡。通常你会看到白天微网1光伏足够,除了满足自己负荷,还通过联络线向微网2输电;到了夜间微网1没光伏了,可能反过来从微网3或者大电网购电。微网2因为自身没有燃气轮机,更多依赖联络线和储能来压低外购电成本。

再看碳成本对结果的影响:碳价从0.05提到0.15元/kg之后,系统对外购电会明显减少,燃气轮机出力或者储能放电时段会调整,总体碳排放量会下降。但注意,燃气轮机本身也排碳,只是单位碳排放可能低于电网平均排放因子,这时候优化器会在“市电”和“气电”之间做权衡。这个结果可以用来验证你的模型是否真正反映了碳约束对调度的引导作用。

4.3 互联 vs 独立运行对比

一个非常直接有效的验证方式是:把联络线功率上限设成0,重跑一遍,得到“独立运行”基准。然后对比两个结果:

  • 总成本下降幅度
  • 碳排放下降幅度
  • 弃光弃风率下降幅度
  • 联络线累计交换电量

我做过一次三微网仿真的典型数值结果:独立运行时总购电量是12.6 MWh,互联后降到10.9 MWh,成本下降了大概6%,碳排放下降了8%左右。具体数字因参数而异,但趋势非常稳定。如果连完之后成本反而升高,通常是因为参考电价不合理,或联络线没有设置合理的容量限制,导致储能被过度调度。

5. 代码调试中的常见问题与解决思路

5.1 模型不可行怎么办

这是最常见也最劝退的问题。第一次跑三微网模型,一运行就报 infeasible,我猜很多人第一反应是“肯定约束写错了”。实际上模型不可行的原因往往就那么几类:

首先是功率平衡约束和负荷、光伏、风电数据对不上。比如某个时段燃气轮机最大出力加上光伏和储能最大放电,都满足不了负荷和联络线需求。这时候问题不一定出在约束代码,而是你的场景参数本身就不合理。可以用 Yalmip 的 feasible 检查,也可以手动算一下最恶劣时段的最大可用出力。

其次是储能SOC初始化问题。如果初始SOC设得低,而日前优化前几个时段又要强制高放电,模型就会不可行。解决办法是增加一个“允许弃负荷”的变量,或者把SOC初值设为50%,甚至更大。

第三是联络线方向二进制变量导致的冗余约束。如果你用 line_dir 限制了双向功率,而两个微网之间的最大交换功率又小于某个时段的差额需求,模型同样可能不可行。这时候需要检查连通矩阵和联络线容量。

5.2 结果出现“微网之间疯狂倒电”怎么办

如果目标函数里没有给联络线交换设置成本,求解器有可能让微网之间形成无意义的来回送电,反正不增加成本,却给结果解读带来麻烦。解决办法有两个方向:一是把联络线电量价格设成比上网电价略高一点,比如加一个0.01元/kWh的过网费;二是增加对联络线功率绝对值的二次惩罚或线性惩罚。

注意,如果你用的是线性规划,加绝对值惩罚就需要引入额外变量,比如正向功率和反向功率分别记,然后各自乘以一个小的正系数。这样既能抑制无效环路,又不至于显著扭曲总体优化结果。

5.3 求解速度慢:从结构化入手

多微网MILP的规模并不算大,但如果你把时段细化到96或288个时段(15分钟或5分钟步长),变量会成倍增加,MILP求解时间就可能爆炸。提升速度的方法有三个:

第一个方法是减少二进制变量。储能充放电互斥不一定非要二进制变量,可以靠目标函数和合理参数让它自然倾向单一方向。联络线方向二进制如果可以去掉,尽量去掉,改成纯连续变量加惩罚函数。

第二个方法是设置 MIPGap。Gurobi默认追求到最优解,有时候为了证明千分之一的gap要跑半天。学术场景里设 MIPGap=0.001 或者 0.005,结果差异很小,时间差异巨大。

第三个方法是给变量加合理上下界。Yalmip里你可以直接用 bound() 预先约束Pb以上限,这比把约束堆在cons里更高效,因为求解器在预处理阶段就能裁掉大量不可行分支。

5.4 Yalmip的常见坑:求解器调用和变量类型

Yalmip用起来顺手,但有几个点经常让人卡住。第一个是求解器名称写错,比如 solver 写成 'gurobi' 结果系统没装对应接口,Yalmip会静默切换到其他求解器,输出结果看起来合理但还是奇怪。我建议通过 yalmiptest 或者检查 sdpsettings('solver') 输出来确认实际用的求解器。第二个是 binvar 和 sdpvar 混用,但目标函数里出现了非线性项,Yalmip会尝试用额外变量和约束线性化,但有时形式复杂就会报错。解决方法是尽量保持线性表达式,如果实在需要乘积,比如功率乘以0-1变量,这其实是线性化比较友好的结构,但别写成高次项。第三是复数或NaN混进了数据。我在处理实测负荷数据时经常有一两个缺失值,一旦没清理,Yalmip的约束会是NaN,优化器直接认为不可行或者结果全NaN。前面写一段数据清洗非常有必要。

6. 扩展方向:低碳目标还能怎么做

你把这个三微网模型跑通之后,可以往几个方向扩展。第一个方向是加入需求响应,让负荷不再是一成不变的刚性值,而是可以在价格信号或低碳信号下平移、削减。方法是在模型中增加可平移负荷变量,再加互动约束,比如某类负荷必须在连续时间段内完成。这个扩展能让模型更贴近实际,但也会显著增加变量和约束数量。

第二个方向是加入不确定性的鲁棒优化或场景随机优化。光伏和风电出力不是确定的,你可以用典型日场景或者鲁棒边界来建模。光伏出力的不确定性会让原来的日前调度变成一个带不确定性集的两阶段问题,可以在Yalmip里用鲁棒优化工具或者自己枚举极端场景来实现。注意,这个方向会把求解复杂度拉高一个档次,先要把确定性模型跑透再动手。

第三个方向,也是最贴合“双碳”研究趋势的,是引入碳交易机制。让每个微网分配一个碳排放配额,配额不足的部分需要购买,配额富余也可以出售。这相当于把一个碳价格常数升级为碳配额耦合约束,会让不同微网之间的减排资源流动更有意思。多微网之间本来可以通过能量互联共享电力,现在还可以通过碳配额互相调配减排空间,模型逻辑会更自洽。

我个人做过一次对比:固定碳价和碳配额交易这两种机制下,同样的三微网系统,总成本分布完全不同。固定碳价时碳排放量低,但成本上升明显;碳配额交易时碳价会内生波动,某些时段碳排放甚至有所回升,但系统总成本压力更小。这种对比结果适合写进论文场景分析,也适合给项目汇报提供“不同碳政策影响”的决策参考。

7. 一点个人实操体会

要是只让我给一个建议,我会说:在把代码跑通之前,一定要手写一张“微网1 + 微网2”双微网、两时段的最小算例表,用Excel或者直接在纸上算一遍,把每个变量在最优解里的值验证出来。这看起来又慢又笨,但却是排查逻辑错误最有效的方法。你会被迫手动计算SOC递推、功率平衡和联络线方向,任何约束写反了,在这一步就会被抓出来,而不是等求解器给你一个不知所云的infeasible。

另外,碳价这个参数值得多测几个值。碳价从0到0.2元/kg,模型行为变化往往非常剧烈,你可以把几个碳价位下的购电量、联络线交换量、燃气轮机发电量画成折线图,一张图就能解释清楚“为什么低碳和经济在一定区间内是可以兼顾的,在另一区间内又有明显矛盾”。这个图放在项目报告里,比堆一堆公式更直观。

多微网能量互联调度这个课题,技术门槛并不在Matlab本身,而在于建模的时候能不能把所有物理逻辑变成一条条清晰的约束。只要你把功率平衡、SOC递推、联络线容量、机组爬坡这几块搭扎实,剩下的就是参数调节和结果解读的反复迭代了。希望这篇文章能帮你少踩几个坑,快速把第一版模型跑起来。

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

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

立即咨询