生成多个资产#

典型的生成配置(在 生成基本物体到场景中 教程中介绍)复制相同的资产(或 USD 基元)到不同的已解析的基元路径表达式中。例如,如果用户指定要在 “/World/Table_.*/Object” 生成资产,则相同的资产将在路径 “/World/Table_0/Object” 、 “/World/Table_1/Object” 等处创建。

然而,我们还支持使用两种机制进行多资产生成:

  1. 刚性物体集合。这允许用户在每个环境中生成多个刚性物体,并使用统一的API访问/修改它们,从而提高性能。

  2. 在同一 prim 路径下生成不同的资产。这允许用户创建各种不同的模拟,其中每个环境都有不同的资产。

本指南描述了如何使用这两种机制。

示例脚本 multi_asset.py 用作参考,位于 IsaacLab/source/standalone/demos 目录中。

multi_asset.py 的代码
  1# Copyright (c) 2022-2024, The Isaac Lab Project Developers.
  2# All rights reserved.
  3#
  4# SPDX-License-Identifier: BSD-3-Clause
  5
  6"""This script demonstrates how to spawn multiple objects in multiple environments.
  7
  8.. code-block:: bash
  9
 10    # Usage
 11    ./isaaclab.sh -p source/standalone/demos/multi_asset.py --num_envs 2048
 12
 13"""
 14
 15from __future__ import annotations
 16
 17"""Launch Isaac Sim Simulator first."""
 18
 19
 20import argparse
 21
 22from omni.isaac.lab.app import AppLauncher
 23
 24# add argparse arguments
 25parser = argparse.ArgumentParser(description="Demo on spawning different objects in multiple environments.")
 26parser.add_argument("--num_envs", type=int, default=512, help="Number of environments to spawn.")
 27# append AppLauncher cli args
 28AppLauncher.add_app_launcher_args(parser)
 29# parse the arguments
 30args_cli = parser.parse_args()
 31
 32# launch omniverse app
 33app_launcher = AppLauncher(args_cli)
 34simulation_app = app_launcher.app
 35
 36"""Rest everything follows."""
 37
 38import random
 39
 40import omni.usd
 41from pxr import Gf, Sdf
 42
 43import omni.isaac.lab.sim as sim_utils
 44from omni.isaac.lab.assets import (
 45    Articulation,
 46    ArticulationCfg,
 47    AssetBaseCfg,
 48    RigidObject,
 49    RigidObjectCfg,
 50    RigidObjectCollection,
 51    RigidObjectCollectionCfg,
 52)
 53from omni.isaac.lab.scene import InteractiveScene, InteractiveSceneCfg
 54from omni.isaac.lab.sim import SimulationContext
 55from omni.isaac.lab.utils import Timer, configclass
 56from omni.isaac.lab.utils.assets import ISAACLAB_NUCLEUS_DIR
 57
 58##
 59# Pre-defined Configuration
 60##
 61
 62from omni.isaac.lab_assets.anymal import ANYDRIVE_3_LSTM_ACTUATOR_CFG  # isort: skip
 63
 64
 65##
 66# Randomization events.
 67##
 68
 69
 70def randomize_shape_color(prim_path_expr: str):
 71    """Randomize the color of the geometry."""
 72    # acquire stage
 73    stage = omni.usd.get_context().get_stage()
 74    # resolve prim paths for spawning and cloning
 75    prim_paths = sim_utils.find_matching_prim_paths(prim_path_expr)
 76    # manually clone prims if the source prim path is a regex expression
 77    with Sdf.ChangeBlock():
 78        for prim_path in prim_paths:
 79            # spawn single instance
 80            prim_spec = Sdf.CreatePrimInLayer(stage.GetRootLayer(), prim_path)
 81
 82            # DO YOUR OWN OTHER KIND OF RANDOMIZATION HERE!
 83            # Note: Just need to acquire the right attribute about the property you want to set
 84            # Here is an example on setting color randomly
 85            color_spec = prim_spec.GetAttributeAtPath(prim_path + "/geometry/material/Shader.inputs:diffuseColor")
 86            color_spec.default = Gf.Vec3f(random.random(), random.random(), random.random())
 87
 88
 89##
 90# Scene Configuration
 91##
 92
 93
 94@configclass
 95class MultiObjectSceneCfg(InteractiveSceneCfg):
 96    """Configuration for a multi-object scene."""
 97
 98    # ground plane
 99    ground = AssetBaseCfg(prim_path="/World/defaultGroundPlane", spawn=sim_utils.GroundPlaneCfg())
