Nvidia Isaac Lab 8 - 在 Isaac Lab 中,训练第二个机器人 - 5. 整合所有内容
至此,已通过类完成管理器和场景的定义。下面还有一步:实例化这些类,并且在基于管理器的环境配置中设置它们。
1. 实例化管理器
像 observations = ObservationsCfg() 这样的行就是实例化管理器的地方。
此外还需要设置关键的仿真参数,包括:
- 环境数量(即 Stage 上训练设置的副本数)
- 环境之间的物理间距
- 训练回合的长度(还记得之前提到的 MDP 超时终止条件吗?)
训练回合是单次试验:机器人从初始状态开始,与仿真环境交互,根据其策略采取动作,接收观测和奖励,持续进行直到达到某个终止条件(例如摔倒或超时),随后环境重置,开始新回合。
替换 reach_env_cfg.py 中的该类:
@configclass
class ReachEnvCfg(ManagerBasedRLEnvCfg):
"""Configuration for the reach end-effector pose tracking environment."""
# Scene settings - how many robots, how far apart?
scene = ReachSceneCfg(num_envs=2000, env_spacing=2.5)
# Basic settings
observations = ObservationsCfg()
actions = ActionsCfg()
commands: CommandsCfg = CommandsCfg()
# MDP settings
rewards = RewardsCfg()
terminations = TerminationsCfg()
events = EventCfg()
curriculum = CurriculumCfg()
def __post_init__(self):
"""Post initialization."""
# general settings
self.decimation = 2
self.sim.render_interval = self.decimation
self.episode_length_s = 3.0
self.viewer.eye = (3.5, 3.5, 3.5)
# simulation settings
self.sim.dt = 1.0 / 60.02. 回放配置
在回放或可视化结果时,可能希望使用与训练时不同的环境设置。
例如,可能希望减少环境副本数,或移除为增强训练鲁棒性而添加的随机化。
在下方添加以下类,创建用于回放的自定义环境配置:
@configclass
class ReachEnvCfg_PLAY(ReachEnvCfg):
def __post_init__(self):
# post init of parent
super().__post_init__()
# make a smaller scene for play
self.scene.num_envs = 50
self.scene.env_spacing = 2.5
# disable randomization for play
self.observations.policy.enable_corruption = False3. 注册入口点
现在,为了能够引用环境的该子类版本,需要将其添加到 source/Reach/Reach/tasks/manager_based/reach/__init__.py 文件中。注意,第二个 kwarg 如何引用上面创建的类。
将以下代码添加到 source/Reach/Reach/tasks/manager_based/reach/__init__.py 文件的底部,用于将 ReachEnvCfg_PLAY 类注册为额外的入口点。
gym.register(
id="Template-Reach-Play-v0",
entry_point="isaaclab.envs:ManagerBasedRLEnv",
disable_env_checker=True,
kwargs={
"env_cfg_entry_point": f"{__name__}.reach_env_cfg:ReachEnvCfg_PLAY",
"skrl_cfg_entry_point": f"{agents.__name__}:skrl_ppo_cfg.yaml",
},
)4. 完整的环境配置
# Copyright (c) 2022-2025, The Isaac Lab Project Developers.
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
import math
import carb
NUCLEUS_ASSET_ROOT_DIR = carb.settings.get_settings().get("/persistent/isaac/asset_root/cloud")
"""Path to the root directory on the Nucleus Server."""
NVIDIA_NUCLEUS_DIR = f"{NUCLEUS_ASSET_ROOT_DIR}/NVIDIA"
"""Path to the root directory on the NVIDIA Nucleus Server."""
ISAAC_NUCLEUS_DIR = f"{NUCLEUS_ASSET_ROOT_DIR}/Isaac"
"""Path to the ``Isaac`` directory on the NVIDIA Nucleus Server."""
import isaaclab.sim as sim_utils
from isaaclab.assets import AssetBaseCfg
from isaaclab.envs import ManagerBasedRLEnvCfg
from isaaclab.managers import ActionTermCfg as ActionTerm
from isaaclab.managers import CurriculumTermCfg as CurrTerm
from isaaclab.managers import EventTermCfg as EventTerm
from isaaclab.managers import ObservationGroupCfg as ObsGroup
from isaaclab.managers import ObservationTermCfg as ObsTerm
from isaaclab.managers import RewardTermCfg as RewTerm
from isaaclab.managers import SceneEntityCfg
from isaaclab.managers import TerminationTermCfg as DoneTerm
from isaaclab.scene import InteractiveSceneCfg
from isaaclab.utils import configclass
from isaaclab.utils.noise import AdditiveUniformNoiseCfg as Unoise
from isaaclab.sim.spawners.from_files.from_files_cfg import GroundPlaneCfg
from . import mdp
##
# Pre-defined configs
##
from .ur_gripper import UR_GRIPPER_CFG # isort:skip
##
# Scene definition
##
@configclass
class ReachSceneCfg(InteractiveSceneCfg):
"""Configuration for a scene."""
# world
ground = AssetBaseCfg(
prim_path="/World/ground",
spawn=sim_utils.GroundPlaneCfg(),
init_state=AssetBaseCfg.InitialStateCfg(pos=(0.0, 0.0, -1.05)),
)
# robot
robot = UR_GRIPPER_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=5000.0),
)
table = AssetBaseCfg(
prim_path="{ENV_REGEX_NS}/Table",
spawn=sim_utils.UsdFileCfg(
usd_path=f"{ISAAC_NUCLEUS_DIR}/Props/Mounts/SeattleLabTable/table_instanceable.usd",
),
init_state=AssetBaseCfg.InitialStateCfg(pos=(0.55, 0.0, 0.0), rot=(0.70711, 0.0, 0.0, 0.70711)),
)
# plane
plane = AssetBaseCfg(
prim_path="/World/GroundPlane",
init_state=AssetBaseCfg.InitialStateCfg(pos=[0, 0, -1.05]),
spawn=GroundPlaneCfg(),
)
##
# MDP settings
##
@configclass
class ActionsCfg:
"""Action specifications for the MDP."""
arm_action: ActionTerm = mdp.JointPositionActionCfg(
asset_name="robot",
joint_names=["shoulder_pan_joint", "shoulder_lift_joint", "elbow_joint", "wrist_1_joint", "wrist_2_joint", "wrist_3_joint"],
scale=.5,
use_default_offset=True,
debug_vis=True
)
@configclass
class CommandsCfg:
"""Command terms for the MDP."""
ee_pose = mdp.UniformPoseCommandCfg(
asset_name="robot",
body_name="ee_link", # This is the body in the USD file
resampling_time_range=(4.0, 4.0),
debug_vis=True,
# These are essentially ranges of poses that can be commanded for the end of the robot during training
ranges=mdp.UniformPoseCommandCfg.Ranges(
pos_x=(0.35, 0.65),
pos_y=(-0.2, 0.2),
pos_z=(0.15, 0.5),
roll=(0.0, 0.0),
pitch=(math.pi / 2, math.pi / 2),
yaw=(-3.14, 3.14),
),
)
@configclass
class ObservationsCfg:
"""Observation specifications for the MDP."""
@configclass
class PolicyCfg(ObsGroup):
"""Observations for policy group."""
# observation terms (order preserved)
joint_pos = ObsTerm(func=mdp.joint_pos_rel, noise=Unoise(n_min=-0.01, n_max=0.01))
joint_vel = ObsTerm(func=mdp.joint_vel_rel, noise=Unoise(n_min=-0.01, n_max=0.01))
pose_command = ObsTerm(func=mdp.generated_commands, params={"command_name": "ee_pose"})
actions = ObsTerm(func=mdp.last_action)
def __post_init__(self):
self.enable_corruption = True
self.concatenate_terms = True
# observation groups
policy: PolicyCfg = PolicyCfg()
@configclass
class EventCfg:
"""Configuration for events."""
reset_robot_joints = EventTerm(
func=mdp.reset_joints_by_scale,
mode="reset",
params={
"position_range": (0.75, 1.25),
"velocity_range": (0.0, 0.0),
},
)
@configclass
class RewardsCfg:
"""Reward terms for the MDP."""
# task terms
end_effector_position_tracking = RewTerm(
func=mdp.position_command_error,
weight=-0.2,
params={"asset_cfg": SceneEntityCfg("robot", body_names=["ee_link"]), "command_name": "ee_pose"},
)
end_effector_position_tracking_fine_grained = RewTerm(
func=mdp.position_command_error_tanh,
weight=0.1,
params={"asset_cfg": SceneEntityCfg("robot", body_names=["ee_link"]), "std": 0.1, "command_name": "ee_pose"},
)
# action penalty
action_rate = RewTerm(func=mdp.action_rate_l2, weight=-0.0001)
joint_vel = RewTerm(
func=mdp.joint_vel_l2,
weight=-0.0001,
params={"asset_cfg": SceneEntityCfg("robot")},
)
@configclass
class TerminationsCfg:
"""Termination terms for the MDP."""
time_out = DoneTerm(func=mdp.time_out, time_out=True)
@configclass
class CurriculumCfg:
"""Curriculum terms for the MDP."""
action_rate = CurrTerm(
func=mdp.modify_reward_weight, params={"term_name": "action_rate", "weight": -0.005, "num_steps": 4500}
)
joint_vel = CurrTerm(
func=mdp.modify_reward_weight, params={"term_name": "joint_vel", "weight": -0.001, "num_steps": 4500}
)
##
# Environment configuration
##
@configclass
class ReachEnvCfg(ManagerBasedRLEnvCfg):
"""Configuration for the reach end-effector pose tracking environment."""
# Scene settings - how many robots, how far apart?
scene = ReachSceneCfg(num_envs=2000, env_spacing=2.5)
# Basic settings
observations = ObservationsCfg()
actions = ActionsCfg()
commands: CommandsCfg = CommandsCfg()
# MDP settings
rewards = RewardsCfg()
terminations = TerminationsCfg()
events = EventCfg()
curriculum = CurriculumCfg()
def __post_init__(self):
"""Post initialization."""
# general settings
self.decimation = 2
self.sim.render_interval = self.decimation
self.episode_length_s = 3.0
self.viewer.eye = (3.5, 3.5, 3.5)
# simulation settings
self.sim.dt = 1.0 / 60.0
@configclass
class ReachEnvCfg_PLAY(ReachEnvCfg):
def __post_init__(self):
# post init of parent
super().__post_init__()
# make a smaller scene for play
self.scene.num_envs = 50
self.scene.env_spacing = 2.5
# disable randomization for play
self.observations.policy.enable_corruption = False