☰
风光储联合优化:用Matlab+Gurobi破解微电网容量配置难题
2026/9/29 17:41:23 网站建设 项目流程

前阵子帮一个园区做微电网规划,一开始按老思路把风电、光伏、储能分开算容量,结果报告刚出来就被领导问住了:风光装机翻了一倍,弃风弃光反而涨了三成,储能利用率却低得吓人。这个问题憋了我好几天,最后把所有设备放进一个模型里,用Matlab+Gurobi做风光储联合优化,才算把账理清楚。这篇博文就围绕这条线展开:先讲清楚独立运营为什么不行,再拆解联合优化的数学建模思路,最后给出可参考的代码框架、求解验证和避坑经验。无论你是正在做数学建模竞赛、研究微电网经济调度,还是实际项目里要写规划方案,这套“从独立到联合”的建模方法都能直接用。

1. 园区微电网规划的两种视角:独立运营模型为什么不是最优解

1.1 园区微电网里到底有哪些设备和决策

园区微电网通常包含风机、光伏、储能、负荷,以及和外部电网的联络线。规划阶段的核心问题很简单:风机装多少千瓦、光伏装多少千瓦、储能功率和容量配多大,才能让整个系统在满足用电需求的同时,综合成本最低。很多人习惯把这个问题拆成几个独立的子问题:先算风电装机,再算光伏装机,最后按负荷低谷和尖峰差来配储能。这种方法看上去清晰,实际上忽略了三个设备之间的强耦合。

举个例子,光伏午间出力大,如果负荷午间小,多余电量要么卖给电网,要么存进储能;储能配多了,光伏弃光可以减少,但储能成本上升;储能配少了,尖峰时段还得靠买电网电。风机晚上出力高,如果负荷夜间低,储能能不能存住这部分风电,又会影响风电装机是否划算。独立运营时,每个设备只盯着自己的发电或充放电特性做规划,互相之间没有反馈,最后得到的“局部最优”合在一起,往往不是系统最优。

1.2 独立运营的典型做法和三个典型问题

独立运营在项目里通常表现为三种做法。第一种是“按比例分摊”:比如负荷年用电量是1000万kWh,让风电承担30%、光伏承担30%,剩下40%买电,然后根据当地等效利用小时数反推装机容量。第二种是“单设备投资回收期”:分别计算风电、光伏、储能在各自电价和补贴下的投资回收期,凡是回收期小于N年的设备就装到资源上限。第三种是“储能为新能源让路”:先定风光容量,再把储能的功率按新能源装机的10%~20%估算,容量按2小时设置。

这三种做法都会带来典型问题。由于没有把“什么时候发电、什么时候用电、电价怎么波动”放到同一个时间轴上考虑,很容易出现新能源装机越多、弃电率越高的情况;储能容量则往往偏保守或偏激进,偏保守时高峰买电成本盖不住储能投资,偏激进时储能大部分时间闲置,投资回报极差。还有一类问题更隐蔽:独立运营下的“最优”可能互相冲突,比如风电为了多发电要求储能容量大,光伏为了降低弃光也需要储能,但两者的出力高峰时间不同,对储能的充放电需求是互相竞争的,单独算出来的储能配置完全没法用。

1.3 联合优化到底“联合”了什么

联合优化的本质,是把容量层决策和运行层决策放进同一个优化问题。容量层决定风机、光伏、储能的装机规模和投资成本,运行层决定每个时段每台设备该怎么出力、储能该充还是该放、买电和卖电怎么安排。这两层原本就是嵌套的:容量配置决定运行可行域,运行策略又决定容量投资的收益。只有把它们同时求解,才能得到真正意义上的最优规划方案。

从数学上看,联合优化是一个带连续变量和0-1变量的混合整数优化问题,规模比独立运营大好几倍。这也是为什么很多团队用Matlab优化工具箱跑不动、最后换Gurobi的原因。Gurobi在求解大规模线性规划、混合整数规划上效率很高,尤其是处理储能充放电状态、买卖电状态这类0-1变量时,明显比通用优化工具箱更适合。

