第 3 章 环境抽象与任务配置¶
一个强化学习任务,最终要落到"环境"这个对象上:它每一步吃什么观测、吐什么动作、怎么算奖励、何时终止、随机化什么。Isaac Lab 提供两条写环境的路线——继承 DirectRLEnv 自己写方法,或用 ManagerBasedRLEnv 声明一组 term 配置。本章讲清两条路线的边界与选用判据,展开后者"五大 term 体系"的组织方式,落到动作空间与执行频率的关系,最后用一个 Direct 与一个 Manager-Based 的最小任务把结构走通。
1. 两条环境抽象路线¶
两条路线都实现 Gymnasium 风格的 env 接口(reset() / step() 返回 obs、reward、terminated、truncated、info),差别只在"逻辑写在哪":
| 路线 | 基类 | 任务逻辑的载体 | 适用 |
|---|---|---|---|
| Direct | DirectRLEnv / DirectRLEnvCfg |
子类里重写 _pre_physics_step、_apply_action、_get_observations、_get_rewards、_get_dones 等方法 |
单机器人、逻辑直白、想一眼看到全部计算 |
| Manager-Based | ManagerBasedRLEnv / ManagerBasedRLEnvCfg |
一组声明式 term 配置,交给各个 manager 调度 | 多实体、多观测组、奖励/事件项多,需可组合与复用 |
选用判据可粗化为一句:任务结构复杂、需要拼装大量观测/奖励/随机化项并跨任务复用时,走 Manager-Based;任务紧凑、逻辑就是几行、要的是可读的"一个类看到底"时,走 Direct。 内置任务两条路线都有——Isaac-Cartpole-Direct-v0 是 Direct 的样板,Isaac-Lift-*、Isaac-Velocity-* 是 Manager-Based 的样板。
Direct 路线的骨架(方法签名,运行环境 Isaac Lab 3.0.0):
class CartpoleEnv(DirectRLEnv):
cfg: CartpoleEnvCfg
def _setup_scene(self):
# 建机器人、加到场景、克隆环境、配置 articulation root
...
def _pre_physics_step(self, actions: torch.Tensor):
# 每仿真步一次:把策略动作转成要下发的目标(在 decimation 前)
self.actions = self.cfg.action_scale * actions
def _apply_action(self):
# 每策略步一次:把目标写入执行器
self._robot.set_joint_position_target(self.actions)
def _get_observations(self) -> dict:
joint_pos = self._robot.data.joint_pos
return {"policy": torch.cat([joint_pos, self._robot.data.joint_vel], dim=1)}
def _get_rewards(self) -> torch.Tensor: ...
def _get_dones(self) -> tuple[torch.Tensor, torch.Tensor]: ...
_get_dones 返回 (terminated, time_out) 两个布尔张量,分别对应"真终止"与"超时截断"——这也是 Manager-Based 里 termination term 用 time_out=True 标记的语义。
判据:结构复杂走 Manager-Based,紧凑逻辑走 Direct
多实体、多观测组、奖励与随机化项需要拼装复用时用 Manager-Based;任务就是几行、要一个类看到底时用 Direct。
2. 五大 term 体系¶
Manager-Based 环境把任务逻辑拆进若干 manager,每个 manager 吃一组 term。核心五类是 observation / action / reward / termination / event,另有 command(指令生成)、curriculum(课程)、recorder(记录)三个扩展 manager。
一个 manager 的配置就是一个 @configclass 容器,容器里每个属性是一个 term(下面的 ObsGroup/ObsTerm/RewTerm/DoneTerm/EventTerm 是任务代码对 ObservationGroupCfg/ObservationTermCfg/RewardTermCfg/TerminationTermCfg/EventTermCfg 的惯用别名,经 from isaaclab.managers import ... as ... 引入):
@configclass
class ObservationsCfg:
@configclass
class PolicyCfg(ObsGroup): # 观测分组
joint_pos = ObsTerm(func=mdp.joint_pos_rel)
joint_vel = ObsTerm(func=mdp.joint_vel_rel)
last_action = ObsTerm(func=mdp.last_action)
policy: PolicyCfg = PolicyCfg() # 交给策略的组
critic: CriticCfg = ... # (可选)只给价值网络用的特权组
@configclass
class ActionsCfg:
joint_pos = mdp.actions.JointPositionActionCfg( # 动作 term 直接是 ActionCfg
asset_name="robot", joint_names=[".*"], scale=0.25, use_default_offset=True
)
@configclass
class RewardsCfg:
alive = RewTerm(func=mdp.is_alive, weight=1.0)
joint_torques = RewTerm(func=mdp.joint_torques_l2, weight=-1e-4) # 惩罚项权重取负
@configclass
class TerminationsCfg:
time_out = DoneTerm(func=mdp.time_out, time_out=True)
tipped = DoneTerm(func=mdp.bad_orientation, params={"limit_angle": 0.6}) # 真终止(姿态超限)
@configclass
class EventCfg:
physics = EventTermCfg(func=mdp.randomize_rigid_body_mass, mode="startup")
各 term 的公共约定:
- 观测按组聚合:
policy组进策略网络,critic组可携带特权信息(真值质量、摩擦等)只给训练期的价值网络,部署时不占用观测维度。这是 Manager-Based 相较 Direct 的一个结构性便利。 - 奖励 = weight × func 返回值:约定奖励函数返回每环境标量,
weight为负即惩罚;求和由 reward manager 完成。 - 终止分两类:
time_out=True标记"超时截断"(价值需要 bootstrap),否则是"真终止"(回合结束,不 bootstrap)。 - event 的
mode:"startup"只在初始化跑一次,"reset"在每次环境重置跑,"interval"按固定周期跑——域随机化就落在这里(第 6 章展开)。
观测噪声不是独立 manager,而是挂在 ObsTerm 的噪声配置字段(UniformNoiseCfg/GaussianNoiseCfg)与 modifiers(如历史堆叠 HistoryBufferCfg、缩放)上,因此可逐项开关——训练期加噪、评估期把整组噪声置零即可对照。
Manager-Based 环境把任务逻辑拆进 manager,每个 manager 吃一组 term:
flowchart LR
env["ManagerBasedRLEnv"]
env --> om["observation manager<br/>观测项 → policy 组"]
env --> am["action manager<br/>动作项 → 关节目标"]
env --> rm["reward manager<br/>奖励项求和"]
env --> tm["termination manager<br/>真终止 / 超时"]
env --> em["event manager<br/>reset 与随机化"]
env -.-> cmd["command manager"]
env -.-> cur["curriculum manager"]
env -.-> rec["recorder manager"]
3. 动作空间与执行频率¶
动作 term 决定策略输出如何映射到机器人。常见几类(均在 isaaclab.envs.mdp.actions):
| 动作配置 | 语义 | 输出维度 |
|---|---|---|
JointPositionActionCfg |
关节位置目标(经执行器 PD 跟踪) | 关节数 |
JointVelocityActionCfg / JointEffortActionCfg |
关节速度 / 力矩直发 | 关节数 |
TaskSpaceActionCfg |
末端位姿增量,走 IK | 6 或 7 |
NonHolonomicActionCfg |
非完整轮式(左右轮速度) | 2 |
关节位置动作有两个常用缩放项:scale 把网络输出的 [-1,1] 乘成关节角增量,use_default_offset=True 让目标叠加在配置的默认关节角上(即"相对目标"而非"绝对目标")。另有一组按关节行程重缩放的变体(mdp.actions 下的 joint_actions_to_limits 模块),省去自己 clip。
执行频率由两个参数配比决定,都写在场景配置里:
sim.dt:物理步长(如1/60或1/120)。decimation:每sim.dt步一次控制更新、多少个物理步才更新一次动作。例如decimation=10表示动作每 10 个物理步刷新一次,策略有效频率 =1 / (decimation · sim.dt)。
这个比值是第 4 章"仿真步长与策略频率之比"的配置入口;动作平滑与观测/执行延迟的随机化则归第 6 章(域随机化)与第 9 章(sim-to-real)。
坑:decimation 改的是控制频率
策略频率 = 物理频率 / decimation;调它等于改控制周期,观测与奖励的时间含义随之变化,不能只当作性能开关。
4. 最小任务示例¶
两个内置任务把上面两条路线各钉一遍:
Direct:Isaac-Cartpole-Direct-v0。一个 DirectRLEnvCfg 声明场景与物理,一个 CartpoleEnv(DirectRLEnv) 用方法体重写观测/奖励/终止。第 1 章的自检正是跑它。
Manager-Based:Isaac-Lift-Cube-Franka-v0 / Isaac-Velocity-*。任务入口 gym.register 把环境配置与各 RL 库的配置一起登记(详见第 5 章):
gym.register(
id="Isaac-Cartpole-Direct-v0",
entry_point="...cartpole_direct_env:CartpoleEnv",
kwargs={"env_cfg_entry_point": "...cartpole_direct_env_cfg:CartpoleEnvCfg",
"rsl_rl_cfg_entry_point": "...rsl_rl_ppo_cfg:CartpoleDirectPPORunnerCfg"},
)
lift 场景配置里的 SceneCfg(num_envs=4096, env_spacing=3, replicate_physics=True) 直接体现第 2 章的复用线:一套 term 配置模板 + num_envs 克隆出 4096 个并行环境。replicate_physics=True 让物理状态张量连续排布以利 GPU 访存(第 4 章展开)。
5. 要点¶
- 两条路线都满足 Gymnasium 接口;Direct 用方法体重写、适合紧凑任务,Manager-Based 用声明式 term、适合多实体/可组合任务,选型按"逻辑写在方法里还是配置里"决策。
- 五大 term 体系 = observation / action / reward / termination / event 五个 manager(外加 command / curriculum / recorder);每个 term 是
func+params+ 组内weight。 - 观测按
policy/critic分组,critic可带只供训练的特权信息;噪声挂在ObsTerm.noise与modifiers上,逐项可关。 - 终止区分
time_out=True(超时截断,价值需 bootstrap)与真终止;奖励约定"返回标量、weight 求和、负权重即惩罚"。 - 动作空间分关节位置/速度/力矩、任务空间、非完整轮式;
scale与use_default_offset决定"相对目标"语义。 - 执行频率由
sim.dt与decimation配比决定,策略频率 =1/(decimation·sim.dt);这是连接建模与训练性能的关键旋钮。