100
101    # lights
102    dome_light = AssetBaseCfg(
103        prim_path="/World/Light", spawn=sim_utils.DomeLightCfg(intensity=3000.0, color=(0.75, 0.75, 0.75))
104    )
105
106    # rigid object
107    object: RigidObjectCfg = RigidObjectCfg(
108        prim_path="/World/envs/env_.*/Object",
109        spawn=sim_utils.MultiAssetSpawnerCfg(
110            assets_cfg=[
111                sim_utils.ConeCfg(
112                    radius=0.3,
113                    height=0.6,
114                    visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 1.0, 0.0), metallic=0.2),
115                ),
116                sim_utils.CuboidCfg(
117                    size=(0.3, 0.3, 0.3),
118                    visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(1.0, 0.0, 0.0), metallic=0.2),
119                ),
120                sim_utils.SphereCfg(
121                    radius=0.3,
122                    visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 0.0, 1.0), metallic=0.2),
123                ),
124            ],
125            random_choice=True,
126            rigid_props=sim_utils.RigidBodyPropertiesCfg(
127                solver_position_iteration_count=4, solver_velocity_iteration_count=0
128            ),
129            mass_props=sim_utils.MassPropertiesCfg(mass=1.0),
130            collision_props=sim_utils.CollisionPropertiesCfg(),
131        ),
132        init_state=RigidObjectCfg.InitialStateCfg(pos=(0.0, 0.0, 2.0)),
133    )
134
135    # object collection
136    object_collection: RigidObjectCollectionCfg = RigidObjectCollectionCfg(
137        rigid_objects={
138            "object_A": RigidObjectCfg(
139                prim_path="/World/envs/env_.*/Object_A",
140                spawn=sim_utils.SphereCfg(
141                    radius=0.1,
142                    visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(1.0, 0.0, 0.0), metallic=0.2),
143                    rigid_props=sim_utils.RigidBodyPropertiesCfg(
144                        solver_position_iteration_count=4, solver_velocity_iteration_count=0
145                    ),
146                    mass_props=sim_utils.MassPropertiesCfg(mass=1.0),
147                    collision_props=sim_utils.CollisionPropertiesCfg(),
148                ),
149                init_state=RigidObjectCfg.InitialStateCfg(pos=(0.0, -0.5, 2.0)),
150            ),
151            "object_B": RigidObjectCfg(
152                prim_path="/World/envs/env_.*/Object_B",
153                spawn=sim_utils.CuboidCfg(
154                    size=(0.1, 0.1, 0.1),
155                    visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(1.0, 0.0, 0.0), metallic=0.2),
156                    rigid_props=sim_utils.RigidBodyPropertiesCfg(
157                        solver_position_iteration_count=4, solver_velocity_iteration_count=0
158                    ),
159                    mass_props=sim_utils.MassPropertiesCfg(mass=1.0),
160                    collision_props=sim_utils.CollisionPropertiesCfg(),
161                ),
162                init_state=RigidObjectCfg.InitialStateCfg(pos=(0.0, 0.5, 2.0)),
163            ),
164            "object_C": RigidObjectCfg(
165                prim_path="/World/envs/env_.*/Object_C",
166                spawn=sim_utils.ConeCfg(
167                    radius=0.1,
168                    height=0.3,
169                    visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(1.0, 0.0, 0.0), metallic=0.2),
170                    rigid_props=sim_utils.RigidBodyPropertiesCfg(
171                        solver_position_iteration_count=4, solver_velocity_iteration_count=0
172                    ),
173                    mass_props=sim_utils.MassPropertiesCfg(mass=1.0),
174                    collision_props=sim_utils.CollisionPropertiesCfg(),
175                ),
176                init_state=RigidObjectCfg.InitialStateCfg(pos=(0.5, 0.0, 2.0)),
177            ),
178        }
179    )
180
181    # articulation
182    robot: ArticulationCfg = ArticulationCfg(
183        prim_path="/World/envs/env_.*/Robot",
184        spawn=sim_utils.MultiUsdFileCfg(
185            usd_path=[
186                f"{ISAACLAB_NUCLEUS_DIR}/Robots/ANYbotics/ANYmal-C/anymal_c.usd",
187                f"{ISAACLAB_NUCLEUS_DIR}/Robots/ANYbotics/ANYmal-D/anymal_d.usd",
188            ],
189            random_choice=True,
190            rigid_props=sim_utils.RigidBodyPropertiesCfg(
191                disable_gravity=False,
192                retain_accelerations=False,
193                linear_damping=0.0,
194                angular_damping=0.0,
195                max_linear_velocity=1000.0,
196                max_angular_velocity=1000.0,
197                max_depenetration_velocity=1.0,
198            ),
199            articulation_props=sim_utils.ArticulationRootPropertiesCfg(
200                enabled_self_collisions=True, solver_position_iteration_count=4, solver_velocity_iteration_count=0
201            ),
202            activate_contact_sensors=True,
203        ),
204        init_state=ArticulationCfg.InitialStateCfg(
205            pos=(0.0, 0.0, 0.6),
206            joint_pos={
207                ".*HAA": 0.0,  # all HAA
208                ".*F_HFE": 0.4,  # both front HFE
209                ".*H_HFE": -0.4,  # both hind HFE
210                ".*F_KFE": -0.8,  # both front KFE
211                ".*H_KFE": 0.8,  # both hind KFE
212            },
213        ),
214        actuators={"legs": ANYDRIVE_3_LSTM_ACTUATOR_CFG},
215    )
216
217
218##
219# Simulation Loop
220##
221
222
223def run_simulator(sim: SimulationContext, scene: InteractiveScene):
224    """Runs the simulation loop."""
225    # Extract scene entities
226    # note: we only do this here for readability.
227    rigid_object: RigidObject = scene["object"]
228    rigid_object_collection: RigidObjectCollection = scene["object_collection"]
229    robot: Articulation = scene["robot"]
230    # Define simulation stepping
231    sim_dt = sim.get_physics_dt()
232    count = 0
233    # Simulation loop
234    while simulation_app.is_running():
235        # Reset
236        if count % 250 == 0:
237            # reset counter
238            count = 0
239            # reset the scene entities
240            # object
241            root_state = rigid_object.data.default_root_state.clone()
242            root_state[:, :3] += scene.env_origins
243            rigid_object.write_root_state_to_sim(root_state)
244            # object collection
245            object_state = rigid_object_collection.data.default_object_state.clone()
246            object_state[..., :3] += scene.env_origins.unsqueeze(1)
247            rigid_object_collection.write_object_state_to_sim(object_state)
248            # robot
249            # -- root state
250            root_state = robot.data.default_root_state.clone()
251            root_state[:, :3] += scene.env_origins
252            robot.write_root_state_to_sim(root_state)
253            # -- joint state
254            joint_pos, joint_vel = robot.data.default_joint_pos.clone(), robot.data.default_joint_vel.clone()
255            robot.write_joint_state_to_sim(joint_pos, joint_vel)
256            # clear internal buffers
257            scene.reset()
258            print("[INFO]: Resetting scene state...")
259
260        # Apply action to robot
261        robot.set_joint_position_target(robot.data.default_joint_pos)
262        # Write data to sim
263        scene.write_data_to_sim()
264        # Perform step
265        sim.step()
266        # Increment counter
267        count += 1
268        # Update buffers
269        scene.update(sim_dt)
270
271
272def main():
273    """Main function."""
274    # Load kit helper
275    sim_cfg = sim_utils.SimulationCfg(dt=0.005, device=args_cli.device)
276    sim = SimulationContext(sim_cfg)
277    # Set main camera
278    sim.set_camera_view([2.5, 0.0, 4.0], [0.0, 0.0, 2.0])
279
280    # Design scene
281    scene_cfg = MultiObjectSceneCfg(num_envs=args_cli.num_envs, env_spacing=2.0, replicate_physics=False)
282    with Timer("[INFO] Time to create scene: "):
283        scene = InteractiveScene(scene_cfg)
284
285    with Timer("[INFO] Time to randomize scene: "):
286        # DO YOUR OWN OTHER KIND OF RANDOMIZATION HERE!
287        # Note: Just need to acquire the right attribute about the property you want to set
288        # Here is an example on setting color randomly
289        randomize_shape_color(scene_cfg.object.prim_path)
290
291    # Play the simulator
292    sim.reset()
293    # Now we are ready!
294    print("[INFO]: Setup complete...")
295    # Run the simulator
296    run_simulator(sim, scene)
297
298
299if __name__ == "__main__":
300    # run the main execution
301    main()
302    # close sim app
303    simulation_app.close()