2. 风光储协同规划的数学建模:变量、约束与目标函数的设计逻辑

2.1 决策变量怎么定:容量层与运行层分开列

建模的第一步是明确决策变量。容量层变量只有4个:风电装机容量、光伏装机容量、储能额定功率、储能额定容量。运行层变量跟时间步长和场景数有关,如果采用典型日缩聚,每个典型日有24个时段,每个时段都需要风电实际出力、光伏实际出力、储能充电功率、储能放电功率、与电网购电功率、向电网售电功率、弃风功率、弃光功率、荷电状态SOC,以及两个描述储能状态的0-1变量。至于要不要加“是否买电”和“是否卖电”的0-1变量,取决于联络线模型怎么建立。

有人会问,为什么需要0-1变量?因为储能不能同时充电和放电,购电和售电也不能同时发生。如果不加0-1变量,模型可能会利用同时充放电制造“伪能量流”,导致目标函数虚低,但实际工程里根本做不到。这类约束在数学上属于“互补性约束”,需要借助二进制变量和大M法线性化。

2.2 目标函数:投资成本与运行成本要放在同一张表上

规划期的目标是年化总成本最小,包括设备投资年值、年运行维护成本、年购电成本,减去年售电收益。设备投资成本不能直接把设备总价放进目标函数,因为风电寿命20年、储能寿命可能只有10年,需要用年值法折算成年均投资成本。简单做法是把静态投资乘以资本回收因子,资本回收因子CRF = r×(1+r)^n / ((1+r)^n − 1),其中r是折现率,n是设备寿命。

目标函数可以写成:

最小化 f = 投资成本(风+光+储) + 运维成本 + 购电成本 − 售电收益 + 弃电惩罚

弃电惩罚不是必须的,因为弃风弃光本身已经通过“实际出力小于可用出力”体现在功率平衡约束里,但如果在实际项目中希望优先消纳新能源,可以加一个很小的惩罚系数,避免模型因为经济性原因无故弃电。

2.3 约束条件:功率平衡、储能SOC、联络线一个都不能少

核心约束包括四类。第一类是逐时功率平衡:风电实际出力+光伏实际出力+储能放电功率+购电功率 = 负荷功率+储能充电功率+售电功率+弃风功率+弃光功率。第二类是新能源出力上限:每个时段风电实际出力和弃风功率之和等于风电装机容量乘以该时段归一化出力系数,光伏同理。第三类是储能运行约束:SOC动态方程、SOC上下限、充放电功率上限、充放电互斥约束、初末SOC一致约束。第四类是电网联络线约束:购电功率和售电功率都必须小于联络线最大传输功率,并且不能同时为正。

注意,功率平衡里的单位必须统一。装机容量常用kW,电量常用kWh,时间步长如果是1小时,功率(kW)乘时间(h)就是电量(kWh),但如果时间步长是15分钟,功率乘0.25h才是电量,建模时特别容易漏掉这个系数。

2.4 为什么这是个MILP问题,为什么选择Gurobi

因为存在储能充放电互斥、购售电互斥这两类0-1逻辑约束,整个问题属于混合整数线性规划MILP。此外,如果目标函数或约束里有储能损耗项、电压潮流等二次项,还会升级为MIQP或MINLP。微电网容量规划常见模型还是MILP,因为线性化之后求解稳定、全局最优性有保证。

选Gurobi而不是其他求解器的原因很简单:一是0-1变量规模大时,Gurobi的割平面和启发式算法成熟,几十万个变量也能在可接受时间内收敛;二是学术版License申请方便,Matlab调用接口顺手;三是和Yalmip配合,建模效率比自己写约束矩阵高很多。实际项目里,我一般用Yalmip搭模型框架,再用Gurobi做后端求解,这样代码可读性和求解速度都能兼顾。

3. Matlab+Gurobi建模环境准备:安装、验证与接口选型

3.1 安装Gurobi与申请学术License

