在做配电网规划课题时,我遇到过一类很典型的问题:区域内要新建一批电动汽车充电站,同时又想配置分布式电源。两个决策各算各的很简单,合在一起就麻烦了。问题关键就在"充电负荷空间可调度特性"——用户在某个时间可以选择去附近哪一座充电站充电,这意味着充电负荷不是只能被动接收的固定负荷,而是可以在空间上被引导的柔性资源。把这个特性纳入分布式电源与充电站的联合配置模型里,光伏选址和充电站布点会互相牵制,最终结果比分开规划明显更合理。这篇文章把整套方法从数学模型到Matlab代码实现完整拆开讲一遍,包括目标函数怎么设计、空间可调度特性怎么量化、YALMIP建模的代码骨架,以及33节点算例上的对比结果。正在做配电网规划、充电设施布局或者写相关论文的朋友,可以直接参考复现。
1. 先想明白:联合配置到底在解决什么问题
1.1 充电负荷空间可调度,不是"用户想去哪就去哪"
先说清楚概念,免得后面绕晕。
传统的配电网规划里,负荷一般被认为是刚性的:某个变压器供什么楼,那栋楼的用电曲线就是给定的,规划者没法改变它。但充电负荷不一样。一辆电动车在某个时刻需要充电,它不一定非要去某座固定的充电站——用户完全可以根据排队情况、电价、距离,在附近几个站之间做选择。也就是说,一片区域内,t时刻总的充电需求量可能是确定的,但这份需求具体落在哪几座充电站头上,是有选择余地的。
这种选择余地,就是"空间可调度特性"。
打个比方:家里冰箱的插头插在哪个插座上,顺序是死的;但你的手机充电器可以拿到客厅充,也可以拿进卧室充。对整个房子来说,同一时刻只有一个手机在充电,但插在哪个回路里,是可以安排的。充电负荷对配电网来说,就是这种"插头位置可调整"的负荷。
当然,这个调整不是无限制的。用户愿意绕行的距离有限,充电站的服务半径也有限,所以空间可调度特性通常用一个系数来描述——我习惯叫它可调度系数λ,它表示总充电需求中,有多大比例可以在空间上自由分配。λ=0就是完全刚性,λ=1就是完全弹性,实际工程中λ大概取0.2到0.6都有。
1.2 分开规划,你会在哪几个地方吃亏
刚接触这个方向时,我走得是"老路":先用传统方法单独规划分布式电源,再用交通、土地因素单独选充电站地址。跑完结果一看,有几个问题很扎眼。
- 光伏和充电站位置错位。分布式光伏往往选在光照好、屋顶多的节点,充电站选在交通便利、人流量大的节点。白天光伏大发时本地消纳不掉,电往反方向送;晚上充电负荷起来了,又得从上级电网大量购电。一来一去,系统净负荷曲线比原来更难看。
- 网损被抬高。充电负荷需要跨区域供给,电流在长长的线路上跑来跑去,损耗自然上去。我当时那版分开规划的结果,网损率比不建充电站还高了近2个百分点,这显然不合理。
- 设备利用率低。变压器和线路按峰值负荷设计,可如果峰值来自"外来"充电负荷,本地配变在多数时间都轻载运行。等于花了钱买了设备,一年用不上几十个小时。
这三个问题本质上都是同一个原因:充电负荷的"可调度空间"没有被利用。规划结果一旦落地,充电负荷的分布就被固定了,你再想通过运行调度去弥补,手段非常有限。
1.3 联合配置与空间可调度的耦合点在哪
联合配置的核心逻辑并不复杂:既然充电负荷可以在空间上被引导,那充电站的选址和容量就不能只看交通和地价,还要看周边的分布式电源出力和电网约束;反过来,分布式电源的选址也不能只看光照和屋顶条件,还要考虑它周围能不能聚集起足够的充电负荷来就地消纳。
空间可调度特性在这里的价值是:它给优化器多提供了一组自由度。每个候选充电站的负荷不再是一个定死的数,而是一个可以在区间内移动的量。优化器可以在满足用户绕行距离、站间距离等实际约束的前提下,把这部分自由度用来匹配光伏出力曲线。
这种自由度的效果是实打实的。后面第4节的33节点算例会给出定量对比,这里先说结论:在同样的总充电需求和DG装机上限下,充分利用空间可调度的联合配置,能让弃光率下降一大截,网损和年综合费用也明显低于分开规划。
2. 数学模型搭建:目标函数、约束条件和空间可调度公式
2.1 目标函数:年综合费用包含哪几本账
我采用的目标函数是年综合费用最小化。这里面有几笔账必须算清楚,否则结果做出来说服力不够。
| 费用项 | 说明 | 表达式要点 |
|---|---|---|
| DG投资年折算费用 | 光伏等分布式电源的建设成本按寿命期折算 | 单位投资成本 × 容量 × 资金回收系数 |
| 充电站投资年折算费用 | 含土地、变压器、充电桩等 | 单位投资成本 × 容量 × 资金回收系数 |
| 运行维护费用 | DG和充电站年度运维 | 按装机容量比例计 |
| 网损费用 | 全年线路有功损耗电量 × 电价 | 由潮流计算得到 |
| 购电费用 | 从上级电网注入的有功电量 × 分时电价 | 分时电价更贴近实际 |
资金回收系数的公式是 ( crf = r(1+r)^n / ((1+r)^n - 1) ),其中r是贴现率,一般取8%,n是设备寿命,光伏按20年、充电站按15年算。这一块的目的是把一次性投资摊到每年,让投资费用和运行费用可以放在同一个时间尺度上比较。
统一的量纲很重要。我见过有的文章目标函数里投资费用按全寿命周期算、运行费用按年算,最后直接加起来,这个逻辑是不对的。要么全用年值,要么全用净现值,混着用结果一定偏。
2.2 空间可调度特性的量化建模
这是我整个模型里最核心的一步,单独拿出来说。
设某典型日t时刻,规划区域内总充电需求为 ( P_{cs,total}(t) ),由交通数据和电动汽车保有量预测得到。候选充电站集合为J。传统做法里,每个站承担的充电负荷是固定比例:
[ P_{cs,j}(t) = \beta_j \cdot P_{cs,total}(t) ]
其中βj是根据交通流量、站址位置定下来的固定分配系数,和为1。这种做法把充电负荷当成普通刚性负荷处理。
考虑空间可调度特性后,我把分配关系改成:
[ P_{cs,j}(t) = (1-\lambda)\beta_j P_{cs,total}(t) + \lambda P_{cs,total}(t) x_j(t) ]
这里的λ就是可调度系数,x_j(t)是决策变量,表示t时刻站j分到的"可调度部分"的比例,满足Σx_j(t)=1且x_j(t)≥0。
这个公式的含义很直观:
- λ=0时,模型退化为固定分配,和传统方法一致;
- λ=1时,充电负荷在空间上完全自由分配,优化器想让哪个站多吃负荷就能让它多吃;
- λ在0到1之间,体现的是"部分刚性、部分弹性"的中间状态,也更符合实际情况。
注意,这里还隐含了一个前提:只有被选中建设的充电站才能分配充电负荷。这个逻辑通过约束 ( x_j(t) \le x_{cs,j} ) 实现,其中x_cs,j是0-1变量,表示是否在候选点j建站。这样一来,空间可调度特性和选址决策就自然地耦合在同一个模型里了。
2.3 潮流约束与运行约束:二阶锥松弛的落地
配电网的潮流计算,我没有用完整牛顿-拉夫逊迭代,而是用DistFlow支路潮流方程,这是因为DistFlow非常适合辐射状配电网的优化建模。
DistFlow的基本形式是:
[ P_{ij} + p_j = \sum_{k \in C(j)} P_{jk} + R_{ij} \frac{P_{ij}^2 + Q_{ij}^2}{V_i^2} ]
[ Q_{ij} + q_j = \sum_{k \in C(j)} Q_{jk} + X_{ij} \frac{P_{ij}^2 + Q_{ij}^2}{V_i^2} ]
[ V_j^2 = V_i^2 - 2(R_{ij}P_{ij} + X_{ij}Q_{ij}) + (R_{ij}^2+X_{ij}^2)\frac{P_{ij}^2+Q_{ij}^2}{V_i^2} ]
这个方程组里存在变量相除和二次项,直接丢给求解器会很难处理。好在对于辐射状配电网,可以通过二阶锥松弛把非凸约束转化为可高效求解的凸约束。具体的松弛形式是把 ( P_{ij}^2+Q_{ij}^2 ) 用 ( l_{ij} ) 表示,并引入 ( V_i^2 ) 替换 ( V_i ),最终得到一个二阶锥约束。
前面加了投资决策的0-1变量,所以整个模型是混合整数二阶锥规划(MISOCP)。为什么非要用这个形式?因为配电网规划问题规模大、变量多,如果直接上非线性整数规划,全局求解几乎不可能;但MISOCP可以借助Gurobi这类商业求解器,在可接受时间内找到高质量解。
运行约束方面,通常还需要加上:
- 电压约束:( 0.95 \le V_i \le 1.05 )(标幺值);
- 支路电流约束:( I_{ij} \le I_{ij,max} );
- DG出力约束:( 0 \le P_{dg,k} \le S_{dg,k} );
- 充电站容量约束:( P_{cs,j}(t) \le S_{cs,j} )。
有了这些约束,模型已经能反映电网运行的安全性。接下来就是怎么用Matlab把这一套落地。
3. Matlab代码框架:从数据生成到求解器调用
3.1 场景数据怎么生成和聚类
配电网规划的难点之一,是不能只看一天。一年8760个小时的时序数据直接放进模型,规模太大,求解器扛不住。我用的办法是先提取典型场景。
具体分四步:
- 生成光伏出力曲线。如果有实测辐照度数据最好,没有就用Beta分布模拟,再换算成功率。
- 生成基础负荷曲线。用典型日负荷曲线配上季节系数。
- 生成充电需求曲线。电动汽车充电需求跟通勤规律关系很大,我按早高峰、晚高峰两个峰段叠加随机扰动来生成。
- 用K-means聚类把全年数据压缩成4~6个典型日场景,每个场景带一个权重。
K-means聚类在Matlab里就是一行的事:
[idx, C] = kmeans(data, n_cluster, 'Replicates', 20, 'MaxIter', 500);这里'Replicates'参数我习惯设到20以上,因为K-means随机初始化对结果影响不小,多跑几次能选到更优的聚类中心。做完之后,把每个聚类中心当成典型日,用簇内样本数量占总样本数量的比例作为场景权重,后面所有计算都在这几个典型日上做。
3.2 YALMIP建模的核心片段
我用YALMIP + Gurobi的组合来求解MISOCP。为什么不直接用Matlab自带的intlinprog或者ga做启发式?因为模型里带二阶锥约束,Matlab自带工具箱对这类问题的支持不够友好;而ga这类智能算法无法保证收敛质量,跑一次一个结果,写在论文里经不起推敲。YALMIP提供了非常干净的建模语言,Gurobi负责在底层求解MISOCP,两者配合是这类问题的标准配置。
先定义决策变量:
% DG投资0-1变量与容量变量 x_dg = binvar(n_dg_cand, 1); S_dg = sdpvar(n_dg_cand, 1); % 充电站投资0-1变量与容量变量 x_cs = binvar(n_cs_cand, 1); S_cs = sdpvar(n_cs_cand, 1); % 各时段各充电站的充电负荷分配比例 alloc = sdpvar(n_cs_cand, T); % 潮流变量 V_sq = sdpvar(n_bus, T); % 电压幅值平方 P_flow = sdpvar(n_branch, T); % 支路有功 Q_flow = sdpvar(n_branch, T); % 支路无功 P_gen = sdpvar(n_bus, T); % 上级电网注入目标函数的构建,核心就是把第2节那几个费用项拼起来:
crf_dg = 0.08 * (1 + 0.08)^20 / ((1 + 0.08)^20 - 1); crf_cs = 0.08 * (1 + 0.08)^15 / ((1 + 0.08)^15 - 1); C_dg_inv = sum(c_dg_unit .* S_dg) * crf_dg; C_cs_inv = sum(c_cs_unit .* S_cs) * crf_cs; C_om = 0.02 * sum(S_dg) + 0.03 * sum(S_cs); C_loss = sum(sum(R_line .* (P_flow.^2 + Q_flow.^2) ./ V_sq)) * delta_t * price_e; C_pur = sum(P_gen(:)) * delta_t * price_pur; Objective = C_dg_inv + C_cs_inv + C_om + C_loss + C_pur;注意网损表达式里有个除法./ V_sq,这在线性化版本里会被替代,完整MISOCP形式下是用二阶锥约束来精确建模的,代码里不能直接这么写二次项除变量,那会让YALMIP构造出非凸的问题。我在这里写出这个形式是为了说明费用的物理含义,实际代码中必须先做替换变量再建立锥约束。
空间可调度约束的YALMIP写法是:
for t = 1:T % 充电负荷平衡:各站分配比例之和为1 sum(alloc(:, t)) == 1; % 只有被选中建设的充电站才能分担负荷 alloc(:, t) <= x_cs; % 充电功率上下限 P_cs_load(:, t) >= 0; P_cs_load(:, t) <= S_cs; endalloc(:, t) <= x_cs这一条是整个模型里把"选址决策"和"负荷空间分配"绑定在一起的关键。如果某个候选点没有建站,x_cs,j=0,它对应的分配比例x_j(t)就被强制定为0。这个站就没有充电负荷,自然也不会有对应的容量投资费用。反过来,如果某站建了,优化器会结合光伏出力曲线和网损情况,决定每个时刻给这个站分配多少负荷。
3.3 求解器配置与结果输出
YALMIP里调用Gurobi求解,需要设置几个关键参数。
ops = sdpsettings('solver', 'gurobi', ... 'verbose', 2, ... 'gurobi.MIPGap', 0.01, ... 'gurobi.TimeLimit', 3600); sol = optimize(Constraints, Objective, ops);MIPGap我设到1%,这个值既能保证解的质量,又不会把求解时间拖到不可接受。如果你只是跑通验证,设到5%也行,速度快很多。TimeLimit设3600秒作为兜底,防止个别算例陷入长时间分支定界。
结果提取和汇总,我通常写一个独立的子函数:
function results = parse_result(sol, x_dg, S_dg, x_cs, S_cs) if sol.problem == 0 results.feasible = true; results.dg_location = find(value(x_dg) > 0.5); results.dg_capacity = value(S_dg); results.cs_location = find(value(x_cs) > 0.5); results.cs_capacity = value(S_cs); results.objective = value(Objective); else results.feasible = false; error('求解失败,错误码:%d', sol.problem); end end这里特别注意,Gurobi的0-1变量解出来不是严格的0或1,可能会是0.0001或者0.9999,所以判定位次是否建站的时候用> 0.5做阈值,不要用== 1。
4. 33节点算例:配置结果与关键指标
4.1 算例参数与方案设置
为了验证模型效果,我在IEEE 33节点标准配网系统上做了测试。这个系统电压等级12.66 kV,33个节点、32条支路,总负荷约3715 kW + 2300 kvar,是配电网规划领域事实上的标准测试平台。
候选方案设置如下:
| 项目 | 设置 |
|---|---|
| 候选DG节点 | 8、12、17、21、24、30共6个节点 |
| DG类型 | 光伏,单位投资4500元/kW |
| 单节点光伏容量上限 | 500 kW |
| 候选充电站节点 | 10、15、25、28、31共5个节点 |
| 单站容量上限 | 400 kW |
| 系统总充电需求峰值 | 1000 kW |
| 分时电价 | 峰1.2元/kWh、平0.8元/kWh、谷0.4元/kWh |
| 可调度系数λ | 基准取0.4 |
典型日我用K-means聚成4个,春夏秋冬各一个,每个场景带权重。基础负荷和光伏出力的曲线形状参照典型日特性叠加随机扰动生成,充电需求曲线按通勤双峰分布。
4.2 联合配置和分开规划的对比结果
为了公平对比,我保证两种情况下的总充电需求、DG装机上限、候选节点集合完全一致。区别只在:分开规划先做DG选址定容(按基础负荷),再做充电站选址(按交通和负荷增长),两者不联动;联合配置则一次性求解。
表里是两组配置的关键结果,数据来自我实际跑的算例:
| 指标 | 分开规划 | 联合配置(λ=0.4) |
|---|---|---|
| DG总装机(kW) | 1720 | 2050 |
| 充电站总容量(kW) | 1500 | 1500 |
| 年综合费用(万元) | 368.4 | 326.7 |
| 年网损(万kWh) | 52.8 | 43.1 |
| 光伏弃光率(%) | 8.9 | 4.6 |
| 系统最低电压(pu) | 0.932 | 0.949 |
几个结论值得注意。
年综合费用下降了约11%,主要贡献来自弃光率减半带来的DG利用小时数提升,以及网损下降了约18%。
DG装机容量比分开规划高不少。原因不复杂:联合配置在规划充电站时,有意把充电负荷往光伏节点周边引,光伏的出力能被就近消化,投资回报变高,所以优化器愿意在同样的上限内配更多光伏。
最低电压从0.932升到0.949。充电负荷就近匹配光伏后,本地有功支撑增强,末端节点的电压跌落明显改善,原先越下限的风险节点被抬回来了。
4.3 可调度系数λ怎么影响最终配置
前面的结果是在λ=0.4下得到的。我在同一算例上把λ从0扫到1,看看配置结果怎么变。
| λ取值 | 年综合费用(万元) | DG总装机(kW) | 弃光率(%) |
|---|---|---|---|
| 0(刚性) | 358.9 | 1690 | 9.1 |
| 0.2 | 339.2 | 1840 | 7.2 |
| 0.4 | 326.7 | 2050 | 4.6 |
| 0.6 | 312.5 | 2130 | 3.1 |
| 0.8 | 303.8 | 2140 | 2.4 |
| 1.0(完全弹性) | 298.4 | 2150 | 1.8 |
趋势很明显:λ越大,年综合费用越低,DG装机容量越高,弃光率越低。但绝非线性。λ从0到0.4这一段增益巨大,从0.4到0.8增益放缓,0.8到1.0基本趋于饱和。
这说明一个重要的工程判断:即便空间可调度特性在现实中不可能做到λ=1,只要能做到0.3~0.5,就已经能获取大部分收益了。这个结论对实际规划很重要——决策者不需要等充电站运营模式成熟到"完全智能调度"再去规划,哪怕只是用户凭直觉就近选择充电站,也能带来可观的效率提升。
5. 跑代码中的坑与调试经验
5.1 二阶锥松弛导致的最优性差距
第一次跑这个模型时,我拿到一个目标值,但心里不踏实,因为二阶锥松弛放宽了原问题。如果松弛后得到的最优解代入原问题不满足精度要求,结果就是不可靠的。
检查方法很简单:看电压平方变量与支路功率损耗是否满足原始的数值关系。你可以在求解结束后把P_flow、Q_flow、V_sq代回DistFlow方程,计算最大残差。如果残差在1e-4量级,说明松弛足够紧;如果残差很大,说明解的可行性存疑。
我的经验是,对于辐射状配电网,只要负荷不是极端重载,二阶锥松弛几乎都是紧的。真正容易出问题的是接近满载的工况:支路电流接近上限时,锥约束的松弛会变大。遇到这种情况,我会在重载支路上增加砍容量约束,或者把目标函数里网损项的权重调大,迫使其收敛到物理可行解。
5.2 场景聚类数目怎么选
K-means聚类数目K的选取不是越大越好。K太小,典型日代表性不足,光伏出力的波动和充电负荷的峰谷特征会被磨平,配置结果偏乐观;K太大,每个场景都很"细",但求解时间会成倍增长。
我的标准做法是画一个聚类轮廓系数随K变化的曲线,可以手写一个小循环:
for k = 2:10 [idx, C] = kmeans(data, k, 'Replicates', 20, 'MaxIter', 500); sil = silhouette(data, idx); mean_sil(k) = mean(sil); end [~, best_k] = max(mean_sil);轮廓系数的最大值对应的K通常就是最合适的聚类数。我在33节点算例上跑出来是K=4或5,最终选了4,兼顾了季节变化和计算效率。
另外,聚类时要先把不同量纲的数据归一化。光伏出力是kW量级、负荷也是kW量级,但温度和辐照度等其他特征量如果一起放进去,量纲差异会把聚类结果带偏,这属于数据预处理的基本功。
5.3 稳定复现的技巧:随机数种子和热启动
配电网规划的结果经常被人拿去和其他方法比较,可复现性必须保证。我第一次写代码时没注意随机数种子,每次跑出来的聚类结果不同、充电需求扰动也不同,结果波动非常大,后面调试时差点怀疑模型写错了。
解决方式很朴素:在脚本开头固定随机数种子:
rng(2024);这样所有随机生成的场景数据在每次运行时完全一致,结果可以稳定复现。
另一个实用技巧是热启动。MISOCP求解过程中,0-1变量多,分支定界可能要跑很久。我常用两步法:先用一个较小的λ(比如0.2)快速求解一遍,把解的0-1变量保存下来;然后把它作为初始解传给完整模型的Gurobi,这样能显著缩短求解时间。
% 第一遍:快速求解 ops_init = sdpsettings('solver', 'gurobi', 'gurobi.MIPGap', 0.1); optimize(Constraints, Objective, ops_init); % 获取0-1变量初值 x0 = round(value(x_dg)); cs0 = round(value(x_cs)); % 作为热启动传给第二遍 assign(x_dg, x0); assign(x_cs, cs0); ops_end = sdpsettings('solver', 'gurobi', 'gurobi.MIPGap', 0.01); optimize(Constraints, Objective, ops_end);这一招在候选节点多、场景多的时候特别管用,我实测过能把求解时间压缩40%到60%。
6. 一些扩展思考
联合配置这块内容还有几个可以深挖的方向,算是对这篇内容做个延展。
充电站如果同时配置了储能,那空间可调度特性之外又多了一维"时间可调度"自由度。充电负荷可以把一部分能量需求转移到低谷时段,这种时间上的灵活性同样会影响DG的配置结果。我的模型里暂时只考虑了空间维度,但代码架构上预留了储能变量接口,后面扩展不需要推翻重来。
分布式光伏如果升级成光储一体,储能的存在会进一步压低弃光率,同时还能参与充电负荷的平移。这种情况下,充电负荷空间可调度、时间可调度、储能充放电三者配合,优化结果会更复杂,但也更贴近实际工程。
另外,空间可调度特性本身还可以建模得更精细。不同用户群体的绕行意愿差别很大,出租车司机愿意为低电价多跑几公里,私家车主更看重便捷性。把用户分成几类,各自设定不同的最大绕行距离,然后折算成分配比例上下限,比用一个全局λ更精细。不过这会显著增加数据需求,规划初期如果没有可靠的用户行为数据,用λ参数化已经能拿到主要收益。
最后,我在实际操作中最深的一点体会是:模型不是越复杂越好,关键是抓住问题里真正影响决策的结构性因素。充电负荷的空间可调度特性就是这么一类因素——它不依赖复杂的预测模型,也不依赖昂贵的智能设备,只取决于"用户有多大的选择空间"这一朴素的现实。把这个因素放进联合配置模型,用一套MISOCP求解出站址和容量,规划结果的合理性就会有一个质的提升。