创建基于管理器的基础环境#

环境将模拟的不同方面,如场景、观测和行动空间、重置事件等集合在一起,为各种应用程序创建一个连贯的接口。在Isaac Lab中,基于管理器的环境被实现为: envs.ManagerBasedEnvenvs.ManagerBasedRLEnv 类。这两个类非常相似,但 envs.ManagerBasedRLEnv 对强化学习任务很有用,并包含奖励、中止、课程表和命令生成。 envs.ManagerBasedEnv 类适用于传统的机器人控制,不包含奖励和中止条件。

在本教程中,我们将研究基础类: envs.ManagerBasedEnv 及其对应的配置类: envs.ManagerBasedEnvCfg ,用于基于管理器的工作流程。我们将使用之前的cartpole环境来说明创建新的 envs.ManagerBasedEnv 环境中的不同组件。

代码#

此教程对应于 source/standalone/tutorials/03_envs 目录中的 create_cartpole_base_env 脚本。

create_cartpole_base_env.py的代码
  1# Copyright (c) 2022-2024, The Isaac Lab Project Developers.
  2# All rights reserved.
  3#
  4# SPDX-License-Identifier: BSD-3-Clause
  5
  6"""
  7This script demonstrates how to create a simple environment with a cartpole. It combines the concepts of
  8scene, action, observation and event managers to create an environment.
  9"""
 10
 11"""Launch Isaac Sim Simulator first."""
 12
 13
 14import argparse
 15
 16from omni.isaac.lab.app import AppLauncher
 17
 18# add argparse arguments
 19parser = argparse.ArgumentParser(description="Tutorial on creating a cartpole base environment.")
 20parser.add_argument("--num_envs", type=int, default=16, help="Number of environments to spawn.")
 21
 22# append AppLauncher cli args
 23AppLauncher.add_app_launcher_args(parser)
 24# parse the arguments
 25args_cli = parser.parse_args()
 26
 27# launch omniverse app
 28app_launcher = AppLauncher(args_cli)
 29simulation_app = app_launcher.app
 30
 31"""Rest everything follows."""
 32
 33import math
 34import torch
 35
 36import omni.isaac.lab.envs.mdp as mdp
 37from omni.isaac.lab.envs import ManagerBasedEnv, ManagerBasedEnvCfg
 38from omni.isaac.lab.managers import EventTermCfg as EventTerm
 39from omni.isaac.lab.managers import ObservationGroupCfg as ObsGroup
 40from omni.isaac.lab.managers import ObservationTermCfg as ObsTerm
 41from omni.isaac.lab.managers import SceneEntityCfg
 42from omni.isaac.lab.utils import configclass
 43
 44from omni.isaac.lab_tasks.manager_based.classic.cartpole.cartpole_env_cfg import CartpoleSceneCfg
 45
 46
 47@configclass
 48class ActionsCfg:
 49    """Action specifications for the environment."""
 50
 51    joint_efforts = mdp.JointEffortActionCfg(asset_name="robot", joint_names=["slider_to_cart"], scale=5.0)
 52
 53
 54@configclass
 55class ObservationsCfg:
 56    """Observation specifications for the environment."""
 57
 58    @configclass
 59    class PolicyCfg(ObsGroup):
 60        """Observations for policy group."""
 61
 62        # observation terms (order preserved)
 63        joint_pos_rel = ObsTerm(func=mdp.joint_pos_rel)
 64        joint_vel_rel = ObsTerm(func=mdp.joint_vel_rel)
 65
 66        def __post_init__(self) -> None:
 67            self.enable_corruption = False
 68            self.concatenate_terms = True
 69
 70    # observation groups
 71    policy: PolicyCfg = PolicyCfg()
 72
 73
 74@configclass
 75class EventCfg:
 76    """Configuration for events."""
 77
 78    # on startup
 79    add_pole_mass = EventTerm(
 80        func=mdp.randomize_rigid_body_mass,
 81        mode="startup",
 82        params={
 83            "asset_cfg": SceneEntityCfg("robot", body_names=["pole"]),
 84            "mass_distribution_params": (0.1, 0.5),
 85            "operation": "add",
 86        },
 87    )
 88
 89    # on reset
 90    reset_cart_position = EventTerm(
 91        func=mdp.reset_joints_by_offset,
 92        mode="reset",
 93        params={
 94            "asset_cfg": SceneEntityCfg("robot", joint_names=["slider_to_cart"]),
 95            "position_range": (-1.0, 1.0),
 96            "velocity_range": (-0.1, 0.1),
 97        },
 98    )
 99