Gurobi对在校学生和教师提供免费的学术License,申请入口在官网直接注册学校邮箱。重点提醒:申请下来的License一般有机器限制,必须安装后先绑定当前电脑,换机器需要重新申请或更新。安装时选择与操作系统匹配的版本,解压到固定目录,比如Windows下的C:\gurobi\win64。安装完不建议把整个文件夹塞进Matlab路径,只需要在Matlab里运行gurobi_setup.m,让Matlab能找到gurobi.mexw64等接口文件。

有些同学安装后报“Unable to load gurobi”,大概率是License没有放到正确位置,或者系统环境变量GRB_LICENSE_FILE没有指向gurobi.lic。Windows下可以把License文件放到用户目录,并在系统环境变量里手动指定,重启Matlab再验证。

3.2 Matlab调用Gurobi的三种方式

第一种是直接用Gurobi官方提供的Matlab接口,构建model结构体,调用gurobi(model, params)。第二种是使用Yalmip工具包,用sdpvar和binvar声明变量,最后用optimize(Constraints, Objective, sdpsettings('solver','gurobi'))求解。第三种是通过MATLAB优化工具箱的intlinprog,再指定求解器,但不推荐。

我个人的建议是:写规划模型用Yalmip,做对比测试时用官方接口。Yalmip的符号建模方式非常贴近数学表达,方便维护和修改,尤其适合从独立运营模型改造成联合优化模型。官方接口更适合性能调优和嵌入到生产系统里,因为省掉了Yalmip的变量解析开销。如果只是做研究或者比赛,Yalmip + Gurobi足够用。

3.3 最小验证Demo:先跑通一个简单的LP/MILP

安装完成后建议先做最小验证。Yalmip版测试代码:

x = sdpvar(2,1); Constraints = [x(1) + x(2) >= 1, x >= 0]; Objective = 2*x(1) + 3*x(2); ops = sdpsettings('solver','gurobi','verbose',0); optimize(Constraints, Objective, ops); value(x)

如果顺利得到[0;1],说明Yalmip + Gurobi链路是通的。官方接口版测试可以用结构体写一个类似问题,重点看result.status是否返回OPTIMAL。这一步看起来简单,但能排查掉80%的环境问题。

4. 从独立到联合优化:Gurobi模型代码的逐步实现与关键API解读

4.1 数据准备:典型日曲线、负荷与电价参数

建模之前必须准备好输入数据。最少需要三类:园区24小时负荷曲线,风机和光伏的归一化出力曲线,以及分时购售电价。归一化出力曲线的含义是“每kW装机容量在某个时段的实际出力”,比如光伏在中午出力系数为0.85,意味着1kW光伏在此时可发0.85kW电。如果有多组典型日数据,还要加上“典型日权重”,用于把单个典型日成本加权折算到全年。

经济参数包括设备单位投资成本、运维成本、储能充放电效率、折现率、设备寿命、最大联络线功率、SOC上下限等。这些参数在建模阶段不要写死在代码里,最好放在一个结构体变量Param里,后面对比方案时改参数非常方便。

4.2 独立运营模型的代码骨架:先看它缺了什么

独立运营模型可以拆成三个独立子问题。风电子问题只优化风电容量,光伏子问题只优化光伏容量,储能子问题则在一组预设的风光容量下优化储能功率和容量。典型的储能独立优化代码如下:

% 储能独立优化示例 cap_p = sdpvar(1); cap_e = sdpvar(1); P_ch = sdpvar(1,T); P_dis = sdpvar(1,T); SOC = sdpvar(1,T+1); u_ch = binvar(1,T); u_dis = binvar(1,T); Constraints = []; for t = 1:T Constraints = [Constraints, P_ch(t) >= 0, P_ch(t) <= cap_p*u_ch(t)]; Constraints = [Constraints, P_dis(t) >= 0, P_dis(t) <= cap_p*u_dis(t)]; Constraints = [Constraints, u_ch(t) + u_dis(t) <= 1]; Constraints = [Constraints, SOC(t+1) == SOC(t) + eta_ch*P_ch(t)*dt - P_dis(t)/eta_dis*dt]; end Constraints = [Constraints, SOC(1) == 0.2*cap_e, SOC(T+1) == 0.2*cap_e]; Constraints = [Constraints, 0.2*cap_e <= SOC, SOC <= cap_e]; Objective = c_p*cap_p + c_e*cap_e + sum(om_ess*(P_ch + P_dis)*dt);

