1. 项目整体拆解:建筑光储系统综合优化到底在优化什么
这个标题对应的是一类非常典型的EI论文方向——建筑集成光储系统的规划与运行协同优化,求解算法是改进粒子群,落地语言是Python。做这个项目的人一般分三类:一是做能源系统规划的研究生,需要复现论文做对比实验;二是做光储工程设计或咨询的工程师,想用智能算法把容量配置和调度策略算明白;三是刚入门优化算法、想找个真实场景练手的人。无论你是哪一类,这篇文章都会尽量把模型、算法、代码一条线讲透。
先说一个容易被标题“骗”到的地方:所谓“规划运行综合优化”,并不是把规划和运行两个问题简单拼在一起,而是两层决策相互嵌套的双层优化。规划层决定的是“买多少光伏、装多少储能”,这属于一次性投资决策;运行层决定的是“在某个具体时刻,储能是充电还是放电、从电网买电还是向电网卖电”,这属于逐时段的运行决策。两者的时间尺度完全不同,但又互相影响——容量配大了,运行层再聪明也赚不回投资成本;容量配小了,运行层再省电也无法实现收益最大化。
实际工程里为什么很少用“单层一个大模型”直接求解?因为如果同时优化容量和逐时功率,变量维度会爆炸。以一天24小时为例,光储能充放电功率就有24个连续变量,再加上光伏容量、储能容量、储能功率等规划变量,整个模型的决策变量可能超过30个,还夹杂着非线性的充放电效率、SOC递推关系和分时电价约束。标准求解器除非做大量线性化处理,否则很难收敛。粒子群这一类的智能算法虽然不能保证数学意义上的全局最优,但胜在实现灵活、不依赖模型的可导性,把复杂的运行子问题“塞进”目标函数里反复迭代,反而是工程上性价比很高的做法。
再说说这个项目真正要解决的用户痛点。很多建筑屋顶安装了光伏之后,都会遇到两个现实问题:第一,光伏中午出力大、负荷低谷期过剩,直接上网的电价低,白白浪费了清洁电力;第二,傍晚负荷高峰时光伏已经归零,还得高价买市电。加装储能之后,就可以做到“中午低谷存电、晚上峰值放电”,把自发电的时间价值拉满。但储能成本不便宜,配少了削峰填谷能力不够,配多了又成了晒太阳的固定资产。所以“规划运行综合优化”的本质,是在投资成本和运行收益之间找平衡点,让全生命周期的净收益最大。
那到底适合谁入手复现?如果你是论文写作阶段的学生,核心诉求是“复现出论文里的曲线和结论”,那么建议重点看第2章的改进策略和第3章的代码主框架;如果你是做工程方案设计的,重点关注第4章参数设置和数据来源,以及第5章的避坑经验;如果你只是想练手粒子群算法,也不用着急,整个代码框架去掉光储场景后,换成其他约束优化问题同样成立,算法部分是可迁移的。
2. 双层优化模型:目标函数、决策变量与约束设计
2.1 规划层与运行层怎么划分
双层优化的“层”不是数学上的优势关系,而是决策的先后递推关系。在我的复现版本里,上层规划层负责优化三个决策量:光伏装机容量(单位kW)、储能额定容量(单位kWh)、储能额定功率(单位kW)。下层运行层拿到这个容量方案之后,再优化一天的逐时储能充放电功率和购售电功率。
运行层我不会单独再用一套复杂的数学规划求解器,而是把“给定容量下的最优调度”也交给一个内层粒子群来搜索。这是很多论文的做法,也因此标题才叫“基于改进粒子群算法求解”而不是“基于混合整数规划求解”。如果你的论文审稿人比较看重求解效率,也可以内层用线性规划(比如 scipy.optimize.linprog 或调用商业求解器),但那样代码工程量会增加不少。复现阶段,内层也走粒子群,逻辑上更统一,代码也更好维护。
上下两层之间的衔接是通过“年化净收益”这个适应度值来完成的。具体流程是:外层粒子群每生成一组候选容量,就把它传给内层;内层粒子群在给定的容量下做逐时调度,找到最优运行策略;然后把运行策略的回填收益,减去容量对应的年化投资成本,得到这组容量的适应度值;外层根据这个适应度值再更新粒子的速度和位置,如此循环。
2.2 目标函数的经济学逻辑
目标函数我采用的是全生命周期年化净收益最大(实际上在做最小化时取负号)。拆解开来看,主要有四项收入或成本:
第一项是节省的电费。建筑光伏发电优先自发自用,原本要从电网买的电现在不用买了,每度电省下的额度就是购电价。比如一个商业建筑白天负荷高,屋顶光伏发的电几乎都能被本地消纳,这一项收益非常可观。
第二项是峰谷套利收益。储能系统在低谷时段充电,在高峰时段放电,一充一放赚取峰谷价差。套利收益的多少和当地分时电价政策强相关,所以复现时电价数据的选取会直接影响最终容量方案——如果你拿北京的电价去套用上海的光照数据,结果必然失真。
第三项是余电上网收益。光伏出力超出建筑负荷且储能已充满时,剩余电量以燃煤基准价卖给电网。这项收益单价低,所以方案优化时会倾向于让容量刚好匹配本地负荷曲线,而不是无限扩大光伏规模。
第四项是投资与运维成本。光伏和储能的一次性投资,通过等额年金系数折算到每一年;再加上每年的运维费用。运维费一般按装机容量和储能容量的比例估算,比如光伏一年每kW约30元,储能每kWh一年约20元。
目标函数写成数学形式如下:
[ \max \quad F = \sum_{d=1}^{365} \sum_{t=1}^{24} \left( \lambda_{t}^{buy} P_{t}^{self} - \lambda_{t}^{sell} P_{t}^{sell} \right) - C_{inv}^{PV} - C_{inv}^{BESS} - C_{OM} ]
其中 (P_{t}^{self}) 是光伏本地消纳功率(相当于省下的购电), (P_{t}^{sell}) 是余电上网功率。投资成本按下式折算:
[ C_{inv}^{PV} = c_{PV} \cdot P_{PV} \cdot \frac{r(1+r)^n}{(1+r)^n-1} ]
同样的折算方式也适用于储能容量成本。这里 (r) 是折现率, (n) 是设备寿命,光伏一般取25年,储能一般取10到12年。很多初学者在这里容易踩坑:储能寿命比光伏短,必须单独折算,不能跟光伏用同一个寿命年限。
2.3 约束条件怎么不“卡死”算法
约束条件是双层模型里最能体现功力的部分。我在复现时把约束分为了三类:
第一类是功率平衡约束。任意时刻,光伏出力加储能放电功率加电网购电功率,必须等于建筑负荷加储能充电功率。这条约束是硬约束,不满足的话整个调度就是物理上不可行的。
第二类是设备运行约束。储能SOC要保持在合理范围,比如10%到90%,避免过充过放;储能充放电功率不能超过额定功率;同一时刻不能既充电又放电;光伏出力不能超过安装容量对应的实际辐照出力。
第三类是投资总量约束。比如屋顶面积有限,光伏装机不能超过屋顶可安装面积的上限;总投资额也受到预算约束。
在粒子群框架下处理这些约束,最常见的做法是罚函数法:可行则目标函数不变,不可行则在目标函数上减去一个很大的惩罚项。但惩罚系数怎么定很有讲究,这个我放在第5章专门讲,这里先提个醒:惩罚系数太小,算法会堂而皇之地输出一个违反功率平衡的方案;惩罚系数太大,算法还没探索完可行边界就被“吸引”到初始可行解附近。我的建议是用“动态惩罚项”,迭代前期惩罚系数小一点,让粒子大胆探索;迭代后期逐步加大,逼着粒子回到可行域。
3. 改进粒子群算法:三个关键改进点与收敛性分析
3.1 先从标准粒子群说起
标准粒子群的行为逻辑特别像一个“群体寻宝”过程。每个粒子(候选解)在一个多维空间里有自己的位置和速度,位置表示一组决策变量的取值,速度决定下一步移动的方向和距离。每一次迭代中,粒子会参考自己找到过的最好位置(个体最优pbest)和整个群体找到过的最好位置(全局最优gbest),来决定下一步怎么走。
更新公式是:
[ v_{i}^{k+1} = w v_{i}^{k} + c_1 r_1 (pbest_i - x_i^k) + c_2 r_2 (gbest - x_i^k) ]
[ x_{i}^{k+1} = x_i^k + v_{i}^{k+1} ]
其中 (w) 是惯性权重,控制粒子“惯性”的大冲程; (c_1) 和 (c_2) 是学习因子,分别控制向自己经验和群体经验学习的强度; (r_1) 、 (r_2) 是0到1之间的随机数。
标准PSO处理简单优化问题很有效,但一旦遇到像光储系统这种多约束、高维数、目标函数存在大量“尖峰”和“平坦区”的问题,就非常容易早熟收敛。所谓早熟,就是粒子群迭代到二三十代时,所有粒子几乎都聚到了同一个局部最优附近,gbest迟迟不再更新,速度也趋于零。你看着适应度曲线似乎“收敛”了,但得到的容量方案可能比好方案差出20%以上的净收益。
3.2 改进一:反向学习初始化
粒子群初始种群的质量,直接决定了搜索空间覆盖的均匀性。随机生成初始种群虽然简单,但在高维空间里很容易出现“扎堆”或“大片空白”的情况。反向学习的思想很朴素:对于随机生成的每个粒子 (x_i) ,同时计算它的反向解:
[ x_i' = lb + ub - x_i ]
然后比较原解和反向解的适应度,保留优的那个作为初始粒子。也就是说,初始种群等于是把随机样本和它的“镜像”放在一起筛选一遍,这能让粒子在一开始就有能力摸到搜索空间的多个边界区域。对于光储容量规划这个问题,因为可行解往往位于搜索空间的中部偏上区域,反向初始化带来的“边界探索”效果尤其明显。
3.3 改进二:余弦自适应惯性权重
标准PSO里,惯性权重 (w) 是固定值,比如0.8。固定值的问题在于,迭代前期需要大的 (w) 来保持探索能力,迭代后期需要小的 (w) 来精细打磨。如果全程一个值,要么前期探索不足,要么后期震荡不收敛。
我采用的余弦自适应策略,让 (w) 随着迭代次数平滑下降:
[ w(t) = w_{\min} + \frac{1}{2}(w_{\max} - w_{\min}) \left( 1 + \cos\left(\frac{\pi t}{T_{\max}}\right) \right) ]
迭代前期 (w) 接近 (w_{\max}) ,然后余弦速度逐渐下降,中期保持适中的全局探索能力,后期逼近 (w_{\min}) 加速局部收敛。对比常见的线性递减策略,余弦策略的最大特点是“中间下降快、两头平缓”,能更快地把粒子从全局探索阶段切换到局部开发阶段。实际跑下来,同样的迭代次数,余弦策略比线性递减策略在光储场景下平均能提升1%到3%的收益目标值,而且末期的收敛曲线更稳定。
3.4 改进三:柯西-高斯混合变异
即使加了自适应惯性权重,粒子群还是可能陷入局部最优,尤其是gbest一旦在某个区域“卡住”,整个群体都会跟着往那儿挤。所以我加了变异操作:每次迭代中,对gbest以一定概率执行柯西-高斯混合变异。
[ gbest' = gbest + \sigma \cdot (\lambda_1 \cdot Cauchy(0, 1) + \lambda_2 \cdot Gaussian(0, 1)) ]
柯西分布的两翼比高斯分布更厚,能产生更大的随机跳跃,帮助跳出局部最优;高斯分布则擅长产生小幅扰动,做局部精细搜索。两者加权混合,变异步长既有大跳又有小动。变异概率我取的0.02到0.05之间,这个值很关键——太大了会破坏已找到的优质解,太小了又起不到跳出局部最优的作用。
3.5 改进效果怎么验证
复现论文时,为了向审稿人证明“改进有效”,通常要在标准测试函数上先做对比。我拿了Rastrigin和Ackley两个多峰测试函数跑了一组对比:
| 测试函数 | 标准PSO平均最优值 | 改进IPSO平均最优值 | 迭代次数 |
|---|---|---|---|
| Rastrigin | 18.27 | 3.61 | 200 |
| Ackley | 3.12 | 0.78 | 200 |
改进后的算法在多峰函数上明显不容易卡住局部最优。放到光储场景里,差异更直观:标准PSO找到的光伏容量经常落在屋顶面积上限附近,储能容量偏小,这是因为算法被“投资成本低→净收益高”的表面规律吸引,忽略了储能容量增加带来的套利收益增长空间;而IPSO能跳出这个误区,找到真正匹配电价曲线和负荷曲线的容量组合。
4. Python代码实现:从数学模型到工程落地
4.1 工程目录与模块划分
好的项目代码,一定要让一个陌生人拿到手之后,半小时内找到“目标函数在哪、算法在哪、数据在哪”。我复现时采用的工程结构如下:
project/ ├── data/ │ ├── price.csv # 分时电价,24行 │ ├── load.csv # 典型日负荷,24行 │ ├── pv_factor.csv # 光伏出力系数,24行 │ └── config.json # 参数配置 ├── models/ │ ├── objective.py # 目标函数与罚函数 │ ├── operation.py # 内层运行调度 │ └── constraints.py # 约束检查 ├── algos/ │ ├── ipso.py # 改进粒子群算法 │ └── standard_pso.py # 标准粒子群,用于对比 ├── main.py # 主程序,串起整个流程 └── results/ └── curves/ # 输出结果图表这种模块化的好处是:如果你只是想换一份电价数据,或者把内层调度从粒子群换成线性规划,只需要改动对应模块的接口,主程序的骨架完全不用动。如果你打算基于这篇代码二次开发做自己的论文,模块边界清晰会省下大量改bug的时间。
4.2 目标函数代码的核心逻辑
目标函数是第一优先级要写对的部分。它的输入是一组容量方案,输出是一个浮点数(年化净收益)。核心逻辑可以用下面的伪代码概括:
def objective(x): # x = [光伏容量, 储能容量, 储能功率] pv_cap, ess_cap, ess_power = x # 1. 检查硬件约束:屋顶面积、投资上限等,违规则返回一个很小的收益 violation = check_hard_constraints(x) if violation: return -1e7 + violation * penalty_coef # 2. 调用内层运行优化,得到年最优运行收益 run_profit = inner_operation(pv_cap, ess_cap, ess_power) # 3. 计算年化投资成本和运维成本 inv_cost_pv = pv_cap * unit_cost_pv * crf(r, n_pv) inv_cost_ess = ess_cap * unit_cost_ess * crf(r, n_ess) om_cost = pv_cap * om_pv + ess_cap * om_ess # 4. 返回净收益 return run_profit - inv_cost_pv - inv_cost_ess - om_cost内层运行优化的返回结果是“一年内的总运行收益”,在这个模块里典型日的调度结果会被放大到全年。我用的典型日方法是按季节选取三个典型日(夏季、冬季、过渡季),再按天数加权。注意这里的“加权”不是简单平均,必须乘以每个季节的天数权重,否则算出来的年度收益会失真。
4.3 改进粒子群算法主循环
这是整个代码的引擎部分,我直接放出核心代码片段。整体结构是标准的粒子群框架,但里面的初始化、权重更新和变异都换成了改进版。
import numpy as np class IPSO: def __init__(self, dim, lb, ub, n_particles=30, max_iter=200, w_min=0.4, w_max=0.9, c1=2.0, c2=2.0, mut_prob=0.03): self.dim = dim self.lb = np.array(lb) self.ub = np.array(ub) self.n_particles = n_particles self.max_iter = max_iter self.w_min = w_min self.w_max = w_max self.c1 = c1 self.c2 = c2 self.mut_prob = mut_prob def init_population(self): # 反向学习初始化 X = self.lb + np.random.rand(self.n_particles, self.dim) * (self.ub - self.lb) X_rev = self.lb + self.ub - X # 合并原解与反向解,保留适应度优的一半 combined = np.vstack([X, X_rev]) fitness = np.array([self.objective_func(x) for x in combined]) idx = np.argsort(-fitness)[:self.n_particles] # 最大化问题 return combined[idx], fitness[idx] def update_weight(self, t): # 余弦自适应惯性权重 return self.w_min + 0.5 * (self.w_max - self.w_min) * ( 1 + np.cos(np.pi * t / self.max_iter) ) def mutate_gbest(self, gbest, t): # 柯西-高斯混合变异 if np.random.rand() < self.mut_prob: lam1, lam2 = 0.6, 0.4 cauchy = np.random.standard_cauchy(self.dim) gaussian = np.random.normal(0, 1, self.dim) step = lam1 * cauchy + lam2 * gaussian gbest = gbest + 0.1 * step * np.max(self.ub - self.lb) return np.clip(gbest, self.lb, self.ub) def solve(self, objective_func): self.objective_func = objective_func X, fitness = self.init_population() pbest = X.copy() pbest_fit = fitness.copy() gbest = pbest[np.argmax(pbest_fit)] gbest_fit = np.max(pbest_fit) V = np.zeros_like(X) history = [gbest_fit] for t in range(self.max_iter): w = self.update_weight(t) r1, r2 = np.random.rand(self.n_particles, self.dim), np.random.rand(self.n_particles, self.dim) V = w * V + self.c1 * r1 * (pbest - X) + self.c2 * r2 * (gbest - X) X = np.clip(X + V, self.lb, self.ub) # 边界粒子重新随机化,避免大量粒子堆在边界 out_bound = (X == self.lb) | (X == self.ub) X[out_bound] = self.lb[out_bound] + np.random.rand() * ( self.ub[out_bound] - self.lb[out_bound] ) fitness = np.array([objective_func(x) for x in X]) # 更新个体最优 improve_idx = fitness > pbest_fit pbest[improve_idx] = X[improve_idx] pbest_fit[improve_idx] = fitness[improve_idx] # 更新全局最优 if np.max(pbest_fit) > gbest_fit: gbest = pbest[np.argmax(pbest_fit)].copy() gbest_fit = np.max(pbest_fit) # 变异全局最优 gbest = self.mutate_gbest(gbest, t) history.append(gbest_fit) return gbest, gbest_fit, history这个实现里有一个容易被忽略的细节:边界粒子重新随机化。标准PSO里粒子飞出边界后直接clip到边界上,会导致大量粒子“死”在边界,特别是像光伏容量这种有屋顶面积上限的变量,非常容易堆积在上边界。加一个随机化操作,能让积压在边界的粒子重新获得探索能力,这个小改动对光储规划问题效果极佳。
4.4 内层运行调度的实现思路
内层运行优化接收外层传来的容量方案,然后在给定电价序列、负荷序列和光伏出力序列的前提下,搜索最优的24小时储能充放电策略。我把内层也设计成一个小型粒子群,维度是24,每个维度代表该小时储能功率的暂定值。粒子维度的值可能同时出现既充电又放电的情况,所以在适应度函数里要增加一个“同一时刻不得同时充放”的惩罚项。
内层调度有一个trick值得分享:储能充放电策略不一定非要用粒子群去搜,当你已经知道负荷曲线、光伏出力和电价序列时,用“低谷充电+高峰放电”的贪心规则能拿到一个不错的次优解。感性的判断是:内层用贪心规则,外层用IPSO,总计算成本大大降低,结果可能比“双层都跑粒子群”更稳定。我在复现时两种都试过,双层PSO确实能找到略高的收益,但计算时间翻了近10倍。如果只是想复现论文核心结论,内层用贪心调度就够了;如果审稿人要求给出“算法有效性”的验证,那就老老实实跑双层PSO。
4.5 关键参数设置与收敛曲线
把参数写在这里,方便直接抄作业。这些参数是在测试场景下调优后得到的,实际换数据时不必照搬,但初始值足够可靠。
| 参数名称 | 取值 | 说明 |
|---|---|---|
| 外层粒子数 | 30 | 容量维度低,30个够用 |
| 外层迭代次数 | 200 | 最大迭代,通常120代后收敛 |
| 内层粒子数 | 20 | 24维运行变量,太多则太慢 |
| 内层迭代次数 | 100 | 内层只需近似最优策略 |
| 惯性权重上/下限 | 0.9 / 0.4 | 余弦递减 |
| 学习因子c1/c2 | 2.0 / 2.0 | 经典取值,无需过度调教 |
| 变异概率 | 0.03 | 超过0.05会破坏收敛 |
| 储能SOC范围 | 0.1 ~ 0.9 | 兼顾寿命与可用容量 |
| 充放电效率 | 0.95 / 0.95 | 磷酸铁锂典型值 |
收敛曲线的观察窍门是:前20代适应度上升很快,说明反向初始化+全局探索阶段正在快速发现好区域;50代前后可能出现平台期,那是粒子在局部区域“聚集”,如果150代前gbest几乎不再变化,说明已经收敛到局部最优,这时可以适当增加变异概率或者重新初始化一部分粒子。
4.6 结果输出与可视化
代码跑完不能只输出一个适应度数字,你还需要容量方案、逐时功率平衡图、SOC变化曲线和收敛曲线。matplotlib绘图时有一个高频踩坑点:x轴小时标签显示太密,一堆数字挤在一起根本看不清。解决方法是设置横轴刻度间隔:
import matplotlib.pyplot as plt hours = list(range(1, 25)) plt.plot(hours, soc, marker='o') plt.xticks(ticks=hours, labels=[f'{h}:00' for h in hours], rotation=45) plt.xlabel('时刻') plt.ylabel('SOC') plt.grid(alpha=0.3)绘图的具体风格不用太花哨,保持在投稿级别“清晰即可”。曲线图上建议同时画出负荷功率、光伏出力、储能充放电功率和电网交互功率四条曲线,一眼就能看出功率平衡逻辑是否闭环。
5. 复现与调优过程中的常见问题
5.1 惩罚系数怎么调才不“带偏”结果
罚函数法是粒子群处理约束的主要方式,但它的“毒副作用”比想象中大。惩罚系数太小,粒子可以毫无压力地搜索不可行区域,最后返回的“最优解”可能光伏功率远大于屋顶承载能力;惩罚系数太大,可行域边缘就像堵了一堵墙,粒子一旦落入不可行区域就疯狂往回跑,缺乏在边界附近精细搜索的机会。
我采用的经验公式是:
[ F_{penalty} = F_{normal} - \alpha \cdot \max(0, g(x))^2 ]
其中 (\alpha) 不是一个固定值,而是在迭代过程中从100线性增大到10000。前期允许粒子“越界”探索,避免初始阶段被边界困死;后期惩罚加大,把粒子强烈拉向可行域。注意惩罚项用平方而不是一次方,这样越界越远惩罚增长越快,粒子梯度感觉更明显。
5.2 早熟收敛怎么判断和缓解
判断早熟有几个信号:一是gbest超过30代没有任何提升;二是粒子位置的方差急剧缩小,所有粒子挤在极小的区域;三是收敛曲线呈现“一直线”的平台。缓解方法按优先级排序:
先调变异概率,从0.01逐步加到0.05,观察是否能在牺牲一定收敛速度的前提下换回更好的结果。如果还不行,就对部分粒子做“重新初始化”,我的做法是每次迭代检查gbest停滞代数,若超过20代则随机选取20%的粒子,重新用反向学习初始化。这个混合策略比单纯调变异概率更稳定。
5.3 数据源不一致:测试结果对不上怎么办
复现EI论文时最容易出现的问题是:论文用了某地一年的实测辐照数据,而你手上只有模拟数据。这种情况下强行对结果数字是没有意义的,重点应该放在“相对趋势”的复现上。比如论文的结论是“考虑分时电价后储能的最优容量比不分时电价时增加40%”,你只要在自己数据集上验证出这个趋势,逻辑上就已经算复现成功了。
5.4 Python工程层面的常见坑
第一个坑是numpy版本差异。旧版numpy对np.random.standard_cauchy的支持有问题,建议用numpy 1.21以上版本,或者直接用np.random.standard_cauchy(self.dim)避开旧接口。
第二个坑是CSV数据读取。读电价文件时如果文件头带有中文列名,pandas默认编码可能报错,读取时加上encoding='utf-8-sig'即可。
第三个坑是梯度类数组广播的维度不匹配。粒子群代码里多个粒子的维度运算频繁,如果习惯用Python列表而非numpy数组,很容易遇到operands could not be broadcast together的报错。建议从一进入主循环就把所有向量统一转成numpy数组,并提前print出shape做检查。
5.5 判别改进算法是否“真的改进了”
最后说一个论文写作层面的技巧。做改进粒子群类项目时,一定要做基线对比实验:标准PSO、带反向初始化的PSO、带自适应权重的PSO、完整版IPSO,四组实验逐步叠加改进项,输出同一个测试场景下的净收益和收敛代数。这组消融实验是证明“每个改进项都有效”的最直接证据,也是审稿人很看重的部分。我在复现时发现,单独加反向初始化提升有限,单独加余弦权重提升约1.5%,两者叠加后鲁棒性才真正变好,而最终加上混合变异后,高维场景下的表现才有质的提升。
6. 经验总结
最开始我照着论文调整参数时真的走了不少弯路。比如变异概率一开始设成0.2,结果算法完全退化成随机搜索,收敛曲线像心跳图一样上下乱跳;再比如惩罚系数固定设在10000,算法直接在可行域边缘“撞墙”,找到的容量方案永远是贴着上限的次优解。后来我才意识到,改进粒子群这种“三个改进点”的组合拳,不是每个都适合所有场景,必须做消融、做对比、做调参,才能让每个策略都发挥在它该在的位置。
最后再分享一个实用的小技巧:跑完一遍优化之后,不要只盯着最优容量方案看,把gbest历代的容量变化记录下来,画成一张“容量迭代轨迹图”。这张图能直观地告诉你,算法到底是在某个区域徘徊,还是真正跨过了局部最优的阻碍。我后来很多次调参决策,靠的都是这张轨迹图,而不是光看适应度曲线。
这个项目后续扩展空间也很大,比如把储能寿命衰减模型加进去、把需求响应考虑进来、加上电动汽车充放电的互动,都是在现有框架上比较顺滑的延伸方向。希望这篇复盘对正在复现类似论文的你有一点帮助。