生成多个资产#
典型的生成配置(在 生成基本物体到场景中 教程中介绍)复制相同的资产(或 USD 基元)到不同的已解析的基元路径表达式中。例如,如果用户指定要在 “/World/Table_.*/Object” 生成资产,则相同的资产将在路径 “/World/Table_0/Object” 、 “/World/Table_1/Object” 等处创建。
然而,我们还支持使用两种机制进行多资产生成:
刚性物体集合。这允许用户在每个环境中生成多个刚性物体,并使用统一的API访问/修改它们,从而提高性能。
在同一 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 机器人的关节
刚性物体集合#
可以在每个环境中生成多个刚性物体,并使用统一的 (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 路径下生成不同的资产#
在每个环境中,可以使用生成器 MultiAssetSpawnerCfg
和 MultiUsdFileCfg
在相同的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
。