100    reset_pole_position = EventTerm(
101        func=mdp.reset_joints_by_offset,
102        mode="reset",
103        params={
104            "asset_cfg": SceneEntityCfg("robot", joint_names=["cart_to_pole"]),
105            "position_range": (-0.125 * math.pi, 0.125 * math.pi),
106            "velocity_range": (-0.01 * math.pi, 0.01 * math.pi),
107        },
108    )
109
110
111@configclass
112class CartpoleEnvCfg(ManagerBasedEnvCfg):
113    """Configuration for the cartpole environment."""
114
115    # Scene settings
116    scene = CartpoleSceneCfg(num_envs=1024, env_spacing=2.5)
117    # Basic settings
118    observations = ObservationsCfg()
119    actions = ActionsCfg()
120    events = EventCfg()
121
122    def __post_init__(self):
123        """Post initialization."""
124        # viewer settings
125        self.viewer.eye = [4.5, 0.0, 6.0]
126        self.viewer.lookat = [0.0, 0.0, 2.0]
127        # step settings
128        self.decimation = 4  # env step every 4 sim steps: 200Hz / 4 = 50Hz
129        # simulation settings
130        self.sim.dt = 0.005  # sim step every 5ms: 200Hz
131
132
133def main():
134    """Main function."""
135    # parse the arguments
136    env_cfg = CartpoleEnvCfg()
137    env_cfg.scene.num_envs = args_cli.num_envs
138    # setup base environment
139    env = ManagerBasedEnv(cfg=env_cfg)
140
141    # simulate physics
142    count = 0
143    while simulation_app.is_running():
144        with torch.inference_mode():
145            # reset
146            if count % 300 == 0:
147                count = 0
148                env.reset()
149                print("-" * 80)
150                print("[INFO]: Resetting environment...")
151            # sample random actions
152            joint_efforts = torch.randn_like(env.action_manager.action)
153            # step the environment
154            obs, _ = env.step(joint_efforts)
155            # print current orientation of pole
156            print("[Env 0]: Pole joint: ", obs["policy"][0][1].item())
157            # update counter
158            count += 1
159
160    # close the environment
161    env.close()
162
163
164if __name__ == "__main__":
165    # run the main function
166    main()
167    # close sim app
168    simulation_app.close()

代码解释#

envs.ManagerBasedEnv 基类包含了模拟交互的许多复杂性,并为用户提供了一个简单的接口来运行模拟并与其交互。它由以下组件组成:

通过配置这些组件,用户可以以最小的工作量创建相同环境的不同变体。在本教程中,我们将浏览 envs.ManagerBasedEnv 类的不同组件以及如何配置它们来创建新环境。

设计场景#

创建新环境的第一步是配置其场景。对于cartpole环境,我们将使用前一个教程中的场景。因此,我们在这里省略场景配置。有关如何配置场景的更多详细信息,请参阅: 使用交互式场景

定义actions#

在前一个教程中,我们直接使用 assets.Articulation.set_joint_effort_target() 方法对cartpole进行动作控制。在本教程中,我们将使用 managers.ActionManager 来处理这些actions。

动作管理器可以包括多个 managers.ActionTerm 。每个动作项负责对环境的特定方面进行*控制*。例如,对于机器人手臂,我们可以有两个动作项–一个用于控制手臂的关节,另一个用于控制夹爪。这种组合允许用户为环境的不同方面定义不同的控制方案。

在cartpole环境中,我们希望控制施加在小车上的力以平衡杆。因此,我们将创建一个动作项来控制施加在小车上的力。

@configclass
class ActionsCfg:
    """Action specifications for the environment."""

    joint_efforts = mdp.JointEffortActionCfg(asset_name="robot", joint_names=["slider_to_cart"], scale=5.0)

定义观测#

