Orbit 使用指南 05 | 使用交互式场景 | Isaac Sim | Omniverse

如是我闻: 到目前为止,在之前的指南中,我们都是手动将资产生成到模拟中,并创建对象实例与它们进行互动。然而,随着场景的复杂性增加,手动执行这些任务会变得很墨迹。在本指南中,我们将介绍scene.InteractiveScene类,它为生成原始物体并在模拟中管理它们提供了一个便捷的接口。

总体上来说,交互式场景是场景实体的集合。每个实体可以是非交互式原始物体(例如,地面平面、光源)、交互式原始物体(例如,关节物体、刚体)或传感器(例如,相机、激光雷达)。交互式场景为这些实体的生成和管理提供了一个便捷的接口。

与纯人工手动方法相比,它有以下的好处:

  1. 缓解用户需要分别生成每个资产的需求,因为这是隐式处理的。

  2. 使得场景原始物体(scene prims)的克隆变得有益身心健康,适用于多环境。

  3. 将所有场景实体收集到一个对象中,这使得它们更容易管理。

在这个指南中,我们取自与指南04关节物体互动指南中的车杆示例,并将design_scene函数替换为scene.InteractiveScene对象。虽然对于这个简单的示例使用交互式场景看起来可能有些过分,但随着更多资产和传感器被添加到场景中,它将变得更加有用。

指南05对应于orbit/source/standalone/tutorials/02_scene目录下的create_scene.py 脚本,让我们先搂一眼完整的代码

# Copyright (c) 2022-2024, The ORBIT Project Developers.
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause

