去年做某课题组的微电网经济调度任务时,我在MATLAB里跑一个72维、十几个约束的调度优化模型。最初试过梯度下降和序列二次规划,结果要么不收敛,要么给出的出力方案成本高得离谱;后来换成差分进化算法,几十行主循环改完,收敛曲线稳定了,调度结果也能解释通了。这篇博文把整个过程复盘一遍:从微电网调度模型的建立、约束处理,到差分进化算法的原理与Matlab代码实现,再到参数调优和结果分析,希望能帮正在做相关课题的同学少踩几个坑。
这篇内容面向两类人:一类是做微电网经济调度方向的研究生,需要快速搭出一个能出结果的仿真框架;另一类是刚接触智能优化算法、想看看差分进化在真实工程问题里怎么落地的开发者。我会尽量把每个步骤背后的理由讲清楚,而不是只给一段能跑通的代码。
1. 微电网调度到底难在哪:一个多能协调的约束优化问题
1.1 先给一个能跑的最小模型:目标函数长什么样
我当时的场景是并网型微电网,包含光伏、风电、燃气轮机、储能电池,通过一条联络线与外部电网交互。调度周期取一天24小时,控制步长1小时,每个时刻需要决定燃气轮机发多少电、储能是充还是放、从电网买多少电或卖给电网多少电。
目标是让一天的总运行成本最低,最常用的目标函数可以写成:
C_total = Σ_t [ C_fuel(P_mt(t)) + C_grid(t) + C_bess(P_bess(t)) + C_curtail(t) ]
其中,C_fuel是燃气轮机的燃料成本,通常拟合成二次函数 a × P_mt² + b × P_mt + c,这个写法来自传统机组组合问题,成本曲线带凸性,直接放进优化模型里是标准的。C_grid(t)是当时刻的购售电费用,等于购电电价乘购电功率,再减去售电电价乘售电功率。C_bess表示储能的损耗或调度代价,我用的是充放电功率绝对值和单位损耗成本的乘积。C_curtail是弃风弃光惩罚,目的是让优化算法优先消纳可再生能源出力,而不是为了省事直接砍掉光伏和风电。
这里要特别注意:目标函数里的每一项都要有对应的物理意义,不能随便加惩罚项。比如储能损耗成本如果设得太大,算法会倾向于不使用储能,这样峰谷套利功能就废了;如果设得太小,储能又会频繁满充满放,严重影响电池寿命。我一般先按设备手册估算损耗成本,再根据调度结果微调。
1.2 四个约束是真正的门槛:等式、不等式、时序、耦合
目标函数只是问题的一半,另一半是约束。做微电网调度时,最常遇到四类约束:
第一是功率平衡等式约束。每个时刻微电网内部所有源和荷必须严格平衡,即光伏出力、风电出力、燃气轮机出力、储能放电、联络线购电之和,等于负荷、储能充电和弃电之和。这一步是等式约束,违反一点都会导致潮流不成立,在仿真里就表现为功率差额不为零。
第二是设备出力不等式约束。燃气轮机有出力上下限,储能充放电功率有最大限制,联络线交换功率也有上限。这些约束相对好处理,直接在生成解时做边界限幅就行。
第三是储能SOC时序约束。SOC(t+1) = SOC(t) + P_charge(t) × η_charge / C_bess - P_discharge(t) / (η_discharge × C_bess),同时SOC必须保持在比如0.1到0.9之间,调度结束时的SOC最好回到初始水平附近,保证第二天还能继续用。
第四是耦合约束。储能不能同时充电和放电;燃气轮机爬坡率有限,相邻15分钟或1小时出力差不能太大;弃风弃光功率与光伏风电实际消纳量之间也是耦合的。
这四个约束放一起,问题性质就变了。目标函数有二次项,变量维度几十上百,储能时序约束让一个时刻的决策会影响后面所有时刻,整体上是一个典型的带约束非线性优化问题。严格来说,如果还要考虑燃气轮机的启停0/1变量,这就是混合整数非线性规划(MINLP),常规解法很容易陷进去出不来。
1.3 为什么梯度法和线性化在这里容易翻车
我最初用SQP(序列二次规划)尝试过。问题在于,目标函数和约束对决策变量的导数不是处处定义的,尤其是储能充放切换这种机制,相当于数学上不光滑的转折点,梯度算法在这种位置会反复震荡。另一个麻烦是初始解敏感性:SQP本质上是一个迭代局部搜索,初值给得不好,最后收敛到哪个局部最优基本靠运气。
也有同学把模型线性化,用线性规划或者混合整数线性规划求解器去做。这种方法在小规模算例上很快,但线性化需要引入大量辅助变量,比如储能充放切换需要0/1变量,购电售电切换也需要0/1变量,一天24小时的模型轻松变成200多个变量、300多条约束。一旦负荷预测更新,整个模型要重新生成,维护成本很高。
所以我在那个课题里做了个判断:模型规模不算小,实时性要求又不算苛刻(1小时步长完全可以离线计算),不如直接用元启发式算法。于是把目光放在差分进化上。
2. 差分进化算法为何适合这类调度场景
2.1 三个算子实现群体进化:变异、交叉、选择
差分进化(Differential Evolution,DE)是粒子群、遗传算法同一代出来的种群智能算法,但它和遗传算法有个本质区别:遗传算法的变异靠随机扰动,DE的变异靠种群个体之间的差分向量。
DE的流程很简单。首先是初始化,在变量上下界内随机生成NP个个体,每个个体就是一个候选调度方案。然后是变异,对每个个体X_i,随机挑三个不同的个体X_r1、X_r2、X_r3,用公式 V_i = X_r1 + F × (X_r2 - X_r3) 生成变异向量。其中F是缩放因子,控制差分向量的放大倍数。这一步是DE的灵魂,因为当种群分散时,个体差分比较大,搜索步长自动变大;当种群收敛到某个区域时,差分向量变小,搜索步长自动变小。这种自适应的步长调节机制,是DE在非线性问题上表现稳定的重要原因。
然后是交叉。变异向量的每个维度按交叉概率CR随机替换掉原个体对应维度的基因,形成试验向量U_i。最后是选择。如果试验向量适应度比原个体好,就替换原个体;否则保留,这一步是真正的"精英保留"思想,保证种群整体质量只升不降。
我用的变异策略是DE/rand/1,这是最稳妥的版本,不容易早熟。如果收敛太慢,可以试DE/best/1,直接用当前最优个体作为基准,但要注意它会加快向当前最优靠拢的速度,反而可能提前收敛到局部最优。
2.2 三个参数的作用与初始推荐
DE只需要三个主要参数:种群规模NP、缩放因子F、交叉概率CR。参数少对工程应用特别友好,PSO还有惯性权重、学习因子好几个参数要调,GA有选择压、交叉率、变异率、代沟等一堆参数,DE省心很多。
参数作用通俗理解:NP决定种群采样密度,太小了搜索范围覆盖不住72维空间,太大了每代计算量成倍增长;F决定"变异步长",F大探索性强,F小收敛稳定但有早熟风险;CR决定试验向量能继承变异向量多少个维度,CR大相当于大面积替换基因,多样性好。
我自己的初始推荐是:NP取50到60,F取0.5,CR取0.9。注意CR取0.9听起来很大,但DE的习惯就是CR偏大,因为变异向量本身质量有保障,多替换一点基因通常利大于弊。F阈值可以通过观察收敛曲线再调,如果下降曲线锯齿感很强,说明F偏大;如果曲线的最终成本很高且长时间不动,可能是F偏小。
2.3 我用DE、GA、PSO实测的对比感受
光讲理论不够,我在同一套微电网模型上把DE、遗传算法和粒子群都跑了一遍。结果不能代表所有场景,但趋势很能说明问题。
从收敛速度看,PSO前期最快,前20代就能压到一个比较低的成本,但后面容易停滞,经常卡在同一个局部最优附近不动。GA整体收敛平稳,但需要的种群规模比DE大,我试过NP=80才开始稳定。DE前期比PSO慢一点,但它到60代以后还在缓慢改进,最后找到的目标值比PSO平均低2%到4%。
从参数敏感性看,PSO对惯性权重和学习因子非常敏感,换了负荷曲线就得重新调;GA对交叉率和变异率也敏感;DE相对好很多,只要F和CR不在极端范围,结果都能接受。这一点对做课题的人很关键,因为你不可能每次换数据都花三天调参。
从稳定性看,我用同一份数据重复跑20次,DE找到最优解的方差最小。后面做算法对比论文时,稳定性指标好很重要,评审经常会问"你的算法多次运行结果是否一致"。DE在这一点上天然占优势。
| 对比维度 | 差分进化DE | 遗传算法GA | 粒子群PSO |
|---|---|---|---|
| 前期收敛速度 | 中等 | 较慢 | 快 |
| 后期精细搜索 | 好 | 中等 | 容易停滞 |
| 参数数量 | 3个 | 5个以上 | 4个左右 |
| 对高维约束问题适应性 | 较好 | 一般 | 一般 |
| 我实测20次最优解波动 | 最小 | 中等 | 较大 |
这也是为什么我最终把差分进化作为主算法,而不是选择更热门的粒子群。它的稳健性在工程问题里比"跑一次碰巧很便宜"重要得多。
3. 建模落地:从约束条件到MATLAB目标函数
3.1 搞清楚数据:24小时负荷、光伏、电价怎么来
写代码的第一步不是写算法,而是把输入数据整理成干净的格式。我的做法是:把一天的负荷、光伏预测出力、风电预测出力、分时电价分别存成24维的列向量,放在一个结构体或者Mat文件里,后续目标函数直接读取。
典型日数据大概是这样的规律:负荷在夜间低、早上8到11点抬升、晚高峰17到21点最高;光伏从早上7点开始增长、13点附近达到峰值、18点后归零;风电往往是夜间出力高、白天偏低;电价跟随负荷分峰平谷三个区间。我用的一组归一化数据大概是这样:谷段电价0.25元,平段0.65元,峰段1.05元。这一组数据看起来简单,却是目标函数计算的基础,一定要先可视化看一眼曲线是否平滑,有没有异常突变,不然后面优化出来的方案会非常奇怪。
3.2 决策变量怎么编码:72维向量的排布
差分进化是在连续空间搜索的,要把调度方案编码成一个一维向量。我用的方案是:
x = [ P_mt(1)...P_mt(24), P_buy(1)...P_buy(24), P_bess(1)...P_bess(24) ]
其中,P_mt是燃气轮机出力,P_buy是从电网购电功率,P_bess是储能功率,约定大于0表示放电、小于0表示充电。这样向量维度就是72,每个分量对应一个时刻的一种设备出力。
这里有一个关键点:光伏和风电的出力在这个模型里不作为决策变量,它们是被优先消纳的电源,当作给定数据传入目标函数。这样既符合微电网调度的工程习惯,也大幅缩小搜索空间。如果硬要把光伏和风电出力也作为决策变量,维度会高很多,收敛难度直线上升,而且不符合"可再生优先消纳"的调度逻辑。
还有同学问为什么不让储能的两个方向分别编码为两个变量,那样试下来很容易出现同时充电和放电的情况,物理上不允许。用一个带符号的变量就不会有这个问题,正数放电、负数充电,天然满足互斥约束。
3.3 约束处理的两种思路:边界修正与罚函数
约束处理是整段代码里最影响结果的环节。我采用的是组合方案:不等式边界直接限幅,等式约束和时序约束用罚函数。
具体来说,燃气轮机出力上下限、购电功率上限这些盒子型约束,直接在生成变异向量后做一次v = max(min(v, ub), lb),保证每个个体都在可行域内部。这样做的好处是减轻罚函数压力,让算法把精力放在功率平衡和SOC约束上。
功率平衡等式约束和SOC上下限,我用罚函数处理。罚函数的基本思路是:目标函数计算完之后,把约束违反量换算成成本,乘以一个很大的惩罚系数再加进去。比如功率不平衡量是2.3kW,罚系数是1000,这一项就加2300成本,算法会逐渐把解推向满足约束的区域。
这里有个特别重要的细节——罚系数的量级必须和目标函数量级匹配。如果总运行成本大概几千块,功率平衡违反量平均是几十kW,你把罚系数设成100,罚项只有几百块,约束等于不存在;设成10的8次方,罚项数值爆炸,算法会为了满足约束牺牲经济性,储能和燃气轮机被过度限制。我后来总结的经验是:先跑一次不罚约束的松弛版本,记录典型目标成本和典型违反量,然后取 penalty ≈ 10 × (典型成本 / 典型违反量) 作为起点,再上下微调。这是从实际调试里摸索出来的经验,比论文里直接告诉你"取很大"要可操作得多。
3.4 目标函数文件的编写骨架
目标函数我单独写成一个函数文件,算法主程序只负责生成个体和调目标函数。这是模块化基础,后面换模型、换数据都不用动DE主循环。
function cost = microgridCost(x) % 输入x: [P_mt(1:24), P_buy(1:24), P_bess(1:24)] T = 24; P_mt = x(1:T); P_buy = x(T+1:2*T); P_bess = x(2*T+1:3*T); % 正数放电,负数充电 % 读取全局数据 global loadData pvData wtData priceBuy priceSell % 注意:课题代码里可以用 struct 传递,避免全局变量的坏习惯 % 燃料成本: a*P^2 + b*P + c a = 0.0003; b = 0.12; c = 1.5; fuelCost = sum(a * P_mt.^2 + b * P_mt + c); % 购电成本减售电收益(这里简化为只购电不售电) gridCost = sum(priceBuy .* P_buy); % 储能损耗 C_bess_unit = 0.02; bessCost = C_bess_unit * sum(abs(P_bess)); % 功率平衡:光伏风电 + 燃气轮机 + 购电 + 放电 - 负荷 - 充电 = 0 imbalance = pvData + wtData + P_mt + P_buy + max(P_bess, 0) ... - loadData - abs(min(P_bess, 0)); % SOC约束 SOC(1) = 0.5; C_bess_kWh = 300; eta = 0.95; for t = 1:T if P_bess(t) >= 0 SOC(t+1) = SOC(t) - P_bess(t) / (eta * C_bess_kWh); else SOC(t+1) = SOC(t) - P_bess(t) * eta / C_bess_kWh; end end socViolation = sum(max(0, SOC(1:T+1) - 0.9)) + sum(max(0, 0.1 - SOC(1:T+1))); % 总成本加罚函数 penalty = 5000; cost = fuelCost + gridCost + bessCost ... + penalty * sum(abs(imbalance)) + penalty * socViolation; end这个代码骨架可以直接扩展:去掉全局变量改成结构体参数,增加售电逻辑、弃风弃光惩罚项、爬坡约束等。核心是理解两个数据流——决策向量如何被拆成物理变量,以及约束违反量如何进入成本函数。
4. 差分进化主循环与代码调试实录
4.1 一套可直接跑进课题的DE主循环代码
差分进化的主循环并不复杂,核心就四个步骤:初始化、变异、交叉、选择。我习惯把主循环写成一个独立函数,只依赖目标函数接口,这样换一种改进DE算法时也只是改这个函数。
function [bestX, bestCost, bestHist] = de_optimizer(NP, F, CR, MaxIter, D, lb, ub) % 初始化种群 pop = rand(NP, D) .* (ub - lb) + lb; costPop = zeros(NP, 1); for i = 1:NP costPop(i) = microgridCost(pop(i, :)); end bestHist = zeros(MaxIter, 1); for g = 1:MaxIter newPop = pop; for i = 1:NP % 变异:随机选三个不同个体 r = randperm(NP, 3); v = pop(r(1), :) + F * (pop(r(2), :) - pop(r(3), :)); % 边界限幅 v = min(max(v, lb), ub); % 交叉 jRand = randi(D); u = pop(i, :); mask = rand(1, D) < CR; mask(jRand) = true; % 保证至少交叉一个维度 u(mask) = v(mask); % 选择 costU = microgridCost(u); if costU <= costPop(i) newPop(i, :) = u; costPop(i) = costU; end end pop = newPop; [bestCost, idx] = min(costPop); bestHist(g) = bestCost; bestX = pop(idx, :); end end注意一个细节:变异时随机选的三个个体理论上不能与当前个体相同,否则差分向量退化。MATLAB的randperm(NP, 3)在小种群时有可能抽到当前个体本身,我的经验是在写严谨版本时要加一个过滤步骤,把当前个体索引排除再抽。另外,交叉的jRand保证至少有一个维度来自变异向量,这是DE的标准做法,不加的话种群可能完全静止。
4.2 收敛判断与随机性:结果可靠吗
DE算完一轮之后,要判断结果是否可信,不是看bestCost一个数字那么简单。我一般分三步:
第一步看收敛曲线。把bestHist画出来,正常情况下成本曲线应该在前期快速下降,后期缓慢平滑地逼近一个稳定值。如果曲线到迭代结束还在明显下降,说明迭代次数不够,要加大MaxIter或者调大NP。如果曲线前期就平坦了,成本还很高,大概率陷入局部最优。
第二步看约束满足情况。把最优解取出来,重新调用microgridCost,分解成功率平衡违反量、SOC违反量、设备越界几个指标,逐一检查。这一步非常关键,因为罚函数给的是综合成本,很可能整体成本很低但某个时刻功率平衡违反严重,算法拿其他时刻的节省补偿了这个时刻,这在物理上是不能接受的。我遇到过两次,都是罚系数量级不对导致的。
第三步看算法稳定性。同一个优化问题跑20次,记录每次的bestCost,统计最小值和均值。如果20次结果相差超过2%,说明参数或种群规模不合适。一个好的DE配置,多次运行结果之间的波动应该很小。这个统计习惯放到论文里是加分项,放到自己课题里能省去很多返工。
4.3 代码调试中最常见的三个坑
第一个坑是决策变量拆分配对错误。72维向量拆成三组24维变量时,经常把索引写错一位,1:24变成2:25,结果燃气轮机和购电的调度反了。这种错很难发现,因为最终成本可能看起来正常。我的经验是在拆完变量后立即加一行测试脚本,随手画一下P_mt曲线,看它是否符合出力范围。
第二个坑是边界限幅和功率平衡的矛盾。限幅会把变异向量压回可行域,但压完后的个体可能不满足功率平衡约束。这是正常现象,靠罚函数去平衡,不要试图用某种复杂修正保证每个个体严格平衡,那会让搜索空间变得支离破碎。
第三个坑是MATLAB的随机数流环境对多轮实验的干扰。如果前面某段代码用了rng或者导致随机数流状态变化,后面所有优化结果都会跟着漂移。做重复实验时,每次运行前都要用rng(固定种子)重置,否则你统计的"稳定性"其实是噪声。
5. 典型算例结果解读:一天24小时调度方案
5.1 算例参数与基础配置
我在某课题里用了一组典型参数来做算例验证:燃气轮机额定功率100kW,燃料成本系数a=0.0003、b=0.12、c=1.5;储能额定容量300kWh,最大充放电功率50kW,初始SOC为0.5,允许工作区间0.1到0.9,充放电效率0.95;联络线最大购电功率200kW;光伏装机80kW,风电装机40kW;系统日峰值负荷约220kW,谷时负荷约110kW。
算法参数按前面推荐的:NP=60,F=0.5,CR=0.9,最大迭代次数200。机器跑完大概十几秒,这个速度对研究阶段完全够用。
5.2 调度方案的时间线解读
把最优解画出来看,调度方案非常有工程直觉,很能说明算法真的学到了调度规律。
凌晨0点到7点,负荷处于低谷,电价也是最便宜的谷段0.25元。最优解选择尽量从电网购电,燃气轮机停机或极低出力,同时储能以最大功率充电,把在低谷时段买的便宜电存起来。这个行为对应储能峰谷套利策略,是电力系统里最基础的"低价存储"逻辑。
白天8点到15点,光伏出力进入高峰,负荷也在攀升。最优解表现为光伏优先消纳、燃气轮机部分出力、购电量明显下降,储能根据电价平段和负荷情况灵活切换充放。如果光伏充足,储能甚至会继续充电,为晚高峰准备库存。
晚上17点到21点,峰段电价1.05元,负荷达到全天最高。最优解里燃气轮机基本满发,储能持续放电,购电功率尽量压低,只有不够的部分才从电网买。整条出力曲线没有出现离奇的振荡,每个设备都在合理区间内工作,这是罚函数和边界约束设置得当的表现。
我整理了几个典型时段的调度结果,方便展示:
| 时段 | 负荷(kW) | 光伏(kW) | 燃气轮机(kW) | 储能功率(kW) | 购电(kW) |
|---|---|---|---|---|---|
| 02:00 | 120 | 0 | 15 | 充电32 | 137 |
| 11:00 | 180 | 62 | 45 | 充电10 | 83 |
| 14:00 | 175 | 68 | 40 | 放电5 | 72 |
| 19:00 | 220 | 0 | 95 | 放电48 | 77 |
这组数据不是真实的测量值,而是算法计算出的典型结果,但它展示了核心调度规律:谷段囤、峰段放,燃气轮机作为平衡基荷,来自电网的购电兜底。
5.3 收敛曲线与算法稳定性
收敛曲线上看,前30代成本快速下降,大概从初始随机方案的三万多降到接近优化值;60代以后趋于平缓,到120代基本稳定。这种情况下,200代最大值设定是合理的,既有充足的收敛空间,又不至于浪费算力。
稳定性方面,同一份数据用20个不同的随机种子跑,最优成本的最大值和最小值相差约1.5%,标准差约0.6%。这个波动水平在智能优化方法里算不错。如果标准差超过2%,我通常会先考虑增大NP,而不是盲目调F,因为种群多样性不足往往是高维优化迟迟找不到稳定区域的主要原因。
5.4 和规则调度对比,能省多少
为了让结果更有说服力,我还做了一组对比:规则调度采用"光伏全额消纳、燃气轮机固定半载、储能按固定峰谷策略充放"的简单方式,差分进化调度则是全时段联合优化。
结果显示,这一组算例里DE调度方案的总运行成本比规则调度降低约6.8%。拆分来看,节省主要来自两个方面:一是峰谷电价套利更精准,储能充放时机不再依赖固定的经验规则;二是燃气轮机的出力在全时段内按成本曲线和负荷曲线联合优化,避免了固定半载造成的浪费。
需要说明的是,这个比例依赖具体算例参数。峰值电价越高、储能容量越大、负荷峰谷差越明显,优化收益越大。如果你的课题里优化收益远低于这个数,要先检查是不是峰谷电价差太小或者储能容量不足,而不是算法有问题。
6. 参数调优经验与复用建议
6.1 F和CR别直接抄论文:先粗调后细调
不少文献上来直接写F=0.8、CR=0.9,看起来标准,但换到你的模型上不一定合适。我踩过的坑是:把文献参数照搬到我的72维模型上时,收敛曲线前期下降很快,但最终成本比调整后的参数高了3%以上。原因是这个模型的目标函数比较"平",局部最优和全局最优之间的差距不算太大,F太大时搜索步长直接跨过了精细区域。
我的调参流程是:先固定NP=50,F=0.5,CR=0.9,跑一次看收敛曲线;如果曲线锯齿严重、后期还出现成本反弹,说明F偏大,降到0.4再试;如果收敛很平滑但最终成本高、多次运行波动大,可能是F太小或者NP太小;如果前期就收敛到了明显不理想的成本,则把CR降到0.7左右增加基因保留程度。注意一次只调一个参数,不要同时动两个,否则出了问题根本定位不到原因。
6.2 罚系数的量级会直接改变最优解形态
罚函数系数这个问题,我在前面的章节提过,这里再展开说。它不仅仅是"有没有满足约束"的问题,而是会改变整个最优解的结构。
我曾经做过一个对比实验:罚系数设成10的2次方量级时,算法给出的调度方案功率平衡大量违反,储能SOC长期越界,但总成本数字很好看;罚系数设成10的6次方时,约束全满足,但燃气轮机为了配合储能被逼得很紧,运行成本反而上升。两种结果单独看都不靠谱。
后来引入一个标幺化做法:先估计目标成本的典型量级C_typ和约束违反量的典型量级V_typ,罚系数从penalty ≈ C_typ / V_typ出发,乘一个3到10倍的安全系数。这样即便系数偏高,也只是在"牺牲少量经济性换取严格约束满足"的方向偏,不会出现约束完全失效或者成本数值爆炸的极端情况。调试过程中,把每个约束违反量的数值打印出来看一眼,比盯着总成本数字猜测有效得多。
6.3 随机优化结果怎么报告才可信
差分进化是随机算法,单次运行结果不能代表算法性能。我见过一些同学论文里写"最优成本是XXX"却不说跑了多少次、结果波动多大,这样很容易被质疑。更稳妥的写法是:独立运行20到30次,报告最优值、平均值、最差值、标准差,并用固定随机种子保存一次完整结果用于复现。代码里可以在优化函数入口加rng(seed),把种子作为参数传入,这样别人拿到代码就能跑出一模一样的结果。
在这个课题里,我最后报告的结果用的是"30次独立运行的最优方案",同时列出平均值和标准差。这样做论文答辩时少了很多争议,自己复盘时也更踏实。
6.4 把DE从单次运行变成可复用模块
做完这个项目我才意识到,最有价值的产品不是那一堆结果图,而是代码结构本身。我的做法是:目标函数文件、DE主循环文件、数据加载文件、结果绘图文件四个模块完全分离。后来课题组新来一个做电动汽车充电调度的同学,他直接复用我的DE主循环,只重写了目标函数和数据加载,两天就出了一版能跑的仿真。这种复用能力在项目多的阶段特别重要。
如果要继续做研究创新点,差分进化也有很多可以扩展的方向:改用自适应F参数、引入混沌初始化、加局部搜索算子、把单目标改成多目标Pareto优化、考虑源荷不确定性的鲁棒优化等。核心的模型骨架和DE框架都不用推翻,改造成本很低。
现在这套框架我已经复用到另一个带电动汽车充电负荷的微源项目上,改的只是建模数据和目标函数里的几项成本。回过头看,做调度研究真正卡人的地方不是算法本身,而是模型是否建对、约束罚值量级是否找准、决策变量编码是否清晰。这三件事做好了,差分进化这样的成熟算法完全够用,甚至可以成为课题组里反复复用的基础工具。