虽然场景定义了环境的状态,但观测定义了代理可以观察到的状态。代理使用这些观测来决定采取什么行动。在Isaac Lab中,观测由 managers.ObservationManager 类计算。

与动作管理器类似,观测管理器可以包含多个观测项。这些进一步分组为观测组,用于定义环境的不同观测空间。例如,对于分层控制,我们可能想定义两个观测组–一个用于低级控制器,另一个用于高级控制器。这些项假定在组中的所有观测项具有相同的维度。

对于本教程,我们只定义了一个名为 "policy" 的观测组。虽然不是完全具体的,但这个组对于Isaac Lab中的各种包装来说是一个必要的要求。我们通过继承 managers.ObservationGroupCfg 类来定义一个组。这个类收集不同的观测项,并帮助定义组的公共属性,比如启用噪声损坏或将观测项连接成一个张量。

通过继承 managers.ObservationTermCfg 类来定义单独的项。这个类采用了 managers.ObservationTermCfg.func ,它指定了用于计算该项观测的函数或可调用类。它包括用于定义噪声模型、剪切、缩放等的其他参数。然而,对于本教程,我们将这些参数保留为其默认值。

@configclass
class ObservationsCfg:
    """Observation specifications for the environment."""

    @configclass
    class PolicyCfg(ObsGroup):
        """Observations for policy group."""

        # observation terms (order preserved)
        joint_pos_rel = ObsTerm(func=mdp.joint_pos_rel)
        joint_vel_rel = ObsTerm(func=mdp.joint_vel_rel)

        def __post_init__(self) -> None:
            self.enable_corruption = False
            self.concatenate_terms = True

    # observation groups
    policy: PolicyCfg = PolicyCfg()

定义事件#

此时,我们已经为cartpole环境定义了场景、动作和观测。所有这些组件的一般思想是定义配置类,然后将它们传递给相应的管理器。事件管理器也不例外。

managers.EventManager 类负责对应于模拟状态的更改的事件。这包括重置(或随机化)场景、随机化物理特性(如质量、摩擦等)以及改变视觉特性(如颜色、纹理等)。每个事件都通过 managers.EventTermCfg 类来指定,该类采用 managers.EventTermCfg.func 来指定执行事件的函数或可调用类。

此外,它期望事件的 模式 。这个模式指定事件项应何时应用。可以指定自己的模式。为此,您需要调整 ManagerBasedEnv 类。但是,Isaac Lab提供了三种常用的模式:

  • "启动(startup)" - 仅在环境启动时发生的事件。

  • "重置(reset)" - 当环境终止和重置时发生的事件。

  • "间隔(interval)" - 在给定间隔后执行的事件,即每隔一定步数之后。

对于此示例,我们定义了在启动时随机化杆的质量的事件。这只发生一次,因为此操作很昂贵,我们不希望在每次重置时都进行。我们还创建了一个事件,以在每次重置时随机化小车和杆的初始关节状态。

@configclass
class EventCfg:
    """Configuration for events."""

    # on startup
    add_pole_mass = EventTerm(
        func=mdp.randomize_rigid_body_mass,
        mode="startup",
        params={
            "asset_cfg": SceneEntityCfg("robot", body_names=["pole"]),
            "mass_distribution_params": (0.1, 0.5),
            "operation": "add",
        },
    )

    # on reset
    reset_cart_position = EventTerm(
        func=mdp.reset_joints_by_offset,
        mode="reset",
        params={
            "asset_cfg": SceneEntityCfg("robot", joint_names=["slider_to_cart"]),
            "position_range": (-1.0, 1.0),
            "velocity_range": (-0.1, 0.1),
        },
    )

    reset_pole_position = EventTerm(
        func=mdp.reset_joints_by_offset,
        mode="reset",
        params={
            "asset_cfg": SceneEntityCfg("robot", joint_names=["cart_to_pole"]),
            "position_range": (-0.125 * math.pi, 0.125 * math.pi),
            "velocity_range": (-0.01 * math.pi, 0.01 * math.pi),
        },
    )

将所有这些组件组合在一起#

定义了场景和管理器配置之后,我们现在可以通过 envs.ManagerBasedEnvCfg 类定义环境配置。这个类接受场景、动作、观测和事件配置。

