任务设计工作流程

任务设计工作流程#

环境定义了智能体和仿真之间的接口。在最简单的情况下,环境为智能体提供当前观察,并执行智能体提供的动作。在马尔可夫决策过程(MDP)的制定中,环境还可以提供额外的信息,比如当前奖励、完成标志以及当前情节的信息。

虽然环境接口易于理解,但其实现可以根据任务的复杂程度而有显著差异。在强化学习(RL)的背景下,环境实现可以分解为几个组件,如奖励函数、观察函数、终止函数和重置函数。每个组件都可以根据任务的复杂程度和所期望的模块化级别以不同的方式实现。

我们提供两种不同的工作流程用于使用该框架设计环境:

  • 基于管理器: 环境被分解为单独的组件(或管理器),这些组件处理环境的不同方面(例如计算观察、应用动作和应用随机化)。用户为每个组件定义配置类,环境负责协调管理器并调用它们的函数。

  • 直接式: 用户定义一个单一类,该类直接实现整个环境,而无需单独的管理器。这个类负责计算观察、应用动作和计算奖励。

两个工作流程各有优缺点。基于管理器的工作流程更具模块化,允许环境的不同组件轻松更换。这在原型环境和实验不同配置时非常有用。另一方面,直接式工作流程更高效,允许对环境逻辑进行更细粒度的控制。这在优化环境性能或实现复杂逻辑时非常有用,而这些复杂逻辑难以拆分为独立组件。

基于管理器的环境#

大多数环境实现遵循类似的结构。环境处理输入动作,经过仿真步骤,计算观察值和奖励信号,应用随机化,并重置终止的环境。基于此,环境可以被分解为处理每个任务的单独组件。例如,观察管理器负责计算观察值,奖励管理器负责计算奖励,终止管理器负责计算终止信号。这种方法在框架中被称为基于管理器的环境设计。

基于管理器的任务工作流基于管理器的任务工作流

基于管理器的环境通过将任务分解为由独立类管理的各个组件,促进任务的模块化实现。任务的每个组件,例如奖励、观察、终止,都可以指定为独立的配置类,然后传递给相应的管理器类。管理器负责解析配置并处理其配置中指定的内容。

不同管理器之间的协调由类 envs.ManagerBasedRLEnv 协调。 它接收一个任务配置类实例 ( envs.ManagerBasedRLEnvCfg ),其中包含任务各个组件的配置。 根据配置,设置场景并初始化任务。 然后,通过环境进行步进时,依次调用所有管理器执行必要的操作。

为了他们自己的任务,我们期望用户主要定义任务配置类,并使用现有的 envs.ManagerBasedRLEnv 类进行任务实现。任务配置类应继承自基类 envs.ManagerBasedRLEnvCfg,并包含分配给每个组件的各种配置类的变量(例如 ObservationCfgRewardCfg)。

用于使用管理器样式定义Cartpole任务的奖励函数的示例

以下类是 Cartpole 环境配置类的一部分。RewardsCfg 类定义了构成奖励函数的各个项。每个奖励项由其函数实现、权重和要传递给函数的附加参数定义。用户可以定义多个奖励项及其权重,以用于奖励函数。

@configclass
class RewardsCfg:
    """Reward terms for the MDP."""

    # (1) Constant running reward
    alive = RewTerm(func=mdp.is_alive, weight=1.0)
    # (2) Failure penalty
    terminating = RewTerm(func=mdp.is_terminated, weight=-2.0)
    # (3) Primary task: keep pole upright
    pole_pos = RewTerm(
        func=mdp.joint_pos_target_l2,
        weight=-1.0,
        params={"asset_cfg": SceneEntityCfg("robot", joint_names=["cart_to_pole"]), "target": 0.0},
    )
    # (4) Shaping tasks: lower cart velocity
    cart_vel = RewTerm(
        func=mdp.joint_vel_l1,
        weight=-0.01,
        params={"asset_cfg": SceneEntityCfg("robot", joint_names=["slider_to_cart"])},
    )
    # (5) Shaping tasks: lower pole angular velocity
    pole_vel = RewTerm(
        func=mdp.joint_vel_l1,
        weight=-0.005,
        params={"asset_cfg": SceneEntityCfg("robot", joint_names=["cart_to_pole"])},
    )

