创建基于管理器的基础环境#
环境将模拟的不同方面,如场景、观测和行动空间、重置事件等集合在一起,为各种应用程序创建一个连贯的接口。在Isaac Lab中,基于管理器的环境被实现为: envs.ManagerBasedEnv
和 envs.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
基类包含了模拟交互的许多复杂性,并为用户提供了一个简单的接口来运行模拟并与其交互。它由以下组件组成:
scene.InteractiveScene
- 用于模拟的场景。managers.ActionManager
- 处理动作的管理器。managers.ObservationManager
- 处理观测的管理器。managers.EventManager
- 安排操作(例如域随机化)在指定的模拟事件上执行。例如,启动时,重置时或定期间隔。
通过配置这些组件,用户可以以最小的工作量创建相同环境的不同变体。在本教程中,我们将浏览 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元素,可用于调试和可视化。
要停止模拟,可以关闭窗口,或在启动模拟的终端中按 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)。