此脚本创建了多个环境,每个环境都包括:

  • 一个包含圆锥体、立方体和球体的刚性物体集合

  • 一个随机选择为圆锥体、立方体或球体的刚性物体

  • 一个随机选择为 ANYmal-C 或 ANYmal-D 机器人的关节

multi_asset.py 的结果

刚性物体集合#

可以在每个环境中生成多个刚性物体,并使用统一的 (env_ids, obj_ids) API 进行访问/修改。虽然用户也可以通过分别生成它们来创建多个刚性物体,但该API更加用户友好且高效,因为它在底层使用单个物理视图来处理所有物体。

# object collection
object_collection: RigidObjectCollectionCfg = RigidObjectCollectionCfg(
    rigid_objects={
        "object_A": RigidObjectCfg(
            prim_path="/World/envs/env_.*/Object_A",
            spawn=sim_utils.SphereCfg(
                radius=0.1,
                visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(1.0, 0.0, 0.0), metallic=0.2),
                rigid_props=sim_utils.RigidBodyPropertiesCfg(
                    solver_position_iteration_count=4, solver_velocity_iteration_count=0
                ),
                mass_props=sim_utils.MassPropertiesCfg(mass=1.0),
                collision_props=sim_utils.CollisionPropertiesCfg(),
            ),
            init_state=RigidObjectCfg.InitialStateCfg(pos=(0.0, -0.5, 2.0)),
        ),
        "object_B": RigidObjectCfg(
            prim_path="/World/envs/env_.*/Object_B",
            spawn=sim_utils.CuboidCfg(
                size=(0.1, 0.1, 0.1),
                visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(1.0, 0.0, 0.0), metallic=0.2),
                rigid_props=sim_utils.RigidBodyPropertiesCfg(
                    solver_position_iteration_count=4, solver_velocity_iteration_count=0
                ),
                mass_props=sim_utils.MassPropertiesCfg(mass=1.0),
                collision_props=sim_utils.CollisionPropertiesCfg(),
            ),
            init_state=RigidObjectCfg.InitialStateCfg(pos=(0.0, 0.5, 2.0)),
        ),
        "object_C": RigidObjectCfg(
            prim_path="/World/envs/env_.*/Object_C",
            spawn=sim_utils.ConeCfg(
                radius=0.1,
                height=0.3,
                visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(1.0, 0.0, 0.0), metallic=0.2),
                rigid_props=sim_utils.RigidBodyPropertiesCfg(
                    solver_position_iteration_count=4, solver_velocity_iteration_count=0
                ),
                mass_props=sim_utils.MassPropertiesCfg(mass=1.0),
                collision_props=sim_utils.CollisionPropertiesCfg(),
            ),
            init_state=RigidObjectCfg.InitialStateCfg(pos=(0.5, 0.0, 2.0)),
        ),
    }
)

