创建基于管理器的强化学习环境#
在 创建基于管理器的基础环境 中学习了如何创建基础环境后,我们现在将学习如何为强化学习创建基于管理器的任务环境。
基础环境被设计为一种感知-行为环境,代理可以向环境发送命令,并从环境中接收观测。这种最小接口对于许多应用程序,如传统运动规划和控制,是足够的。然而,许多应用程序需要任务规范,这通常用作代理的学习目标。例如,在导航任务中,代理可能需要到达目标位置。为此,我们使用 envs.ManagerBasedRLEnv
类扩展基础环境以包含任务规范。
与 Isaac Lab 中的其他组件类似,我们鼓励用户不要直接修改 envs.ManagerBasedRLEnv
基类,而是简单地为他们的任务环境实现一个配置 envs.ManagerBasedRLEnvCfg
。这种做法使我们能够将任务规范与环境实现分开,使得更容易地重用相同环境的组件用于不同的任务。
在本教程中,我们将使用 envs.ManagerBasedRLEnvCfg
配置 cartpole 环境,以创建一个平衡杆的基于管理器的任务。我们将学习如何使用奖励项、终止条件、课程和命令来指定任务。
代码#
在本教程中,我们使用 omni.isaac.lab_tasks.manager_based.classic.cartpole
模块中定义的 cartpole 环境。
cartpole_env_cfg.py 代码
1# Copyright (c) 2022-2024, The Isaac Lab Project Developers.
2# All rights reserved.
3#
4# SPDX-License-Identifier: BSD-3-Clause
5
6import math
7
8import omni.isaac.lab.sim as sim_utils
9from omni.isaac.lab.assets import ArticulationCfg, AssetBaseCfg
10from omni.isaac.lab.envs import ManagerBasedRLEnvCfg
11from omni.isaac.lab.managers import EventTermCfg as EventTerm
12from omni.isaac.lab.managers import ObservationGroupCfg as ObsGroup
13from omni.isaac.lab.managers import ObservationTermCfg as ObsTerm
14from omni.isaac.lab.managers import RewardTermCfg as RewTerm
15from omni.isaac.lab.managers import SceneEntityCfg
16from omni.isaac.lab.managers import TerminationTermCfg as DoneTerm
17from omni.isaac.lab.scene import InteractiveSceneCfg
18from omni.isaac.lab.utils import configclass
19
20import omni.isaac.lab_tasks.manager_based.classic.cartpole.mdp as mdp
21
22##
23# Pre-defined configs
24##
25from omni.isaac.lab_assets.cartpole import CARTPOLE_CFG # isort:skip
26
27
28##
29# Scene definition
30##
31
32
33@configclass
34class CartpoleSceneCfg(InteractiveSceneCfg):
35 """Configuration for a cart-pole scene."""
36
37 # ground plane
38 ground = AssetBaseCfg(
39 prim_path="/World/ground",
40 spawn=sim_utils.GroundPlaneCfg(size=(100.0, 100.0)),
41 )
42
43 # cartpole
44 robot: ArticulationCfg = CARTPOLE_CFG.replace(prim_path="{ENV_REGEX_NS}/Robot")
45
46 # lights
47 dome_light = AssetBaseCfg(
48 prim_path="/World/DomeLight",
49 spawn=sim_utils.DomeLightCfg(color=(0.9, 0.9, 0.9), intensity=500.0),
50 )
51 distant_light = AssetBaseCfg(
52 prim_path="/World/DistantLight",
53 spawn=sim_utils.DistantLightCfg(color=(0.9, 0.9, 0.9), intensity=2500.0),
54 init_state=AssetBaseCfg.InitialStateCfg(rot=(0.738, 0.477, 0.477, 0.0)),
55 )
56
57
58##
59# MDP settings
60##
61
62
63@configclass
64class CommandsCfg:
65 """Command terms for the MDP."""
66
67 # no commands for this MDP
68 null = mdp.NullCommandCfg()
69
70
71@configclass
72class ActionsCfg:
73 """Action specifications for the MDP."""
74
75 joint_effort = mdp.JointEffortActionCfg(asset_name="robot", joint_names=["slider_to_cart"], scale=100.0)
76
77
78@configclass
79class ObservationsCfg:
80 """Observation specifications for the MDP."""
81
82 @configclass
83 class PolicyCfg(ObsGroup):
84 """Observations for policy group."""
85
86 # observation terms (order preserved)
87 joint_pos_rel = ObsTerm(func=mdp.joint_pos_rel)
88 joint_vel_rel = ObsTerm(func=mdp.joint_vel_rel)
89
90 def __post_init__(self) -> None:
91 self.enable_corruption = False
92 self.concatenate_terms = True
93
94 # observation groups
95 policy: PolicyCfg = PolicyCfg()
96
97
98@configclass
99class EventCfg:
100 """Configuration for events."""
101
102 # reset
103 reset_cart_position = EventTerm(
104 func=mdp.reset_joints_by_offset,
105 mode="reset",
106 params={
107 "asset_cfg": SceneEntityCfg("robot", joint_names=["slider_to_cart"]),
108 "position_range": (-1.0, 1.0),
109 "velocity_range": (-0.5, 0.5),
110 },
111 )
112
113 reset_pole_position = EventTerm(
114 func=mdp.reset_joints_by_offset,
115 mode="reset",
116 params={
117 "asset_cfg": SceneEntityCfg("robot", joint_names=["cart_to_pole"]),
118 "position_range": (-0.25 * math.pi, 0.25 * math.pi),
119 "velocity_range": (-0.25 * math.pi, 0.25 * math.pi),
120 },
121 )
122
123
124@configclass
125class RewardsCfg:
126 """Reward terms for the MDP."""
127
128 # (1) Constant running reward
129 alive = RewTerm(func=mdp.is_alive, weight=1.0)
130 # (2) Failure penalty
131 terminating = RewTerm(func=mdp.is_terminated, weight=-2.0)
132 # (3) Primary task: keep pole upright
133 pole_pos = RewTerm(
134 func=mdp.joint_pos_target_l2,
135 weight=-1.0,
136 params={"asset_cfg": SceneEntityCfg("robot", joint_names=["cart_to_pole"]), "target": 0.0},
137 )
138 # (4) Shaping tasks: lower cart velocity
139 cart_vel = RewTerm(
140 func=mdp.joint_vel_l1,
141 weight=-0.01,
142 params={"asset_cfg": SceneEntityCfg("robot", joint_names=["slider_to_cart"])},
143 )
144 # (5) Shaping tasks: lower pole angular velocity
145 pole_vel = RewTerm(
146 func=mdp.joint_vel_l1,
147 weight=-0.005,
148 params={"asset_cfg": SceneEntityCfg("robot", joint_names=["cart_to_pole"])},
149 )
150
151
152@configclass
153class TerminationsCfg:
154 """Termination terms for the MDP."""
155
156 # (1) Time out
157 time_out = DoneTerm(func=mdp.time_out, time_out=True)
158 # (2) Cart out of bounds
159 cart_out_of_bounds = DoneTerm(
160 func=mdp.joint_pos_out_of_manual_limit,
161 params={"asset_cfg": SceneEntityCfg("robot", joint_names=["slider_to_cart"]), "bounds": (-3.0, 3.0)},
162 )
163
164
165@configclass
166class CurriculumCfg:
167 """Configuration for the curriculum."""
168
169 pass
170
171
172##
173# Environment configuration
174##
175
176
177@configclass
178class CartpoleEnvCfg(ManagerBasedRLEnvCfg):
179 """Configuration for the locomotion velocity-tracking environment."""
180
181 # Scene settings
182 scene: CartpoleSceneCfg = CartpoleSceneCfg(num_envs=4096, env_spacing=4.0)
183 # Basic settings
184 observations: ObservationsCfg = ObservationsCfg()
185 actions: ActionsCfg = ActionsCfg()
186 events: EventCfg = EventCfg()
187 # MDP settings
188 curriculum: CurriculumCfg = CurriculumCfg()
189 rewards: RewardsCfg = RewardsCfg()
190 terminations: TerminationsCfg = TerminationsCfg()
191 # No command generator
192 commands: CommandsCfg = CommandsCfg()
193
194 # Post initialization
195 def __post_init__(self) -> None:
196 """Post initialization."""
197 # general settings
198 self.decimation = 2
199 self.episode_length_s = 5
200 # viewer settings
201 self.viewer.eye = (8.0, 0.0, 5.0)
202 # simulation settings
203 self.sim.dt = 1 / 120
204 self.sim.render_interval = self.decimation
用于运行环境的脚本 run_cartpole_rl_env.py
存在于 isaaclab/source/standalone/tutorials/03_envs
目录中。该脚本与前一个教程中的 cartpole_base_env.py
脚本类似,只是它使用 envs.ManagerBasedRLEnv
而不是 envs.ManagerBasedEnv
。
run_cartpole_rl_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"""This script demonstrates how to run the RL environment for the cartpole balancing task."""
7
8"""Launch Isaac Sim Simulator first."""
9
10import argparse
11
12from omni.isaac.lab.app import AppLauncher
13
14# add argparse arguments
15parser = argparse.ArgumentParser(description="Tutorial on running the cartpole RL environment.")
16parser.add_argument("--num_envs", type=int, default=16, help="Number of environments to spawn.")
17
18# append AppLauncher cli args
19AppLauncher.add_app_launcher_args(parser)
20# parse the arguments
21args_cli = parser.parse_args()
22
23# launch omniverse app
24app_launcher = AppLauncher(args_cli)
25simulation_app = app_launcher.app
26
27"""Rest everything follows."""
28
29import torch
30
31from omni.isaac.lab.envs import ManagerBasedRLEnv
32
33from omni.isaac.lab_tasks.manager_based.classic.cartpole.cartpole_env_cfg import CartpoleEnvCfg
34
35
36def main():
37 """Main function."""
38 # create environment configuration
39 env_cfg = CartpoleEnvCfg()
40 env_cfg.scene.num_envs = args_cli.num_envs
41 # setup RL environment
42 env = ManagerBasedRLEnv(cfg=env_cfg)
43
44 # simulate physics
45 count = 0
46 while simulation_app.is_running():
47 with torch.inference_mode():
48 # reset
49 if count % 300 == 0:
50 count = 0
51 env.reset()
52 print("-" * 80)
53 print("[INFO]: Resetting environment...")
54 # sample random actions
55 joint_efforts = torch.randn_like(env.action_manager.action)
56 # step the environment
57 obs, rew, terminated, truncated, info = env.step(joint_efforts)
58 # print current orientation of pole
59 print("[Env 0]: Pole joint: ", obs["policy"][0][1].item())
60 # update counter
61 count += 1
62
63 # close the environment
64 env.close()
65
66
67if __name__ == "__main__":
68 # run the main function
69 main()
70 # close sim app
71 simulation_app.close()
代码解释#
我们已经在 创建基于管理器的基础环境 教程中学习了上述部分,以了解如何指定场景、观测、动作和事件。因此,在本教程中,我们将只专注于环境的强化学习组件。
在 Isaac Lab 中,我们提供了 envs.mdp
模块中不同术语的各种实现。我们将在本教程中使用其中一些术语,但用户也可以自由定义自己的术语。这些通常被放置在他们任务特定的子包中(例如,在 omni.isaac.lab_tasks.manager_based.classic.cartpole.mdp
中)。
定义奖励#
managers.RewardManager
用于计算代理的奖励项。与其他管理器类似,它的术语是使用 managers.RewardTermCfg
配置的。 managers.RewardTermCfg
类指定了计算奖励的函数或可调用类,以及与之相关联的权重。它还使用 "params"
的参数字典,在奖励函数被调用时传递参数。
对于 cartpole 任务,我们将使用以下奖励项:
存活奖励:鼓励代理尽可能长时间保持存活状态。
终止奖励:同样惩罚代理的终止。
杆角度奖励:鼓励代理保持杆在期望的直立位置。
小车速度奖励:鼓励代理尽可能保持小车速度较小。
杆速度奖励:鼓励代理尽可能保持杆速度较小。
@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"])},
)
定义终止条件#
大多数学习任务在有限数量的步骤中进行,我们称之为一个回合。例如,在 cartpole 任务中,我们希望代理尽可能长时间地保持杆的平衡。然而,如果代理达到不稳定或不安全状态,我们希望终止回合。另一方面,如果代理能够长时间保持杆平衡,我们希望终止回合并开始新的回合,以便代理可以学会从不同的起始配置中平衡杆。
managers.TerminationsCfg
配置了何时终止一个回合。在本例中,我们希望当满足以下任一条件时终止任务:
回合长度:回合长度大于定义的最大回合长度。
小车越界:小车走出边界 [-3, 3]。
标志 managers.TerminationsCfg.time_out
指定了术语是时间限制(截断)术语还是终止术语。这些用于指示 Gymnasium’s documentation 中描述的两种终止类型。
@configclass
class TerminationsCfg:
"""Termination terms for the MDP."""
# (1) Time out
time_out = DoneTerm(func=mdp.time_out, time_out=True)
# (2) Cart out of bounds
cart_out_of_bounds = DoneTerm(
func=mdp.joint_pos_out_of_manual_limit,
params={"asset_cfg": SceneEntityCfg("robot", joint_names=["slider_to_cart"]), "bounds": (-3.0, 3.0)},
)
定义命令#
对于各种目标条件的任务,指定代理的目标或命令是有用的。这通过 managers.CommandManager
处理。命令管理器在每一步中处理重新采样和更新命令。它还可以用作向代理提供命令的观测。
对于这个简单的任务,我们不使用任何命令。这通过使用 envs.mdp.NullCommandCfg
配置的命令项来指定。但是,您可以在 locomotion 或 manipulation 任务中看到命令定义的示例。
@configclass
class CommandsCfg:
"""Command terms for the MDP."""
# no commands for this MDP
null = mdp.NullCommandCfg()
定义课程#
在训练学习代理时,往往从一个简单的任务开始,并随着代理的训练逐渐增加任务的难度。这就是课程学习的理念。在 Isaac Lab 中,我们提供了一个 managers.CurriculumManager
类,可以用来为您的环境定义课程。
在本教程中,为了简单起见,我们不实现课程,但是您可以在其他 locomotion 或 manipulation 任务中看到课程定义的示例。我们使用一个简单的经过课程来定义一个不修改环境的课程管理器。
@configclass
class CurriculumCfg:
"""Configuration for the curriculum."""
pass
将所有内容联系起来#
通过定义上述所有组件,我们现在可以为 cartpole 环境创建 ManagerBasedRLEnvCfg
配置。这类似于 创建基于管理器的基础环境 中定义的 ManagerBasedEnvCfg
,只是在上述部分中添加了解释的强化学习组件。
@configclass
class CartpoleEnvCfg(ManagerBasedRLEnvCfg):
"""Configuration for the locomotion velocity-tracking environment."""
# Scene settings
scene: CartpoleSceneCfg = CartpoleSceneCfg(num_envs=4096, env_spacing=4.0)
# Basic settings
observations: ObservationsCfg = ObservationsCfg()
actions: ActionsCfg = ActionsCfg()
events: EventCfg = EventCfg()
# MDP settings
curriculum: CurriculumCfg = CurriculumCfg()
rewards: RewardsCfg = RewardsCfg()
terminations: TerminationsCfg = TerminationsCfg()
# No command generator
commands: CommandsCfg = CommandsCfg()
# Post initialization
def __post_init__(self) -> None:
"""Post initialization."""
# general settings
self.decimation = 2
self.episode_length_s = 5
# viewer settings
self.viewer.eye = (8.0, 0.0, 5.0)
# simulation settings
self.sim.dt = 1 / 120
self.sim.render_interval = self.decimation
运行模拟循环#
回到 run_cartpole_rl_env.py
脚本,模拟循环类似于之前的教程。唯一的区别是,我们创建了一个 envs.ManagerBasedRLEnv
的实例,而不是 envs.ManagerBasedEnv
。因此,现在 envs.ManagerBasedRLEnv.step()
方法返回额外的信号,例如奖励和终止状态。信息字典还保持记录诸如来自各个术语奖励的贡献,每个术语的终止状态,回合长度等的日志。
def main():
"""Main function."""
# create environment configuration
env_cfg = CartpoleEnvCfg()
env_cfg.scene.num_envs = args_cli.num_envs
# setup RL environment
env = ManagerBasedRLEnv(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, rew, terminated, truncated, info = 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()
代码执行#
与之前的教程类似,可以通过执行 run_cartpole_rl_env.py
脚本来运行环境。
./isaaclab.sh -p source/standalone/tutorials/03_envs/run_cartpole_rl_env.py --num_envs 32
这应该会打开与上一个教程中类似的模拟。然而,这次,环境返回了更多的信号,指定了奖励和终止状态。此外,各个环境在根据配置中指定的终止条件终止时会重新进行重置。
要停止模拟,您可以关闭窗口,或者在启动模拟的终端中按 Ctrl+C
。
在本教程中,我们学习了如何为强化学习创建任务环境。我们通过扩展基础环境来包括奖励、终止条件、命令和课程术语来实现这一点。我们还学习了如何使用 envs.ManagerBasedRLEnv
类来运行环境并从中接收各种信号。
虽然可以手动为所需的任务创建 envs.ManagerBasedRLEnv
类的实例,但这并不可伸缩,因为它需要为每个任务使用专门的脚本。因此,我们利用 gymnasium.make()
函数来创建具有 gym 接口的环境。我们将在下一个教程中学习如何做到这一点。