这段代码的问题在于:储能不知道风电和光伏的具体出力曲线,也不知道负荷和电价的全貌,它只能在自己“削峰填谷”的独立目标下选择容量,逻辑上天然缺失了“多存一单位风电可以少弃多少电”这类协同价值。

4.3 联合优化模型的构建:把容量层和运行层一起声明

联合优化模型只需要一个优化问题,把所有容量变量和运行变量放进去。核心代码框架如下:

T = 24; dt = 1; cap_wt = sdpvar(1); cap_pv = sdpvar(1); cap_p = sdpvar(1); cap_e = sdpvar(1); P_wt = sdpvar(1,T); P_pv = sdpvar(1,T); curtail_wt = sdpvar(1,T); curtail_pv = sdpvar(1,T); P_ch = sdpvar(1,T); P_dis = sdpvar(1,T); P_buy = sdpvar(1,T); P_sell = sdpvar(1,T); SOC = sdpvar(1,T+1); u_ch = binvar(1,T); u_dis = binvar(1,T); y_import = binvar(1,T); Constraints = []; % 功率平衡 for t = 1:T Constraints = [Constraints, P_wt(t)+P_pv(t)+P_dis(t)+P_buy(t) == ... P_load(t)+P_ch(t)+P_sell(t)+curtail_wt(t)+curtail_pv(t)]; % 新能源实际出力上限 Constraints = [Constraints, P_wt(t)+curtail_wt(t) == cap_wt*wind_cf(t)]; Constraints = [Constraints, P_pv(t)+curtail_pv(t) == cap_pv*pv_cf(t)]; % 储能充放电互斥 Constraints = [Constraints, P_ch(t) >= 0, P_ch(t) <= cap_p*u_ch(t)]; Constraints = [Constraints, P_dis(t) >= 0, P_dis(t) <= cap_p*u_dis(t)]; Constraints = [Constraints, u_ch(t)+u_dis(t) <= 1]; % 储能SOC Constraints = [Constraints, SOC(t+1) == SOC(t) + eta_ch*P_ch(t)*dt - P_dis(t)/eta_dis*dt]; % 购售电互斥和联络线限制 Constraints = [Constraints, P_buy(t) >= 0, P_buy(t) <= P_line_max*y_import(t)]; Constraints = [Constraints, P_sell(t) >= 0, P_sell(t) <= P_line_max*(1-y_import(t))]; end % SOC边界和初末约束 Constraints = [Constraints, 0.2*cap_e <= SOC(1:T), SOC(1:T) <= cap_e]; Constraints = [Constraints, SOC(1) == 0.2*cap_e, SOC(T+1) == 0.2*cap_e]; % 目标函数 Objective = c_wt*cap_wt + c_pv*cap_pv + c_p*cap_p + c_e*cap_e + ... sum(om_wt*P_wt + om_pv*P_pv + om_ess*(P_ch+P_dis))*dt + ... sum(buy_price.*P_buy - sell_price.*P_sell)*dt; ops = sdpsettings('solver','gurobi','verbose',2,'gurobi.MIPGap',0.01); optimize(Constraints, Objective, ops);

这段代码有几个关键点。第一,P_wt和P_pv必须是连续变量,但它们的上限由cap_wt和wind_cf的乘积决定,这样就把容量变量和运行变量耦合成整体。第二,y_import是购售电互斥的0-1标志变量,比引入两个互斥约束更直观。第三,SOC(1)和SOC(T+1)的初末约束是必须的,否则模型会在一个调度周期内把储能能量“透支”光,配置结果会失真。第四,目标函数里买电和售电使用分时价格向量,直接用矩阵乘法代替循环,在Matlab里更快。

