资讯动态

微电网日前经济调度Matlab建模与求解实战指南

发布时间:2026/10/15 6:15:37 来源:尧图企业网站定制
微电网经济调度这个话题这几年关注的人越来越多。一方面是多能互补和分布式能源并网的项目在增加另一方面是风光储需求响应这个组合提出来之后很多做研究或者做工程的朋友都想知道日前调度计划到底怎么排、成本能降多少、Matlab里该怎么落地。这篇博文我会把整个思路捋清楚先讲清楚微电网日前经济调度的核心矛盾再把模型里的变量、目标函数、约束条件一项项拆开最后落到Matlab代码的架构和调试技巧上。无论你是刚接触调度的研究生还是在做微电网示范项目的工程师这篇文章都值得收藏慢慢看。1. 微电网日前经济调度到底在解决什么问题1.1 调度的本质在不确定性里做一天的计划日前经济调度的日前两个字指的是提前一天、通常以1小时为时间间隔滚动制定未来24小时各台设备的出力计划。这个计划要回答的问题很朴素明天每个小时光伏发多少、风机发多少、蓄电池充多少放多少、联络线从主网购多少电或者卖多少电、需求响应负荷削减多少才能让一整天的总运行成本最低同时满足供电可靠性。这听起来就是一个优化问题但实际上难点在不确定性。光伏出力受云层影响风速变化更是毫无规律可言负荷本身也有较强的随机波动。你前一天做的计划到了第二天实际运行时可能完全对不上。所以在模型层面最常见的处理是先做确定性日前调度也就是基于光照、风速、负荷的预测曲线把明天当成一个已知的24小时序列来优化后续再通过滚动修正、鲁棒优化或者场景随机规划来兜底不确定性。这不是偷懒而是行业里的通用做法——先解决计划怎么排的问题再去解决计划怎么跟上实际的问题。1.2 各个角色谁花成本、谁省钱微电网里的成员各有各的成本特性这也是为什么调度不是简单地把所有电源加在一起打个包就行。光伏和风电的边际成本几乎为零所以从成本最优的角度看调度策略一定会优先消纳所有可用的风光出力。但风光出力不能随意控制除非你有弃风弃光的条件否则它应该作为基荷被全额吸收。这也是为什么经济调度结果里风光出力曲线一般会贴着预测上限走。储能是微电网中最灵活的缓冲带。它既能当电源放电又能当负荷充电关键约束在于SOC不能越界、充放电功率有上限、一天之内还要尽量回到初始荷电状态否则第二天就没法继续用了。储能本质上不是在发电而是在搬电——把低谷时段便宜的电搬到高峰时段用赚的是峰谷价差。因此储能参与调度时会有一个循环寿命损耗成本一般折算成每次充放电的折旧费否则模型会倾向于无节制地频繁充放现实里电池根本扛不住。需求响应是把负荷侧从被动变主动的机制。分时电价下用户可以在峰时减少用电、把可转移负荷挪到谷时但这需要给用户补偿所以需求响应在目标函数里不是免费资源而是一项有成本的调节手段。它和储能有些功能重叠但时间尺度不同储能能应对分钟级到小时级的波动需求响应更适合小时级以上的负荷转移。联络线是微电网和主网之间的边界。它可以买电也可以卖电买电价格按分时电价卖电价格通常设定为固定上网电价或者较低的反向价格以避免微电网故意在低谷卖高价电套利。很多模型里还会加一个联络线功率上限防止微电网把电网当成一个无限容量的垃圾桶想送多少就送多少。1.3 目标函数和决策变量的一次性梳理从数学角度归纳日前经济调度是一个典型的带约束混合整数规划问题。目标函数包括五个部分向主网购电成本、机组运行维护成本、储能充放电的折旧损耗成本、需求响应补偿成本再减去售电收益。约束条件包括功率平衡约束、各设备出力上下限、储能SOC连续性约束、需求响应容量上下限、联络线功率约束等连续约束以及储能充放电互斥、机组启停状态等整数约束。决策变量分成两类连续变量每小时光伏实际出力、风电实际出力、储能充放电功率、联络线交换功率、可转移负荷量等和整数变量储能充放电状态0-1变量、机组启停0-1变量。因为有了整数变量这个模型是混合整数线性规划只能靠分支定界法这类算法求解规模不大时Matlab的intlinprog或者Yalmip配置Gurobi/Cplex都能吃掉。2. 模型搭建变量、目标、约束怎么一步步写清楚2.1 风光出力的简化处理在实际工程中光伏和风机不是独立的免费电源它们有各自的出力特性约束。光伏出力上限由光照强度和环境温度决定风速出力则受切入风速、额定风速和切出风速三阶段约束。但在日前调度模型里通常不会把风速到功率的完整映射公式写进去因为那是微观选址和功率预测层的事调度层只需要拿到明天24小时的风光预测出力曲线然后把它作为上限约束实际出力不能超过预测值如果允许弃风弃光则实际出力可以小于等于预测值如果要求全额消纳则直接令实际出力等于预测值。在Matlab里这只是一条约束P_pv(t) P_pv_forecast(t)。但我想提醒一个细节如果你在代码里直接写等式约束P_pv(t) P_pv_forecast(t)模型其实就退化成纯功率平衡调度了储能和需求响应的调节空间会变窄。更常见且更实用的写法是允许弃光弃风给实际出力设一个上限让优化器自己去判断要不要全额消纳。因为从经济角度风光边际成本为零优化器大概率会选择全额消纳但当风光过剩、储能已满、联络线已到上限时弃风弃光反而是全局最优选择。2.2 储能的SOC递推关系和充放电互斥储能建模是整个微电网模型中最容易出错的地方也是Matlab代码实现时bug的高发区。SOC荷电状态的递推公式是这样的SOC(t1) SOC(t) (eta_c * P_ch(t) - P_dis(t) / eta_d) * dt / E_max其中eta_c是充电效率eta_d是放电效率E_max是储能额定容量dt是调度时间间隔1小时。这个公式的含义很直观电池里存的电量充电时并不是1度电进去就存了1度电会有充电损耗放电时为了放出1度电电池内部实际要消耗比1度电更多的化学能所以放到SOC变化上要除以放电效率。充放电互斥约束在代码里通常这样写P_ch(t) M * u_ch(t); P_dis(t) M * u_dis(t); u_ch(t) u_dis(t) 1;M是一个大数一般取储能最大充放电功率的几倍即可不需要特别大减少大M数值对求解器的影响。u_ch和u_dis就是0-1整数变量保证同一时刻不能既充电又放电。这里有一个我自己踩过的坑如果把充放电功率上限都设为同一个值但充电和放电的最大功率允许不同那么两条约束的M和上限都要分别设置别取一个统一大M了事否则模型会失真。SOC的边界约束是SOC_min SOC(t) SOC_max一般取0.1到0.9之间。很多刚入门的朋友会把SOC_min设成0在模型里看似可行但实际电池管理系统的最低荷电状态通常不会低于10%到20%因为深度放电会严重损伤电池寿命。我个人的习惯是SOC范围先按0.1到0.9设置留出安全余量后期如果想测试储能容量大小对调度成本的影响再把这个区间放宽重跑几组对比。还有一个容易被忽略的约束调度周期末SOC要回到初始值。也就是SOC(24) SOC(0)。如果你不加这个约束优化器会把储能里的电全部放光因为放电能降低购电成本反正模型只管一天内的成本最小化。这会导致你算出来的成本被低估第二天根本没法按这个SOC继续跑。不少论文里会加这个约束他们的说法是储能日循环约束保证每天的调度计划具有可持续性。实际工程里如果储能确实能跨天调度那么末态SOC可以由第二天的预测来决定但在单日调度模型里加上末态等于初态其实是一种更稳妥的做法。2.3 需求响应可转移负荷和可削减负荷怎么建模需求响应听起来高深但建模的方式其实很直接。常用的两种机制是可转移负荷和可削减负荷。可转移负荷是指那些用电时间可以灵活调整、但总用电量固定的负荷。比如洗衣机的洗涤程序、电动汽车充电、工业上的批次生产设备。这类负荷在模型中的处理方式是每个时刻的可转移负荷功率不应超过它的调度功率上限同时一天内可转移负荷的总用电量必须等于原始电量。后一条约束是关键它保证了需求响应只是搬移用电时间而不是凭空减少用电需求。可削减负荷则是指那些可以直接切除一部分用电量的负荷。比如商业楼宇的空调在尖峰时段削减20%的功率用户会短时间牺牲一点舒适度微电网给用户相应的补偿。这类负荷在模型里允许一天内总用电量减少减少量乘以补偿单价就是需求响应成本。和可转移负荷相比可削减负荷更灵活也更好建模因为不需要总量守恒的约束只需要设置每个时刻可削减量的上下限。在Matlab代码实现里两类需求响应变量都用连续变量表示即可不一定要设置整数变量。原因是用户的负荷响应通常是大规模聚合后的连续调节量粒度已经足够细强行用整数变量反而会让模型求解时间成倍增加。2.4 功率平衡和联络线的完整约束体系功率平衡是电力系统最基本的物理定律在微电网模型里就是任意时刻所有发电功率加上购电功率等于所有负荷用电功率加上充电功率和售电功率。在Matlab里用一行约束代码即可表达P_pv(t) P_wt(t) P_dis(t) P_buy(t) P_load(t) P_load_shift(t) P_ch(t) P_sell(t) P_cut(t);注意这里P_load_shift(t)表示可转移负荷在当前时刻的额外用电量正值表示在原来基础上多用电P_cut(t)表示当前时刻削减的负荷功率。这样写的好处是功率平衡中每个变量都带上了正负方向检查起来一目了然。联络线约束包含三层含义一是交换功率的上下限二是买电和卖电不能同时发生三是购电电价和售电电价通常是不同值。买电卖电互斥在代码里和储能充放电互斥一样也是用0-1变量加大M实现。有几个工程上的细节值得注意分时电价表通常是这样的结构——峰时电价高、谷时电价低、平时段居中售电价格通常比购电价格低否则微电网可以靠低买高卖套利这虽然也是最优解但已经偏离了经济调度的本意在模型里需要明确限制。3. 求解路线与Matlab代码架构设计3.1 为什么首选混合整数线性规划微电网日前经济调度这个模型目标函数是线性的约束也基本都是线性的只有两类非线性因素储能充放电效率导致的乘积项以及机组启停、充放电互斥带来的逻辑判断。前者可以通过分段线性化处理后者则引入整数变量整体就是一个混合整数线性规划。我见过不少初学者一上来就用粒子群算法或者遗传算法去求解理由是微电网问题太复杂需要用智能优化算法找到全局最优——这是误区。对于MILP模型粒子群这类启发式算法不仅求解时间不稳定而且每次结果可能有差异完全无法保证得到全局最优解。相比之下商业求解器Gurobi、Cplex和开源求解器SCIP都内置了分支定界和割平面技术能稳定给出最优解和最优性间隙。对于这个规模的问题——24个时段、几十个连续变量、十几个整数变量Gurobi通常只需要几秒到几十秒就能证明最优性。intlinprog作为Matlab自带的MILP求解器也可以求解中等规模的问题但性能和稳定性比Gurobi差不少。我的建议是优先用Yalmip工具箱统一建模底层求解器可以灵活切换先跑intlinprog验证正确性再切到Gurobi跑大算例。Yalmip最大的优势在于你不需要为了换求解器而改动模型代码只需要ops sdpsettings(solver,gurobi)一行就能切换。3.2 Yalmip工具箱的安装与配置Yalmip不是求解器它只是一个建模层负责把Matlab里的优化表达式翻译成求解器能理解的格式。安装Yalmip很简单去官网下载压缩包解压后把整个文件夹加入Matlab路径然后在命令行输入yalmiptest它会检测你装了哪些求解器。如果你只装了intlinprog它检测到的就是内置的Matlab求解器如果你装了Gurobi它也会显示Gurobi已就绪。Gurobi的安装比Yalmip麻烦一些因为需要申请学术许可或者商业许可。申请学术许可时填学校邮箱和工作单位信息就可以下载安装包后把gurobi.m这个文件名对应版本号改好然后确保Yalmip能找到它。一个常被忽略的问题是Matlab是64位还是32位Gurobi也要装对应版本这个匹配不对的话Yalmip会报求解器初始化失败。如果你不想折腾Gurobi直接用Matlab自带的intlinprog也是可以的。只不过要把建模语言从Yalmip的optimize改成intlinprog需要的矩阵形式。手动转换成向量和矩阵虽然可行但非常繁琐一旦链条长就会有变量对应关系上的错误。所以我更推荐Yalmip这也是目前主流论文中Matlab实现优化问题最常见的选择。3.3 代码的整体模块划分写微电网调度代码不要把所有内容堆在一个脚本里。我习惯的模块划分是这样的data/ # 存储负荷、风光预测、分时电价、储能参数等数据 model/ # 建立目标函数和约束的Yalmip代码 solver/ # 求解设置和求解器调用 result/ # 结果存储和图表输出 main.m # 主程序入口主程序main.m的流程大致是加载数据 → 定义决策变量 → 添加目标函数 → 添加约束 → 求解 → 提取结果 → 画图。这样写的最大好处是你想更换数据文件或者修改某个约束条件时不需要动主程序逻辑只改对应模块即可。而且不同约束条件写在独立的函数里比如constraints_storage.m、constraints_dr.m、constraints_balance.m每一块的检查边界都能做到很清楚。实际上我在带新手做项目的时候发现很多人喜欢把模型分得很碎结果画图时找不到结果存储在哪个变量里。我的建议是变量命名尽量采用统一前缀比如P_pv、P_wt、P_ch、SOC导出结果时用一个result结构体把所有变量存起来画图脚本直接从result里面取数据。这样后期做灵敏度分析或者多方案对比时可以省去大量的调试时间。3.4 从确定性模型到不确定性扩展的思路基础的日前调度模型是确定性的但在实际工程中光靠确定性模型远远不够因为预测误差在真实运行中会实实在在地影响系统。比如你预测明天中午光伏出力是100kW但实际上只有80kW储能和联络线如果没能及时顶上就可能会切负荷。所以很多项目在确定性模型的基础上会做两层升级第一层是滚动修正。日前调度给出下一小时的出力计划然后在运行当天每隔15分钟或1小时根据最新预测重新优化未来几个小时的计划。这叫日前日内双层调度。这种思路实现起来不难只需要把确定性模型的时段缩短然后在一个滚动循环里反复调用求解器。第二层是鲁棒优化或者随机规划。鲁棒优化处理的是最坏情况你设定一个不确定性预算让调度方案在所有可能的风光出力范围内都可行随机规划则是给出一组可能的风光场景每个场景带一个概率用一个两阶段模型同时优化日前计划和实时调整。这两类方法复杂度比确定性模型高不少但从科研角度和工程前瞻性角度都很值得做。4. 关键代码段的逐行解读4.1 数据初始化的正确姿势数据初始化看起来简单但直接影响后续约束建模的正确性。我是这样组织的%% 基础参数 T 24; % 调度总时段数24小时 dt 1; % 时间步长小时 P_load [...]; % 24小时负荷预测kW P_pv_forecast [...]; % 24小时光伏预测kW P_wt_forecast [...]; % 24小时风电预测kW price_buy [...]; % 分时购电电价元/kWh price_sell [...]; % 售电电价通常是固定值 %% 储能参数 E_max 600; % 储能额定容量kWh SOC_init 0.5; % 初始荷电状态0.5表示50% SOC_min 0.1; SOC_max 0.9; P_ch_max 100; % 最大充电功率kW P_dis_max 100; % 最大放电功率kW eta_ch 0.95; % 充电效率 eta_dis 0.95; % 放电效率 cost_battery_per_kwh 0.02; % 储能单位循环损耗成本元/kWh %% 需求响应参数 P_dr_shift_max 30; % 每时段可转移负荷最大功率kW P_dr_cut_max 20; % 每时段可削减负荷最大功率kW price_dr_shift 0.3; % 可转移负荷补偿价格元/kWh price_dr_cut 0.5; % 可削减负荷补偿价格元/kWh这里的单位统一非常重要。有些模型里用kW、kWh、元另一些用MW、MWh、万元如果你混着用求解器不会报错但结果的量级会非常奇怪。我见过有人把功率单位写成kW容量写成MWh最后优化结果储能SOC看起来正常但实际上功率平衡约束根本对不上。全文保持统一的量纲体系这是写这段代码时最高优先级的纪律。4.2 用Yalmip定义决策变量和目标函数定义决策变量时我习惯把连续变量和整数变量分开写这样每一次调试时定位更快P_pv sdpvar(T, 1); % 光伏实际出力kW P_wt sdpvar(T, 1); % 风电实际出力kW P_ch sdpvar(T, 1); % 储能充电功率kW P_dis sdpvar(T, 1); % 储能放电功率kW SOC sdpvar(T1, 1); % 荷电状态维度T1因为包含初始状态 P_buy sdpvar(T, 1); % 购电功率kW P_sell sdpvar(T, 1); % 售电功率kW P_dr_shift sdpvar(T, 1); % 可转移负荷的功率变化量kW P_dr_cut sdpvar(T, 1); % 可削减负荷量kW %% 整数变量储能充放电状态、购售电状态 u_ch binvar(T, 1); u_dis binvar(T, 1); u_buy binvar(T, 1); u_sell binvar(T, 1);目标函数写成这样objective 0; for t 1:T objective objective price_buy(t) * P_buy(t) * dt; % 购电成本 objective objective - price_sell(t) * P_sell(t) * dt; % 售电收益 objective objective cost_battery_per_kwh * (P_ch(t) P_dis(t)) * dt; % 储能折旧成本 objective objective price_dr_shift * abs(P_dr_shift(t)) * dt; % 可转移负荷调度成本 objective objective price_dr_cut * P_dr_cut(t) * dt; % 可削减负荷补偿成本 end这里有两个容易踩的坑。第一个是abs(P_dr_shift(t))因为可转移负荷可以增加用电把负荷搬过来也可以减少用电把负荷搬走所以功率变化量有正有负。如果你直接把它写进目标函数里且单位价格是正的那么优化器会倾向于把可转移负荷变成减少用电的白嫖行为——因为减少用电相当于降低了负荷功率本身就能降低购电成本同时它还获得补偿。但实际上可转移负荷是双向等概率的事件你不能只奖励减少而不惩罚增加。用绝对值函数把它处理成双向调度成本虽然不完美但比直接用线性项合理得多。第二个坑是储能折旧成本项。如果你给它设置得太小优化器会让储能频繁充放赚取峰谷价差但如果设置得太大储能就基本不参与调度了系统的灵活性无从体现。我的经验是先跑一版不带储能的模型得出基准成本再跑带储能模型时把折旧成本从小往大调观察充放次数和系统总成本的变化。这样可以找到一个合理区间。4.3 约束条件逐段添加的实操细节约束添加顺序不直接影响求解结果但影响你排查问题的效率。我习惯按设备约束 → 交互约束 → 平衡约束的顺序添加%% 设备约束储能SOC递推 Constraints []; for t 1:T Constraints [Constraints, SOC(t1) SOC(t) (eta_ch * P_ch(t) - P_dis(t)/eta_dis) * dt / E_max]; end Constraints [Constraints, SOC(1) SOC_init]; Constraints [Constraints, SOC(T1) SOC_init]; % 日循环约束 Constraints [Constraints, SOC_min SOC SOC_max]; %% 储能充放电功率上限和互斥 for t 1:T Constraints [Constraints, 0 P_ch(t) P_ch_max * u_ch(t)]; Constraints [Constraints, 0 P_dis(t) P_dis_max * u_dis(t)]; Constraints [Constraints, u_ch(t) u_dis(t) 1]; end这里有个性能优化技巧如果把0 P_ch(t) P_ch_max * u_ch(t)改成P_ch(t) P_ch_max * u_ch(t)因为你没有给P_ch增加非负约束理论上优化器可以让P_ch变成负值从而绕过约束。所以非负约束是不能少的。但这个写法在Yalmip里是允许的求解器也能正确识别但如果你切换到某些底层求解器可能会遇到约束雅可比矩阵奇异的问题。稳妥的写法是拆成两条Constraints [Constraints, P_ch(t) 0]; Constraints [Constraints, P_ch(t) P_ch_max * u_ch(t)];这样写虽然多了几行代码但兼容性更好也更方便你在调试时单独注释查看某个约束的效果。需求响应约束的写法如下%% 可转移负荷全天净转移量之和为零只搬移不增减 Constraints [Constraints, sum(P_dr_shift) 0]; %% 可转移负荷每时段调节量上下限 Constraints [Constraints, -P_dr_shift_max P_dr_shift P_dr_shift_max]; %% 可削减负荷每时段削减量上下限 Constraints [Constraints, 0 P_dr_cut P_dr_cut_max];还需要加一条可转移负荷的实际效果是改变负荷曲线所以在功率平衡里负荷侧要加上P_dr_shift和减去P_dr_cut。具体写法是for t 1:T Constraints [Constraints, P_pv(t) P_wt(t) P_dis(t) P_buy(t) ... P_load(t) P_dr_shift(t) - P_dr_cut(t) P_ch(t) P_sell(t)]; end这里如果你把P_dr_cut写成加号就相当于削减负荷变成了新增负荷方向就反了优化结果会非常离谱。我调试时经常干这件事所以后来习惯在所有变量名里方向性很强的变量统一带上前缀或者后缀比如P_load_shift表示负荷偏移量P_load_reduce表示负荷削减量避免靠正负号记忆来维持正确性。4.4 功率平衡约束里的小细节功率平衡约束是模型的核心但也最容易出现模型没收敛但也不知道哪里错的情况。第一个常见错误是SOC维度对不上。SOC变量定义成了T1长度功率平衡里使用的是P_ch(t)而不是SOC(t1)-SOC(t)两者在时间尺度上等价但一旦写错索引求解出来SOC曲线会莫名其妙地跳变。第二个常见错误是忽略P_buy和P_sell的互斥。如果你只在目标函数里区分购电和售电价格但没有加互斥约束优化器可能会同时给出正值的P_buy和正值的P_sell两个源都卖电、同时又买电这在实际电网中不可能发生。用一个u_buy和u_sell把两个功率互斥掉或者更简单的方法是把净交换功率定义成一个变量P_exchange当它为正时表示购电为负时表示售电加上分段线性化的价格约束。后者更经济但代码更复杂。我通常先加互斥约束验证正确性后续优化时再考虑合并变量。4.5 求解和结果输出求解部分的代码不难但有几个设置值得调优ops sdpsettings(solver, gurobi, verbose, 2, showprogress, 1); ops.gurobi.MIPGap 0.001; % 设置MIP间隙为0.1% ops.gurobi.TimeLimit 300; % 设置求解时间上限为300秒 sol optimize(Constraints, objective, ops);MIPGap这个参数很关键。默认情况下Gurobi会一直算到证明最优性但对于大规模模型证明最优性本身可能就需要很长时间。你如果在做参数灵敏度分析可以放宽MIPGap到1%甚至5%求解速度能快一个数量级结果差异通常在工程可接受范围内。时间限制参数能防止个别算例卡死尤其是当你把整数变量增加之后分支定界树可能非常大。求解完成后提取结果时要格外检查求解状态if sol.problem 0 P_pv_opt value(P_pv); P_ch_opt value(P_ch); SOC_opt value(SOC); else disp(求解失败请检查约束和参数) return endsol.problem等于0表示求解成功1表示不可行2表示无界3表示求解器报错。这里我强烈建议你养成先检查状态再处理结果的习惯否则如果模型不可行你提取出来的value(P_pv)是一堆NaN画出来的图会让你误以为是数据问题而不是模型问题。5. 调试与避坑从不可行解到求解缓慢5.1 模型不可行时如何十分钟内定位到问题模型不可行是调度代码里最常见的故障。我处理这个问题的标准流程是先检查基本参数有没有明显矛盾SOC范围是否合理、储能容量是否小到连一次充电都放不下、负荷预测和风光预测的量级是否匹配等。再看功率平衡约束。如果可转移负荷的P_dr_shift_max设置得比实际负荷还大那么可转移后负荷为负这件事就可能发生破坏功率平衡约束的可行性。解决方法是把P_dr_shift_max限制在负荷的一定比例内比如不超过该时段负荷的20%。接着检查储能SOC递推的方向。很多人会在P_dis/eta_dis这一项上弄反效率和分母导致SOC一边充电一边往下掉模型当然不可行。自检方法很简单写一个小脚本固定P_ch50P_dis0连续推24小时看SOC是否单调上升再反过来推看是否单调下降。如果方向都不对模型模型必挂。最后一个狠招是把所有约束都注释掉只保留目标函数和变量边界跑一次求初解。然后逐步加约束每次加一组就求解一次直到找到哪组约束让模型不可行。这样虽然繁琐但通常十分钟内就能定位到问题。用过Yalmip的同学可以额外利用sol.info和diagnostics字段打印的更详细报错信息配合relax模式把整数约束松弛掉能更直观找到不满足的连续约束。5.2 求解时间爆炸整数变量才是最贵的同一个模型把所有变量设为连续变量后求解只要0.5秒加上整数变量后可能要10分钟这是非常常见的现象。原因在于分支定界法在最坏情况下需要遍历所有整数组合虽然Gurobi会通过割平面和启发式剪枝大幅减少节点数但如果模型规模大或者整数变量设置不当求解时间会指数增长。优化思路有几个方向。一是减少不必要的整数变量。比如储能充放电互斥如果你的储能是全程要么充电要么放电这种策略性很强的场景就可以用固定时序代替互斥变量把24个互斥约束变成12个或者更少二是添加更多有效约束来收紧可行域。Gurobi一般来说很擅长处理MILP但如果你的模型里有大量大M约束求解器的预求解时间会显著增加尽量把大M设小一点或者改成指标约束。三是考虑把MILP改为线性规划来求解一个近似版本。比如储能充放电互斥如果你允许储能同时充放且净功率为两者之差其实在物理上也不完全错因为实际变流器可以做功率抵消的。只是效率上会有差异。如果只是做方案比较和趋势分析线性松弛版本完全够用。5.3 Yalmip/Gurobi的版本兼容性问题版本兼容性这个问题是很多人在装完工具箱之后第一个碰到的坑。Yalmip对Gurobi版本的支持有滞后性往往新版本的Gurobi出来后旧版Yalmip无法识别。如果你用gerbo求解器去解优化问题时报错Unable to initialize Gurobi或者gurobi: command not found优先检查版本匹配题。一个简单的规避方案是装LTS版本的Gurobi而不是最新版本因为Yalmip对LTS版本的支持通常更好。如果实在不想碰Gurobi推荐试试开源方案。Matlab自带的intlinprog足够验证小型模型更大规模可以用Cbc或SCIP这些求解器Yalmip也都能对接。我在一个模拟项目X中对比过同样模型规模下Gurobi比intlinprog快大约3到5倍但intlinprog最终也能在几分钟内收敛对于教学和验证完全够用。5.4 单位、量纲和数值尺度问题数值尺度问题很少被初学者重视但它在求解器中非常关键。一般优化求解器内部都有预处理过程但如果你的目标函数里有一项是10的6次方量级另一项是10的负2次方量级数值上会给求解器带来极大麻烦。比如购电成本可能是几千元量级储能折旧可能是几块钱量级需求响应补偿可能是几十块钱量级。这些量级差异很大的项出现在同一优化问题里求解器在计算对偶间隙和收敛判据时容易出现误判。解决办法有三种统一使用合适单位比如功率用MW而不是kW费用用万元而不是元这样数值能控制在1到1000的范围内或者对目标函数每一项做一个归一化权重但这种归一化会改变优化问题的经济意义不推荐再或者调整求解器容差参数比如把Gurobi的FeasibilityTol从默认值改成1e-6也能缓解一部分数值问题。我在实际调试时发现调整量纲后效果是最明显的。原来某个算例求解时间要5分钟把kW换成MW后求解时间降到约30秒而且最优性间隙收敛得更平滑。这背后没有复杂的理论就是纯数值问题。6. 从基础模型到进阶扩展的路径6.1 考虑风光不确定性的两阶段随机规划前面的基础模型是确定性模型适合做入门和基准测试。但如果你要做科研或者工程应用风光不确定性是绕不开的坎。两阶段随机规划是比较自然的扩展第一阶段是在知道预测值但不知道真实值的情况下确定日前调度计划比如机组启停、储能充放电计划、可转移负荷的时间安排第二阶段是在给定真实风光出力场景后以最小的调整成本去修正计划比如调整储能功率、购电功率、削减负荷量。在Matlab里实现两阶段模型时我习惯用cell数组存储每个场景的决策变量P_dis_scene cell(N_scene, 1); for s 1:N_scene P_dis_scene{s} sdpvar(T, 1); end第一阶段变量整数变量和可转移负荷的日前计划对所有场景共享第二阶段变量则随场景变化。目标函数是第一阶段的成本加上每个场景概率加权的第二阶段成本。两阶段模型比单阶段模型大N倍但对N取几十个场景时Gurobi依然能在可接受时间内求解。6.2 鲁棒优化的工程意义鲁棒优化更适合对最坏情况有严格要求的场景。假设光伏出力在一定区间内波动目标是最小化最坏情况下的调度成本同时保证每个场景下系统约束都成立。它不会给你一个期望成本最低的方案而是给你一个无论明天实际天气如何系统都能安全运行的方案。实现上有一种比较流行的盒式预算不确定性集合风光出力的每个时段波动范围给定整个调度周期内的总波动量受限。这种集合的好处是参数少、直观、不容易过于保守。鲁棒优化在Matlab里很难手写但Yalmip内置了鲁棒优化的部分支持你可以直接把不确定性变量声明为uncertain类型然后定义它们的范围其余约束语法不变。遇到不方便直接支持的场景可以采用对抗性场景枚举法用一组最恶劣场景代替连续不确定性集合来逼近鲁棒解实现简单且效果不错。6.3 多微电网互联和碳排放约束的扩展思路单微电网模型写通之后往多微电网方向扩展的核心是把功率平衡约束改成带耦合项的公式每个微电网内部的平衡依然成立但微电网之间增加交换功率变量并在全网层面增加交换功率互斥和容量约束。在Matlab里用三维索引的sdpvar变量来存储多微电网的交换功率是比较直观的做法。碳排放约束近年越来越受关注。标准做法是在目标函数中加入碳成本项给火电和购电分别赋予碳排放因子碳排放总量不能超过配额如果超额就需要购买碳排放权。这样模型目标函数就从纯经济成本扩展成经济环境的双目标但求解方式没有本质变化。如果想认真研究碳约束的影响我建议先跑一遍无碳约束基准模型然后把碳配额从大往小扫一遍记录总成本和碳排放量的变化关系画成曲线比单点结论直观得多。6.4 代码复用的工程经验很多人写完一套调度代码下一套模型又从头开始写。这是个常见问题。我建议从一开始就按照参数结构体 约束函数 求解封装的方式组织代码把参数统一放进结构体params.T 24; params.E_max 600; params.P_ch_max 100; params.price_buy [...];约束函数只接受params和决策变量返回Constraints。这样一个函数跑到另一个项目时只需要改params不用改模型主体。我自己的代码库里storage_constraints.m、balance_constraints.m、dr_constraints.m这三个函数被多个项目反复复用稳定性和可维护性都远高于每次重新写一遍的方式。7. 调度结果的分析与可视化7.1 结果表格应该展示哪些关键指标调度跑完之后不能只看一张曲线图就完事要形成系统性的指标表。我习惯输出这样一张表指标数值说明日运行总成本元目标函数值最终判定调度的好坏购电成本元从主网购电的总支出售电收益元向主网售电的总收入储能折旧成本元储能损耗折算出的总成本需求响应补偿成本元两个DR项的总补偿费用峰时段最大购电功率kW考核联络线容量是否够用弃风弃光总量kWh风光削减的总电量SOC最低值-储能运行最极限的点这张表有两个地方值得关注一是购电成本和售电收益的差代表了微电网和主网打交道时的净支出二是弃风弃光总量如果这个数值大意味着你的储能容量或者联络线容量可能不够或者储能的功率上限限制了充入的速度。如果你优化出来的SOC在全时段几乎都是贴着0.1在跑说明你的储能折旧成本可能设置得太高了它只想用最低电量根本不想充电。这时候把折旧成本降一个数量级再对比一次结果你会看到SOC明显变得更加活跃。7.2 图的布局功率平衡图和SOC曲线可视化方面我推荐最少画两张图一张是24小时的功率平衡堆叠图一张是SOC和购售电功率的双轴曲线图。功率平衡堆叠图用bar或者area展示光伏出力、风电出力、储能放电、购电功率的堆叠叠起来的高度正好等于负荷加储能充电。如果面积图上有明显的凹陷或溢出说明功率平衡约束被破坏这通常会暴露你在约束里写反了符号的问题。SOC图用stairs画阶梯状曲线更符合时段离散的物理意义别用连续平滑的plot因为SOC是按小时递推的楼散会让读者误以为SOC连续变化。配合右侧Y轴画购电功率曲线可以很直观地看到储能谷充峰放的行为购电功率低谷时段SOC上升购电功率高峰时段SOC下降。如果SOC的谷值和峰值出现在错误的时间段你就要回过头去检查分时电价表的时段划分有没有设置反。7.3 对比实验怎么证明需求响应真正降低了成本写论文、做项目汇报需求响应有没有用是最常被问到的问题。最好的回答方式是给出对比实验数据用同一套系统和同一份负荷、风光数据分别跑三组配置——无储能无需求响应、仅有储能、储能加需求响应然后对比总成本。我按这个方法做过一次模拟项目X结果非常典型无储能无DR时总成本最高纯靠固定联络线功率追随负荷峰时不得不高价购电加入储能后借助峰谷价差降低了一部分成本再加入需求响应后成本进一步下降原因不仅是负荷被搬移到谷时还因为需求响应让储能能够在更合适的时段运行。这里我建议大家做分析时不仅关注总成本数值也关注需求响应的净收益——DR的总补偿成本是多少DR帮助系统省了多少钱差额才是DR的真实贡献。有些时候需求响应看似降低了总成本但补偿成本本身很高真实收益为负。这在工程上也是很重要的发现。8. 进阶调参和灵敏度分析的实战经验8.1 储能容量和成本的关系怎么评估做微电网规划时储能容量往往是一个有待决策的量。你可以在调度模型外面套一层容量扫描从100 kWh到1000 kWh每次增加50 kWh分别运行一遍调度模型记录总成本和总弃风弃光量。把结果画成曲线后你会看到一个典型的边际收益递减过程储能从100加到300 kWh时成本下降非常明显从500加到800 kWh时曲线趋于平坦。这种曲线对投资决策很有参考价值——选择拐点附近的容量往往性价比最高。我见过很多项目直接按最大负荷的20%来配储能这个粗略公式在部分场景下并不合理还是应该根据当地峰谷价差和负荷特性做灵敏度扫描再拍板。8.2 分时电价对调度行为的影响分时电价是微电网外部最重要的经济信号。我做过一组测试改变峰谷价差大小从2倍到5倍观察储能利用率的变化。价差小时储能几乎没有充放行为因为折旧成本比套利收益还高价差拉大到3倍以后储能开始谷充峰放到了5倍SOC曲线几乎天天走满整个运行区间。这个测试还有一个重要的实用角度如果当地实际峰谷价差不大那么投资储能的经济性就存疑用需求响应或者直接调峰可能是更划算的选项。调度模型在这里不只是排计划它还可以帮决策者回答配置多少储能、配什么类型的响应资源这类规划问题。8.3 联络线功率上限的影响联络线功率上限直接决定了微电网在高峰时段能依赖主网到什么程度。上限设得很小时微电网基本要自给自足建模结果里会看到更多的储能放电和需求响应调用上限设得很大时微电网倾向于直接从主网购电因为主网电价在谷时段非常便宜。我在调试时发现如果把联络线功率上限设置得太低模型有时候会得到弃风这样看起来很不合理的解——原因很简单电力送不出去储能容量不够负荷也消纳不了那么多只能弃掉。这种结果其实不是模型错了而是系统物理能力的真实体现。在汇报时可以把这条曲线加进灵敏度分析直观说明微电网对外部电网的依赖程度。8.4 需求响应补偿价格的边界怎么找需求响应补偿价格直接影响DR参与度。如果把补偿单价设得非常高模型会把所有可转移负荷都搬到谷时段下达所有可削减负荷都削减掉。这样虽然总成本表格好看但用户舒适度大打折扣实际上未必可行。好的做法是设置一个DR预算上限比如全天的DR补偿总额不能超过某个值或者给DR设置参与率上限。也可以用多目标分析来看成本-舒适度之间的权衡从补偿单价0.1元到1.0元之间扫描你会看到DR调用量的S形增长曲线。价格低时用户不愿响应价格高时响应饱和中间过渡带的斜率就是最佳补偿定价区间。这条经验在实际的微电网负荷聚合商谈判中非常有用——你给的补偿单价不用最高在S形曲线拐点附近就已经能获得大部分DR收益。9. 最后的几点实操心得我在多个微电网项目里用过这套模型——包括模拟项目X仿真数据和实测数据都验证过。个人最大的体会是模型本身的数学形式并不复杂真正难的是把你的物理直觉转写成可求解的约束和合理的目标函数。很多初学者拒绝了需求响应是因为搞不定abs函数、搞不定SOC日循环约束很多工程师放弃了可转移负荷是因为不知道它的总量守恒到底约束在哪一行。这些细节一旦打通整个调度模型就活了。上面这些技巧和踩坑记录是我在一次次的模型不可行和求解不收敛中折腾出来的。如果你们在自己的代码里遇到一些更特殊的问题——比如储能限功率充电、需求响应带有最小连续运行时间约束、联络线功率有爬坡限制等等这些扩展在Yalmip里其实也都有成熟写法主要思路就是加新的约束和时间耦合条件。微电网经济调度的核心逻辑一通后面做多微电网、多能互补、碳交易约束的扩展都只是工作量问题不是思维问题。另外建议你动手敲代码时别贪多把最基础的一天24小时模型完整跑通、调试到结果合理再考虑加场景、加不确定性、加碳成本。这个领域没有捷径但每一步走得稳后边的路就能跑得快。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