"""This script demonstrates how to use the interactive scene interface to setup a scene with multiple prims.

.. code-block:: bash

    # Usage
    ./orbit.sh -p source/standalone/tutorials/03_scene/create_scene.py --num_envs 32

"""

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 using the interactive scene interface.")
parser.add_argument("--num_envs", type=int, default=2, 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

import omni.isaac.orbit.sim as sim_utils
from omni.isaac.orbit.assets import ArticulationCfg, AssetBaseCfg
from omni.isaac.orbit.scene import InteractiveScene, InteractiveSceneCfg
from omni.isaac.orbit.sim import SimulationContext
from omni.isaac.orbit.utils import configclass

##
# Pre-defined configs
##
from omni.isaac.orbit_assets import CARTPOLE_CFG  # isort:skip


@configclass
class CartpoleSceneCfg(InteractiveSceneCfg):
    """Configuration for a cart-pole scene."""

    # ground plane
    ground = AssetBaseCfg(prim_path="/World/defaultGroundPlane", spawn=sim_utils.GroundPlaneCfg())

    # lights
    dome_light = AssetBaseCfg(
        prim_path="/World/Light", spawn=sim_utils.DomeLightCfg(intensity=3000.0, color=(0.75, 0.75, 0.75))
    )

    # articulation
    cartpole: ArticulationCfg = CARTPOLE_CFG.replace(prim_path="{ENV_REGEX_NS}/Robot")


def run_simulator(sim: sim_utils.SimulationContext, scene: InteractiveScene):
    """Runs the simulation loop."""
    # Extract scene entities
    # note: we only do this here for readability.
    robot = scene["cartpole"]
    # Define simulation stepping
    sim_dt = sim.get_physics_dt()
    count = 0
    # Simulation loop
    while simulation_app.is_running():
        # Reset
        if count % 500 == 0:
            # reset counter
            count = 0
            # reset the scene entities
            # root state
            # we offset the root state by the origin since the states are written in simulation world frame
            # if this is not done, then the robots will be spawned at the (0, 0, 0) of the simulation world
            root_state = robot.data.default_root_state.clone()
            root_state[:, :3] += scene.env_origins
            robot.write_root_state_to_sim(root_state)
            # set joint positions with some noise
            joint_pos, joint_vel = robot.data.default_joint_pos.clone(), robot.data.default_joint_vel.clone()
            joint_pos += torch.rand_like(joint_pos) * 0.1
            robot.write_joint_state_to_sim(joint_pos, joint_vel)
            # clear internal buffers
            scene.reset()
            print("[INFO]: Resetting robot state...")
        # Apply random action
        # -- generate random joint efforts
        efforts = torch.randn_like(robot.data.joint_pos) * 5.0
        # -- apply action to the robot
        robot.set_joint_effort_target(efforts)
        # -- write data to sim
        scene.write_data_to_sim()
        # Perform step
        sim.step()
        # Increment counter
        count += 1
        # Update buffers
        scene.update(sim_dt)


def main():
    """Main function."""
    # Load kit helper
    sim_cfg = sim_utils.SimulationCfg(device="cpu", use_gpu_pipeline=False)
    sim = SimulationContext(sim_cfg)
    # Set main camera
    sim.set_camera_view([2.5, 0.0, 4.0], [0.0, 0.0, 2.0])
    # Design scene
    scene_cfg = CartpoleSceneCfg(num_envs=args_cli.num_envs, env_spacing=2.0)
    scene = InteractiveScene(scene_cfg)
    # Play the simulator
    sim.reset()
    # Now we are ready!
    print("[INFO]: Setup complete...")
    # Run the simulator
    run_simulator(sim, scene)


if __name__ == "__main__":
    # run the main function
    main()
    # close sim app
    simulation_app.close()

代码解析

虽然代码与前一个指南相似,但有几个关键区别我们将详细讨论一下。

场景配置

场景由一系列各自拥有配置的实体组成。这些配置在一个继承自scene.InteractiveSceneCfg的配置类中指定。然后,配置类传递给scene.InteractiveScene构造函数以创建场景。

对于车杆示例,我们指定了与前一个指南中相同的场景,但现在在配置类CartpoleSceneCfg中列出它们,而不是手动生成它们。

@configclass
class CartpoleSceneCfg(InteractiveSceneCfg):
    """Configuration for a cart-pole scene."""

    # ground plane
    ground = AssetBaseCfg(prim_path="/World/defaultGroundPlane", spawn=sim_utils.GroundPlaneCfg())

    # lights
    dome_light = AssetBaseCfg(
        prim_path="/World/Light", spawn=sim_utils.DomeLightCfg(intensity=3000.0, color=(0.75, 0.75, 0.75))
    )

    # articulation
    cartpole: ArticulationCfg = CARTPOLE_CFG.replace(prim_path="{ENV_REGEX_NS}/Robot")

配置类中的变量名被用作键,以从scene.InteractiveScene对象访问相应的实体。例如,车杆可以通过scene["cartpole"]访问。不过,我们稍后再详细说明。首先,让我们看看单个场景实体是如何配置的。

与之前指南中配置刚体和关节物体的方式类似,配置是使用配置类指定的。然而,地面平面和光源的配置与车杆配置之间存在一个关键区别。地面平面和光源是非交互式原始物体,而车杆是一个交互式原始物体。这一区别反映在用于指定它们的配置类中。地面平面和光源的配置使用assets.AssetBaseCfg类的实例指定,而车杆则使用assets.ArticulationCfg的实例配置。任何不是交互式原始物体的东西(即既不是资产也不是传感器)在模拟步骤中被处理。

另一个需要注意的关键区别是在不同原始物体的原始路径规格中:

  • 地面平面:/World/defaultGroundPlane
  • 光源:/World/Light
  • 车杆:{ENV_REGEX_NS}/Robot

正如我们早前学到的,Omniverse 在USD舞台上创建了一个原始物体图。原始路径用于指定原始物体在图中的位置。地面平面和光源使用绝对路径指定,而车杆使用相对路径指定。相对路径使用ENV_REGEX_NS变量指定,这是一个特殊变量,在场景创建期间被替换为环境名称。任何在其原始路径中包含ENV_REGEX_NS变量的实体都会为每个环境被克隆。这个路径由场景对象替换为/World/envs/env_{i},其中i是环境索引。

初始化场景

与之前调用design_scene函数创建场景不同,我们现在创建一个scene.InteractiveScene类的实例,并将配置对象传递给其构造函数。在创建CartpoleSceneCfg的配置实例时,我们使用num_envs参数指定我们想要创建多少个环境副本。这将用于克隆每个环境的场景。

    # Design scene
    scene_cfg = CartpoleSceneCfg(num_envs=args_cli.num_envs, env_spacing=2.0)
    scene = InteractiveScene(scene_cfg)

访问场景元素

类似于之前的指南中从字典访问实体,可以使用[]操作符从InteractiveScene对象访问场景元素。操作符接受一个字符串键并返回相应的实体。每个实体的键通过配置类指定。例如,车杆在配置类中使用键“cartpole”指定。

    # Extract scene entities
    # note: we only do this here for readability.
    robot = scene["cartpole"]

运行模拟循环

脚本的其余部分看起来与之前与assets.Articulation接口的脚本类似,但在调用的方法上有一些小的不同:

assets.Articulation.reset()scene.InteractiveScene.reset()

assets.Articulation.write_data_to_sim()scene.InteractiveScene.write_data_to_sim()

assets.Articulation.update()scene.InteractiveScene.update()

在底层,scene.InteractiveScene的方法调用场景中实体的相应方法。

代码运行

让我们运行脚本在场景中模拟32个车杆。我们可以通过向脚本传递–num_envs参数来实现这一点。

./orbit.sh -p source/standalone/tutorials/02_scene/create_scene.py --num_envs 32

这应该会打开一个展台,有32个车杆随机摆动。
在这里插入图片描述

在这个指南中,我们看到了如何使用scene.InteractiveScene来创建包含多个资产的场景。我们还看到了如何使用num_envs参数为多个环境克隆场景。

omni.isaac.orbit_tasks扩展下找到的任务中,有许多更多的scene.InteractiveSceneCfg的示例用法。那些例子我还得细琢磨一下子

愿本文渡一切机器人模拟器苦

以上

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值