风光场景生成与自适应遗传算法在IEEE33配电网EV调度中的应用
1. 这个项目到底在解决什么问题1.1 风、光、EV一起并网配置难在哪做配电网优化调度的同行应该都遇到过类似的感受单看风电或光伏接入问题还好建模无非是多加几个随机变量再加一个电动汽车集群整个问题的性质就变了。因为风电和光伏出力是看天吃饭的随机量而电动汽车是看人吃饭的可移动负荷充电时间、充电功率、剩余电量、出行规律全部不确定。两者叠加在一个IEEE33节点的配电网里你要做的不是在确定性条件下求一个最优解而是要在几十上百种可能的风光出力EV行为组合下找到一组能兼顾经济性和电压质量的调度策略。我做这个项目时的第一反应是能不能用确定性规划硬解把风电、光伏当成恒功率注入EV当成固定负荷跑一次最优潮流就能出结果。算完一看网损和电压都挺理想但心里不踏实。因为风光出力的方差很大午间光伏大发、夜间风电爬坡、早高峰EV集中充电这些工况切换非常剧烈。你把所有随机量压成一个平均值算出来的调度方案在实际运行中几乎必然越限。这就是为什么要做风光场景生成——你必须把不确定性显式地放进优化模型里。又有人会问既然有随机量直接用鲁棒优化不是更保守没错鲁棒优化适合对安全性要求极高的场景但它通常给出的是最坏情况下的可行解经济性往往偏保守。而场景法可以兼顾概率信息和经济性每个场景代表一类典型出力曲线赋予一个发生概率优化时对不同场景的目标加权求和。这样得到的调度策略不是针对某个时刻的最优而是长期期望意义下的最优。1.2 论文复现和实际做项目思路不同这里顺便聊聊论文复现和实际工程项目的区别。如果纯粹为了复现某篇论文核心是跑通流程、对得上指标——你要复现的往往是一个已经被验证过的算法框架参数甚至可以直接抄论文。但如果你想把这套方法用起来比如给自己的配网做日前调度方案就要考虑很多论文里没写的事情风光数据从哪来、EV充电行为怎么假设、场景削减到什么规模计算才扛得住、IEEE33节点的负荷基准值要不要按比例缩放、联络开关是否参与优化等等。这篇博文我按照项目实践的路线来写也就是带着大家把整个链路走一遍从风光出力的物理模型到场景生成与削减到自适应遗传算法的设计与代码实现再到IEEE33节点算例的搭建与结果分析。适合电气工程方向的研究生、做配电网优化调度相关工作的工程师以及刚入门想找一个完整Matlab代码框架来改造的开发者。看完之后你拿到的不是一个黑盒代码而是一套可以自己改模型、换参数、加约束的方法论。2. 风光出力不确定性建模从随机性到可计算的场景集2.1 风速和光照怎么变成风电、光伏出力先把时空尺度统一到24小时日前调度把一天按1小时间隔切成24个时段每个时段内的风光出力认为是常数时段间的变化由场景来描述。风电出力由风速决定风速在统计上通常用威布尔分布描述。概率密度函数写成f(v) (k/c) · (v/c)^(k-1) · exp(-(v/c)^k)其中v是风速k是形状参数一般取2左右也叫瑞利分布c是尺度参数和平均风速直接相关。有了风速再通过风机的功率特性曲线折算成电功率。典型的折算规则是分三段风速低于切入风速v_cin或者高于切出风速v_co时出力为0风速在v_cin和额定风速v_rated之间时出力按线性插值从0升到额定功率P_r风速在v_rated和v_co之间时出力等于额定功率P_r。这三段规则虽然简单但求出来的功率曲线形状跟实测数据对得上工程上完全够用。我在项目里取切入风速3m/s、额定风速12.6m/s、切出风速25m/s单台风机额定功率按0.5MW折算再按接入台数放大到IEEE33节点对应的渗透率水平。光伏出力走另一套逻辑光照强度用贝塔分布描述然后乘上光电转换效率、阵列面积和额定容量。很多论文直接用P_pv P_rated · (r/R_stc) 来算即光照强度与额定辐照度之比乘上额定功率这个线性近似在光照不太强时误差不大做日前调度够了。如果追求更细致的模拟可以用二次效率模型但那样需要提供温度、辐照修正系数参数不好标定所以我的代码里用的是线性模型。要特别提醒一点风速和光照的时序相关性非常重要。如果你对每个小时独立抽样得到的一条24小时风光曲线会在各个时段随机跳动完全不符合真实的风速惯性变化规律这样的场景代入优化模型会得到没有实际意义的结果。处理办法是直接按连续时段向量做采样——一次采样生成一条24维的时序曲线而不是逐小时独立采样。这样虽然边际分布不完全严格但时序相关性保留得足够好计算代价也小。2.2 蒙特卡洛采样与场景削减生成场景集的通行做法是蒙特卡洛采样用威布尔分布采样24小时风速向量用贝塔分布采样24小时光照强度向量分别折算成功率曲线就得到了一个风光联合场景。重复采样1000次就得到1000个场景。问题来了如果有1000个场景每个场景都要在优化模型里当成约束条件去算那这个优化问题的规模会爆炸。如果优化变量是24小时×EV组数每个场景都要跑一次潮流1000个场景一次适应度计算就要跑1000次潮流遗传算法跑50代、种群40个个体整体计算量是40×50×1000200万次潮流Matlab直接卡死。所以必须做场景削减。我的做法是两步走第一步用K-means聚类把1000个场景聚成K类K一般取3到5个每一类的聚类中心作为这个类的典型场景。第二步把每个类的样本数量除以总样本数得到典型场景的出现概率。这样最终得到的K个典型场景和各自的概率就代替了原始的1000个场景计算复杂度降了两个数量级。如果要做更严格的同步回代削减法forward/backward selection原理是这样计算所有场景两两之间的距离找到距离最近的两个场景把概率较小的那个删除把它的概率加到概率较大的那个上然后重复这个过程直到剩下指定数量的场景。这个方法在理论美感上更优但实现起来要反复计算距离矩阵在1000个24维向量上跑一遍也不算慢。我在代码里默认用的是K-means概率分配简单稳定Matlab的kmeans函数是现成的调用起来非常方便。如果你要复现某篇论文里的同步回代削减自己实现一遍也不难。场景削减后还要检查一件事典型场景的概率之和必须为1每个场景的各时段风电、光伏出力是否还在合理范围内。我见过有人聚类后把概率归一化的时候出错导致优化结果整体偏小或偏大这种低级错误会直接毁掉后面的所有结果。3. 自适应遗传算法它和普通GA差在哪3.1 固定交叉变异概率的麻烦遗传算法本身不复杂编码、初始化种群、算适应度、选择、交叉、变异循环迭代。很多人在项目里直接用固定交叉概率比如0.8和固定变异概率比如0.05跑下来发现要么收敛太慢、要么陷入局部最优。问题在于遗传算法在不同进化阶段的需求是不同的。种群初期个体差异大多样性充足这时候应该让交叉概率大一些加速优良基因的组合与搜索同时让变异概率小一些避免破坏太多已发现的优良模式。到了进化后期种群趋于一致大家长得都差不多交叉产生的后代大同小异这时候真正能跳出局部最优的是变异——所以变异概率应该增大交叉概率应该适当降低。固定的概率无法反映这种动态需求于是自适应就来了。自适应遗传算法的核心思想就一句话根据种群的收敛状态或个体的适应度水平动态调整交叉概率和变异概率。实现方式有很多种我实际用下来最有效的是基于种群适应度方差的动态调节。具体思路是每一代计算种群所有个体适应度的标准差sigma再和上一代的sigma做对比。如果sigma明显变小说明种群多样性在快速下降要防止早熟——此时提高变异概率、适当降低交叉概率如果sigma变大说明种群还在充分探索可以维持较大的交叉概率和较小的变异概率。归一化之后交叉概率在0.6到0.9之间浮动变异概率在0.01到0.2之间浮动。另一个常见的做法是基于个体适应度做调节。经典自适应公式是当某个个体的适应度高于种群平均值时给它较小的交叉概率和变异概率保护优秀个体低于平均值时给它较大的概率促进探索。这种策略我也试过优点是能保护精英个体缺点是低于平均值的个体概率被拉得过高种群容易被搅乱收敛曲线波动很大。所以在我的代码里我做了折中精英个体保留前2个不参与交叉变异其余个体在种群方差策略下统一使用动态概率而不是逐个体设定概率。3.2 自适应策略的具体实现方式用一张表把我试验过的两种方案放在一起比较方案调节依据优点缺点适用情况个体适应度法每个个体适应度与平均值的关系保护优秀个体种群震荡大、易不收敛小规模简单问题种群方差法全体适应度标准差或方差收敛平稳、全局搜索均衡参数需要调下限中大规模组合优化混合策略法精英保留种群方差兼顾稳定性和探索性实现稍复杂本文推荐在Matlab里实现混合策略时我一共写了三个关键步骤每一代先按适应度排序前2个个体直接复制到下一代不参与任何交叉变异剩下的个体先根据种群方差计算当前代的交叉概率和变异概率所有个体按这个统一概率执行单点交叉和均匀变异。这种精英保留全局自适应概率的组合既能防止最优解丢失又能让种群在后期保持必要的变异能力。在我跑的IEEE33节点算例中普通固定GA在40代左右就陷入局部最优网损曲线不再下降而自适应GA能继续搜索最终找到的调度方案网损比固定GA低了大约8%到10%——这个差距在高渗透率下还会继续拉大。4. IEEE33节点算例与Matlab代码实现框架4.1 为什么选IEEE33节点做验证做配电网优化调度的人对IEEE33节点系统都不陌生。它由一个33节点的放射状配电网络构成32条支路、5个联络开关基准电压12.66kV总负荷一般在5000kW加减容量的水平数据公开、拓扑经典、潮流计算收敛性好长期以来都是各种配电网算法验证的标准对象。选IEEE33节点做这个项目有几点实际考虑。第一节点规模适中——既不像3节点9节点那样过于简单看不出调度策略的差异也不像123节点甚至更大系统那样单次潮流计算都要花不少时间和遗传算法这种动不动几万次适应度评估的算法组合起来很吃力。第二IEEE33的标准拓扑中包含了不同类型的负荷节点和线路长度分布能够区分EV接入点放在哪里对网损和电压带来的不同影响。第三绝大多数审稿人和同行都认识这个系统结果出来有横向对比意义。在刻度上要注意一个问题IEEE33节点的原始数据单位是kW和kvar基准容量通常取1MVA基准电压12.66kV。你的优化目标、罚函数系数、EV充放电功率都要跟这个基准匹配。如果不匹配适应度函数数值会非常难看罚函数权重也会很难调。4.2 算例参数与EV、风光接入方案我先给出我当时用的参数方案供大家参考。场景侧初始蒙特卡洛采样1000个24小时风光场景削减成5个典型场景风速威布尔形状参数k2尺度参数c7.5光照贝塔分布参数alpha2.1beta2.5风机额定功率按接入容量1.5MW折算光伏额定功率1.2MW。EV侧的建模方式是分组聚合。不模拟每一台EV的独立行为而是假设一个EV集群分成若干组每组有相同的电池容量、初始SOC、可调度时间段和目标SOC。这样做的好处是优化变量数量大幅减少。如果模拟1000辆独立EV那每个时段都有上千个充放电决策变量遗传算法的染色体维度爆炸。分组聚合之后一组EV只需要一个24维的充放电功率序列三组就只要72个决策变量遗传算法完全可以处理。我在论文复现中常用的是三组EV参数组别电池容量(kWh)初始SOC目标SOC可调度时段最大充放电功率(kW)第一组通勤型400.40.918:00-次日7:007第二组营运型600.50.859:00-17:0015第三组混合型500.60.8全天10这里的可调度时段不代表EV一直在充放电而是允许电网在此时段内对EV功率进行调节。EV在不可调度时段内按固定负荷接入在可调度时段内由优化算法决定是充电还是放电V2G模式。所有EV的SOC必须在20%到95%之间避免过度充放损伤电池。风光和EV的接入位置我放在这些节点风电接入节点17末端馈线光伏接入节点33系统末端电压最薄弱的点EV集群分别接在节点24、25负荷集中区域以及节点9中间馈线段。这样安排的目的是让风光出力和EV负荷分散在不同的电气位置能够比较完整地检验算法应对局部电压越限的能力。4.3 代码结构与潮流计算的嵌入Matlab代码的整体结构我建议这样组织main.m % 主程序参数设置、场景生成、调用AGA scenario_gen.m % 蒙特卡洛采样 K-means场景削减 powerflow33.m % IEEE33节点潮流计算牛顿-拉夫逊或PQ分解 objectfun.m % 适应度函数网损 购电成本 罚函数 aga_operator.m % 自适应遗传算法选择、交叉、变异main.m里先把系统参数读进来用稀疏矩阵存储IEEE33的节点导纳矩阵和线路参数。潮流计算是整个优化过程最底层的调用函数每一次适应度评估都要对每个场景、每个时段分别跑一次潮流。我的做法是写一个函数输入各节点有功无功注入和平衡节点电压输出各节点电压幅值和相角以及系统网损内部用直角坐标牛顿法迭代6到8次就能收敛。优化主体的主循环框架大致是这样% 初始化种群 pop init_population(Npop, dim, bounds); for gen 1:MaxGen % 计算适应度 for i 1:Npop fitness(i) objectfun(pop(i,:), scenario_data, sys_data); end % 自适应参数调整 [pc, pm] adaptive_param(fitness); % 选择、交叉、变异 newpop select_crossover_mutate(pop, fitness, pc, pm); % 精英保留 pop elitism(pop, newpop, fitness, 2); end编码方式用的是实数编码不需要转二进制。染色体的每一段对应一组EV的24小时充放电功率序列实数编码的好处是精度可控、交叉变异操作直接不用在二进制和十进制之间来回切换。我之前用过二进制编码结果染色体长度动辄几百上千位遗传操作效率低适应度计算还容易因为解码误差引入不必要的抖动换了实数编码之后收敛稳定性明显好了。5. 调度模型的关键约束与罚函数处理5.1 目标函数怎么写目标函数这一块要跟需求匹配。如果论文的落脚点是经济运行那么目标就是最小化日运行总成本一般包含三部分向上级电网购电的费用、系统网损折算的费用、弃风弃光的惩罚费用。如果是侧重电压质量那可以把电压偏差也加进去。我这里采用的综合目标函数是min f C_buy C_loss C_penaltyC_buy是24小时从平衡节点购电的总费用电价用分时电价峰时段贵、谷时段便宜用分时电价才能体现EV在谷时段充电的经济性C_loss是24小时系统网损之和用网损电量乘以单位电价折算C_penalty是各种约束违反的罚函数和。罚函数的设计特别值得说。很多人一上来给罚函数权重设一个很大的值比如电压越限一个单位罚100000结果遗传算法前期几乎所有个体的适应度都被罚函数主导目标函数信息完全被淹没种群筛选完全由谁越限少决定经济性反而被丢到一边。这种情况下的收敛曲线非常好看——一路下降但最后解出来的方案要么过于保守要么电压完全没越限但网损和购电成本高得离谱。我的经验是先算一个不做优化的基准方案比如所有EV按固定时间充电、风光直接全额消纳得到基准网损和基准电压偏移量。以这个基准的量级来设罚函数的权重。比如基准网损是200kWh电量单价0.5元/kWh那C_loss的正常量级是100元电压越限罚函数权重就可以设在每单位平方100到500元保证轻微越限时目标函数还能反映一部分经济性不至于完全压过C_loss的信息。5.2 约束条件逐条拆解调度模型的约束条件要注意内在逻辑不能想起来一条加一条。我维护了一个检查清单每次改模型都要重新过一遍第一潮流约束。这是最底层的等式约束保证每个节点有功无功平衡。潮流计算本身已经强制满足不需要额外罚函数但如果潮流不收敛就必须返回一个很大的适应度值把不合格的个体淘汰。第二电压约束。IEEE33节点允许的电压范围一般取上下偏差不超过7%也就是标幺值0.93到1.07。注意这是一天所有时段所有节点都要满足的所以适应度计算时要把24×33个节点的电压全部检查一遍。第三EV充放电功率约束。每组EV每个时段的功率不能超过最大充放电功率。此外还有SOC连续方程约束SOC按时段递推更新必须落在安全区间内。第四风光出力约束。在最优解中可能出现弃风弃光的情况即风电光伏的实际出力小于预测出力这是允许的但要付惩罚费用实际出力不能超过场景中的预测值这是上限约束。这里有个容易被忽略的细节EV集群在可调度时段末端必须达到目标SOC这个约束如果不加优化算法会倾向于尽量不用EV或少用EV因为用EV会增加网损和成本约束最后得到的最优解就是所有EV不充电或少量充电的解完全失去调度意义。我在一开始跑通代码时忘了这个约束结果风、光渗透率提高后最优解里EV总的消耗电量少得可怜一开始还以为是算法问题后来排查了半天才发现是目标SOC约束缺失。6. 实测效果、踩坑记录与几点建议6.1 自适应GA和固定GA的收敛性对比在5个典型场景、3组EV、24个调度时段的设定下我分别用固定GA和自适应GA跑过多次。种群规模设50迭代80代。固定GA取交叉概率0.8、变异概率0.05自适应GA的交叉概率区间为0.6到0.9变异概率区间为0.01到0.15每隔5代采样一次种群方差做调节。结果上有一个很直观的规律固定GA在前20代下降很快但到第30代到40代之间曲线开始走平多次运行都停留在一个相近但不理想的局部最优值附近自适应GA前期下降稍慢但中后期仍在持续爬坡最终收敛值普遍比固定GA低8%到10%。这说明自适应机制起作用的地方在于避免早熟收敛而不是提高单次搜索效率。电压质量方面不加调度时IEEE33末端节点在某些场景的电压已经低于0.93出现越限加入EV调度和风光消纳优化之后末端电压被拉回到0.95以上效果非常明显。风光渗透率越高调度策略的调整空间越大方案的差异化也越明显——这一点可以通过对比不同场景下的EV充放电功率曲线看出来场景1光伏大发正午时刻EV几乎没有充电甚至反向放电而场景3风电大发夜间EV充电功率明显加大。这种看场景行事的调度策略正是场景法相对确定性优化的优势。6.2 常见翻车现场与解决经验第一场景削减数量不是越多越好。我把场景从5个增加到10个时优化结果变化很小但计算时间几乎翻倍。在做方案对比的时候如果你只关心调度策略的整体规律3到5个典型场景就足够了如果你要发文章做收敛性分析建议至少做一次场景数量敏感性分析画出场景数-目标值曲线。第二EV参数和风光场景之间的时序匹配经常出问题。比如夜间风电大发时如果EV的可调度时段只设定在白天那么即使夜间有便宜的绿电也无法利用。这个问题的根源在参数设置而不是算法改起来也简单但最初很容易忽略。第三Matlab代码里的一个坑是全局变量和函数句柄的使用。如果objectfun里要通过全局变量传递场景数据和系统数据并行计算或者多次调用时容易出错。我最终改成把所有数据封装进一个结构体通过参数列表传给objectfun虽然代码啰嗦一点但可读性和稳定性都好了很多。第四罚函数权重的调试最好用基准方案对比法。先把EV调度变量固定为零即不优化EV只让风光全额消纳算一个基准目标值。然后把权重从0开始逐步增大观察目标函数和约束满足度怎么变化找到约束刚好满足、目标值相对合理的权重区间。这个过程做一次后面换参数都不用再动。6.3 后续扩展方向这个框架的扩展性很强我自己做完之后有三个方向准备继续深挖。一是把EV的无序充电和有序调度做对比放入无序充电的蒙特卡洛模拟量化有序调度的经济性收益这部分可以单独写成一节报告。二是把场景生成里加入负荷不确定性也就是把负荷也当成随机变量一起采样可以增强模型对实际情况的覆盖能力。三是把自适应遗传算法换成混合算法比如AGA结果作为初值再交给内点法精调可能进一步缩小最优解的gap。我做完整个项目的体会是这个小系统的价值不在于算法本身多么高深而在于把不确定性建模、智能优化、配电网潮流、EV约束四块东西有机地串在了一起。你别指望一上来就把所有模块调到最好比较务实的路径是先跑通一个最简单版本——比如场景数削减到1个、EV只设1组、固定GA——然后把模块逐个丰富起来每增加一个模块就跑一次对比这样出了问题也能快速定位。这套思路对任何类似的项目都适用。