
做微电网调度这几年我最大的感受是模型本身并不复杂真正卡住人的往往是那些“看似不起眼”的细节——储能SOC到底怎么更新才不会越界可转移负荷的总量守恒约束怎么加才不至于让模型直接无解风光出力的预测曲线到底怎么处理才能避免调度结果“只存在于论文里”如果你也在纠结这些问题那这篇笔记应该能帮到你。这篇文章围绕“基于风光储能和需求响应的微电网日前经济调度”把Matlab代码实现从建模思路、约束推导到排错经验完整过一遍。内容按实战来写适合正在做微电网调度、综合能源系统优化、或者毕业设计需要复现同类代码的同学参考。我会把每一步为什么这么设计、约束为什么这么写、代码哪里容易爆雷都讲清楚。1. 日前经济调度的整体思路拆解1.1 这个调度问题到底在“优化”什么微电网日前经济调度说白了就是提前一天把第二天的运行计划定下来每个时段光伏出多少、风机出多少、储能是充电还是放电、跟大电网买多少电、哪些柔性负荷可以挪一挪位置。目标只有一个——在满足用电需求的前提下让第二天的总运行成本尽可能低。但这里有个容易被忽略的点这是“日前”调度不是“实时”调度。日前意味着我们用的是预测数据比如明天的光伏预测曲线、风电预测曲线、负荷预测曲线。既然是预测就一定有误差所以调度结果必须留有余地。这也是为什么很多代码里会加旋转备用约束——就是为了兜住预测误差带来的风险。比如某时段预测光伏出力是100kW但实际可能只有80kW那储能和主网购电就得有足够的上调空间把这20kW补上。从数学上讲这是一个带约束的优化问题。决策变量包括储能各时段充放电功率、与主网的交换功率、可转移负荷的调整量、可中断负荷的削减量等。目标函数是所有成本项的总和最小化。约束条件包括功率平衡、储能状态约束、负荷调整边界、电网交互上下限等。整套东西形成一个标准的线性规划(LP)或者混合整数线性规划(MILP)求解器解出来的就是最优调度方案。1.2 为什么风光储和需求响应要放在一个框架里看先说储能。光伏夜间出力为零风电又有很强的间歇性如果光靠预测曲线来安排运行出力波动一大电网就得频繁调峰。储能的角色是“缓冲池”白天光伏大发时把多余电量存起来晚高峰时再放出去既减少了向大电网的购电费用也平抑了功率波动。再说需求响应。储能只能解决“电量平移”的问题但负荷本身如果也具备调节能力整个系统的灵活性就会上一个台阶。需求响应一般分两类一类是可转移负荷比如洗衣机、蓄热式电采暖、工业中的可间歇流程这类负荷的特点是总量不变但可以在时间轴上平移另一类是可中断负荷比如非关键辅助设备这类负荷响应速度最快但削减的是真实用电量需要支付补偿费用。所以整体框架是这样的风光提供“便宜但随机”的电量储能提供“双向调节”的能力需求响应提供“负荷侧配合”的余量再加上与主网的交易作为最终兜底手段。四者配合才能在成本和可靠性之间找到平衡点。这也是为什么做日前调度时不能单独看某一类资源必须把它们统一进一个优化模型里协同求解。1.3 用Matlab实现时的技术路线选型Matlab里做优化调度主流的方案有这么几条一是用自带的linprog线性规划或intlinprog混合整数线性规划函数。优点是零安装成本Matlab自带缺点是建模自由度低约束一多代码就变得很长且难调试。二是用Yalmip工具箱建模后端接Cplex、Gurobi这类商业求解器或者Matlab自带的求解器。这是目前科研和工程里最常见的组合。Yalmip的建模语法和数学表达式几乎一一对应比如约束条件直接用“”写进去目标函数直接用表达式加和代码可读性非常高。缺点是Cplex和Gurbi是商业软件需要额外安装授权。三是用Matlab的Optimization Toolbox里的prob2struct等结构化建模方式或者用CVX等凸优化工具箱。这些各有适用场景但对于标准的微电网日前经济调度我个人的建议是直接用Yalmip作为建模层求解器优先接Cplex因为CPLEX处理MILP非常稳定。实在没有Cplex用Matlab自带的intlinprog也能跑对于维度不大的微电网问题比如24时段完全够用。选型时要考虑一个实际问题如果要做的是可转移负荷意味着要引入0-1变量判断负荷是否发生转移整个问题就从LP变成了MILP。MILP的求解复杂度和决策变量数量直接相关如果调度时段是96点15分钟一个点0-1变量的数量会明显膨胀这时候求解器的性能差异就很明显了。2. 核心环节解析与实操要点2.1 目标函数每一项成本都要有来源经济调度的目标函数是整个模型的“指挥棒”每一项成本必须能解释清楚它的实际含义。我见过太多代码把所有成本堆在一起结果某个权重大到失真调度结果完全被该权重带着走。以典型的风光储需求响应系统为例目标函数通常包含这几项购电成本是主项。微电网与主网交换功率时买电需要付费有些场景下允许向主网售电则售电产生收益。这里要注意购电和售电价格往往不同而且分时电价下各时段价格也各不相同。代码用价格向量price_buy和price_sell分别表示各时段购售电价与交换功率相乘再求和。储能折旧成本也常被计入。储能电池每充放一次都会产生损耗简单处理方式是给单位充放电电量一个损耗系数比如每kWh充放电计0.02元折旧费。这样模型不会为了“好玩”频繁充放电而会主动追求有效率地利用储能。需求响应成本是第三项。可转移负荷和可中断负荷都需要给用户补偿。可转移负荷通常按转移电量乘以单位补偿单价计算可中断负荷按削减电量乘以中断补偿单价计算。补偿单价一般低于购电价格否则调用需求响应反而亏钱这在经济上就没有意义了。弃风弃光惩罚也很常见。光伏和风电虽然是免费能源但如果没有储能在场某些时段出力确实用不完直接弃掉反而比让储能充满再放出来更划算。但为了鼓励新能源消纳会给弃风弃光量设一个较大的惩罚系数。这个系数的大小很讲究设得太大模型会不惜成本把电存起来设得太小模型会倾向直接弃电。逐项列出来就是这样成本项计算公式说明购电成本sum(buy_price(t) * P_buy(t) * dt)各时段购电量乘对应电价售电收益sum(sell_price(t) * P_sell(t) * dt)余电上网时产生负成本收益储能损耗sum(deg_rate * (P_dis P_ch) * dt)统一按充放电电量计损耗可转移负荷补偿sum(price_trans * P_shift(t) * dt)转移电量乘补偿单价可中断负荷补偿sum(price_inter * P_inter(t) * dt)削减电量乘补偿单价弃风弃光惩罚sum(penalty * (P_curtail_w P_curtail_pv) * dt)惩罚系数乘弃电量这里有个实操细节所有的能量型变量功率乘以时段步长dt之后才变成电量成本算的是电量和单价的乘积所以dt必须统一。24时段调度dt1小时96时段调度dt0.25小时。这个看起来简单但很多人算出来的成本数字不合理十有八九是dt没乘或者乘错了地方。2.2 功率平衡与储能约束最容易写错的地方功率平衡约束是模型的物理基础每个时段系统内所有电源出力加上购电量必须等于所有负荷加上售电量再加上储能充电功率。写成表达式就是P_pv(t) P_wind(t) P_dis(t) P_buy(t) P_load(t) P_ch(t) P_sell(t) P_shift_in(t) P_inter(t)这里需要特别注意P_shift_in是“转入”的负荷。可转移负荷的本质是负荷在时间上的移动所以如果某时段的负荷因为转移而增加了这个增加量要体现在功率平衡方程式右边。相应地转出负荷的时段负荷会减少。储能约束是个魔鬼细节集中的地方。核心是SOC荷电状态的递推公式SOC(t) SOC(t-1) (eta_ch * P_ch(t) - P_dis(t) / eta_dis) * dt / E_max其中eta_ch和eta_dis是充放电效率E_max是储能额定容量。充放电效率说明充进去1kWh电量实际能存进电池的是eta_ch倍放出1kWh电量电池实际要消耗1/eta_dis倍的内部能量。如果两段效率不统一继续运行几个时段就会出现SOC“越跑越歪”的现象。同时还要约束每个时段的SOC都在安全范围内通常取0.1到0.9之间避免过充过放。另外必须限制充放电功率不超过额定值。我建议加上充放电互斥约束虽然理论上储能同时充电和放电既浪费又不符合设备工作原理但如果不加约束模型在特定电价条件下真有可能钻空子利用充放电同时进行来逃避功率平衡约束或套取收益。加互斥约束的常见做法是引入一个二进制变量充时该变量为1放时为0用大M法把充电功率和放电功率分别限制在0或0。还有一个常被忽略的约束调度周期结束时SOC应该回到初始值附近。这保证储能调度的“可持续性”——如果今天把电放光了明天没法继续用。具体做法是约束SOC(最后一个时段) SOC(初始)或者允许一定范围的偏差。在日前调度中这相当于强制储能完成一个完整的工作循环。2.3 风光出力预测与需求响应参数的设定风光出力在模型里通常处理成不等约束实际出力不能超过预测出力但可以低于预测出力也就是允许弃风弃光。用预测出力作为上限让模型自己决定要不要弃电这样就给调度留出了灵活性。举个实际例子中午光伏大发时如果储能已经满电负荷又不高此时最优解往往是弃掉一部分光伏而不是让主网反向倒送电或让储能过充。需求响应参数设定里面最关键的可转移负荷约束是“总量守恒”sum(P_shift_out(t)) sum(P_shift_in(t))这个约束的意思是转移出去的负荷总量必须等于转移进来的负荷总量可转移负荷只是改变了用电时刻并没有减少总用电量。如果漏掉这条模型就可能“凭空消失”部分负荷结果算出来的成本低得不真实但实际根本做不到。另外还要限制每个时段的转移量上下限不能让负荷在一个时段内疯狂转移否则实际电网无法承受。比较常见的设置是单时段最大转移量不超过该时段原负荷的10%~20%同时设置设备允许转移的时间窗口比如某类负荷只能在9点到17点之间运行。可中断负荷的参数相对简单直接限制单时段削减量上限和全天最大削减次数即可。中断补偿单价通常设得比购电价高一些否则用户没有动力参与但会比储能调度的综合成本低一些这样模型才会优先用储能再用需求响应最后才考虑从主网购电。3. Matlab实现全流程与关键代码拆解3.1 数据准备与参数初始化写代码的第一步是定义所有输入数据。我习惯把参数分成三块系统参数、预测数据、成本参数这样读代码的人一看就明白每个数的用途。下面给出一个参考结构用的是24时段调度。%% 系统参数 dt 1; % 时段步长单位小时 T 24; % 调度时段数 % 储能参数 E_max 500; % 储能额定容量 kWh P_ch_max 100; % 最大充电功率 kW P_dis_max 100; % 最大放电功率 kW eta_ch 0.95; % 充电效率 eta_dis 0.95; % 放电效率 SOC_init 0.2; % SOC初始值 SOC_min 0.1; % SOC下限 SOC_max 0.9; % SOC上限 SOC_end 0.2; % 调度结束SOC目标值 % 需求响应参数 P_shift_max 30; % 单时段最大可转移负荷量 kW price_trans 0.6; % 可转移负荷补偿单价 元/kWh price_inter 0.8; % 可中断负荷补偿单价 元/kWh P_inter_max 50; % 单时段最大可中断量 kW % 电网交互 P_buy_max 300; % 最大购电功率 kW P_sell_max 200; % 最大售电功率 kW % 成本参数 price_buy [0.8*ones(1,8), 1.2*ones(1,4), 0.9*ones(1,4), 1.4*ones(1,4), 0.7*ones(1,4)]; price_sell 0.4 * ones(1, T); % 售电价格通常低于购电价 deg_rate 0.02; % 储能单位充放电损耗成本 元/kWh penalty 1.0; % 弃风弃光惩罚系数 元/kWh这里的分时电价向量是我为示例编的前8小时低谷0.8元随后两个时段1.2元午后0.9元晚高峰1.4元夜间低谷0.7元。实际用的时候要根据当地的电价政策替换。需要特别注意电价向量的长度必须和T一致不然矩阵运算会报错。我调试代码时踩过很多次这种低级坑所以强烈建议在参数定义之后加一行断言检查assert(length(price_buy) T, 电价向量长度必须等于调度时段数);3.2 Yalmip建模与约束构建数据准备好了接下来就是建模。用Yalmip建模的思路非常直白先用sdpvar定义决策变量再写目标函数和约束条件最后调用求解器。决策变量定义如下% 决策变量定义 P_pv sdpvar(1, T); % 光伏实际出力 P_wind 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, T1); % SOCT1是为了方便递推 P_shift_out sdpvar(1, T); % 转出负荷 P_shift_in sdpvar(1, T); % 转入负荷 P_inter sdpvar(1, T); % 可中断负荷削减量 P_curtail sdpvar(1, T); % 弃风弃光总量 u_ch binvar(1, T); % 充电状态0-1变量 u_dis binvar(1, T); % 放电状态0-1变量注意SOC定义成T1个变量索引1对应的初始值已知索引2到T1对应第1到第T个时段末的状态。这样递推时用SOC(t) SOC(t-1) ...的写法非常自然避免数组索引错位的问题。约束条件按组添加Constraints []; % 功率平衡约束 for t 1:T Constraints [Constraints, P_pv(t) P_wind(t) P_dis(t) P_buy(t) ... P_load(t) P_ch(t) P_sell(t) P_shift_in(t) P_inter(t)]; end % 储能SOC递推 Constraints [Constraints, SOC(1) SOC_init]; for t 1:T Constraints [Constraints, SOC(t1) SOC(t) (eta_ch * P_ch(t) - P_dis(t)/eta_dis) * dt / E_max]; Constraints [Constraints, SOC_min SOC(t1) SOC_max]; 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 Constraints [Constraints, SOC(T1) SOC_end]; % 风光出力与弃电约束 for t 1:T Constraints [Constraints, 0 P_pv(t) P_pv_pred(t)]; Constraints [Constraints, 0 P_wind(t) P_wind_pred(t)]; Constraints [Constraints, P_pv(t) P_wind(t) P_curtail(t) P_pv_pred(t) P_wind_pred(t)]; end % 需求响应约束 Constraints [Constraints, sum(P_shift_out) sum(P_shift_in)]; for t 1:T Constraints [Constraints, 0 P_shift_out(t) P_shift_max]; Constraints [Constraints, 0 P_shift_in(t) P_shift_max]; Constraints [Constraints, 0 P_inter(t) P_inter_max]; end % 电网交互约束 for t 1:T Constraints [Constraints, 0 P_buy(t) P_buy_max]; Constraints [Constraints, 0 P_sell(t) P_sell_max]; end这里讲几个关键的建模细节。第一充放电互斥约束我用了两个0-1变量u_ch和u_dis并通过u_ch(t) u_dis(t) 1保证不会同时充放。0-1变量直接参与了功率变量上限的约束这种方式叫做大M法的特殊形式——把功率上限乘以0-1状态变量当状态为0时功率必须为0状态为1时功率上限是额定值。第二SOC初值必须等于SOC_init这是递推的起点。调度结束时约束SOC(T1) SOC_end让储能回到初始状态形成完整循环。有些代码只约束初值不约束终值那末调度结果会出现储能“趁机偷懒”的问题比如最后一个时段把电全部放光虽然降低了当天成本但第二天就没法正常运作了。第三需求响应的总量守恒约束sum(P_shift_out) sum(P_shift_in)是整个模型中最容易出现不可行问题的约束之一。模型不可行通常表现为求解器报“Infeasible problem”或者“No feasible solution found”。原因多半是负荷总量太小转移窗口有限无法满足总量守恒。后续在第4节排错部分会详细展开。目标函数用如下方式定义objective sum(price_buy .* P_buy * dt) - sum(price_sell .* P_sell * dt) ... deg_rate * sum(P_ch P_dis) * dt ... price_trans * sum(P_shift_out) * dt ... price_inter * sum(P_inter) * dt ... penalty * sum(P_curtail) * dt; optimize(Constraints, objective, sdpsettings(solver, cplex, verbose, 1));Yalmip会自动识别问题类型因为模型中有0-1变量它会以MILP的形式交给求解器处理。这里面的核心逻辑是目标函数每一项都是决策变量的线性组合所以整个目标也是线性的。只要约束条件保持线性模型就是一个标准的MILP。3.3 求解结果处理与可视化求解完之后用value命令把所有变量的数值取出来。我发现不少新手会忘记在优化结束后执行value提取直接操作sdpvar对象导致后续的数据绘制全是错的。这一点务必注意。P_pv_opt value(P_pv); P_wind_opt value(P_wind); P_ch_opt value(P_ch); P_dis_opt value(P_dis); P_buy_opt value(P_buy); P_sell_opt value(P_sell); SOC_opt value(SOC); P_shift_out_opt value(P_shift_out); P_shift_in_opt value(P_shift_in); P_inter_opt value(P_inter); P_curtail_opt value(P_curtail);还可以顺手算一下总成本作为经济性指标total_cost value(objective); fprintf(调度总成本%.2f 元\n, total_cost);经典的绘图输出包括电力平衡堆叠图、储能SOC变化曲线、需求响应前后的负荷曲线对比图。下面给一个基础的绘画代码绘制功率平衡和SOCfigure; t 1:T; subplot(2,1,1); bar(t, [P_pv_opt; P_wind_opt; P_dis_opt; P_buy_opt], stacked); hold on; plot(t, P_load P_shift_in_opt P_inter_opt, r-o, LineWidth, 1.5); plot(t, P_load, k--, LineWidth, 1.2); legend(光伏出力,风电出力,储能放电,购电功率,调整后负荷,原始负荷); xlabel(时段/h); ylabel(功率/kW); title(功率平衡与负荷调整); subplot(2,1,2); plot(t, SOC_opt(2:end), b-o, LineWidth, 1.5); xlabel(时段/h); ylabel(SOC); title(储能荷电状态变化); grid on;这个结果图基本可以支撑对调度方案的分析。从堆叠图中可以看到哪些时段光伏和风电大发、哪些时段需要储能放电或者从大电网购电调整后负荷曲线与原始负荷曲线的对比则能直观看出需求响应的削峰填谷效果。3.4 求解器安装与报错排查用Yalmip时经常碰到的问题是求解器没装好或者路径没配好。Yalmip本身只是一个建模层它不求解需要调用后端求解器。装Cplex或Gurobi时要把求解器的bin文件夹路径加入Matlab的路径addpath(C:\Program Files\IBM\ILOG\CPLEX_Studio128\cplex\matlab\x64_win64); savepath;装完之后在Matlab里运行yalmip(clear)再重新optimize确保Yalmip重新加载求解器列表。如果求解器没有正确加载Yalmip会报“No suitable solver found”之类的错误那说明Yalmip根本没检测到Cplex或Gurobi。如果不想装商业求解器也可以直接使用intlinprog作为求解器。Yalmip支持设置solver为intlinprogMatlab自带的优化工具箱就能跑MILP只是求解速度和稳定性稍逊色一些。对于24时段的微电网调度来说intlinprog通常够用。4. 常见问题与排查技巧实录4.1 模型无解不是代码错是约束过紧调度模型最常见的问题是求解器报infeasible。很多新手第一反应是代码写错了但根据我的经验代码本身往往没问题问题出在约束条件过强。最典型的场景就是需求响应的总量守恒约束加进去之后模型立刻无解。为什么会出现这种情况因为如果可转移负荷的比例设得太大但负荷曲线本身的波动又不够大模型找不到足够的低谷时段来安置转移过来的负荷。比如午夜时段负荷本身就只有50kW但你允许单时段转移量最大60kW这就意味着最多只能有50kW负荷能转进来没法满足总量守恒模型直接无解。排查方法也很直接把总量守恒约束注释掉看模型是否还能求解。如果能solve说明问题就出在这个约束上。然后逐步收紧允许转入的负荷上限比如把P_shift_max从60降到40、30直到找到可行域。这里体现出的规律是可转移负荷的最大转移量不是越大越好它受到实际负荷曲线形状的约束。另外也可能是因为奖惩系数矛盾导致目标无界或者约束冲突。比如可中断负荷补偿单价设得比购电价高出很多而负荷削减量又设得很大模型可能倾向于尽量削减负荷来降低成本但负荷削减量过多后功率平衡约束难以满足解空间被挤压。我建议在调试时候加入一个可行性诊断脚本逐个打印各约束的松弛量check(Constraints);Yalmip的check命令会显示每个约束的残差非常高效。哪一行残差特别大哪一行基本就是问题所在。4.2 储能SOC越界效率参数和递推逻辑的坑SOC越界一般表现在求解结果里SOC曲线超出0到1范围或者在某个时段突变到负值。这种问题的根源几乎都在递推公式。最常见的低级错误是dt和E_max的单位不匹配。比如储能容量是500kWh但调度步长是15分钟dt0.25小时如果代码里写成dt1那每次递推的SOC变化量会被夸大4倍SOC很容易冲出上限。这种问题核对了代码往往一眼就能发现但确实容易踩。另外一个常见问题是充放电效率的放置位置。正确的公式是SOC(t1) SOC(t) (eta_ch * P_ch - P_dis / eta_dis) * dt / E_max有些代码把放电效率写成了乘以效率而不是除以效率这会导致放电时SOC下降过快而且整个循环里储能的“有效吞吐量”对不上。检查效率公式有没有写反最直接的办法是做一个手动推演让储能从SOC0.5开始以P_ch100充电1小时如果eta_ch0.95E_max500充完之后SOC应该等于0.50.951001/5000.69。如果代码算出来的不一样那就是公式的问题。还有一种情况是SOC初始值没有赋值。Yalmip的sdpvar默认值不是0而是符号变量如果不约束SOC(1) SOC_init直接参与递推结果会非常荒诞。所以SOC初值约束必须放在递推之前。4.3 求解结果出现同时充放电如果没加互斥约束或者加了但大M系数选得不合适可能出现同一时段充放电功率都为正的结果。很多情况下求解器给出的最优解表面上看起来充放同时进行实际上去核对目标函数值会发现这其实是模型在利用充放电同时发生来“制造”损耗——这在某些设置下会让目标函数变小但物理上是荒唐的。解决方法是严格加上互斥约束。如果担心0-1变量计算量扩大还有个折中办法给同时充放电额外加惩罚比如在目标函数里加一个大系数的P_chP_dis项人为抑制同时充放行为。但这个方法只能算补救最彻底的做法还是用二进制变量做硬约束。4.4 需求响应参数怎么定才合理需求响应参数设不好调度结果容易出现两种极端要么需求响应参与量极小形同虚设要么负荷曲线被“削”得过分平整实际没法执行。先说参与量极小的情况。如果补偿单价远低于低谷电价与高峰电价差模型计算后会发现“把负荷从高峰挪到低谷”省下的电费还抵不上要支付给用户的补偿费自然就不愿意调用需求响应。以我的经验可转移负荷补偿单价应该设置在峰谷价差的50%~70%之间才有合理的调用频率。比如峰谷价差0.6元/kWh补偿单价定在0.3到0.4元/kWh比较合适。再说削得太狠的情况。如果单时段允许的转移量特别大模型很可能会把某个高峰时段的全部负荷都平移到低谷时段造成新的“次高峰”。实际操作中单时段可转移量最好控制在原始负荷的10%~20%以内。同时可转移负荷总量也需要控制在日总负荷的一定比例内比如不超过总用电量的10%。这样需求响应才符合物理可行的调整能力调度结果也才具备指导意义。4.5 求解速度过慢问题规模控制当调度时段为96点甚至更多时MILP的0-1变量数量会剧增导致求解时间过长。有时候跑一个调度要等几十分钟甚至几小时这种体验非常痛苦。可以从几个方向压缩计算量一是减少不必要的0-1变量。比如储能互斥约束可以用互补约束近似处理或者通过目标函数中设惩罚项间接避免同时充放省掉2T个0-1变量。二是缩小决策变量的取值范围。比如将P_buy的搜索上界设为实际最大购电功率避免求解器在宽松范围内盲目搜索。三是调整求解器的容差参数。在sdpsettings中设置cplex的MIP gap比如将相对gap设为0.011%让求解器在解足够接近最优解时就提前终止能大幅度缩短求解时间。对工程应用来说1%的gap远够用了。最后再分享几个实际调试中的小技巧我做这类调度代码调试时习惯先跑一个简化版的模型——把需求响应去掉只保留储能和购电先确认功率平衡和储能递推逻辑没问题然后逐步加入可转移负荷、可中断负荷每加一个模块就重新验算一次可行性。这样出问题时能快速锁定是哪个新加的约束引起的而不是面对一整套复杂模型无从下手。另一个实用习惯是把每一步运算的物理单位在心里过一遍。功率单位是kW乘上时间单位h得到电量单位kWh再除以储能容量单位kWh得到的才是无量纲的SOC变化量。很多奇怪的报错归根到底都是单位不统一闹的。这个项目后续还可以继续扩展比如把确定性日前调度升级为多场景随机优化处理风光出力的不确定性或者引入实时滚动修正形成日前日内两级调度框架再或者把电动汽车充放电也纳入需求响应资源做成车网互动(V2G)的模式。这些都是在现在这个模型框架上自然生长出来的方向代码基础打好了后面加东西会顺畅很多。