类和配置#
要开始,请导航到任务: source/isaac_lab_tutorial/isaac_lab_tutorial/tasks/direct/isaac_lab_tutorial
,查看 isaac_lab_tutorial_env_cfg.py
的内容。您应该看到以下内容
from isaaclab_assets.robots.cartpole import CARTPOLE_CFG
from isaaclab.assets import ArticulationCfg
from isaaclab.envs import DirectRLEnvCfg
from isaaclab.scene import InteractiveSceneCfg
from isaaclab.sim import SimulationCfg
from isaaclab.utils import configclass
@configclass
class IsaacLabTutorialEnvCfg(DirectRLEnvCfg):
# Some useful fields
.
.
.
# simulation
sim: SimulationCfg = SimulationCfg(dt=1 / 120, render_interval=2)
# robot(s)
robot_cfg: ArticulationCfg = CARTPOLE_CFG.replace(prim_path="/World/envs/env_.*/Robot")
# scene
scene: InteractiveSceneCfg = InteractiveSceneCfg(num_envs=4096, env_spacing=4.0, replicate_physics=True)
# Some more useful fields
.
.
.
这是一个简单的倒立摆环境的默认配置,与模板一起提供,并定义了对应环境中任何操作的 self
作用域。
首先要注意的是 @configclass
装饰器的存在。这将一个类定义为配置类,在 Isaac Lab 中具有特殊功能。根据您的目标,Isaac Lab 提供不同的基本配置类,而在这种情况下,我们使用 DirectRLEnvCfg
类,因为我们对在直接工作流中执行强化学习感兴趣。
其次要注意的是配置类的内容。作为作者,您可以指定任何您希望定义的字段,但通常来说,您总是会在此处定义三件事: sim 、 scene 和 robot 。请注意,这些字段也是配置类!这种配置类的组合方式是解决克隆任意复杂环境的方案之一。
sim 是 SimulationCfg
的一个实例,这是控制我们正在构建的模拟现实性质的配置。这个字段是基类 DirecRLEnvCfg
的成员,但具有默认的模拟配置,因此 从技术上讲 它是可选的。 SimulationCfg
决定了时间步长(dt)的精度,重力的方向,甚至是如何模拟物理。在这种情况下,我们仅指定时间步长和渲染间隔,前者指示每个时间步长模拟 \(1/120\) 秒,后者是我们在渲染一帧之前应该采取的步骤数量(值为2表示每隔一帧渲染一次)。
scene 是 InteractiveSceneCfg
的一个实例。场景描述了舞台上发生的事情,并管理要在各个环境中复制的模拟实体。场景也是基类 DirectRLEnvCfg
的成员,但与 sim 不同,它没有默认值,必须在每一个 DirectRLEnvCfg
中定义。 InteractiveSceneCfg
描述了我们想要为训练创建多少个场景的副本,以及它们在舞台上应该被分隔多远。
最后,我们有 robot 的定义,它是 ArticulationCfg
的一个实例。一个环境可能有多个关节,因此不严格要求存在 ArticulationCfg
来定义 DirectRLEnv
。相反,通常的工作流程是为机器人定义一个正则表达式路径,并在基本配置中替换 prim_path
属性。在这种情况下, CARTPOLE_CFG
是在 isaaclab_assets.robots.cartpole
中定义的配置,通过用 /World/envs/env_.*/Robot
替换 prim 路径,我们在暗示舞台上的每个场景都有一个名为 Robot
的机器人。
环境#
接下来,让我们看看我们任务目录中的另一个 python 文件的内容: isaac_lab_tutorial_env_cfg.py
#imports
.
.
.
from .isaac_lab_tutorial_env_cfg import IsaacLabTutorialEnvCfg
class IsaacLabTutorialEnv(DirectRLEnv):
cfg: IsaacLabTutorialEnvCfg
def __init__(self, cfg: IsaacLabTutorialEnvCfg, render_mode: str | None = None, **kwargs):
super().__init__(cfg, render_mode, **kwargs)
. . .
def _setup_scene(self):
self.robot = Articulation(self.cfg.robot_cfg)
# add ground plane
spawn_ground_plane(prim_path="/World/ground", cfg=GroundPlaneCfg())
# add articulation to scene
self.scene.articulations["robot"] = self.robot
# clone and replicate
self.scene.clone_environments(copy_from_source=False)
# add lights
light_cfg = sim_utils.DomeLightCfg(intensity=2000.0, color=(0.75, 0.75, 0.75))
light_cfg.func("/World/Light", light_cfg)
def _pre_physics_step(self, actions: torch.Tensor) -> None:
. . .
def _apply_action(self) -> None:
. . .
def _get_observations(self) -> dict:
. . .
def _get_rewards(self) -> torch.Tensor:
total_reward = compute_rewards(...)
return total_reward
def _get_dones(self) -> tuple[torch.Tensor, torch.Tensor]:
. . .
def _reset_idx(self, env_ids: Sequence[int] | None):
. . .
@torch.jit.script
def compute_rewards(...):
. . .
return total_reward
为了便于讨论,某些代码已被省略。这是直接工作流的实际 “核心” 存在之处,我们会在此处进行大部分修改以调整模板以满足我们的需求。当前, IsaacLabTutorialEnv
的所有成员函数都直接从 DirectRLEnv
继承。这个已知接口是 Isaac Lab 及其支持的 RL 框架与环境交互的方式。
当环境初始化时,它接收到自己的配置作为参数,然后立即传递给 super,以初始化 DirectRLEnv
。这个 super 调用还调用 _setup_scene
,它实际上构建了场景并适当地克隆了它。值得注意的是如何在 _setup_scene
中创建机器人并将其注册到场景中。首先,通过使用我们在 IsaacLabTutorialEnvCfg
中定义的 robot_config
创建机器人关节: 在此之前,它是不存在的!当关节创建时,机器人存在于舞台上的 /World/envs/env_0/Robot
处。然后调用 scene.clone_environments
适当地复制了 env_0
。此时,机器人作为多个副本存在于舞台上,所以唯一剩下的就是通知 scene
对象这个关节的存在以进行跟踪。场景的关节被保存为一个字典,因此 scene.articulations["robot"] = self.robot
创建了 articulations
字典的一个新的 robot
元素,并将值设置为 self.robot
。
还请注意,其余函数除了 _reset_idx
不接受附加参数之外。这是因为环境只管理将动作应用于正在模拟的智能体,并更新模拟。这就是 _pre_physics_step
和 _apply_action
步骤的作用: 我们设置机器人的驱动命令,这样当模拟向前走时,动作被应用并关节被驱动到新的目标。这样分步处理的过程旨在确保对环境的执行有系统性控制,并且在管理工作流中特别重要。类似的关系也存在于 _get_dones
函数和 _reset_idx
之间。前者, _get_dones
确定每个环境是否处于终端状态,并生成表示哪些环境因进入终端状态而中止,而哪些因超时而中止的布尔值张量。后者, _reset_idx
会取一个环境索引值(整数)的列表,然后实际重置这些环境。重要的是,像更新驱动目标或重置环境这样的操作不应该发生在物理或渲染步骤 期间 ,因此通过这种方式分割接口有助于防止这种情况发生。