使用PuLP实现时间序列LP优化的故障排查与优化需求
时间序列线性规划优化问题排查与修正
我用Python的PuLP库做时间序列LP优化项目,目前遇到问题,以下是相关信息及需要协助的方向:
代码
import pandas as pd from pulp import LpMaximize, LpProblem, LpVariable, lpSum, value hourly_prices_df = pd.read_csv("new_datav2v1.csv") hourly_prices = hourly_prices_df["price"].tolist() hours = len(hourly_prices) print(hours) # Decision variables P_nominal = 24 # 额定功率 C_nominal = 24 # 额定容量 h_rt = 0.85 # 充放电效率 Pd = LpVariable.dicts("Pd", range(1, hours + 1), lowBound=0, upBound=P_nominal) Pc = LpVariable.dicts("Pc", range(1, hours + 1), lowBound=0, upBound=P_nominal) C = LpVariable("C", lowBound=0, upBound=C_nominal) lp_problem = LpProblem("Energy_Optimization", LpMaximize) # 目标函数 lp_problem += lpSum((Pd[i] * hourly_prices[i - 1] - Pc[i] * hourly_prices[i - 1]) for i in range(1, hours + 1)), "Objective" for i in range(1, hours + 1): lp_problem += 0 <= (Pc[i] - (1/h_rt) * Pc[i]) <= C_nominal, f"Charge_Constraint_Hour_{i}" for i in range(2, hours + 1): lp_problem += Pc[i] == Pc[i-1] + Pd[i-1], f"Hour_{i}_Cyclic_Behavior_Constraint" lp_problem += Pc[1] == Pc[hours] + Pd[hours], "Hour_1_Cyclic_Behavior_Constraint" for i in range(1, hours + 1): lp_problem += 0 <= Pd[i] <= P_nominal lp_problem += 0 <= Pc[i] <= P_nominal lp_problem += sum(Pd[i] for i in range(1, hours + 1)) == 2 * C_nominal lp_problem.solve() print("Optimization Status:", lp_problem.status) print("Optimal Revenue (Rday):", lp_problem.objective.value()) charged_hours = [i for i in range(1, hours + 1) if Pc[i].value() > 0] discharged_hours = [i for i in range(1, hours + 1) if Pd[i].value() > 0] print("Charged Hours:", charged_hours) print("Discharged Hours:", discharged_hours) for i in range(1, hours + 1): print(f"Hour {i}: Charge = {Pc[i].value()}, Discharge = {Pd[i].value()}")
运行结果
Optimization Status: -1 Optimal Revenue (Rday): 2880.0 Charged Hours: [] Discharged Hours: [24] Hour 1: Charge = 0.0, Discharge = 0.0 Hour 2: Charge = 0.0, Discharge = 0.0 Hour 3: Charge = 0.0, Discharge = 0.0 Hour 4: Charge = 0.0, Discharge = 0.0 Hour 5: Charge = 0.0, Discharge = 0.0 Hour 6: Charge = 0.0, Discharge = 0.0 Hour 7: Charge = 0.0, Discharge = 0.0 Hour 8: Charge = 0.0, Discharge = 0.0 Hour 9: Charge = 0.0, Discharge = 0.0 Hour 10: Charge = 0.0, Discharge = 0.0 Hour 11: Charge = 0.0, Discharge = 0.0 Hour 12: Charge = 0.0, Discharge = 0.0 Hour 13: Charge = 0.0, Discharge = 0.0 Hour 14: Charge = 0.0, Discharge = 0.0 Hour 15: Charge = 0.0, Discharge = 0.0 Hour 16: Charge = 0.0, Discharge = 0.0 Hour 17: Charge = 0.0, Discharge = 0.0 Hour 18: Charge = 0.0, Discharge = 0.0 Hour 19: Charge = 0.0, Discharge = 0.0 Hour 20: Charge = 0.0, Discharge = 0.0 Hour 21: Charge = 0.0, Discharge = 0.0 Hour 22: Charge = 0.0, Discharge = 0.0 Hour 23: Charge = 0.0, Discharge = 0.0 Hour 24: Charge = 0.0, Discharge = 48.0
CSV数据
time,price 2023-11-30 00:00:00,104.79 2023-11-30 01:00:00,104.52 2023-11-30 02:00:00,102.2 2023-11-30 03:00:00,100.01 2023-11-30 04:00:00,101.63 2023-11-30 05:00:00,106.3 2023-11-30 06:00:00,129.41 2023-11-30 07:00:00,156.07 2023-11-30 08:00:00,76.49 2023-11-30 09:00:00,95.01 2023-11-30 10:00:00,60.0 2023-11-30 11:00:00,67.13 2023-11-30 12:00:00,75.01 2023-11-30 13:00:00,105.68 2023-11-30 14:00:00,111.59 2023-11-30 15:00:00,195.0 2023-11-30 16:00:00,226.0 2023-11-30 17:00:00,253.76 2023-11-30 18:00:00,187.24 2023-11-30 19:00:00,153.94 2023-11-30 20:00:00,122.01 2023-11-30 21:00:00,122.0 2023-11-30 22:00:00,87.0 2023-11-30 23:00:00,60.0
需要协助的方向
- 目标函数:验证是否符合最大化收益的目标;
- 充电水平约束:当前用
(Pc[i] - (1/h_rt)*Pc[i])表示充电水平,是否需要修正; - 循环行为约束:当前约束可能存在错误,需调整;
- 代码整体优化:包括结构、逻辑及效率方面的建议。
问题排查与修正方案
1. 目标函数验证
目标函数逻辑正确:收益 = 各小时放电收入(Pd[i] * 电价) - 各小时充电成本(Pc[i] * 电价),符合最大化收益的核心目标。但需注意后续约束中要体现充放电效率对实际电量的影响,否则会导致收益计算与实际电量变化脱节。
2. 充电水平约束修正
原约束完全错误:Pc[i] - (1/h_rt)*Pc[i]等价于Pc[i]*(1-1/h_rt),这是一个负数(因为h_rt<1),和电池充电水平(电量)毫无关系。
正确做法是引入电池电量状态变量S[i],表示第i小时末的电池剩余电量,约束逻辑应为:
- 电量初始值等于结束值(循环场景,次日初始电量=当日结束电量)
- 每小时电量变化:
S[i] = S[i-1] + h_rt*Pc[i] - Pd[i]/h_rt(充电时,实际进入电池的电量是充电功率×效率;放电时,电池消耗的电量是放电功率÷效率,因为效率损失) - 电量范围:
0 ≤ S[i] ≤ C_nominal
3. 循环行为约束修正
原约束混淆了Pc(充电功率)和电量状态的关系,逻辑完全错误。正确的循环约束是当日最后一小时末的电量等于次日第一小时初的电量,即S[hours] = S[0](假设S[0]是初始电量,循环场景下S[0]=S[24])。
4. 代码整体优化与修正
以下是修正后的完整代码,包含逻辑修复、变量命名优化、约束分块注释:
import pandas as pd from pulp import LpMaximize, LpProblem, LpVariable, lpSum, value, LpStatus # 读取数据 hourly_prices_df = pd.read_csv("new_datav2v1.csv") hourly_prices = hourly_prices_df["price"].tolist() hours = len(hourly_prices) # 参数定义 P_NOMINAL = 24 # 额定充放电功率 C_NOMINAL = 24 # 电池额定容量 EFFICIENCY = 0.85 # 充放电效率 # 决策变量 # Pd[i]: 第i小时放电功率,Pc[i]: 第i小时充电功率 Pd = LpVariable.dicts("Discharge_Power", range(1, hours+1), lowBound=0, upBound=P_NOMINAL) Pc = LpVariable.dicts("Charge_Power", range(1, hours+1), lowBound=0, upBound=P_NOMINAL) # S[i]: 第i小时末的电池电量,S[0]为初始电量 S = LpVariable.dicts("Battery_SOC", range(0, hours+1), lowBound=0, upBound=C_NOMINAL) # 初始化问题 lp_problem = LpProblem("Battery_Energy_Optimization", LpMaximize) # 目标函数:最大化总收益 = 放电总收入 - 充电总成本 lp_problem += lpSum( Pd[i] * hourly_prices[i-1] - Pc[i] * hourly_prices[i-1] for i in range(1, hours+1) ), "Total_Revenue" # 1. 电量状态转移约束 for i in range(1, hours+1): # 电量变化 = 充电有效电量 - 放电消耗电量 lp_problem += S[i] == S[i-1] + EFFICIENCY * Pc[i] - Pd[i] / EFFICIENCY, f"SOC_Transition_Hour_{i}" # 2. 循环约束:当日结束电量 = 次日初始电量(即S[24] = S[0]) lp_problem += S[hours] == S[0], "Cyclic_SOC_Constraint" # 3. 充放电功率上下限约束(已在变量定义中设置,此处可省略,若需显式约束可保留) # for i in range(1, hours+1): # lp_problem += Pd[i] <= P_NOMINAL, f"Discharge_Upper_Bound_Hour_{i}" # lp_problem += Pc[i] <= P_NOMINAL, f"Charge_Upper_Bound_Hour_{i}" # 4. 可选:充放电总量约束(若业务需要,否则可删除) # lp_problem += lpSum(Pd[i] for i in range(1, hours+1)) == 2 * C_NOMINAL, "Total_Discharge_Constraint" # 求解问题(可指定求解器,如CBC,默认即可) lp_problem.solve() # 输出结果 print(f"Optimization Status: {LpStatus[lp_problem.status]}") print(f"Optimal Revenue (Rday): {round(lp_problem.objective.value(), 2)}") charged_hours = [i for i in range(1, hours+1) if Pc[i].value() > 1e-6] # 避免浮点精度问题 discharged_hours = [i for i in range(1, hours+1) if Pd[i].value() > 1e-6] print("Charged Hours:", charged_hours) print("Discharged Hours:", discharged_hours) print("\nHourly Details:") for i in range(1, hours+1): print(f"Hour {i}: Charge = {round(Pc[i].value(), 2)}, Discharge = {round(Pd[i].value(), 2)}, SOC = {round(S[i].value(), 2)}")
关键优化点说明
- 引入
S[i]电量状态变量,修正了核心约束逻辑; - 变量命名更具可读性(如
Discharge_Power代替Pd); - 约束分块添加注释,便于维护;
- 使用
LpStatus[lp_problem.status]输出更友好的状态描述(如"Optimal"代替-1); - 输出时加入浮点精度判断(
>1e-6),避免因浮点误差导致的误判; - 可选约束单独标注,便于根据业务需求调整。
内容的提问来源于stack exchange,提问作者cebep27
相关产品推荐
相关产品推荐

