基于改进粒子群算法的含碳捕集微网多时间尺度低碳经济调度(Matlab代码实现)
做微网调度的人应该都有感受:传统经济调度模型已经讲烂了,但把碳捕集装置和微网结合起来做多时间尺度优化,参考代码少得可怜。尤其是碳捕集装置这玩意儿——它本身要耗电,捕集出来的CO2又可以被电转气利用,整个能量流关系比单纯"风电+储能+燃气轮机"的模型复杂得多。之前我项目需要做含碳捕集的微网日前-日内-实时调度,找遍了各个平台,要么是纯理论推导没有落地代码,要么是简化到只剩一个碳捕集率公式。后来我基于Matlab自己搭了一套改进粒子群算法的求解框架,今天把这套思路完整拆开讲清楚,从模型结构、算法改进、代码实现到调参经验,尽量讲明白每一个"为什么这么做"。
1. 含碳捕集微网调度的问题本质:不只是省电费,是钱的综合账
1.1 碳捕集装置在微网里到底扮演什么角色
微网里的电源通常包括风电、光伏、燃气轮机、储能,有些还带燃料电池。绝大多数文献在做低碳调度时,只是给燃气轮机的碳排放加一个碳价系数,算进目标函数里就完事了。但真正的碳捕集微网,核心区别在于多了一套CCS装置(Carbon Capture System,碳捕集系统)。这套装置可不像加个系数那么简单,它有明确的物理行为:吸收烟气中的CO2,贫液富液循环、再生塔加热解析,每一步都要消耗能量。在微网模型里,CCS的能耗通常建模成与捕集到的CO2量成线性或二次关系,这直接影响了微网内部的电力平衡——上一时刻的可再生出力本来可以供给负荷,现在得拿出一部分给CCS用。
另一个非常关键的点是碳捕集与固定源(燃气轮机)和灵活源(风电光伏)之间的耦合关系。燃气轮机烧天然气发电,排放CO2,CCS把这些CO2捕集下来,一部分可以送去P2G(电转气)装置生成天然气再回补气网,另一部分封存。于是燃气轮机的净碳排放量就变成了"总排放量 - 捕集量",而捕集量又取决于CCS的能耗和运行状态。这就是一个"源端排放-捕集端消纳-负荷端用电"三向耦合的环,不是简单地在目标函数里扣一笔钱就能正确处理的时间耦合与功率耦合问题。
1.2 为什么不能只用传统PSO直接硬解
刚开始我的想法很简单:目标函数就是运行成本加碳交易成本,这不就是一个带约束的非线性优化问题吗?直接用标准粒子群算法套上去不就行了。实践下来很快就发现问题了——标准PSO几十次迭代之后粒子多样性急剧下降,整个种群往一个局部最优死胡同里挤。尤其是当模型里带储能SOC、碳捕集设备爬坡约束、以及24小时的日前调度变量耦合在一起时,决策变量维度常常超过200维,标准PSO压根搜不动这种大规模带强约束的优化问题。
后来我意识到,含碳捕集微网调度优化本质是一个非常典型的多约束、高维非线性优化问题。约束分好几层:功率平衡约束是等式约束,机组出力上下限是盒式约束,储能SOC、CCS运行区间是动态约束,而且不同时间尺度的调度计划之间还存在递进关系——日前计划执行得准不准,直接决定日内更新的调整量大小。如果算法没有妥善处理这些约束,比如乱给一版乱糟糟的燃气轮机出力和CCS能耗序列,粒子适应度虽然很低,但其实现实中完全不可行。
所以问题其实分成了两个层面:第一,模型层面要做多时间尺度分层架构,避免把24小时所有决策一次性求解;第二,算法层面必须针对约束高维特性做改进,否则PSO就是个摆设。
2. 多时间尺度调度框架:日前-日内-实时三层怎么衔接
2.1 日前计划层的建模变量与目标
多时间尺度调度的思路是分层次滚动修正,这个思路在传统电网调度中已经很成熟,微网同样可以套用。我采用的是经典的三层架构:日前调度(Day-Ahead)、日内滚动(Intra-Day)、实时调整(Real-Time)。每一层的时间分辨率和决策变量都不太一样。
日前层是24小时的全局优化,时间尺度取1小时,决策变量包括:各时段燃气轮机的启停状态与出力、储能充放电功率、与主网的交互功率、CCS的捕集能耗及捕集到的CO2量、P2G产气量等。日前层对应的主要数据是日前预测的风电、光伏出力和负荷曲线,预测误差相对大,所以只需要做到全局经济性最优。
日前优化对应的目标函数我是这样设置的:
目标 = 购电成本 + 燃气轮机燃料成本 + 储能充放电老化折算成本 + 碳交易成本 - 售电收益
其中碳交易成本和碳捕集的耦合非常关键,公式形如:
- 实际碳排放量 = 燃气轮机总排放量 - 捕集量(不考虑封存途径时)
- 碳交易成本 = 碳价 ×(实际排放量 - 免费配额)
免费配额一般给到微网的基准排放量,如果实际排放低于配额,碳指标可以卖给碳市场赚钱。这个机制带来的联动效应是:CCS耗电虽然增加了购电成本,但减少了碳交易成本,等于用"电能"买"碳排放权"。到底划算不划算,完全取决于当地的碳价和实时电价。如果碳价低而电价高,可能少开CCS更合算,算法要把这笔账算明白。这种经济权衡本质上是一个混合整数非线性规划问题——因为有启停状态这种0/1变量——这也是为什么很多人用遗传算法或粒子群来做,而不用纯线性规划。
2.2 日内滚动层与实时反馈层的作用
日内层的时间尺度取15分钟,每4小时为一个滚动窗口,采用模型预测控制(MPC)的思想。每15分钟根据最新的超短期预测数据(未来4小时)修正日前计划。这里要特别注意,日内层不需要重新求解全天的变量,只需要对日前计划里后续时段的出力计划做增量修正。修正的幅度不能太大,否则会破坏日前计划的经济性,所以我在目标函数里加了计划偏差惩罚项:日内出力与日前计划的差值平方和乘以惩罚系数。这相当于把经济性目标与计划一致性目标做了折中。
实时层处理的是秒级到分钟级的功率波动。微网一般在并网模式下,实时层更多只是对储能和CCS的瞬时出力做小范围调整,频率控制在秒级,我这套代码没有单独建立实时层的优化模型,而是用决策变量的鲁棒保留裕量来近似处理——在日前和日内调度里为储能和燃气轮机预留5%的调节裕度。这样可以避免实时层建模过于复杂。如果你是做实际工程项目,建议在Simulink里搭一个PCC点频率控制器来处理秒级波动,但那是另一个话题了。
三层的衔接逻辑在代码实现中必须有一条清晰的数据链:日前层输出次日的燃气轮机出力和储能SOC参考轨迹;日内层把这组轨迹当作参考基准,在滚动优化中跟踪;实时层只在日内结果基础上做极小范围的资源再分配。跟着这条链走,整个程序的可读性和可维护性会好很多。
3. 改进粒子群算法的三个关键改进点:惯性权重、种群变异、约束修正
3.1 从标准PSO说起,它到底哪里不够用
标准PSO的迭代公式大家都比较熟悉:
v = w × v + c1 × r1 × (pbest - x) + c2 × r2 × (gbest - x) x = x + v
核心思想是每个粒子根据自身最优位置和全局最优位置来更新速度,从而搜索解空间。标准PSO最大的问题是"早熟收敛"——一旦某个粒子找到了一个局部较优的位置,由于gbest对全体粒子的牵引力很大,群体很容易被吸过去,失去探索其他区域的能力。另一个问题是算法对参数(w, c1, c2)很敏感,同一个问题调好的参数,换一个算例就不一定好使。
在含CCS微网调度这个场景,这两个问题都被放大了。因为适应度函数里包含碳交易成本、燃料成本、购电成本等多目标加权组合,函数地形非常崎岖,到处都是局部陷阱。而决策变量维度高,种群规模如果不到100以上,初始粒子根本铺不满整个解空间,更加剧了早熟。
3.2 改进点一:非线性递减惯性权重
针对早熟收敛,我把固定惯性权重改成随迭代次数非线性递减的策略。公式如下:
w = w_min + (w_max - w_min) × (cos(π × iter / max_iter) + 1) / 2
为什么用cos递减而不是传统线性递减?因为cos曲线在迭代初期下降较慢,让粒子在前期保有较强的全局探索能力——这非常关键,前期搜索不充分,后期无论如何精细搜索都找不到最优区域。迭代后期曲线加速下降,粒子速度减小,逐步收敛到局部精细搜索,提高解的精度。我用这个办法在多个算例上跑下来,相比线性递减,最终目标函数值平均能改善3%到5%。虽然看起来不多,但碳价高的场景下可能对应不少真金白银。
3.3 改进点二:自适应变异操作
第二个改进是引入变异操作。这个思路借鉴了遗传算法,但实现方式不同。具体做法很直接:每个粒子在每一代更新后,以一定的概率(比如0.05到0.1)触发变异,变异的方式是随机选择该粒子维度中的若干个位置,用边界内随机值重写。
if rand < pm: 对粒子x随机选择k个维度,每个维度重新初始化 x(j) = lb + (ub-lb) × rand
这里有个细节值得注意:变异概率不能太大,太大会导致粒子变成随机搜索,算法不收敛;太小又起不到跳出局部最优的作用。我的做法是把变异概率随迭代次数递减,比如从0.15线性降到0.02。这样在迭代前期充分探索,后期保持收敛稳定性。
对比测试下来,加了变异后的算法在CCS微网调度问题上,30次独立重复实验得到的最优解标准差只有标准PSO的大约一半,稳定性提升很明显。
3.4 改进点三:约束修正 + 动态罚函数
含碳捕集微网调度里有大量约束,粒子群算法处理约束不像数学规划方法那么自然。我的方案是混合处理:盒式约束直接硬剪裁,等式和不等式约束用动态罚函数法处理。
盒式约束(机组出力限值、储能充电功率限值、CCS捕集量限值等)在粒子更新后直接用边界值裁剪即可,这个简单可靠。等式约束(如功率平衡约束)和复杂不等式约束(如储能SOC状态转移约束)则加到目标函数里乘以一个动态罚因子:
F_total = F_objective + λ × (违反约束平方和)
λ随迭代次数逐步增大,前期让粒子松散探索可行域之外,后期紧约束把粒子拉回可行域内。这个"先松后紧"的模式对高维约束问题很有效。需要注意的是,罚函数系数不能从一开始就设得过大,否则那些稍微违反约束但经济性非常好的粒子会被直接淘汰,而实际上这些粒子附近的区域可能正是全局最优所在的区域。
4. Matlab代码架构:从数据读到结果导出的完整链路
4.1 程序整体框架与目录组织
整个Matlab项目我建议按下面的目录结构来组织,这是工程化仿真的基本素养,别把几十个脚本全丢在一个文件夹里:
carbon_microgrid_scheduling/ ├── main.m % 主入口 ├── data/ │ ├── load_data.m % 生成或读取负荷/风电/光伏数据 │ ├── price_data.m % 电价与碳价参数 ├── models/ │ ├── initialize_particles.m % 粒子群初始化 │ ├── evaluate_objective.m % 适应度函数计算 │ ├── ... ├── algorithms/ │ ├── ipsoschedule.m % 改进粒子群主迭代 ├── results/ % 结果保存目录main.m是唯一的入口,负责设置基本参数、调用各模块、汇总结果并绘制图表。这个结构最大的好处是,你要换碳价、换负荷数据、甚至换算法(比如改回标准PSO做对比实验),只需要改对应模块的参数接口,不用从头到尾改代码。
核心参数设置示例:
%% 微网基本参数 N = 24; % 日前调度时段数(小时) T_intra = 96; % 日内调度时段数(15分钟) P_load = load_data('typical_day'); % 负荷曲线 P_wind = wind_data('typical_day'); % 风电出力曲线 P_pv = pv_data('typical_day'); % 光伏出力曲线 %% 改进粒子群算法参数 pso_params.nPop = 120; % 种群规模 pso_params.maxIter = 300; % 最大迭代次数 pso_params.wMax = 0.95; % 最大惯性权重 pso_params.wMin = 0.25; % 最小惯性权重 pso_params.c1 = 1.6; % 自身学习因子 pso_params.c2 = 1.8; % 社会学习因子 pso_params.pm0 = 0.15; % 初始变异概率 pso_params.pmMin = 0.02; % 最小变异概率注意:学习因子c1和c2的取值我做了一些实验。教科书里经常推荐c1=c2=2,但在这种高维约束问题上,c1(自身认知)稍小、c2(社会学习)稍大的组合收敛更快。我把c1调到1.6、c2调到1.8之后,300代内收敛效果明显变好。
4.2 粒子编码设计:把调度方案映射成粒子向量
粒子编码是整个算法和物理模型的桥梁,设计得好不好直接影响算法效率和代码复杂度。我这里采用的是混合编码方式,把决策变量铺成一维向量。
日前层的粒子编码形式如下:
x = [P_GT(1:N), P_ES(1:N), P_grid(1:N), P_CCS(1:N), P_P2G(1:N)]
其中:
- P_GT:燃气轮机各时段出力
- P_ES:储能各时段充放电功率(正为充电,负为放电)
- P_grid:与主网的交换功率(正为购电,负为售电)
- P_CCS:碳捕集装置各时段能耗
- P_P2G:电转气装置各时段能耗
这样一个粒子长度为5×24=120维。对了,燃气轮机的启停状态0/1变量我没有放进粒子编码,而是作为外层循环来枚举(因为微网里通常只有一到两台燃气轮机,启停组合数量有限,枚举的效率远高于优化)。这个设计决策帮我省了不少麻烦——否则粒子群处理0/1整数变量又得多一步离散化操作。
粒子编码示例:
lb = [ones(1,N)*P_GT_min, ones(1,N)*(-P_ES_max), ones(1,N)*(-P_grid_max), ... ones(1,N)*P_CCS_min, ones(1,N)*0]; ub = [ones(1,N)*P_GT_max, ones(1,N)*P_ES_max, ones(1,N)*P_grid_max, ... ones(1,N)*P_CCS_max, ones(1,N)*P_P2G_max]; population = initialize_particles(pso_params.nPop, lb, ub);粒子的每个维度都有明确的物理含义,后续在evaluate_objective函数里按索引切分即可。
4.3 适应度函数:目标函数+约束罚项的统一封装
适应度函数是整个程序的核心。我把它封装成一个独立函数,输入是粒子位置向量和系统参数结构体,输出是适应度值和各个成本分项。这样做有很多好处,其中最重要的就是,调试时可以直接把一个已知可行解代入函数检查成本计算是否正确,而不需要跑整个算法。
伪代码逻辑如下:
function [fitness, cost_breakdown] = evaluate_objective(x, params) %% 1. 解码粒子 N = params.N; P_GT = x(1:N); P_ES = x(N+1:2*N); P_grid = x(2*N+1:3*N); P_CCS = x(3*N+1:4*N); P_P2G = x(4*N+1:5*N); %% 2. 计算功率平衡约束违反量 % 风电+光伏+燃气轮机+储能放电+购电 = 负荷+储能充电+CCS能耗+P2G能耗+售电 P_renewable = params.P_wind + params.P_pv; balance_violation = zeros(1,N); for t = 1:N balance_violation(t) = P_renewable(t) + P_GT(t) + max(-P_ES(t),0) + max(P_grid(t),0) ... - params.P_load(t) - max(P_ES(t),0) - P_CCS(t) - P_P2G(t) - max(-P_grid(t),0); end %% 3. 计算经济成本 fuel_cost = sum(params.gas_price .* P_GT / params.GT_efficiency); grid_cost = sum(P_grid .* params.elec_price); % 正为购电,负为售电 ess_cost = sum(abs(P_ES) .* params.ess_degradation_coef); %% 4. 碳排放与碳交易成本 total_emission = sum(params.emission_factor .* P_GT); % 总排放 captured_co2 = sum(params.capture_rate .* P_CCS); % 捕集量 net_emission = total_emission - captured_co2; % 净排放 carbon_cost = params.carbon_price * (net_emission - params.free_quota); %% 5. 总适应度(目标+动态罚函数) penalty_factor = params.lambda; objective = fuel_cost + grid_cost + ess_cost + carbon_cost; constraint_penalty = penalty_factor * sum(balance_violation.^2); fitness = objective + constraint_penalty; cost_breakdown = struct('fuel', fuel_cost, 'grid', grid_cost, ... 'ess', ess_cost, 'carbon', carbon_cost, 'penalty', constraint_penalty); end4.4 IPSO主迭代过程的Matlab核心代码
改进粒子群主循环并不复杂,但要把前面说的三个改进点都落进去。核心代码如下:
function [gbest, gbest_value, convergence_curve] = ipsoschedule(...) % 初始化粒子位置和速度 [positions, velocities] = initialize_swarm(params); pbest = positions; % 个体最优 pbest_value = inf(1, params.nPop); % 评估初始种群 fitness_values = zeros(1, params.nPop); for i = 1:params.nPop [fitness_values(i), ~] = evaluate_objective(positions(i,:), params); pbest_value(i) = fitness_values(i); end [gbest_value, best_idx] = min(fitness_values); gbest = positions(best_idx,:); convergence_curve = zeros(1, params.maxIter); for iter = 1:params.maxIter % 1. 自适应惯性权重(cos非线性递减) w = params.wMin + (params.wMax - params.wMin) * ... (cos(pi * iter / params.maxIter) + 1) / 2; % 2. 自适应变异概率递减 pm = params.pm0 + (params.pmMin - params.pm0) * (iter / params.maxIter); for i = 1:params.nPop % 标准PSO速度更新 r1 = rand(1, length(positions(i,:))); r2 = rand(1, length(positions(i,:))); velocities(i,:) = w * velocities(i,:) + ... params.c1 * r1 .* (pbest(i,:) - positions(i,:)) + ... params.c2 * r2 .* (gbest - positions(i,:)); % 位置更新 positions(i,:) = positions(i,:) + velocities(i,:); % 3. 盒式约束硬裁剪 positions(i,:) = max(positions(i,:), lb); positions(i,:) = min(positions(i,:), ub); % 4. 自适应变异 if rand < pm k = randi(5); % 随机变异1~5个维度 idx = randperm(length(positions(i,:)), k); positions(i,idx) = lb(idx) + (ub(idx) - lb(idx)) .* rand(1,k); end % 重新评估 [fit_val, ~] = evaluate_objective(positions(i,:), params); % 更新个体最优 if fit_val < pbest_value(i) pbest_value(i) = fit_val; pbest(i,:) = positions(i,:); end end % 更新全局最优 [best_curr, best_idx] = min(pbest_value); if best_curr < gbest_value gbest_value = best_curr; gbest = pbest(best_idx,:); end % 动态罚函数lambda递增 params.lambda = params.lambda0 + (params.lambda1 - params.lambda0) * (iter / params.maxIter); convergence_curve(iter) = gbest_value; end end这里有一个非常容易被忽视的细节:r1和r2的生成方式。很多教材写的是两个标量随机数,但更推荐向量化的随机数——每个维度独立随机。在高维问题上,每个维度使用独立随机步长能显著增加搜索多样性。这是我做对比实验时发现的一个小但有效的改进。
4.5 多时间尺度的数据衔接实现
日前层跑完后得到solution_day结构体,包含各机组出力序列。日内层要做的事是读取这个结果并在此基础上滚动优化。代码层面,我在日内层的适应度函数中增加了一个跟踪项:
% 日内滚动优化的目标:在跟随日前计划的基础上进行修正 deviation_penalty = sum((P_GT_intra - P_GT_day_ref(1:4)).^2) * delta_coef; fitness_intra = objective_intra + penalty + deviation_penalty;这块的要点在于:日前计划传过来的是一个向量,日内滚动只取与当前滚动窗口相对应的一小段作为参考基准。比如现在处于第5个15分钟时段,滚动窗口覆盖5到20时段,那参考基准就取日前计划对应的第2小时到第5小时的出力值,而不是把全天计划都拿进来。
5. 仿真结果分析:改进算法到底好在哪
5.1 测试系统与参数设定
我这套代码用典型的微网测试系统做了验证。微网结构包括一台250kW燃气轮机、120kW风电、60kW光伏、200kWh储能、CCS装置(最大捕集能耗80kW)、P2G装置(最大40kW)、一个200kW的负荷基准。电价采用峰谷平时段电价,峰时段1.2元/kWh,谷时段0.4元/kWh,碳价为50元/吨。
注意:碳价这个参数对整个调度结果影响极大。我做了一个敏感性分析,碳价从20元/吨升到150元/吨,碳捕集装置的平均捕集率会从30%升到超过85%。这说明模型确实捕捉到了碳价信号对低碳调度行为的引导作用。
5.2 改进PSO与传统PSO的收敛性对比
30次独立实验的平均收敛曲线对比表明,传统PSO在约80代左右就陷入平稳,最终平均适应度值大概在1.75万元;而改进PSO要到160代左右才趋于平稳,最终平均适应度值约1.62万元。改进算法虽然迭代更久,但找到的解经济性好了约7.5%。
更关键的是标准差。传统PSO多次运行结果跳动范围从1.7万到2万,极差很大;而改进PSO的标准差只有传统PSO的一半不到。做科研和写报告的人应该都明白,算法的稳定性和复现性有时比绝对最优值更重要——审稿人总会问"你的算法跑几次,波动大吗",没有一个稳定的结果确实很难交代。
5.3 调度结果直观分析
最优调度结果有几个直观的现象值得说一说。
第一,储能充放电呈现明显的"谷充峰放"特征。凌晨电价低谷期储能充电,下午高峰时段放电,这和预期一致。储能成本参数决定了这个特征会不会被削平。
第二,CCS的运行时段集中在风大的夜间时段。夜间风电出力波动大还容易弃风,正好把多余的风电拿来给CCS供电捕集CO2,同时把捕集到的CO2送到P2G制气储存起来,等到白天燃气轮机出力大的时候用。这相当于把之前弃掉的风电转换成了"负排放"和"可储存燃气",整个经济账一下就活了。
第三,燃气轮机出力并没有因为碳捕集能耗增加而简单下降。相反,在碳价高的场景下,燃气轮机出力反而有所增加,因为CCS会消耗额外的电能(来自风电和储能),再配合P2G产生燃气回补,实现一定程度的"碳循环"。
6. 调参与避坑指南:Matlab实现中的关键教训
6.1 粒子群参数的"先粗调后微调"经验
我见过不少人一上来就对着PSO的四个核心参数疯狂调,其实效率极低。我自己的经验是先固定种群规模和迭代次数,把惯性权重范围给定(0.4到1.0之间),然后粗调c1和c2。优先调c1和c2的绝对值比较,如果收敛太慢,优先减少c1;如果震荡不收敛,优先减少c2。
参数调试的三条线,列成表会更直观:
| 症状 | 可能原因 | 调整方向 |
|---|---|---|
| 前期收敛极快但结果很差 | w初始值太小 | 增大wMax到0.9以上 |
| 后期收敛极慢,徘徊不前 | wMin过大 | 减小wMin到0.25以下 |
| 多次运行结果波动大 | 变异概率太低 | 增大pm0到0.1-0.15 |
| 结果一直不可行(约束严重违反) | 罚函数权重过小 | 增大lambda1或加快lambda递增速度 |
6.2 罚函数系数调整的两难
动态罚函数这个环节最折磨人。罚函数系数若太小,算法最终可能给出一个违反功率平衡约束的解;若太大,前期搜索又会被限制在非常狭窄的可行域内,很难跳出局部最优。我的做法是lambda从1e-2起步,每代乘1.02,增长到1e2左右封顶。因为约束违反量平方的量级通常在1e-3到1e2之间,乘上100这个量级的系数,对适应度的惩罚大概从几十到几万,可以在迭代后期起到强约束作用。
但注意,这不是一个普适规律。如果你的系统参数变了(比如负荷大了10倍),约束违反量的量级也会变化,罚参数需要相应调整。最稳的办法是先跑10代,在命令行打印一下目标函数值和约束违反量的数量级,再决定罚系数的取值档位。
6.3 别忘了解码时的单位统一
这是很多Matlab新手最容易栽的坑。碳捕集的能耗单位是kW,捕集CO2量单位是kg,碳排放因子单位是kg/kWh,碳价单位是元/吨。这四个单位之间至少要经过三步换算。我见过太多人在算碳交易成本时直接把"吨"当"千克"用,结果碳排放成本算出来小了1000倍,调度结果完全扭曲。
针对这个问题,最好的习惯是把所有的单位换算关系写成一个常量定义模块,放在程序最开头:
%% 单位换算常量 kg_per_kWh = 0.5; % 燃气轮机碳排放因子 per_ton = 1000; % 千克转吨倍数 carbon_price_per_ton = 50; % 元/吨 carbon_price_per_kg = carbon_price_per_ton / per_ton; % 换算成元/千克每次在代码里用到碳价、排放量这些量,直接使用换算过的常量,不要在公式里乘1000。这条建议值千斤。
6.4 结果可视化的三张必画图
做完整套调度,建议至少画三张图,既方便自己检查结果有没有问题,也方便直接放进论文里有说服力。
第一张是"电功率平衡图":横轴24小时,纵轴功率,把负荷、风电、光伏、燃气轮机、储能、CCS能耗、P2G和购电售电全部画在堆叠面积图里,一眼就能看出每个时段功率是否平衡。如果出来上下面积不闭合,说明约束处理还有问题。
第二张是"储能SOC与充放电功率曲线":SOC是平滑的锯齿状变化,充放电功率和SOC的增减方向必须匹配。这张图可以快速检验储能建模是否正确。
第三张是"碳排放与碳捕集量逐时对比图":柱状图放燃气轮机总排放量和捕集量,折线放净排放量。如果捕集量出现脉冲式的高峰,看是否与风电高发时段重合,核对该时段的功率平衡是否在合理范围内。这张图我能用于快速判断优化出来的策略是否符合低碳目标在时序上的预期。
7. 这套代码的扩展方向与实际工程应用建议
结构上这套代码为后续扩展留了比较大的余地。最常见的扩展方向包括:
第一,考虑不确定性。把风电和光伏的预测误差用场景法或机会约束的方式引入模型。比如可以生成多组风电场景,在日前调度时考虑最坏场景下的约束满足情况,这会显著增加计算量,但改进的粒子群算法相对于标准优化算法反而有了优势——因为粒子群天然适合并行计算场景评估。
第二,加入需求响应。把可平移负荷、可削减负荷作为新的决策变量。操作上只需在粒子编码中增加对应的控制变量维度(比如负荷平移量向量),并修改功率平衡约束的左侧负荷项即可,适应性很强。
第三,换成多目标优化。很多时候我们不仅关心经济成本,还要同时权衡碳排放量,这就是典型的多目标优化问题。可以把适应度函数改成非支配排序结构,参考NSGA-II的框架实现多目标改进PSO。所有前面说的约束处理技巧都可以原样复用,难度主要在拥挤距离排序和外部档案维护上。
当然,做工程应用来提高代码可信度,最好与商业求解器做个交叉验证。比如用Gurobi或Cplex求解同一个线性化模型,把结果作为参考解,对比改进PSO的解与全局最优解之间的差距百分比。我这条流程里使用了线性化处理与PSO对比,在典型工况下差距在2%以内,这个数据拿出来无论是写论文还是向领导汇报都比较有说服力。
最后再分享一个小细节:粒子群算法是多起点随机搜索算法,强烈建议在调用主函数之前先执行一次rng shuffle或用时间戳作为随机种子,避免每次跑出相同的初始化结果。做实验时记录种子值,出问题时可以复现,这样可以大幅减少"上次结果跑出来,这次怎么跑不出来了"的困惑。我在实际项目中吃过这个亏,现在所有涉及随机数的代码都会统一保存随机种子状态,这个习惯已经成了我的固定流程。