课程首页· 技术栈地图· 参考页 · 代码速查

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-v15 区办公楼 / 炎热 / 连续 / 随机天气(本课默认)
Eplus-5zone-cool-continuous-v15 区 / 寒冷 / 确定
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 ODEz(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|)
完整说明见: 第 4 课 · 第 5 课 · 第 6 课 · 第 7 课 · 技术栈地图