4.4 调用Gurobi求解与结果后处理

求解完用value()取出变量值,然后做后处理。后处理至少要输出:装机容量表、典型日运行曲线、年化成本分项、弃风弃光率、储能循环次数、最大购电功率等。如果是报告,我还会专门生成一张“储能SOC曲线叠加负荷曲线”的图,评审专家最喜欢看这种图,直观说明储能有没有干活。

Gurobi求解器本身会输出大量日志,包括节点数、上下界、MIP Gap。在sdpsettings里设置'gurobi.MIPGap',0.01可以让求解器在相对最优间隙低于1%时提前停止,显著减少求解时间。如果模型很大,还可以设置'gurobi.TimeLimit',300。

5. 结果对比与灵敏度分析:联合优化收益到底从哪里来

5.1 独立结果与联合结果会差多少

我拿一个典型园区数据进行过对比。负荷峰值2MW,日用电量2.8万kWh,工业分时电价峰平谷价差约0.8元/kWh,储能单位投资成本按1200元/kWh计算。独立运营模型给出的配置大约为:风电800kW、光伏600kW、储能300kW/600kWh,年化总成本约320万元,弃风弃光率约18%。联合优化模型给出的配置大约为:风电650kW、光伏750kW、储能400kW/800kWh,年化总成本约275万元,弃风弃光率降到7%。

这不是说联合优化一定会同时减少风电、增加光伏,趋势取决于是晚间还是午间缺电、哪个地方光照资源更好。关键在于:独立运营里风电和光伏都在争抢“负荷满足率”这个指标,储能则是事后补救;联合优化里储能既帮风电消纳也帮光伏消纳,它会在充放策略上自动平衡两边的边际价值。最终结果是风光总容量更低,但发电利用率更高。

5.2 把收益拆开看:储能消纳了多少弃电,又转移了多少负荷

联合优化收益可以拆成三个渠道。第一是峰谷套利收益,储能低谷买电、高峰放电,直接降低购电成本。第二是新能源消纳收益,储能把午间低价甚至负价格的光伏电存起来,晚上释放,减少了弃电损失。第三是容量替代收益,因为储能优化了负荷曲线,风电和光伏的实际利用率提高,等量新能源装机提供了更多有效电量,因此不需要装那么多风机或光伏也能满足负荷。

在报告里做敏感性分析时,我喜欢把储能目标函数中的“边际价值”单独输出,也就是储能每增加1kWh容量,对系统年化成本的降低值。如果这个值接近储能边际投资成本,说明储能配置到了经济平衡点;如果远小于投资成本,说明储能可能配多了。

5.3 灵敏度分析:峰谷价差、储能成本、新能源渗透率

三个参数最值得做灵敏度分析。第一个是峰谷价差:价差从0.5元/kWh拉大到1.2元/kWh时,储能最优容量往往成倍增长,但增长不是线性的,因为储能容量越增加,边际套利小时数越少。第二个是储能单位成本:成本从1500元/kWh降到800元/kWh时,不仅储能容量上升,风电和光伏的配比也会变化,可能允许更多弃电但总成本更低。第三个是可再生能源渗透率约束:如果规定园区可再生能源电量占比不能低于40%,联合优化会自动增加新能源装机并同步增加储能,用来平抑新能源波动;但渗透率超过某个阈值后,边际成本会快速上升。

灵敏度分析还有一个作用:检验模型的鲁棒性。如果改变某个参数后,容量配置结果出现剧烈跳变,往往说明模型少了约束或者目标函数里某些惩罚项系数不合理,需要回头检查。

6. 建模过程中容易踩的坑与调优经验

6.1 时间粒度和典型日缩聚怎么选才不会被骂