配置 RigidObjectCollectionCfg 用于创建集合。它的属性 rigid_objects 是包含 RigidObjectCfg 对象的字典。键作为集合中每个刚性物体的唯一标识符。

在同一 prim 路径下生成不同的资产#

在每个环境中,可以使用生成器 MultiAssetSpawnerCfgMultiUsdFileCfg 在相同的prim路径下生成不同的资产和USD:

  • 我们将 RigidObjectCfg 中的生成配置设置为 MultiAssetSpawnerCfg

    object: RigidObjectCfg = RigidObjectCfg(
        prim_path="/World/envs/env_.*/Object",
        spawn=sim_utils.MultiAssetSpawnerCfg(
            assets_cfg=[
                sim_utils.ConeCfg(
                    radius=0.3,
                    height=0.6,
                    visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 1.0, 0.0), metallic=0.2),
                ),
                sim_utils.CuboidCfg(
                    size=(0.3, 0.3, 0.3),
                    visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(1.0, 0.0, 0.0), metallic=0.2),
                ),
                sim_utils.SphereCfg(
                    radius=0.3,
                    visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 0.0, 1.0), metallic=0.2),
                ),
            ],
            random_choice=True,
            rigid_props=sim_utils.RigidBodyPropertiesCfg(
                solver_position_iteration_count=4, solver_velocity_iteration_count=0
            ),
            mass_props=sim_utils.MassPropertiesCfg(mass=1.0),
            collision_props=sim_utils.CollisionPropertiesCfg(),
        ),
        init_state=RigidObjectCfg.InitialStateCfg(pos=(0.0, 0.0, 2.0)),
    )
    

    此函数允许您定义可以作为刚性对象生成的不同资产的列表。当 random_choice 设置为 True 时,将从列表中随机选择一个资产,并在指定的基元路径上生成它。

  • 类似地,我们将 ArticulationCfg 中的生成配置设置为 MultiUsdFileCfg

    robot: ArticulationCfg = ArticulationCfg(
        prim_path="/World/envs/env_.*/Robot",
        spawn=sim_utils.MultiUsdFileCfg(
            usd_path=[
                f"{ISAACLAB_NUCLEUS_DIR}/Robots/ANYbotics/ANYmal-C/anymal_c.usd",
                f"{ISAACLAB_NUCLEUS_DIR}/Robots/ANYbotics/ANYmal-D/anymal_d.usd",
            ],
            random_choice=True,
            rigid_props=sim_utils.RigidBodyPropertiesCfg(
                disable_gravity=False,
                retain_accelerations=False,
                linear_damping=0.0,
                angular_damping=0.0,
                max_linear_velocity=1000.0,
                max_angular_velocity=1000.0,
                max_depenetration_velocity=1.0,
            ),
            articulation_props=sim_utils.ArticulationRootPropertiesCfg(
                enabled_self_collisions=True, solver_position_iteration_count=4, solver_velocity_iteration_count=0
            ),
            activate_contact_sensors=True,
        ),
        init_state=ArticulationCfg.InitialStateCfg(
            pos=(0.0, 0.0, 0.6),
            joint_pos={
                ".*HAA": 0.0,  # all HAA
                ".*F_HFE": 0.4,  # both front HFE
                ".*H_HFE": -0.4,  # both hind HFE
                ".*F_KFE": -0.8,  # both front KFE
                ".*H_KFE": 0.8,  # both hind KFE
            },
        ),
        actuators={"legs": ANYDRIVE_3_LSTM_ACTUATOR_CFG},
    )
    

    与之前类似,此配置允许选择代表关节资产的不同 USD 文件。

