Reference · 速查
代码速查表
忘了就回来翻。安装命令、env id、各支柱最小模板——一页带走。
一、环境与安装
bash · 推荐路径:Docker(跨平台)复制
docker pull jajimer/sinergym:latest docker run -it --name sinergym-lab -v "$PWD/workspace":/workspace jajimer/sinergym:latest bash python --version # Python 3.12.3 pip show sinergym | grep Version # 3.11.0 / 3.12.x energyplus --version # EnergyPlus, Version 25.1.0
bash · 本地装(非 Docker)复制
pip install sinergym stable-baselines3[extra] torchdiffeq eppy # 再从 https://energyplus.net/downloads 装 EnergyPlus 25.1.0,然后: export PYTHONPATH="$PYTHONPATH:/usr/local/EnergyPlus-25-1-0"
二、Sinergym 环境速查
| env id | 建筑 / 气候 |
|---|---|
Eplus-5zone-hot-continuous-stochastic-v1 | 5 区办公楼 / 炎热 / 连续 / 随机天气(本课默认) |
Eplus-5zone-cool-continuous-v1 | 5 区 / 寒冷 / 确定 |
Eplus-datacenter-mixed-continuous-stochastic-v1 | 数据中心 / 混合 |
Eplus-office-cool-continuous-v1 | 办公 / 寒冷 |
python · 最小跑通复制
import gymnasium as gym, sinergym env = gym.make("Eplus-5zone-hot-continuous-stochastic-v1") obs, info = env.reset() obs, reward, terminated, truncated, info = env.step(env.action_space.sample()) # obs: 时间 + zone温湿度 + 全楼表计 + 室外扰动;action: [-1,1] 归一化设定点
三、支柱 A · Neural ODE 预测
python · Neural ODE 最小模板复制
import torch, torch.nn as nn from torchdiffeq import odeint_adjoint as odeint class ODEFunc(nn.Module): def __init__(self, dim=5): super().__init__() self.net = nn.Sequential(nn.Linear(dim,32), nn.Tanh(), nn.Linear(32,dim)) def forward(self, t, z): return self.net(z) # dz/dt = f(z,t) func = ODEFunc(5) t_grid = torch.linspace(0, 6.0, 24) # 6h 前瞻,24×15min traj = odeint(func, z0, t_grid, method="dopri5", rtol=1e-3, atol=1e-3) # [T,batch,dim]
python · PINN 损失(数据 + 物理)复制
L_data = nn.MSELoss()(pred, Y_target) # 物理残差:检查轨迹是否满足 1R1C 右端 d_pred = (pred[:,1:] - pred[:,:-1]) / (t_grid[1]-t_grid[0]) rhs = torch.stack([func(t_grid[i], pred[:,i]) for i in range(24)], dim=1) L_phys = nn.MSELoss()(d_pred, rhs[:,:-1]) loss = L_data + 0.01 * L_phys # λ 可调
python · 1R1C 物理 ODE(PINN 项来源)复制
C_z · dT_z/dt = (T_out − T_z)/R_env + Q_solar + Q_int + Q_hvac
四、支柱 B · 无模型 DRL(SAC)
python · SAC 训练循环复制
import gymnasium as gym, sinergym from stable_baselines3 import SAC from stable_baselines3.common.vec_env import DummyVecEnv from gymnasium.wrappers import NormalizeObservation from sinergym.utils.wrappers import NormalizeAction, LoggerWrapper env = NormalizeObservation(NormalizeAction(LoggerWrapper(gym.make("Eplus-5zone-hot-continuous-stochastic-v1")))) env = DummyVecEnv([lambda: env]) model = SAC("MlpPolicy", env, learning_rate=3e-4, buffer_size=100_000, verbose=1) model.learn(total_timesteps=200_000) model.save("sac_hvac") action, _ = model.predict(obs, deterministic=True)
python · 多目标奖励复制
def hvac_reward(obs, action, prev_action, pwr_max=1e6): T_zone, co2, fan, chill = obs[0], obs[1], obs[2], obs[3] e_energy = (fan + chill) / pwr_max comfort = 2.0 * (max(0.0, T_zone-26.0) + max(0.0, 18.0-T_zone)) co2_pen = max(0.0, co2-1000.0) / 1000.0 wear = np.sum(np.abs(action - prev_action)) return -(0.4*e_energy + 0.4*comfort + 0.1*co2_pen + 0.1*wear)
python · 动作去归一化 + 限幅 + EMA 平滑复制
SAFE = {"sat":(12.0,18.0), "chw":(5.0,10.0), "airflow":(0.2,1.0)}
def to_safe(a, prev):
vals = [(a[i]+1)/2*(rng[1]-rng[0])+rng[0] for i,rng in zip([0,1,2], SAFE.values())]
raw = np.clip(vals, [r[0] for r in SAFE.values()], [r[1] for r in SAFE.values()])
return 0.7*prev + 0.3*raw
五、支柱 C · 安全门控(四层)
python · 门控调用骨架复制
gate = SafetyGate(pid, thermal_model=forecast_model) safe_action, reason = gate.safe_action(raw_action, obs, prev_obs) # reason: "rl_ok" / "hard_violation" / "model_unstable" / "manual" # 非 rl_ok 时自动回退 PID;shield 触发率应趋近 0
| 层 | 做什么 | 触发后 |
|---|---|---|
| 1 硬约束 | T∈[18,26]°C / CO₂<1000 / 冷冻水∈[5,12] | 否决→PID |
| 2 限幅 | 单步变化 < Δ_max | 截断 |
| 3 回退 | 预测误差连续>ε / 断网 / 缺数据 | 切 PID |
| 4 人工 | 一键切手动(最高优先) | 纯人工 |
六、关键公式速记
| 概念 | 公式 |
|---|---|
| Neural ODE | z(t₁)=ODESolve(z(t₀), f_θ, t₀, t₁) |
| 1R1C 热方程 | C·dT/dt = (T_out−T)/R + Q_solar + Q_int + Q_hvac |
| PINN 损失 | L = L_data + λ·L_physics |
| 多目标奖励 | r = −(α·能耗 + β·舒适 + γ·CO₂ + δ·磨损) |
| 精度(MAPE 口径) | acc = 1 − mean(|y−ŷ|/|y|) |