如是我闻 在谈论了如何创建基础环境后,我们现在将探索如何为强化学习创建任务环境。
基础环境被设计为一个感知-行动环境(sense-act environment),代理(agent)可以向环境发送命令并从环境接收观测。这种最小接口对于许多应用来说已经足够,例如传统的运动规划和控制。然而,许多应用需要一个任务规范,这通常作为代理的学习目标。例如,在导航任务中,代理可能需要到达一个目标位置。为此,我们使用envs.RLTaskEnv
类,它扩展了基础环境以包括任务规范。
与Orbit中的其他组件类似,使用者还是不要直接修改基类RLTaskEnv
,而是简单地为他们的任务环境实现一个配置RLTaskEnvCfg
。这种做法允许我们将任务规范与环境实现分离,使得更容易重用同一环境的组件用于不同的任务。
在这个教程中,我们将使用RLTaskEnvCfg
配置车杆环境,以创建保持杆直立的任务。我们将学习如何使用奖励项、终止条件、课程和命令来指定任务。
还是那个问题,大家都是来这里训练机器人,你好歹给个机械臂的例子也行阿,一直说车杆我也用不到我的例子上去阿,啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊
在本指南中,我们使用在omni.isaac.orbit_tasks.classic.cartpole
模块中定义的车杆环境。
# Copyright (c) 2022-2024, The ORBIT Project Developers.
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
import math
import omni.isaac.orbit.sim as sim_utils
from omni.isaac.orbit.assets import ArticulationCfg, AssetBaseCfg
from omni.isaac.orbit.envs import RLTaskEnvCfg
from omni.isaac.orbit.managers import EventTermCfg as EventTerm
from omni.isaac.orbit.managers import ObservationGroupCfg as ObsGroup
from omni.isaac.orbit.managers import ObservationTermCfg as ObsTerm
from omni.isaac.orbit.managers import RewardTermCfg as RewTerm
from omni.isaac.orbit.managers import SceneEntityCfg
from omni.isaac.orbit.managers import TerminationTermCfg as DoneTerm
from omni.isaac.orbit.scene import InteractiveSceneCfg
from omni.isaac.orbit.utils import configclass
import omni.isaac.orbit_tasks.classic.cartpole.mdp as mdp
##
# Pre-defined configs
##
from omni.isaac.orbit_assets.cartpole import CARTPOLE_CFG # isort:skip
##
# Scene definition
##
@configclass
class CartpoleSceneCfg(InteractiveSceneCfg):
"""Configuration for a cart-pole scene."""
# ground plane
ground = AssetBaseCfg(
prim_path="/World/ground",
spawn=sim_utils.GroundPlaneCfg(size=(100.0, 100.0)),
)
# cartpole
robot: ArticulationCfg = CARTPOLE_CFG.replace(prim_path="{ENV_REGEX_NS}/Robot")
# lights
dome_light = AssetBaseCfg(
prim_path="/World/DomeLight",
spawn=sim_utils.DomeLightCfg(color=(0.9, 0.9, 0.9), intensity=500.0),
)
distant_light = AssetBaseCfg(
prim_path="/World/DistantLight",
spawn=sim_utils.DistantLightCfg(color=(0.9, 0.9, 0.9), intensity=2500.0),
init_state=AssetBaseCfg.InitialStateCfg(rot=(0.738, 0.477, 0.477, 0.0)),
)
##
# MDP settings
##
@configclass
class CommandsCfg:
"""Command terms for the MDP."""
# no commands for this MDP
null = mdp.NullCommandCfg()
@configclass
class ActionsCfg:
"""Action specifications for the MDP."""
joint_effort = mdp.JointEffortActionCfg(asset_name="robot", joint_names=["slider_to_cart"], scale=100.0)
@configclass
class ObservationsCfg:
"""Observation specifications for the MDP."""
@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()
@configclass
class EventCfg:
"""Configuration for events."""
# 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.5, 0.5),
},
)
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.25 * math.pi, 0.25 * math.pi),
"velocity_range": (-0.25 * math.pi, 0.25 * math.pi),
},
)
@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"])},
)
@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_manual_limit,
params={"asset_cfg": SceneEntityCfg("robot", joint_names=["slider_to_cart"]), "bounds": (-3.0, 3.0)},
)
@configclass
class CurriculumCfg:
"""Configuration for the curriculum."""
pass
##
# Environment configuration
##
@configclass
class CartpoleEnvCfg(RLTaskEnvCfg):
"""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
运行环境的脚本run_cartpole_rl_env.py
位于orbit/source/standalone/tutorials/03_envs
目录中。这个脚本与前一个教程中的cartpole_base_env.py
脚本类似,不同之处在于它使用envs.RLTaskEnv
而不是envs.BaseEnv
。让我们搂一眼他的代码。
# Copyright (c) 2022-2024, The ORBIT Project Developers.
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
"""
This script demonstrates how to run the RL environment for the cartpole balancing task.
"""
from __future__ import annotations
"""Launch Isaac Sim Simulator first."""
import argparse
from omni.isaac.orbit.app import AppLauncher
# add argparse arguments
parser = argparse.ArgumentParser(description="Tutorial on running the cartpole RL environment.")
parser.add_argument("--num_envs", type=int, default=16, help="Number of environments to spawn.")
# append AppLauncher cli args
AppLauncher.add_app_launcher_args(parser)
# parse the arguments
args_cli = parser.parse_args()
# launch omniverse app
app_launcher = AppLauncher(args_cli)
simulation_app = app_launcher.app
"""Rest everything follows."""
import torch
from omni.isaac.orbit.envs import RLTaskEnv
from omni.isaac.orbit_tasks.classic.cartpole.cartpole_env_cfg import CartpoleEnvCfg
def main():
"""Main function."""
# create environment configuration
env_cfg = CartpoleEnvCfg()
env_cfg.scene.num_envs = args_cli.num_envs
# setup RL environment
env = RLTaskEnv(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()
if __name__ == "__main__":
# run the main function
main()
# close sim app
simulation_app.close()
代码解析
我们已经在创建基础环境中讲解了上述内容的一部分,了解了如何指定场景 (scene)、观察 (observations)、行为 (actions) 和事件 (events)。因此,在这个教程中,我们将只关注环境的RL(强化学习)组件。
在Orbit中,提供了envs.mdp
模块中不同项的各种实现。我们将在这个教程中使用其中的一些项,但用户也可以自定义他们自己的项。这些通常放在他们的任务特定子包中(例如,在omni.isaac.orbit_tasks.classic.cartpole.mdp
中)。
定义奖励
managers.RewardManager
用于计算代理的奖励项。与其他管理器类似,我们使用managers.RewardTermCfg
类来配置它的像。managers.RewardTermCfg
类指定计算奖励的函数或可调用类以及与之关联的权重。它还接受一个参数字典"params",这些参数在调用奖励函数时传递给它。
对于车杆任务,我们将使用以下奖励项:
- 存活奖励:鼓励代理尽可能长时间地存活。
- 终止奖励:同样地,对代理终止行为进行惩罚。
- 杆角度奖励:鼓励代理保持杆在期望的直立位置。
- 小车速度奖励:鼓励代理尽可能保持小车速度较小。
- 杆速度奖励:鼓励代理尽可能保持杆速度较小。
@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"])},
)
定义终止条件
大多数学习任务发生在我们称之为一个周期(episode)的有限步骤数内。例如,在车杆任务中,我们希望代理尽可能长时间地平衡杆。然而,如果代理达到不稳定或不安全的状态,我们想要终止周期。另一方面,如果代理能够长时间平衡杆,我们希望终止周期并开始一个新的,以便代理可以从不同的起始配置学习平衡杆。
managers.TerminationsCfg
配置了什么构成周期的终止。在这个示例中,我们希望任务在满足以下任一条件时终止:
- 周期长度 大于定义的
max_episode_length
- 小车超出界限 小车超出了界限[-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_manual_limit,
params={"asset_cfg": SceneEntityCfg("robot", joint_names=["slider_to_cart"]), "bounds": (-3.0, 3.0)},
)
定义命令
对于各种条件目标任务(goal-conditioned tasks),指定代理的目标或命令是很有用的。这些通过managers.CommandManager
处理。命令管理器处理在每一步重采样和更新命令。它也可以用来将命令作为观察提供给代理。
对于这个简单任务,我们不使用任何命令。这是通过使用带有envs.mdp.NullCommandCfg
配置的命令项来指定的。我们也可以在运动或操纵任务中看到命令定义的示例。
@configclass
class CommandsCfg:
"""Command terms for the MDP."""
# no commands for this MDP
null = mdp.NullCommandCfg()
定义课程(curriculum)
在训练学习代理时,从一个简单的任务开始并随着代理训练的进展逐渐增加任务的难度往往是有帮助的。这就是课程学习背后的思想(curriculum learning)。在Orbit中,我们提供了一个managers.CurriculumManager
类,可以用来为环境定义一个课程。
在本指南中,为了简单起见,我们没有实现一个课程,但我们可以在其他运动或操纵任务中看到课程定义的示例。我们使用一个简单的直通课程来定义一个不修改环境的课程管理器。
@configclass
class CurriculumCfg:
"""Configuration for the curriculum."""
pass
将所有内容整合在一起
定义了上述所有组件后,我们现在可以为车杆环境创建RLTaskEnvCfg
配置。这类似于在创建基础环境中定义的BaseEnvCfg
,只是增加了上述部分中解释的RL组件。
@configclass
class CartpoleEnvCfg(RLTaskEnvCfg):
"""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
运行模拟循环
回到run_cartpole_rl_env.py
脚本,模拟循环与之前的教程类似。唯一的区别是我们创建了一个envs.RLTaskEnv
的实例,而不是envs.BaseEnv
。因此,现在envs.RLTaskEnv.step()
方法返回额外的信号,如奖励和终止状态。信息字典也保持了如各个项的奖励贡献、每个项的终止状态、周期长度等数量的记录。
def main():
"""Main function."""
# create environment configuration
env_cfg = CartpoleEnvCfg()
env_cfg.scene.num_envs = args_cli.num_envs
# setup RL environment
env = RLTaskEnv(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
脚本来运行环境。
./orbit.sh -p source/standalone/tutorials/03_envs/run_cartpole_rl_env.py --num_envs 32
这应该会打开一个与之前教程中相似的模拟。然而,这一次,环境返回更多指定奖励和终止状态的信号。此外,当基于配置中指定的终止标准终止时,各个环境会自行重置。
要停止模拟,可以关闭窗口,或在启动模拟的终端中按Ctrl+C。
在本指南中,我们看到了如何为强化学习创建一个任务环境。我们通过扩展基础环境来包含奖励、终止、命令和课程条款来实现这一点。我们还浏览了如何使用envs.RLTaskEnv
类运行环境并从中接收各种信号。
虽然可以手动为期望的任务创建envs.RLTaskEnv
类的实例,但这不具有可扩展性,因为它需要每个任务的专门脚本。因此,我们利用gymnasium.make()
函数来用gym接口创建环境。我们将在下一个指南中讨论如何做到这一点。
愿本文除一切机器人模拟器苦
以上