需要注意的事项#

相似的资产结构#

在使用相同的物理接口(刚性对象或关节类)生成和处理多个资产时,必须确保所有基元路径上的资产遵循相似的结构。对于关节来说,这意味着它们都必须具有相同数量的链接和关节、相同数量的碰撞体,以及相同的名称。如果不是这种情况,基元的物理解析可能会受到影响并且失败。

此功能的主要目的是使用户能够创建相同资产的随机版本,例如具有不同链接长度的机器人,或具有不同碰撞器形状的刚性对象。

在交互式场景中禁用物理复制#

默认情况下,标志 scene.InteractiveScene.replicate_physics 被设置为 True。此标志告知物理引擎模拟环境是彼此的副本,因此只需解析第一个环境即可了解整个模拟场景。这有助于加快模拟场景的解析速度。

但是,在不同环境中生成不同的资产的情况下,这一假设不再成立。因此,必须禁用标志 scene.InteractiveScene.replicate_physics

# Design scene
scene_cfg = MultiObjectSceneCfg(num_envs=args_cli.num_envs, env_spacing=2.0, replicate_physics=False)
with Timer("[INFO] Time to create scene: "):
    scene = InteractiveScene(scene_cfg)

代码执行#

要执行带有多个环境和随机资产的脚本,请使用以下命令:

./isaaclab.sh -p source/standalone/demos/multi_asset.py --num_envs 2048

此命令运行具有 2048 个环境的模拟场景,每个环境都具有随机选择的资产。要停止模拟,可以关闭窗口或在终端中按下 Ctrl+C