通过这种方法,可以轻松地通过切换某些组件而保持其余代码不变来改变任务的实现。这种灵活性在原型设计环境和尝试不同配置时是非常可取的。它还允许与他人轻松合作实现一个环境,因为贡献者可以选择为他们自己的任务规范使用不同组合的配置。

参见

我们为使用基于管理器的工作流设置环境提供了更详细的教程,位于 创建基于管理器的强化学习环境

直接式的环境#

直接式的环境更符合传统环境实现,其中单个脚本直接实现奖励函数、观测函数、重置和环境的所有其他组件。这种方法不需要管理类。相反,用户可以通过基类 envs.DirectRLEnvenvs.DirectMARLEnv 的API自由实现他们的任务。对于从 IsaacGymEnvsOmniIsaacGymEnvs 框架迁移的用户,这种工作流程可能更为熟悉。

直接式的任务工作流直接式的任务工作流

在使用直接式实现定义环境时,我们期望用户定义一个实现整个环境的单个类。任务类应该继承自基类 envs.DirectRLEnvenvs.DirectMARLEnv,并且应该有其相应的配置类,该配置类分别继承自 envs.DirectRLEnvCfgenvs.DirectMARLEnvCfg 。任务类负责设置场景、处理动作、计算奖励、观测、重置和终止信号。

为 Cartpole 任务定义奖励函数的示例,使用直接式风格

以下函数是Cartpole环境类的一部分,负责计算奖励。

def _get_rewards(self) -> torch.Tensor:
    total_reward = compute_rewards(
        self.cfg.rew_scale_alive,
        self.cfg.rew_scale_terminated,
        self.cfg.rew_scale_pole_pos,
        self.cfg.rew_scale_cart_vel,
        self.cfg.rew_scale_pole_vel,
        self.joint_pos[:, self._pole_dof_idx[0]],
        self.joint_vel[:, self._pole_dof_idx[0]],
        self.joint_pos[:, self._cart_dof_idx[0]],
        self.joint_vel[:, self._cart_dof_idx[0]],
        self.reset_terminated,
    )
    return total_reward

它调用了 compute_rewards() 函数,该函数经过Torch JIT编译以获得性能优势。

@torch.jit.script
def compute_rewards(
    rew_scale_alive: float,
    rew_scale_terminated: float,
    rew_scale_pole_pos: float,
    rew_scale_cart_vel: float,
    rew_scale_pole_vel: float,
    pole_pos: torch.Tensor,
    pole_vel: torch.Tensor,
    cart_pos: torch.Tensor,
    cart_vel: torch.Tensor,
    reset_terminated: torch.Tensor,
):
    rew_alive = rew_scale_alive * (1.0 - reset_terminated.float())
    rew_termination = rew_scale_terminated * reset_terminated.float()
    rew_pole_pos = rew_scale_pole_pos * torch.sum(torch.square(pole_pos).unsqueeze(dim=1), dim=-1)
    rew_cart_vel = rew_scale_cart_vel * torch.sum(torch.abs(cart_vel).unsqueeze(dim=1), dim=-1)
    rew_pole_vel = rew_scale_pole_vel * torch.sum(torch.abs(pole_vel).unsqueeze(dim=1), dim=-1)
    total_reward = rew_alive + rew_termination + rew_pole_pos + rew_cart_vel + rew_pole_vel
    return total_reward

这种方法提供了更高的透明度,因为逻辑在任务类中定义,而不是通过管理器进行抽象。当实现复杂的逻辑时,这可能是有益的,因为这些逻辑难以分解成独立的组件。此外,直接式风格的实现可能会为环境带来更多的性能收益,因为它允许使用优化框架,如 PyTorch JITWarp 来实现大量逻辑。当需要大规模训练并优化环境中的单个操作时,这可能是有价值的。

参见

我们为使用直接式工作流程设置强化学习环境提供了更详细的教程,可参考 创建直接工作流RL环境