创建基于管理器的强化学习环境#
在 创建基于管理器的基础环境 中学习了如何创建基础环境后,我们现在将学习如何为强化学习创建基于管理器的任务环境。
基础环境被设计为一种感知-行为环境,智能体可以向环境发送命令,并从环境中接收观测。这种最小接口对于许多应用程序,如传统运动规划和控制,是足够的。然而,许多应用程序需要任务规范,这通常用作智能体的学习目标。例如,在导航任务中,智能体可能需要到达目标位置。为此,我们使用 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
52
53##
54# MDP settings
55##
56
57
58@configclass
59class ActionsCfg:
60 """Action specifications for the MDP."""
61
62 joint_effort = mdp.JointEffortActionCfg(asset_name="robot", joint_names=["slider_to_cart"], scale=100.0)
63
64
65@configclass
66class ObservationsCfg:
67 """Observation specifications for the MDP."""
68
69 @configclass
70 class PolicyCfg(ObsGroup):
71 """Observations for policy group."""
72
73 # observation terms (order preserved)
74 joint_pos_rel = ObsTerm(func=mdp.joint_pos_rel)
75 joint_vel_rel = ObsTerm(func=mdp.joint_vel_rel)
76
77 def __post_init__(self) -> None:
78 self.enable_corruption = False
79 self.concatenate_terms = True
80
81 # observation groups
82 policy: PolicyCfg = PolicyCfg()
83
84
85@configclass
86class EventCfg:
87 """Configuration for events."""
88
89 # 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.5, 0.5),
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.25 * math.pi, 0.25 * math.pi),
106 "velocity_range": (-0.25 * math.pi, 0.25 * math.pi),
107 },
108 )
109
110
111@configclass
112class RewardsCfg:
113 """Reward terms for the MDP."""
114
115 # (1) Constant running reward
116 alive = RewTerm(func=mdp.is_alive, weight=1.0)
117 # (2) Failure penalty
118 terminating = RewTerm(func=mdp.is_terminated, weight=-2.0)
119 # (3) Primary task: keep pole upright
120 pole_pos = RewTerm(
121 func=mdp.joint_pos_target_l2,
122 weight=-1.0,
123 params={"asset_cfg": SceneEntityCfg("robot", joint_names=["cart_to_pole"]), "target": 0.0},
124 )
125 # (4) Shaping tasks: lower cart velocity
126 cart_vel = RewTerm(
127 func=mdp.joint_vel_l1,
128 weight=-0.01,
129 params={"asset_cfg": SceneEntityCfg("robot", joint_names=["slider_to_cart"])},
130 )
131 # (5) Shaping tasks: lower pole angular velocity
132 pole_vel = RewTerm(
133 func=mdp.joint_vel_l1,
134 weight=-0.005,
135 params={"asset_cfg": SceneEntityCfg("robot", joint_names=["cart_to_pole"])},
136 )
137
138
139@configclass
140class TerminationsCfg:
141 """Termination terms for the MDP."""
142
143 # (1) Time out
144 time_out = DoneTerm(func=mdp.time_out, time_out=True)
145 # (2) Cart out of bounds
146 cart_out_of_bounds = DoneTerm(
147 func=mdp.joint_pos_out_of_manual_limit,
148 params={"asset_cfg": SceneEntityCfg("robot", joint_names=["slider_to_cart"]), "bounds": (-3.0, 3.0)},
149 )
150
151
152##
153# Environment configuration
154##
155
156
157@configclass
158class CartpoleEnvCfg(ManagerBasedRLEnvCfg):
159 """Configuration for the cartpole environment."""
160
161 # Scene settings
162 scene: CartpoleSceneCfg = CartpoleSceneCfg(num_envs=4096, env_spacing=4.0)
163 # Basic settings
164 observations: ObservationsCfg = ObservationsCfg()
165 actions: ActionsCfg = ActionsCfg()
166 events: EventCfg = EventCfg()
167 # MDP settings
168 rewards: RewardsCfg = RewardsCfg()
169 terminations: TerminationsCfg = TerminationsCfg()
170
171 # Post initialization
172 def __post_init__(self) -> None:
173 """Post initialization."""
174 # general settings
175 self.decimation = 2
176 self.episode_length_s = 5
177 # viewer settings
178 self.viewer.eye = (8.0, 0.0, 5.0)
179 # simulation settings
180 self.sim.dt = 1 / 120
181 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
处理。命令管理器在每一步中处理重新采样和更新命令。它还可以用作向智能体提供命令的观测。
对于这个简单的任务,我们不使用任何命令。因此,我们将这个属性保留为默认值,即 None。您可以在其他运动或操控任务中看到如何定义命令管理器的示例。
定义课程#
在训练学习智能体时,往往从一个简单的任务开始,并随着智能体的训练逐渐增加任务的难度。这就是课程学习的理念。在 Isaac Lab 中,我们提供了一个 managers.CurriculumManager
类,可以用来为您的环境定义课程。
在本教程中,为了简单起见,我们不实现课程,但是您可以在其他 locomotion 或 manipulation 任务中看到课程定义的示例。我们使用一个简单的经过课程来定义一个不修改环境的课程管理器。
将所有内容联系起来#
通过定义上述所有组件,我们现在可以为 cartpole 环境创建 ManagerBasedRLEnvCfg
配置。这类似于 创建基于管理器的基础环境 中定义的 ManagerBasedEnvCfg
,只是在上述部分中添加了解释的强化学习组件。
@configclass
class CartpoleEnvCfg(ManagerBasedRLEnvCfg):
"""Configuration for the cartpole 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
rewards: RewardsCfg = RewardsCfg()
terminations: TerminationsCfg = TerminationsCfg()
# 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 接口的环境。我们将在下一个教程中学习如何做到这一点。