除此之外,它还接收了 envs.ManagerBasedEnvCfg.sim ,它定义了仿真参数,如时间步长、重力等。这些参数被初始化为默认值,但可以根据需要进行修改。我们建议通过在 envs.ManagerBasedEnvCfg 类中定义 __post_init__() 方法来实现,该方法在配置初始化后被调用。

@configclass
class CartpoleEnvCfg(ManagerBasedEnvCfg):
    """Configuration for the cartpole environment."""

    # Scene settings
    scene = CartpoleSceneCfg(num_envs=1024, env_spacing=2.5)
    # Basic settings
    observations = ObservationsCfg()
    actions = ActionsCfg()
    events = EventCfg()

    def __post_init__(self):
        """Post initialization."""
        # viewer settings
        self.viewer.eye = [4.5, 0.0, 6.0]
        self.viewer.lookat = [0.0, 0.0, 2.0]
        # step settings
        self.decimation = 4  # env step every 4 sim steps: 200Hz / 4 = 50Hz
        # simulation settings
        self.sim.dt = 0.005  # sim step every 5ms: 200Hz

运行模拟#

最后,我们再次访问模拟执行循环。现在,这非常简单,因为我们已经抽象出大部分细节到环境配置中。我们只需要调用 envs.ManagerBasedEnv.reset() 方法来重置环境,以及在 envs.ManagerBasedEnv.step() 方法中步进环境。这两个函数都会返回观测和信息字典,后者可能包含环境提供的其他信息。代理可以使用这些信息做决策。

envs.ManagerBasedEnv 类没有任何中止概念,因为该概念对于周期性任务是特定的。因此,用户需要负责为环境定义终止条件。在本教程中,我们定期重置模拟。

def main():
    """Main function."""
    # parse the arguments
    env_cfg = CartpoleEnvCfg()
    env_cfg.scene.num_envs = args_cli.num_envs
    # setup base environment
    env = ManagerBasedEnv(cfg=env_cfg)

    # simulate physics
    count = 0
    while simulation_app.is_running():
        with torch.inference_mode():
            # reset
            if count % 300 == 0:
                count = 0
                env.reset()
                print("-" * 80)
                print("[INFO]: Resetting environment...")
            # sample random actions
            joint_efforts = torch.randn_like(env.action_manager.action)
            # step the environment
            obs, _ = env.step(joint_efforts)
            # print current orientation of pole
            print("[Env 0]: Pole joint: ", obs["policy"][0][1].item())
            # update counter
            count += 1

    # close the environment
    env.close()

上面需要注意的一个重要事项是整个模拟循环包含在 torch.inference_mode() 上下文管理器中。这是因为环境在幕后使用PyTorch操作,我们希望确保模拟不会因PyTorch的自动求导引擎的开销而变慢,也不会为模拟操作计算梯度。

代码执行#

运行此教程中创建的基本环境,您可以使用以下命令:

./isaaclab.sh -p source/standalone/tutorials/03_envs/create_cartpole_base_env.py --num_envs 32

这会打开一个具有地平面、光源和cartpoles的场景。模拟应该正在进行中,并对cartpole进行随机动作。此外,它还在屏幕右下角打开一个名为 "Isaac Lab" 的UI窗口。此窗口包含不同的UI元素,可用于调试和可视化。

create_cartpole_base_env.py 的结果

要停止模拟,可以关闭窗口,或在启动模拟的终端中按 Ctrl+C

在这个教程中,我们学习了不同的管理器,来帮助定义基础环境。我们还在 source/standalone/tutorials/03_envs 目录中包含了更多定义基础环境的示例。为了完整起见,可以使用以下命令来运行它们:

# Floating cube environment with custom action term for PD control
./isaaclab.sh -p source/standalone/tutorials/03_envs/create_cube_base_env.py --num_envs 32

# Quadrupedal locomotion environment with a policy that interacts with the environment
./isaaclab.sh -p source/standalone/tutorials/03_envs/create_quadruped_base_env.py --num_envs 32

在接下来的教程中,我们将看看 envs.ManagerBasedRLEnv 类以及如何使用它来创建马尔可夫决策过程(MDP)。