如是我闻:
环境将模拟的不同方面如场景、观测和行动空间、重置事件等汇集在一起,为各种应用创建一个清晰一致 的接口。在Orbit中,环境是作为envs.BaseEnv
和envs.RLTaskEnv
类实现的。这两个类非常相似,但envs.RLTaskEnv
对强化学习任务很有用,包含奖励、终止条件、课程(curriculum)和命令生成。envs.BaseEnv
类针对传统机器人控制很有用,并且不包含奖励和终止条件。
在本指南中,我们将看看基类envs.BaseEnv
及其对应的配置类envs.BaseEnvCfg
。我们将使用之前的车杆环境来说明创建一个新的envs.BaseEnv
环境的不同组件。
指南06对应于orbit/source/standalone/tutorials/03_envs
目录下的create_cartpole_base_env.py
脚本,让我们先搂一眼完整的代码
# Copyright (c) 2022-2024, The ORBIT Project Developers.
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
"""
This script demonstrates how to create a simple environment with a cartpole. It combines the concepts of
scene, action, observation and event managers to create an environment.
"""
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 creating a cartpole base 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 math
import torch
import omni.isaac.orbit.envs.mdp as mdp
from omni.isaac.orbit.envs import BaseEnv, BaseEnvCfg
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 SceneEntityCfg
from omni.isaac.orbit.utils import configclass
from omni.isaac.orbit_tasks.classic.cartpole.cartpole_env_cfg import CartpoleSceneCfg
@configclass
class ActionsCfg:
"""Action specifications for the environment."""
joint_efforts = mdp.JointEffortActionCfg(asset_name="robot", joint_names=["slider_to_cart"], scale=5.0)
@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()
@configclass
class EventCfg:
"""Configuration for events."""
# on startup
add_pole_mass = EventTerm(
func=mdp.add_body_mass,
mode="startup",
params={
"asset_cfg": SceneEntityCfg("robot", body_names=["pole"]),
"mass_range": (0.1, 0.5),
},
)
# 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),
},
)
@configclass
class CartpoleEnvCfg(BaseEnvCfg):
"""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
def main():
"""Main function."""
# parse the arguments
env_cfg = CartpoleEnvCfg()
env_cfg.scene.num_envs = args_cli.num_envs
# setup base environment
env = BaseEnv(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()
if __name__ == "__main__":
# run the main function
main()
# close sim app
simulation_app.close()
代码解析
基础类envs.BaseEnv
围绕模拟交互的许多复杂性提供了一个简单的接口,供用户运行模拟并与之交互。它由以下组件组成:
scene.InteractiveScene
- 用于模拟的场景。managers.ActionManager
- 处理行动的管理器。managers.ObservationManager
- 处理观察的管理器。managers.EventManager
- 在指定的模拟事件上安排操作(如域随机化)的管理器。例如,在启动时、重置时或周期性间隔时。
通过配置这些组件,用户可以用不费力气的创建相同环境的不同变体。在本指南中,我们将通过envs.BaseEnv
类的不同组件以及如何配置它们来创建一个新环境。
设计场景
创建新环境的第一步是配置其场景。对于车杆环境,我们将使用前一个指南中的场景。因此,我们这里省略场景配置。有关如何配置场景的更多详情,请参见Orbit 使用指南 05
你可能好奇这个车杆的配置怎么没有明确写出来,实际上他在这里被引入了
from omni.isaac.orbit_tasks.classic.cartpole.cartpole_env_cfg import CartpoleSceneCfg
如果你去翻CartpoleSceneCfg的代码,他是这么写的
@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)),
)
定义动作空间
在前一个指南中,我们直接使用assets.Articulation.set_joint_effort_target()
方法将动作输入给车杆。在这个指南中,我们将使用managers.ActionManager
来处理动作。
动作管理器可以包括多个managers.ActionTerm
。每个动作项(action term)负责对环境的特定方面施加控制。例如,对于机器人手臂,我们可以有两个动作项——一个用于控制手臂的关节,另一个用于控制夹持器。这种组合允许用户为环境的不同方面定义不同的控制方案。
在车杆环境中,我们想要控制施加在小车上的力以平衡杆。因此,我们将创建一个控制施加在小车上的力的动作项。
@configclass
class ActionsCfg:
"""Action specifications for the environment."""
joint_efforts = mdp.JointEffortActionCfg(asset_name="robot", joint_names=["slider_to_cart"], scale=5.0)
定义观察空间 (observations)
虽然场景定义了环境的状态,但观察定义了代理( agent)可观察到的状态。这些观察被代理用来决定采取什么行动。在Orbit中,观察由managers.ObservationManager
类计算。
与动作管理器类似,观察管理器可以包括多个观察项。这些观察项进一步分组成观察组,用于为环境定义不同的观察空间。例如,对于层次控制,我们可能想要定义两个观察组——一个用于低级控制器,另一个用于高级控制器。这里假设了同一组中的所有观察项具有相同的维度。
对于本指南,我们将只定义一个名为"policy"
的观察组。虽然不完全是规定性的,这个组是Orbit中各种包装器的必要要求。我们通过继承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()
定义模拟事件
此时,我们已为车杆环境定义了场景、行动和观测。所有这些组件的一般思路是定义配置类,然后将它们传递给相应的管理器。 事件管理器也不例外。
managers.EventManager
类负责与模拟状态变化相对应的事件。这包括重置(或随机化)场景、随机化物理属性(如质量、摩擦等)和变化视觉属性(如颜色、纹理等)。每个事件都通过managers.EventTermCfg
类指定,该类接受managers.EventTermCfg.func
,它指定执行事件的函数或可调用类。
此外,它期望事件的模式。模式指定应用事件项的时机。可以指定自己的模式。为此,您需要适应BaseEnv
类。然而,Orbit开箱即用提供了三种常用模式:
"startup"
- 环境启动时只发生一次的事件。"reset"
- 环境终止和重置时发生的事件。"interval"
- 在给定间隔执行的事件,即,在一定数量的步骤后定期执行。
在这个示例中,我们定义了在启动时随机化杆的质量的事件。由于这个操作的代价很昂贵,我们不想在每次重置时都进行,所以这个操作只做一次。我们还创建了一个事件,在每次重置时随机化车杆和杆的初始关节状态。
@configclass
class EventCfg:
"""Configuration for events."""
# on startup
add_pole_mass = EventTerm(
func=mdp.add_body_mass,
mode="startup",
params={
"asset_cfg": SceneEntityCfg("robot", body_names=["pole"]),
"mass_range": (0.1, 0.5),
},
)
# 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.BaseEnvCfg
类定义环境配置。这个类接受场景、行动、观察和事件配置。
除此之外,它还接受envs.BaseEnvCfg.sim
,它定义了诸如时间步、重力等模拟参数。这被初始化为默认值,但可以根据需要进行修改。我们推荐通过在envs.BaseEnvCfg
类中定义__post_init__()
方法来进行修改,该方法在配置初始化后被调用。
大家都要是来这训练机器人的,谁要在这种关键的地方看车杆的例子阿 5555555555555555555555
@configclass
class CartpoleEnvCfg(BaseEnvCfg):
"""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.BaseEnv.reset()
方法来重置环境和envs.BaseEnv.step()
方法来步进环境。这两个函数都返回观测值和可能包含环境提供的额外信息的info字典。这些可以被代理用于决策。
envs.BaseEnv
类没有任何关于终止的概念,因为那是特定于情节任务的概念。因此,用户负责为环境定义终止条件。在这个教程中,我们在规律的间隔内重置模拟。
def main():
"""Main function."""
# parse the arguments
env_cfg = CartpoleEnvCfg()
env_cfg.scene.num_envs = args_cli.num_envs
# setup base environment
env = BaseEnv(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自动微分引擎的开销而变慢,并且不会为模拟操作计算梯度。
代码运行
要运行本教程中制作的基础环境,可以使用以下命令:
./orbit.sh -p source/standalone/tutorials/03_envs/create_cartpole_base_env.py --num_envs 32
这应该会打开一个包含地面平面、光源和车杆的展台。模拟应该在车杆上播放随机动作。此外,它在屏幕右下角打开一个名为"Orbit"的UI窗口。这个窗口包含了可以用于调试和可视化的不同UI元素。
要停止模拟,可以关闭窗口,或在启动模拟的终端中按Ctrl+C。
在这个教程中,我们了解了帮助定义基础环境的不同管理器。我们在orbit/source/standalone/tutorials/03_envs
目录中包含了更多定义基础环境的示例。为了完整性,它们可以使用以下命令运行:
# Floating cube environment with custom action term for PD control
./orbit.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
./orbit.sh -p source/standalone/tutorials/03_envs/create_quadruped_base_env.py --num_envs 32
在接下来的教程中,我们将研究envs.RLTaskEnv
类以及如何使用它来创建一个马尔科夫决策过程(MDP)。
愿本文除一切机器人模拟器苦
非常的有品
以上