准备数据
训练数据和决策流图共同描述一个建模任务:数据记录系统的运行过程,决策流图定义变量之间的依赖关系。 本节介绍轨迹数据、变量类型、节点关系和动作物理边界。需要训练策略时,还应根据业务目标定义奖励函数。
下面以配套的倒立摆数据为例,说明数据格式,以及连续、类别和有序离散变量的配置方法。 请从源码仓库根目录执行其中的命令;真实业务的抽取与对齐方法见 业务接入。
查看数据与轨迹
revive info --data examples/pendulum/data/pendulum.npz--- 变量详情 ---
actions (20000, 1) float32 min=-2.000, max=2.000, mean=-0.146
index (100,) int64 min=200.000, max=20000.000, mean=10100.000
states (20000, 3) float32 min=-8.000, max=8.000, mean=0.542倒立摆数据仅包含三项内容:
| 名称 | 形状 | 含义 |
|---|---|---|
states | [20000, 3] | 摆杆姿态:角度的余弦、正弦、角速度 |
actions | [20000, 1] | 施加的力矩 |
index | [100] | 各段运行的结束行位置:100 段,每段 200 步 |
本例无需单独保存 next_states。REVIVE 在每条轨迹内部使用下一行作为下一时刻的状态, 并丢弃没有后续状态的最后一行。
index 将数据划分为 100 段独立运行记录。第 199 行与第 200 行属于不同轨迹, 两者之间不存在连续的状态转移,因此系统不会跨轨迹取样。
图:index 使用不包含结束位置的累计下标;下一状态只从同一条轨迹内取得。
定义变量、列与边界
这里的“连续/离散”指变量的取值类型,不是时间是否连续采样。三种数据都需要保持时间对齐和轨迹边界。 原始数组按 [样本数, 变量维度] 保存,列顺序与 columns 一致;归一化和独热编码(one-hot)由框架处理,保存数据时无需提前转换。
| 业务类型 | 配置类型 | 如何保存 | 配置重点 |
|---|---|---|---|
| 连续量,如温度、流量、力矩 | continuous | 原始量纲数值,通常用 float32 | 写明单位;动作需声明物理 min/max |
| 离散类别,如模式、工况编号 | category | 一列固定数字编码,如 0、1、2,不保存字符串或 one-hot | 用 values 列出合法编码,并单独保留编码字典 |
| 离散有序量,如等间距档位、计数 | discrete | 保存真实档位值,如 0、25、50、75、100 | 用 min、max、num 定义等间距取值,num 至少为 2 |
以下 columns: 配置片段均放在 graph: 下,不是完整训练配置。
连续数据
保留原始单位和数值精度,排查 NaN、无穷、故障占位值与单位混用。 状态列的范围用于数据解释,不能用观测最大最小值替代执行器物理边界。
columns:
temperature: [{name: water_temperature, type: continuous}]
actions: [{name: flow_setpoint, type: continuous, min: 0.0, max: 100.0}]离散类别数据
例如停机/手动/自动分别编码为 0/1/2,数组形状是 [N, 1]。 这些编号用于区分类别,其数值差不表示类别之间的距离,因此应配置为 category。
columns:
mode: [{name: operating_mode, type: category, values: [0, 1, 2]}]框架在内部处理类别编码;values 的列表顺序决定内部编码位置,不代表业务顺序。 训练、验证、推理必须复用同一编码字典;新类别需更新配置并重新验证,不能临时更换编号。 缺失值及 -999 等占位值需要在训练前处理。类别列通过合法值表定义取值范围,无需填写连续边界。
离散有序数据
discrete 适合数值间距也有意义的等间距档位。以下为 0、25、50、75、100 五档, 档距为 (max - min) / (num - 1):
columns:
gear: [{name: power_level, type: discrete, min: 0, max: 100, num: 5}]仅有“低/中/高”的顺序、但无法定义间距,或合法值为 0、10、50 等不等间距集合时, 应使用 category 并列出合法值;num: 3 仅适用于三个等间距取值。 确认数据均落在合法档位;保存为浮点数也不改变其离散语义。 归一化不执行档位合法性检查,部署时仍需确认输出符合设备实际档位。
以上都是列配置片段,不是完整训练配置。不同类型可属于不同节点,也可在同一节点中按列声明; 输入输出维度和分布由列布局决定。完整规则见列与物理边界。 类别状态不能套用“类别编号相减”的增量语义;有序离散状态也不要直接套用连续 delta 配置。
声明列与物理边界
graph.columns 定义每个变量各维度的含义与取值范围。下面的片段放在 graph: 下,与 nodes: 同级:
columns:
states: [cos_theta, sin_theta, theta_dot]
actions: [{name: torque, min: -2.0, max: 2.0}]列的顺序必须与数组最后一维的顺序一致。 列名用于解释各维度,并显示在日志与图表中。 例如 states 是一个输入变量,cos_theta、sin_theta、theta_dot 是它的三个维度。 ONNX 的张量名称以导出说明文件为准,不会把每个列名自动变成独立输入。
连续与有序离散动作需要显式物理 min/max;类别动作通过 values 声明合法值。 该错误表示直接策略 ONNX 导出缺少动作的原始量及处理后边界。以下保留程序报错原文便于检索:
preflight (kind=run, phase=onnx_action_bounds): direct policy ONNX requires explicit
physical raw and processed bounds for every action;
missing=['actions:raw', 'actions:processed']策略的动作范围由这些边界确定。边界需要符合执行器的实际能力,避免输出设备无法执行的指令。
数据中的最大最小值不是物理边界
倒立摆力矩的 ±2.0 来自任务本身的执行器上限,而非「数据中出现过的最大力矩」。 历史运行可能只覆盖设备允许范围的一部分,因此物理边界应以设备规格或工艺规程为依据。
若难以确定,可使用工具基于数据生成候选值:
revive suggest-bounds \
--config config.yaml \
--data train.npz \
--ratio 1.5工具输出的为 中心 ± 1.5 × 观测半幅,仅为候选值。已声明 min/max 的项不会被覆盖。 写入配置前需要逐项核对。revive init 使用相同规则,对倒立摆力矩给出的建议为 ±3.0,比真实上限宽了 50%。逐节点调整比例、直接写回新配置等用法见 物理边界建议。
构建决策流图
图:先将物理对象对应到状态与控制量,再根据变量关系定义决策流图。
倒立摆的决策流图包含动作、状态增量和下一状态三个计算节点,其依赖关系如下:
flowchart LR
S["<b>states</b><br/>cos θ、sin θ、θ̇"] --> A["<b>actions</b><br/>力矩(决策)"]
S --> D["<b>delta_states</b><br/>状态变化量"]
A --> D
S --> N["<b>next_states</b><br/>下一时刻状态"]
D --> N
style S fill:#e3f2fd,stroke:#1565c0,color:#0d47a1
style A fill:#fff3e0,stroke:#ef6c00,color:#e65100
style D fill:#e8f5e9,stroke:#2e7d32,stroke-width:1.5px,color:#1b5e20
style N fill:#e3f2fd,stroke:#1565c0,color:#0d47a1
在 graph.nodes 中对应以下配置片段:
graph:
nodes:
actions: {inputs: [states]}
delta_states: {inputs: [states, actions]}
next_states: {inputs: [states, delta_states], function: builtin.delta_add}各节点的作用如下:
actions— 策略节点。根据当前姿态确定施加的力矩。delta_states— 世界模型节点。预测施加力矩后的状态变化。next_states— 函数节点。当前值与变化量相加,属于确定的算术运算,无需学习。builtin.delta_add是内置函数,不必自行编写。
节点配置说明
节点名即其输出变量名。 定义了名为 delta_states 的节点后,下游节点即以 delta_states 引用其输出。此约定贯穿全部配置与报错信息。
inputs 的顺序具有语义。 它决定网络输入的拼接方式。[states, actions] 与 [actions, states] 对应两个结构不同的模型,其权重不可互换。确定之后不应随意调整。
连续状态推荐预测增量,也支持直接预测下一状态。 当前示例使用默认 raw_delta: 学习 delta_X,再通过 builtin.delta_add 合成 next_X。相邻状态变化较小时, 这样可以集中学习变化部分;是否改善效果仍需对照验证。
直接预测时设置 graph.transition_contract: direct_next,将目标写成网络节点:
graph:
transition_contract: direct_next
nodes:
next_states: {inputs: [states, actions]}
columns:
states: [cos_theta, sin_theta, theta_dot]
actions: [{name: torque, min: -2.0, max: 2.0}]上例是仅世界模型的图,actions 由数据或调用者提供,不要求 delta_states。 需要策略时再声明策略节点与阶段。当前版本支持 direct_next; 配套倒立摆的默认配置仍使用增量写法,可在源码的 examples/pendulum/config.min.yaml 中查看。 ADM2 依赖显式增量节点,不适用 direct_next。更改状态转移方式后,应创建新的训练任务, 旧检查点不支持按新的转移方式恢复训练。
默认情况下,next_states 节点的输出作为下一时间步 states 的输入 (transitions: auto 是默认值)。静态任务应显式声明 transitions: none。
定义奖励
奖励函数用于定义策略的优化目标。它接收当前时间步的变量,返回该时间步的奖励。 倒立摆任务要求摆杆保持倒立,同时控制角速度和力矩开销,对应实现见 examples/pendulum/reward.py:
import torch
def get_reward(data):
states = data["states"] # [B, 3] = cos θ, sin θ, θ̇
u = torch.clamp(data["actions"][..., 0:1], -2, 2) # [B, 1] 力矩
theta = torch.atan2(states[:, 1:2], states[:, 0:1]) # 有符号角度,正上方为 0
thdot = states[:, 2:3]
costs = theta ** 2 + 0.1 * thdot ** 2 + 0.001 * u ** 2
return -costs # [B, 1],越大越好三项代价的权重(1 : 0.1 : 0.001)分别控制姿态误差、角速度和力矩开销的相对影响。 例如,提高力矩项权重会增加大动作的惩罚,可能影响摆杆达到目标姿态的能力,需结合任务效果验证。
奖励函数的接口要求如下:函数须使用张量运算、保留批次维度、返回有限值;不得读取文件、 不得转换为 NumPy、不得在函数内生成随机数。预检会检查函数调用与导出兼容性,奖励的业务含义和数值尺度仍需自行核对。
若仅训练世界模型而不训练策略,本步骤可跳过。
准备检查与业务迁移
完成倒立摆示例后,进入配置步骤前应备好以下内容:
| 准备内容 | 检查结果 |
|---|---|
| 轨迹数据 | 各变量时间对齐,维度和数据类型明确,index 正确标记边界 |
| 变量与边界 | 列顺序、单位和动作物理范围与实际任务一致 |
| 决策流图 | 节点名与变量对应,输入依赖与状态转移关系明确 |
| 奖励函数 | 策略任务已定义奖励;仅训练世界模型时可跳过 |
接入自己的业务时,按“目标与变量 → 图与数据对齐 → 奖励与检查”的顺序整理。 下方实操以加氯和温控为例,说明历史数据抽取、时间可用性、轨迹划分和奖励设计;仅运行倒立摆时可直接进入下一步。
业务实操:从目标、变量字典到决策流图与奖励
从目标开始
业务建模需要同时考虑变量之间的关系和数据的可用性。用户可以先根据工艺建立变量表与决策流图, 再核对数据源,补充缺失信息或调整任务范围,使图结构与数据对应。
以下按变量选择、图结构定义和数据准备的顺序说明。
从业务目标倒推候选变量
历史数据通常保存在业务数据库、时序数据库或 CSV 文件中。选择变量时,需要结合业务目标、 采集情况与工艺关系。建议先确定动作和目标量,再选择相关状态与外部条件:
| 顺序 | 类别 | 选择依据 | 加氯任务中的例子 |
|---|---|---|---|
| 1 | 动作 | 业务系统可下发的执行器设定值 | 次氯酸钠投加泵的设定流量 |
| 2 | 目标量 | 评价业务效果所需的观测量或可验证计算量 | 出水余氯浓度 |
| 3 | 状态 | 影响目标量动态变化的可观测变量 | 进水余氯、加药点水温、清水池液位 |
| 4 | 外部条件 | 影响系统、不能由策略控制且决策时可获得的变量 | 进水流量、原水浊度 |
动作应对应实际可下发的控制量,目标量应具有可观测或可验证的计算方式。两者决定模型需要学习的响应关系和奖励函数的计算依据。
状态变量应与目标量的动态变化相关。对于信息重复或关联较弱的变量,可以通过工艺分析与对照试验判断是否保留。
应当排除的三类变量
以下三类需要检查是否适合进入在线模型:
- 上线时拿不到的量。 化验室隔天出的结果、人工填报的台账、事后才计算的指标。训练时 可用而推理时不可用的变量,会导致训练与使用时的输入条件不一致,并可能使离线评估高估模型效果。
- 由动作单向决定、不含额外信息的量。 例如泵的电流完全由泵频率决定。它不提供新信息, 可考虑用确定性函数表示,或在不参与任务时省略。
- 同一物理量的重复测点。 三个并联测温点读数一致时,取其一或取均值,不必三个都抽。
反过来应优先评估一类变量:长滞后过程中间环节的测点。加药点至出水口之间的中途余氯、 多级换热的中间温度——它们把一段长滞后拆成几段短滞后,可能改善动态过程的可观测性,需要用对照验证。
对照数据源核对
对照数据源,逐项核对候选变量:
| 核对项 | 不满足时的后果 |
|---|---|
| 测点是否存在、有无历史 | 必需变量缺失时,需要补充采集或调整任务定义 |
| 各变量的采样频率 | 同时记录采样时间与可用时间;控制周期、聚合与延迟处理需共同确认 |
| 覆盖时长与工况范围 | 数据未覆盖到的工况,模型的输出不可作为依据 |
| 历史动作是否有变化 | 动作近乎常数时学不出「改变操作后系统如何响应」,且与数据量多少无关 |
历史动作的变化范围决定数据能够提供多少操作响应信息。若投加量长期固定,仅增加同类记录可能无法改善这一问题。 准备数据前,可绘制动作时间序列,检查动作变化幅度、频率及对应工况。
关键变量缺失的处理方式
关键变量缺失时,可结合采集成本和业务目标选择以下处理方式:
- 换用代理量。 目标量不可测时,找一个与之强相关且在线可测的量替代(出水余氯不可测 → 取管网首端在线余氯仪的读数)。
- 缩小任务范围。 只在数据覆盖良好的那段工况上做决策,其余工况仍走原有控制方案。
- 补充采集。 历史数据缺少动作变化时,可在业务允许的范围内安排试验,补充不同操作下的响应记录。
定义决策流图
确定变量表后,按变量之间的计算依赖定义决策流图。配置方式与倒立摆示例一致。 以水厂加氯为例:进水流量为外部条件,投加量为动作,出水余氯为状态。
flowchart LR
S["余氯浓度<br/>(状态)"] --> A["投加量<br/>(动作)"]
S --> D["余氯变化量"]
A --> D
D --> N["下一时刻余氯"]
S --> N
style S fill:#e3f2fd,stroke:#1565c0,color:#0d47a1
style A fill:#fff3e0,stroke:#ef6c00,color:#e65100
style D fill:#e8f5e9,stroke:#2e7d32,stroke-width:1.5px,color:#1b5e20
style N fill:#e3f2fd,stroke:#1565c0,color:#0d47a1
nodes:
dosage:
inputs: [residual_cl]
delta_residual_cl:
inputs: [residual_cl, dosage]
next_residual_cl:
inputs: [residual_cl, delta_residual_cl]
function: builtin.delta_add与倒立摆逐一对应:dosage 是策略节点,delta_residual_cl 是世界模型节点, next_residual_cl 是函数节点。列与物理边界的写法也相同——dosage 的 min/max 取投加泵的实际流量范围,而不是历史上出现过的最大投加量。
已知且适用的确定性关系优先用公式表达。 单位换算、配比等可写成函数 节点(function:),减少需要学习的部分;但公式自身的适用条件、单位、数值误差和可微性仍需验证。 函数节点的写法(内置函数与自定义 Python 函数)见图结构。
系统存在滞后时
若当前状态不足以决定下一时刻(如加药后需数十分钟才能在出水端见效、室温受数小时前的 供热功率影响),可令节点读取若干步的历史信息:
nodes:
delta_residual_cl:
inputs: ["residual_cl@[-6:]", "dosage@[-6:]"]@[-6:] 表示「最近 6 步」。引号不可省略——方括号在 YAML 流式序列中具有特殊含义, 缺少引号将被解析为嵌套列表而失败。语法与适用场景见历史窗口。
已知存在滞后时,应在建模之初检查是否需要历史输入或中间状态;可将无历史模型作为对照。 多步发散还可能来自数据覆盖、时间对齐或模型误差,不能据此单独认定需要历史窗口。
按图结构准备数据
确定图结构后,按节点名称组织 .npz 数据: 图中每个输入变量在数据中必须有同名数组,数据中抽出来的每个变量在图中应有下游去向。
抽取与时间对齐。 先定义决策时刻 t 对应的实际可用信息,再统一步长。 以下为预处理片段,需要 pandas 和含所示列的 raw.csv;不是适用于所有业务的固定规则。 此处显式使用右闭、右标记区间 (t-1min, t],避免把 t 之后的数据放进 t 的输入; 即使时间戳对齐,晚到的测量也不能提前使用。
import pandas as pd
df = pd.read_csv("raw.csv", parse_dates=["ts"]).set_index("ts")
resampled = pd.concat(
[
df[["dosage_sp"]].resample("1min", closed="right", label="right").last(), # 控制量:取区间内最后一次下发值
df[["cl_out", "q_in", "temp"]].resample("1min", closed="right", label="right").mean(), # 测量量:取均值
],
axis=1,
)采样步长应能反映系统响应过程。步长过长可能遗漏动态变化,过短则可能使相邻步的变化接近测量噪声。 候选值需结合采集延迟、设备响应与控制周期验证。
缺失与异常的处理:
- 短缺口仅在允许信号保持、确认无未来信息泄漏时考虑前向填充,记录最大允许间隔;
- 长缺口不要填充,直接在缺口处切断为两段运行;
- 传感器故障值(
-9999、常数卡死、超量程)按缺口处理,不可留在数据中当作真实观测。
REVIVE 按轨迹处理时序数据,每条轨迹对应一段连续运行记录。以下事件可能使状态转移不再连续,需要据此划分运行段:
- 停机、检修、大修
- 控制模式切换(手动 ↔ 自动)
- 数据缺口超过所定义的有效间隔,或无法可信填补
- 工艺参数大改(配置方案、牌号、目标值变更)
若未标记运行段边界,停机前与重启后的相邻记录可能被当作一个状态转移样本。数据格式检查不能识别所有业务上的不连续情况。
写成 .npz 文件。 到这一步,数据已经是「按时间排序的若干段运行记录」。保存为一个 .npz 文件:
- 每个业务变量保存为一个数组,第一维为时间轴,各数组长度一致;
- 一个
index数组标注各段运行的结束位置(不含该行)。
以下是保存结构示意,obs_rows / action_rows 需由自己的预处理生成,示例段长也需替换。
import numpy as np
obs = np.asarray(obs_rows, dtype=np.float32) # [N, obs_dim]
action = np.asarray(action_rows, dtype=np.float32) # [N, action_dim]
index = np.asarray([100, 250, 420], dtype=np.int64) # 三段运行
assert obs.shape[0] == action.shape[0] == index[-1]
assert np.all(np.diff(index) > 0)
assert np.isfinite(obs).all() and np.isfinite(action).all()
np.savez("train.npz", obs=obs, action=action, index=index)index=[100, 250, 420] 表示共三段运行,分别对应第 0~99、100~249、250~419 行。
轨迹边界不可跨越
一段运行的最后一行之后并非另一段运行的第一行——这两行之间的「变化」并不真实存在。 index 的作用即在于向系统标明边界位置。标注错误不会触发报错,但会导致模型学到 不存在的转移关系。
变量的分组方式。 按节点角色组织 .npz 键。属于同一节点的物理向量分量 放在数组最后一维,例如三个温度测点可组成 [N, 3] 的 temperatures。 若需要不同的输入依赖或网络,也可拆为独立节点,并分别声明列映射。分组的依据是「哪些变量在图中扮演同一角色」——正如倒立摆把三个姿态量合并成 一个 [N, 3] 的 states。
使用 done 标志。 若数据本身带有 done 标志(每段运行的最后一行为 True),可用其 替代 index。两种方式任选其一。
独立的验证集。 如需严格的泛化评估,应按完整运行段划分为两个文件并分别传入:
revive validate \
--config config.yaml \
--train-data train.npz \
--val-data val.npz未传入 --val-data 时系统将自动划分,默认以完整运行段为单位(见 data.split_mode)。划分依据不得涉及模型误差或未来标签—— 否则验证集将失去独立性。
时间可用性与轨迹边界
| 信息 | 决策时刻 t 能否使用 | 处理 |
|---|---|---|
| 已到达系统的当前测量 | 可以 | 记录测量时间、到达时间与单位 |
| 已下发且仍有效的动作 | 可以 | 按实际执行与保持语义对齐 |
| t 时已经发布的天气或排产预测 | 可以,需版本留痕 | 使用当时的预测值,不替换成事后真实值 |
| 下一时刻真实输出或次日化验结果 | 不可提前作为策略输入 | 可作训练目标或离线评估,不能伪装成当前输入 |
flowchart LR
A["段 A:行 0…99"] --> X["结束位置 index = 100"]
X -. "不构造跨段转移" .-> B["段 B:行 100…249"]
B --> Y["结束位置 index = 250"]
图中的虚线表示存储上相邻,但动力学上不连续。末行不能连接到下一段首行; 训练目标可以使用同段下一时刻观测,策略输入只能使用决策时刻已获得的信息。 最终还应保留不参与调参的测试数据,见业务验收。
图与数据互查
图结构与数据准备完成后,检查缺失输入和未使用变量,并据此调整变量表:
- 节点缺少输入:已有测点时补充抽取;缺少测点时,选择代理量、调整任务范围或补充采集;
- 某个变量没有任何下游 → 核对是否用于奖励、评估或外生输入;确认不参与任务后再移出模型输入。
数据保存后再用命令核对一轮:
revive info --data train.npz该命令列出数据中包含的变量、各变量的维度与取值范围,对照图的 inputs 逐项检查—— 维度不匹配是最常见的首个问题,应在编写配置之前发现。
数据量是否充足不存在通用阈值,但可通过以下三点自查:
- 操作量是否存在变化。 若历史动作接近常数,模型将无法学到「改变操作后系统如何响应」—— 需要结合动作分布评估,不能仅根据样本数判断。
- 工况覆盖范围。 期望模型在何种范围内决策,数据就应覆盖至该范围。
- 单段运行时长。 至少应长于计划让模型推演的步数(第 3 步训练时的
rollout_horizon)。
写出自己的奖励函数
结构与倒立摆的那份相同:取出需要的变量,算一个「越大越好」的分数。加氯任务的写法是 「余氯偏离目标值的代价 + 投药量的代价」:
def get_reward(data):
# 余氯与目标值 0.5 mg/L 的偏差越小越好,投药量越少越好
deviation = (data["next_residual_cl"] - 0.5).abs().sum(dim=-1, keepdim=True)
cost = 0.1 * data["dosage"].abs().sum(dim=-1, keepdim=True)
return -(deviation + cost)两项之间的权重(此处为 0.1)用于平衡余氯达标与节约药剂的目标。 奖励函数需使用张量运算,保留批次维度,并返回有限值。
温控奖励与输入可用性示例
联合动作与奖励配置见多控制节点。所有 reward 输入为 raw 物理空间,每个量 [B,1];采样周期固定为一分钟。定义三个无量纲代价:
tracking = ((T_next - 22 °C) / 2 °C)²
energy = (4 kW * heater + 0.2 kW * fan²) / 4.2 kW
change = [(heater - previous_heater)² + (fan - previous_fan)²] / 2
reward = -(tracking + 0.1 * energy + 0.05 * change)change 是本例以每分钟满量程变化为尺度的动作变化率代价;若采样周期不为一分钟,应使用 (u-u_prev)/Δt 并同步修改尺度。能耗项目前是归一化平均功率;固定时长比较能量时乘共同 Δt 不改变排序,不同采样周期/时域下则需要明确能量积分和奖励聚合规则。
| 业务检查 | 手算结果 / 要求 |
|---|---|
| T_next=22、两动作及上一动作全为 0 | 三项为 0,reward=0 |
| T_next=24、两动作为 1、上一动作均为 0 | 三项均为 1,reward=-1.15 |
| 温度改用 K、功率改用 W | 设定值和尺度一起换算,代价不变 |
| 动作连续保持 | change 为 0;必须使用实际执行值,而非另一条策略的历史动作 |
| 缺上一动作 | 完整 reward 拒绝;调用方显式提供初始上一动作 |
| NaN、缺外生序列、动作超界 | 模型调用端在推理前报错;不凭空补未来信息 |
reward.py:get_reward 提供完整三项,模型调用端逐步保存实际执行动作。当前无状态 PPO/SAC 短训配置使用 get_training_reward,只包含 tracking 和 energy;其 rollout 没有上述上一 动作状态,因此不把三项评估奖励写成训练目标。将日志里的 previous_heater 当成新策略的 上一动作会得到错误的平滑代价。三项策略训练只在业务需要动作平滑时考虑,相应 history/状态 路径需另行验证;它不是多控制节点联合优化的前置条件。
只依赖本步状态转移和动作的业务约束,可直接加入同一个 reward,例如总功率超限的归一化 平方代价。可选函数、尺度与手算示例见多控制节点。 这种奖励写法无需上一动作状态;奖励惩罚属于软约束,已有动作取值边界继续生效。 策略收益比较按具体业务问题安排,不要求先完成固定规则、两项与三项奖励的三方比较。
在一步训练中,网络损失衡量模型预测误差;reward 表达控制偏好。改变 reward 权重不会 直接使世界模型更准。先检查各分项的尺度和数据分布,再用独立业务指标判断偏好是否合理。 0.1 和 0.05 只用于教学,不是业务默认权重。
未来计划、预测、情景、真实回放的可用时刻不同,详见历史与外生输入。 不能把未来真实温度当作计划输入;也不能用事后环境实测替换上线时只有的预测。