做配电网研究这些年,最让我觉得值得写一篇文章分享的,就是空调负荷优化这一点。每年夏天,傍晚光伏出力开始往下掉,空调负荷却踩着高峰往上蹿,配电网的变压器和线路被压得喘不过气。而另一边,白天光照最好的时候,光伏又呼呼往电网里送电,出现倒送功率、电压越限。一个要削峰,一个要消纳,两件事拧在一起,很自然的思路就是让空调负荷动起来,跟着可再生出力的节奏走。这篇东西我就围绕“含可再生能源的配电网最佳空调负荷优化控制”,把问题建模、Matlab实现、算例分析和踩坑经验完整记录下来,给正在做类似课题的同学和工程师一个可以直接参考的路线。
1. 问题拆解与整体思路
1.1 空调负荷凭什么能参与配电网优化
一台家用空调的功率通常在1kW到3kW之间,单看不起眼,但一个台区几百台空调同时运行,聚合起来的功率可能就是几百千瓦甚至兆瓦级。更重要的是,空调本身就是带储能特性的负荷——房间墙体、室内空气和家具会储存热量,所以空调具备“短时关机但温度不会立刻失控”的能力。这种热惯性决定了空调负荷不是刚性的,它可以在一定范围内调整用电时机和用电功率,本质上就是一种灵活资源。
参与优化控制时,空调负荷大致有两种调节手段。一种是直接负荷控制,也就是我们常说的DLC,由调度端下发指令,对空调进行周期性启停或温度设定值调整。另一种是通过价格信号诱导,比如分时电价下用户自主调整温度。针对配电网层面的优化控制,我通常更倾向于温度设定值调节,因为它对用户舒适度的影响更可控,也更容易建模成连续变量或离散档位。
一个很关键的点是:空调负荷优化的价值必须放到“含可再生能源”的场景下才真正体现出来。光伏在午间大发,净负荷被压得很低,甚至出现负值,这时可以把空调温度调低一点,让空调多制冷,相当于把光伏电量用掉,转化为房间冷量存储起来。等到傍晚光伏迅速回落,负荷尖峰出现时,再把温度调高/关掉一批空调,利用房间冷量释放来削减峰值。这个“储能”过程完全不需要电池,代价只是房间温度在舒适区间内有变化。
1.2 含可再生能源配电网的核心矛盾
传统配电网的调控思路主要是“源随荷动”,但可再生能源接入后,电源侧变得不可控,净负荷曲线出现了剧烈的双向波动。我在处理实际算例时,最常见的两个现象就是:
- 午间光伏出力高而负荷低,馈线潮流倒送,节点电压升高,存在过电压风险;
- 傍晚光伏出力快速下降,负荷快速上升,主变和馈线过载,节点电压偏低。
如果只靠调节有载调压变压器和投切电容器,很难解决这种短时、反复的波动问题。储能当然可以,但投资成本高。而空调负荷分布广泛、响应速度快,通过优化控制可以起到类似储能的一部分作用。
但这里有个约束:配电网运行必须保证用户舒适度。空调负荷的优化不是“关掉就行”,而是要在温度允许偏差范围(比如设定温度26℃,允许上浮/下浮1℃,也就是温度维持在25~27℃之间)内寻找最佳控制策略。这个约束让问题从简单的“直接切除负荷”变成了一个有边界条件的最优控制问题。
所以我理解的“最佳空调负荷优化控制”,核心是找一个控制策略序列,在满足温度舒适度和配电网安全运行的前提下,最大化可再生能源消纳、最小化网损或者削峰效果,并且可落地到Matlab代码上。
1.3 优化目标与建模路线选择
建模之前,必须先想清楚目标函数是什么。不同目标下,最优控制策略差别很大:
| 目标类型 | 数学表达式 | 典型应用场景 |
|---|---|---|
| 削峰填谷 | 最小化净负荷曲线峰谷差 | 配变容量紧张、需量电费高 |
| 网损最小 | 最小化支路功率损耗之和 | 高渗透率光伏、运行经济性要求高 |
| 运行成本最小 | 最小化购电成本或弃光惩罚 | 市场化售电或微电网 |
| 电压控制 | 最小化节点电压偏差 | 低压线路长、光伏倒送明显 |
| 多目标加权 | 上述若干项加权组合 | 综合示范工程 |
我的经验是,第一版模型尽量先做单一目标,比如以“净负荷峰谷差最小”为目标,跑通之后再慢慢加权重。一上来就写多目标加权,很容易陷入参数调不过来、结果解释不清的窘境。
求解路线方面,如果只考虑温度设定值的离散档位,空调优化问题可以建模成混合整数线性规划(MILP),用解算器能拿到全局最优解。如果考虑连续温度调节,热动力学方程本身是线性差分方程,所以仍然可以保持线性结构。配电网潮流如果需要精确计算,可以用DistFlow方程,做二阶锥松弛(SOCP)后,问题就变成混合整数二阶锥规划(MISOCP),Matlab下可以用Yalmip建模,再交给Gurobi或Cplex求解。对于刚起步的同学,我建议先把潮流做成直流潮流或线性化的DistFlow,用MILP先跑通,再逐步提高精度,否则模型复杂度和调试难度同时上来,容易劝退。
2. 数学模型构建与关键约束
2.1 空调负荷热动力学模型(ETP模型)
单个房间的热过程,可以用等效热参数(ETP)模型描述。最常见是一阶等效模型,就像给房间画一个热容量和一个热阻构成的电路。一阶ETP模型的状态方程是:
T_in(t+1) = T_out - η·R·P·u(t) - [T_in(t) - T_out + η·R·P·u(t)]·exp(-Δt/(R·C))
这里:
- T_in:室内温度(℃)
- T_out:室外温度(℃)
- η:空凋能效比(EER,或COP)
- R:房间等效热阻(℃/kW)
- C:房间等效热容(kWh/℃)
- P:空调额定电功率(kW)
- u(t):空调启停状态,0或1
- Δt:时间步长(h)
这个模型物理含义很直观:房间温度会朝“室外温度叠加空调制冷/制热等效温度”这个稳态值趋近,趋近的快慢由R·C时间常数决定。我一般把Δt设为15分钟或1小时。1小时步长模型简单,但空调启停控制粒度太粗,15分钟步长的模型能描述更精细的调节策略,缺点是优化变量会增加到4倍。
聚合空调时,不能简单把所有空调的R、C、P取平均就完事。更稳妥是保留多样性:温度初始值、时间常数、设备容量按一定分布抽样生成,然后聚合成若干个“空调群”,每个群内部参数接近,用“群聚合模型”参与优化。这样既能降低变量维数,又不会损失太多精度。
2.2 配电网潮流与可再生能源出力模型
配电网潮流我推荐用DistFlow方程。对于辐射状配电网,每条支路和节点满足:
P_ij - R_ij·L_ij = sum(P_jk) - P_j_inj Q_ij - X_ij·L_ij = sum(Q_jk) - Q_j_inj V_j^2 = V_i^2 - 2(R_ij·P_ij + X_ij·Q_ij) + (R_ij^2 + X_ij^2)·L_ij
其中L_ij = (P_ij²+Q_ij²)/V_i²是支路电流平方项。这个方程本身是二次的,一般做法是引入二阶锥松弛,把等式L_ij = (P_ij²+Q_ij²)/V_i²松弛为不等式,从而让整个问题变成可以高效求解的凸问题。算例规模不大时,也可以直接用matpower做交流潮流,把优化和潮流迭代耦合起来,不过那样求解速度会慢很多,调试也更麻烦。
可再生能源出力模型相对简单。光伏出力曲线可以用某典型日的小时数据,也可以加一定比例的正态扰动模拟预测误差:
P_pv(t) = P_pv_forecast(t) + ΔP(t)
风电类似。我建议在代码里单独写一个renewable_forecast.m,这样后续做鲁棒优化时,改起来很顺手。另外要强调的是,可再生能源出力的时间尺度和空调控制的时间尺度必须对齐,都用15分钟,否则末端控制执行的时候会跟实际出力对不上。
2.3 目标函数与约束条件详解
我常用的一个综合目标函数是:
min sum_t [ α·(P_net(t) - P_avg)² + β·sum_branches(I²(t)·R) + γ·sum_nodes(V(t) - V_ref)² + δ·sum_aircon(P_aircon_baseline - P_aircon(t)) ]
其中P_net是配电网根节点处与上级电网交换的功率,P_avg是全天平均交换功率,I²R是网损,V是节点电压偏差,最后一项是空调调节量惩罚,防止为了削峰过度牺牲用户舒适度。
约束条件大致分四类:
- 空调运行约束:室内温度必须在舒适区间内,比如[T_set - ΔT_max, T_set + ΔT_max];空调功率有上下限;如果采用启停控制,启停变量是0/1整数;如果采用温度设定档位,档位也是离散整数。
- 配电网安全约束:节点电压上下限,通常取0.95~1.05pu;支路电流或视在功率不超过上限;根节点交换功率不超过主变容量。
- 可再生能源约束:光伏/风电出力不能超过预测值的上限;若允许弃光,则实际出力是可调的,且弃光量有上限或惩罚项。
- 用户舒适度约束:这是容易漏掉的关键点。如果目标函数里没有对空调功率偏离的惩罚,优化器可能把空调全关或者全开,虽然满足了电网目标,但室温会越界。所以温度约束和功率调节惩罚要同时存在,一“硬”一“软”搭配。
我一般会把温度舒适度设成硬约束(硬边界),把用户可感知的“温变速率”设成软约束(惩罚项)。因为就算温度没越界,如果半小时内从26℃掉到24.5℃,用户也会觉得不舒服。
3. Matlab代码实现架构
3.1 整体文件结构与数据准备
Matlab实现不必一开始就把所有代码写进一个大脚本。我推荐的文件结构是:
- main.m:主程序,负责全局配置、调用各模块、展示结果
- load_input_data.m:读取系统参数和预测数据
- build_aircon_model.m:生成空调群的ETP参数和初始状态
- build_distflow_model.m:构建配电网DistFlow约束矩阵
- formulate_optimization.m:用Yalmip定义变量、目标函数和约束
- solve_optimization.m:调用求解器,处理求解选项和结果解析
- run_validation.m:用matpower或直接计算验证解是否满足潮流约束
- plot_compare.m:绘制优化前后对比曲线
数据准备方面,配电网参数我建议直接用IEEE 33节点算例的公开数据,把线路阻抗、节点负荷改成带空调负荷的形式。可再生能源数据可以找一个典型日的光照强度,然后转换成光伏出力。整个过程一定不要忘记设置时间参数:total_period(比如96个15分钟时段)、dt = 0.25h。
3.2 核心代码模块讲解
先看build_aircon_model.m的核心结构:
function aircon = build_aircon_model() % 生成空调群参数,聚合N台空调 n_aircon = 200; % 空调台数 % 按分区抽样生成房间热阻、热容和空调功率 aircon.R = sort(18 + 6*rand(n_aircon,1)); % ℃/kW aircon.C = sort(0.5 + 0.2*rand(n_aircon,1)); % kWh/℃ aircon.P = 2.5 + 0.8*rand(n_aircon,1); % kW aircon.T0 = 26 + 0.3*randn(n_aircon,1); % 初始室温 aircon.eta = 3.2; % 能效比 aircon.Tset = 26; % 设定温度 aircon.dT = 1; % 允许波动范围±1℃ end这里R和C的分布要合理,住宅房间典型热阻在10~30℃/kW,热容在0.3~0.8kWh/℃,别随手乱设,否则温度响应时间会异常。
formulate_optimization.m里,用Yalmip定义变量和约束:
% u是n_aircon×T维的0/1启停变量,或温度档位整数变量 u = binvar(n_aircon, T_full); T_in = sdpvar(n_aircon, T_full+1); % 室内温度 % 温度递推约束 for k = 1:n_aircon for t = 1:T_full T_in(k,t+1) == T_out(t) - aircon.eta*aircon.R(k)*aircon.P(k)*u(k,t) ... - (T_in(k,t) - T_out(t) + aircon.eta*aircon.R(k)*aircon.P(k)*u(k,t))*exp(-dt/(aircon.R(k)*aircon.C(k))); end end如果温度全程写成变量,维度会很大。200台空调、96个时段,T_in就有19200个变量,u又有19200个二进制变量,这个规模对MILP来说还勉强能解,如果空调数量到1000台就如坐针毡。我的做法是采用“空调群聚合”策略:把200台空调按参数相似性聚类成10~20个群,每个群内的空调启停比例是连续变量,用“群内可中断比例”代替每台独立开关,这样变量数大幅下降。代价是精度稍低,但工程上完全够用。
DistFlow约束我通常不直接展开写成大矩阵,而是用Yalmip的循环赋值方式:
for i = 1:n_branch constraints = [constraints, P_branch(i) - R(i)*L_branch(i) == sum(P_child(i,:)) - P_inj(i)]; constraints = [constraints, V_child(i) <= V_parent(i) - 2*(R(i)*P_branch(i) + X(i)*Q_branch(i)) + (R(i)^2+X(i)^2)*L_branch(i)]; % 二阶锥松弛 constraints = [constraints, L_branch(i) >= (P_branch(i)^2 + Q_branch(i)^2)/V_parent(i)]; end注意二阶锥约束在Yalmip里可以直接写成不等式,它会自动识别为SOC约束。
3.3 求解器选择与参数调试
我首选的求解组合是Yalmip + Gurobi。Gurobi对MILP和MISOCP的支持都很好,尤其是节点数到几万、变量几千时依然稳健。如果电脑没有Gurobi,可以用Cplex,或者退一步用MATLAB自带的intlinprog。不过intlinprog处理二次约束和SOCP的能力较弱,所以模型里的二阶锥约束就得丢掉,改回直流潮流或线性化潮流。
求解器选项值得花时间调整。我最常用的是:
ops = sdpsettings('solver','gurobi','verbose',2); ops.gurobi.MIPGap = 0.01; % 1%的最优间隙 ops.gurobi.TimeLimit = 300; % 限制5分钟 ops.gurobi.NumThreads = 4; % 多核MIPGap不要设成0,一方面很难在有限时间达到,另一方面意义也不大,工程上1%~5%的间隙完全可接受。TimeLimit很重要,否则算例稍大就可能跑一整晚。调试阶段先用8个时段跑通,再扩展到24、96个时段,这样能快速发现模型错误。
4. 算例分解与效果分析
4.1 典型日场景设置
我用IEEE 33节点配电网做一个代表性算例。33节点系统包含32条支路,基座负荷约3.7MW,我在节点6和节点18接入两座光伏电站,装机分别800kW和600kW。空调负荷集中挂接在节点8、12、20及24,总共模拟了200台用户空调,聚合基线功率约300~400kW。时间范围选夏季典型日,从上午6点到次日6点,步长1小时,总共24个时段。光伏出力按典型晴朗日曲线,午间12~14点出力最大,又叠加一个出力缩放系数。空调需求高峰在傍晚18~20点,室外温度用一天的正弦曲线模拟,集中在30~38℃之间。
目标函数第一版设为:minimize sum((P_net(t)-P_net_avg)^2),即最小化根节点交换功率的方差,这样直接抑制净负荷波动。约束包括室内温度24~28℃,节点电压0.95~1.05pu,支路电流上限参照原算例热稳定极限。求解用Gurobi,MIPGap=1%,TimeLimit=120s。
4.2 优化前后对比
无控制时,净负荷曲线呈现典型的“鸭子曲线”:午间光伏大发,根节点交换功率降到1.8MW左右;傍晚光伏降、负荷升,交换功率冲到4.5MW,峰谷差约2.7MW。
优化控制后,午间空调群温度设定值整体下调,空调多出力,净负荷低谷被“填高”到2.3MW左右;傍晚温度上调,部分空调停机,净负荷峰值被削到3.6MW左右。峰谷差降到1.3MW,削峰效果超过50%。节点电压方面,午间原本节点18电压达到1.07pu,优化后压到1.04pu以内;傍晚原本节点24电压最低0.94pu,优化后回升到0.97pu以上。
空调用户舒适度如何呢?优化后的室内温度曲线在午间会略低于设定值,比如从26℃降到25℃,傍晚略高于设定值,最高到27℃,始终在24~28℃区间内。这说明了柔性负荷的潜力:用户几乎无感知,电网却获得了可观的调节空间。
4.3 敏感性分析
我习惯把几个关键参数各跑一遍,看结果是否稳健:
- 空调渗透率从20%提升到80%,峰谷差削减率从12%提升到58%,但渗透率超过60%后收益增速放缓,原因是配电网其他安全约束开始占主导,再增加空调可调容量也发挥不出来。
- 允许温度波动范围从±0.5℃放宽到±2℃,可调功率容量提升了约70%,峰谷差削减率提升了约23个百分点。这说明舒适度区间是影响可调潜力的最大因素。
- 光伏预测误差从±5%增加到±20%时,如果采用确定性模型,午间电压越限风险明显上升。后来我改成了鲁棒优化的简单版本(用预测区间边界参与约束),虽然目标函数值比确定解略保守,但电压越限概率大幅下降。
这些敏感性分析能帮你向评审或领导解释项目价值,也更接近实际工程中的决策过程。
5. 常见问题与排查技巧实录
5.1 模型不收敛的排查清单
遇到求解器长时间不收敛,或者返回infeasible,别急着怀疑求解器,先按这个清单排查:
| 症状 | 可能原因 | 处理办法 |
|---|---|---|
| 返回Infeasible | 温度约束与功率约束相互矛盾,比如要求温度同时低于25℃且空调停机过热 | 检查舒适度范围、空调功率上下限,适当放宽温度边界或增加调节惩罚项 |
| 求解进度长时间卡在0% | 二进制变量过多或约束过于复杂 | 用空调分群聚合、缩短时段数、先线性化潮流 |
| 解出来了,但节点电压越限 | 潮流模型与优化模型不一致,比如DistFlow松弛不紧 | 用matpower或前推回代校验,必要时收紧松弛条件,加罚额外松弛量 |
| 温度曲线出现振荡 | 时间步长太大或模型参数R、C不合理 | 减小Δt到0.25h,检查热时间常数R·C是否在数小时量级 |
有一个很隐蔽的错误:我在空调群聚合时,对空气混合平均后直接求温度递推,结果完全忽略了个体差异,导致优化结果看似很好,但单台空调的温度却越界。后来改为保留群体内温度的上下包络作为约束,问题就正常了。
5.2 参数整定心得
ETP参数R和C是影响结果的第一要素。R和C没有实测值时,不要随便从网络上抄一组数,建议根据房间面积估算。我常用经验公式:R约为20~25℃/kW,C约为0.4~0.7kWh/℃,时间常数τ=R·C约在8~15小时之间。τ太小,房间保温差,空调一停温度立刻反弹,优化空间小;τ太大,温度对空调启停不敏感,控制效果也不明显。
目标函数权重α、β、γ的比例调起来很费时间。我建议先固定β和γ为0,纯跑削峰目标,得到功率曲线之后,再把网损和电压偏差的权重从小往大加。注意权重跨数量级时,比如α=1,β=1000,V=0.01,Yalmip自动归一化并不总是智能,需要你手调数量级。
另一个经验是Big-M取值。在很多约束中要引入启停的辅助变量,需要一个足够大的M。如果M太大,Gurobi求解数值会变差;M太小,又会错误地激活或钳制约束。我的方式是先计算所有变量可能的最大范围,比如支路功率不超过5MW,那么M取10就够了,不要为了省事写1e6。
5.3 代码实现中的几个隐藏坑
第一,Matlab的Yalmip在定义二进制变量时,如果直接使用binvar(n,T),后续矩阵运算很容易因为维数不一致报错。我建议保持所有的变量名称带明确后缀,比如u_open、P_aircon_agg、T_room,不要复用变量名。否则Yalmip不会报错,但会把新变量悄悄覆盖掉,极端情况下结果完全错误却找不到原因。
第二,时间序列索引偏移。温度递推时,T_in(k,1)是初始状态,T_in(k,2)是第一个时段结束的温度。约束里如果把t=1时的温度错误写成T_in(k,1),整个系统移位一个时段,结果看起来也合理,但物理意义是不对的。排查时我会打印前几个时段的温度值与递推结果核对。
第三,光伏出力数据的归一化。光伏出力原始数据可能是kW,而配电网负荷单位是kW,但节点基值可能用了MVA。DistFlow方程里的所有量纲必须统一,否则电压计算结果会出现0.001量级的诡异偏差。我的习惯是所有数据进入优化函数之前,全部转换为标幺值(基准功率1MVA、基准电压12.66kV),输出结果再转回有名值。
第四,关于“最佳”的误解。很多同学把“最佳”理解成单个空调的温度设定点,最终输出一个全局最优温度曲线。实际上工程上更常用是“群组调节指令”,也就是对每群空调发布一个温度调节量或者最大允许功率百分比,而不是管每台空调的启停。所以代码实现时,我更推荐输出“空调群功率调节比例”,再在群内通过轮询启停实现,这样既保护了用户私密性,又降低了通信要求。
结尾分享一下我个人的操作体会
我最初做这个课题时,一上来就把目标函数写成网损、电压、舒适度、弃光四项加权,空调用了单台模型,加上二阶锥潮流,整个模型维度非常恐怖。Gurobi跑了两小时没出结果,当时差点崩溃。后来我退回到“单目标+聚合空调+直流潮流”,两天内就把整个流程跑通了,再逐步加回精度,效果反而更好。所以我特别建议:第一版代码务必追求“能跑通、能出图、能解释”,不要追求模型面面俱到。
另外,优化控制落地到实际系统时,最容易被忽略的是通信时延和用户行为不确定性。模型求出来的“最佳”控制序列,在实际执行时可能因为用户手动调温度、空调进入除湿模式等原因失效。一个务实的做法是采用滚动时域控制:每15分钟重新求解未来1小时的优化问题,只执行当前时段的指令。虽然代码里需要额外写一个循环调用求解器,但这个改动能让控制效果非常稳定,这一点在我后期做半实物仿真时体会特别深。
如果你正准备复现这个题目,我建议从IEEE 33节点加上200台空调聚合这个搭配开始,先把优化后的峰谷差图跑出来,再研究电压和消纳效果。代码写完之后,一定要用matpower验证一下你优化结果对应的是否真正满足交流潮流约束。这一步能帮你发现绝大多数建模错误。希望这份经验能让你少走一些弯路。