很多新手一上来就用全年8760小时数据建模,结果变量爆炸、求解时间变成几个小时,最后还要为报告圆场。更合理的做法是用K-means聚类把全年缩聚成4~8个典型日,并为每个典型日赋予权重。但聚类时要注意:不能只看负荷和新能源出力的平均值,要把极端场景单独保留,比如全年最大负荷日、新能源出力最低的连续阴雨天、以及电价尖峰出现最多的那一天。如果不保留极端场景,规划出来的容量很可能在峰荷时力不从心。

6.2 储能SOC初末约束:最容易被忽略、又最容易导致结果虚构

有些代码不加SOC初末一致约束,结果优化器会让储能从0开始,在一个周期结束时刚好放完,甚至实现“凭空发电”。这相当于给了系统一笔免费初始电量,储能投资回报会虚高。我的习惯是令SOC(1)=SOC(T+1)=0.2*cap_e,这样即使模型有误差,也不会影响储能的有效容量。SOC下限设在0.1~0.2而不是0,是为了保护电池寿命,同时也让模型更贴近实际运行策略。

6.3 大M法和二进制变量有关的数值陷阱

购售电互斥约束P_buy ≤ P_line_maxy和P_sell ≤ P_line_max(1-y),这里P_line_max就是大M。如果最大联络线功率是10MW,而其他变量单位是kW,会出现数量级差10^3,Gurobi求解时容易警告数值问题。解决方案是统一单位,或者把大M尽量缩小到“该变量在可行域内能被观测到的最大合理值”。另一个常见错误是给0-1变量和连续变量乘上超大系数,比如投资成本是百万元,运行成本是元,目标函数里数值量级差10^6,求解器精度会退化,最好都统一到万元。

6.4 Gurobi求解参数:别一上来就默认设置

默认参数能解小规模问题,但对规模较大的微电网规划模型,建议调整几个参数。MIPGap是最常用的,设置0.01或0.005,代表允许1%或0.5%的相对最优性间隙,节省大量时间。TimeLimit设置300秒或600秒,防止求解器陷入无穷节点。还有方法的调整:如果模型主要是MILP,用默认的branch-and-cut即可;如果线性松弛很少,可以尝试设置MIPFocus为2。不过这些参数都依赖具体模型,我在每个项目里都会做一个小实验,先在3个典型日上测试不同参数组合,再放到完整模型上跑。

6.5 独立运营和联合优化代码之间怎么复用

从独立模型改成联合模型,尽量把参数、约束函数、求解后处理拆成三个独立函数。我会写一个getData()读入所有参数,一个buildConstraints()通过开关控制是否启用某个模块,比如flagIndependent=1时只生成风电子问题的SOC约束和功率平衡,flagIndependent=0时生成完整联合约束。这样对比方案时只需要切换开关,不需要复制粘贴大段代码,也能减少因为两套代码不一致导致的低级错误。

6.6 汇报结果时最重要的两张图

规划项目交付时,数据表格再多都不如图直观。我通常只画两张图:一张是联合优化后的典型日调度曲线,横轴是24小时,图上包含负荷曲线、风电出力、光伏出力、储能SOC、购电功率;另一张是成本分摊堆叠柱状图,柱状图按典型日权重展开,能清楚看到投资成本、购电成本、售电收益、运维成本各占多少。这两张图一放,评审基本不会再追问“设备配置是否合理”,因为运行层面的电力平衡已经一目了然。

踩过几次坑之后,我现在做园区微电网规划都会坚持一个习惯:先搭联合优化模型的骨架,用最粗的时间粒度和最小的典型日集合跑通,再逐步加约束、缩聚典型日。每加一个约束,就重新对比一次独立运营与联合优化的结果差异。如果差异突然变大,不是模型出bug就是遗漏了重要边界条件。规划模型不怕复杂,怕的是每个模块单独看都对,放在一起却总给不出一个能落地的容量配置。把“独立运营”和“联合优化”放进同一个代码框架里反复对比,是让我快速发现问题、又能在截止日期前交付可靠方案的最好办法。

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

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

立即咨询