与刚性物体交互#

这是 资产 中的第一个运行示例:生成刚体、重置状态、推进仿真并读取更新后的状态。配置细节见资产编写指南;由关节连接的刚体见 与关节交互 。

在以前的教程中,我们学习了独立脚本的基本工作原理以及如何将不同的对象(或 对象 )生成到仿真中。本教程展示了如何创建和与刚性物体交互。为此,我们将使用 Isaac Lab 中提供的 assets.RigidObject 类。

代码#

该教程对应于 scripts/tutorials/01_assets 目录中的 run_rigid_object.py 脚本。

run_rigid_object.py 的代码
  1# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md).
  2# All rights reserved.
  3#
  4# SPDX-License-Identifier: BSD-3-Clause
  5
  6"""
  7This script demonstrates how to create a rigid object and interact with it.
  8
  9.. code-block:: bash
 10
 11    # Usage
 12    uv run python scripts/tutorials/01_assets/run_rigid_object.py
 13
 14"""
 15
 16"""Parse the command-line arguments first."""
 17
 18
 19import argparse
 20
 21from isaaclab.app import add_launcher_args, launch_simulation
 22
 23# add argparse arguments
 24parser = argparse.ArgumentParser(description="Tutorial on spawning and interacting with a rigid object.")
 25# append simulation launcher cli args
 26add_launcher_args(parser)
 27# parse the arguments
 28args_cli = parser.parse_args()
 29
 30"""Rest everything follows."""
 31
 32import torch
 33
 34import isaaclab.sim as sim_utils
 35import isaaclab.utils.math as math_utils
 36from isaaclab.assets import RigidObject, RigidObjectCfg
 37from isaaclab.sim import SimulationContext
 38
 39
 40def design_scene():
 41    """Designs the scene."""
 42    # Ground-plane
 43    cfg = sim_utils.GroundPlaneCfg()
 44    cfg.func("/World/defaultGroundPlane", cfg)
 45    # Lights
 46    cfg = sim_utils.DomeLightCfg(intensity=2000.0, color=(0.8, 0.8, 0.8))
 47    cfg.func("/World/Light", cfg)
 48
 49    # Create separate groups called "Origin1", "Origin2", "Origin3"
 50    # Each group will have a robot in it
 51    origins = [[0.25, 0.25, 0.0], [-0.25, 0.25, 0.0], [0.25, -0.25, 0.0], [-0.25, -0.25, 0.0]]
 52    for i, origin in enumerate(origins):
 53        sim_utils.create_prim(f"/World/Origin{i}", "Xform", translation=origin)
 54
 55    # Rigid Object
 56    cone_cfg = RigidObjectCfg(
 57        prim_path="/World/Origin.*/Cone",
 58        spawn=sim_utils.ConeCfg(
 59            radius=0.1,
 60            height=0.2,
 61            rigid_props=sim_utils.UsdPhysicsRigidBodyCfg(),
 62            mass_props=sim_utils.MassCfg(mass=1.0),
 63            collision_props=sim_utils.UsdPhysicsCollisionCfg(),
 64            visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 1.0, 0.0), metallic=0.2),
 65        ),
 66        init_state=RigidObjectCfg.InitialStateCfg(),
 67    )
 68    cone_object = RigidObject(cfg=cone_cfg)
 69
 70    # return the scene information
 71    scene_entities = {"cone": cone_object}
 72    return scene_entities, origins
 73
 74
 75def run_simulator(sim: sim_utils.SimulationContext, entities: dict[str, RigidObject], origins: torch.Tensor):
 76    """Runs the simulation loop."""
 77    # Extract scene entities
 78    # note: we only do this here for readability. In general, it is better to access the entities directly from
 79    #   the dictionary. This dictionary is replaced by the InteractiveScene class in the next tutorial.
 80    cone_object = entities["cone"]
 81    # Define simulation stepping
 82    sim_dt = sim.get_physics_dt()
 83    sim_time = 0.0
 84    count = 0
 85    # Simulate physics
 86    while sim.is_running():
 87        # reset
 88        if count % 250 == 0:
 89            # reset counters
 90            sim_time = 0.0
 91            count = 0
 92            # reset root state
 93            root_pose = cone_object.data.default_root_pose.torch.clone()
 94            # sample a random position on a cylinder around the origins
 95            root_pose[:, :3] += origins
 96            root_pose[:, :3] += math_utils.sample_cylinder(
 97                radius=0.1, h_range=(0.25, 0.5), size=cone_object.num_instances, device=cone_object.device
 98            )
 99            # write root state to simulation
100            cone_object.write_root_pose_to_sim_index(root_pose=root_pose)
101            root_vel = cone_object.data.default_root_vel.torch.clone()
102            cone_object.write_root_velocity_to_sim_index(root_velocity=root_vel)
103            # reset buffers
104            cone_object.reset()
105            print("----------------------------------------")
106            print("[INFO]: Resetting object state...")
107        # apply sim data
108        cone_object.write_data_to_sim()
109        # perform step
110        sim.step()
111        # update sim-time
112        sim_time += sim_dt
113        count += 1
114        # update buffers
115        cone_object.update(sim_dt)
116        # print the root position
117        if count % 50 == 0:
118            print(f"Root position (in world): {cone_object.data.root_pos_w.torch}")
119
120
121def main():
122    """Main function."""
123    # Configure the simulation
124    sim_cfg = sim_utils.SimulationCfg(device=args_cli.device)
125    # Launch the simulator runtime that the configuration needs
126    with launch_simulation(sim_cfg, args_cli):
127        # Initialize the simulation context
128        sim = SimulationContext(sim_cfg)
129        # Set main camera
130        sim.set_camera_view(eye=[1.5, 0.0, 1.0], target=[0.0, 0.0, 0.0])
131        # Design scene
132        scene_entities, scene_origins = design_scene()
133        scene_origins = torch.tensor(scene_origins, device=sim.device)
134        # Play the simulator
135        sim.reset()
136        # Now we are ready!
137        print("[INFO]: Setup complete...")
138        # Run the simulator
139        run_simulator(sim, scene_entities, scene_origins)
140
141
142if __name__ == "__main__":
143    # run the main function
144    main()

代码解释#

在此脚本中,我们将 main 函数拆分为两个单独的函数,这两个函数强调了在仿真器中设置任何仿真的两个主要步骤:

  1. 设计场景: 顾名思义,这一部分负责将所有对象添加到场景中。

  2. 运行仿真: 这部分负责推进仿真器,与场景中的对象交互,例如改变它们的姿势,并对其施加任何命令。

这两个步骤之间的区别是必需的,因为只有在第一步完成并且仿真器被重置后,第二步才会发生。一旦仿真器被重置(自动播放仿真),就不应该再向场景中添加新的(带有物理效果)对象,因为这可能会导致意外行为。但是,可以通过它们各自的句柄与对象进行交互。

设计场景#

与之前的教程类似,我们使用一个地面平面和一个光源填充场景。此外,我们使用 assets.RigidObject 类将一个刚性物体添加到场景中。该类负责在输入路径处生成对象,同时初始化其对应的刚性物理句柄。

在本教程中,我们使用与 Spawn Objects 教程中刚性锥体类似的生成配置创建一个圆锥形刚性物体。唯一的区别是现在我们将生成配置封装到 assets.RigidObjectCfg 类中。该类包含有关资产生成策略、默认初始状态和其他元信息的信息。当将此类传递给 assets.RigidObject 类时,当播放仿真时,它会生成对象并初始化相应的物理句柄。

作为多次生成刚性物体原语的示例,我们创建其父级 Xform 原语 /World/Origin{i} ,它们对应不同的生成位置。当将正则表达式 /World/Origin.*/Cone 传递给 assets.RigidObject 类时,它会在每个 /World/Origin{i} 位置生成刚性物体原语。例如,如果场景中存在 /World/Origin1 和 /World/Origin2 ,则刚性物体原语会分别生成在 /World/Origin1/Cone 和 /World/Origin2/Cone 位置。

    # Create separate groups called "Origin1", "Origin2", "Origin3"
    # Each group will have a robot in it
    origins = [[0.25, 0.25, 0.0], [-0.25, 0.25, 0.0], [0.25, -0.25, 0.0], [-0.25, -0.25, 0.0]]
    for i, origin in enumerate(origins):
        sim_utils.create_prim(f"/World/Origin{i}", "Xform", translation=origin)

    # Rigid Object
    cone_cfg = RigidObjectCfg(
        prim_path="/World/Origin.*/Cone",
        spawn=sim_utils.ConeCfg(
            radius=0.1,
            height=0.2,
            rigid_props=sim_utils.UsdPhysicsRigidBodyCfg(),
            mass_props=sim_utils.MassCfg(mass=1.0),
            collision_props=sim_utils.UsdPhysicsCollisionCfg(),
            visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 1.0, 0.0), metallic=0.2),
        ),
        init_state=RigidObjectCfg.InitialStateCfg(),
    )
    cone_object = RigidObject(cfg=cone_cfg)

因为我们想要与刚性物体交互,我们将此实体传递回主函数。然后,该实体用于在仿真循环中与刚性物体交互。在后续教程中,我们将看到如何使用 scene.InteractiveScene 类更方便地处理多个场景实体。

    # return the scene information
    scene_entities = {"cone": cone_object}
    return scene_entities, origins

运行仿真循环#

我们修改了仿真循环以与刚性物体交互,包括三个步骤——在固定间隔重置仿真状态,推进仿真,以及更新刚性物体的内部缓冲区。为了方便本教程,我们从场景字典中提取刚性物体的实体并将其存储在一个变量中。

重置仿真状态#

为了重置生成的刚性物体对象的仿真状态,我们需要设置其姿势和速度。它们共同定义了生成的刚性物体对象的根状态。重要的是要注意,此状态定义在 仿真世界坐标系 中,而不是它们父级 Xform 对象的坐标系。这是因为物理引擎只能理解世界坐标系,而不能理解父级 Xform 对象的坐标系。因此,在设置之前,我们需要将刚性物体对象的期望状态转换为世界坐标系。

我们使用 assets.RigidObject.data.default_root_state 属性获取生成的刚性物体对象的默认根状态。该默认状态可以从 assets.RigidObjectCfg.init_state 属性中配置,我们在本教程中将其保留为单位状态。然后,我们随机化根状态的平移,并使用 assets.RigidObject.write_root_pose_to_sim() 和 assets.RigidObject.write_root_velocity_to_sim() 方法设置刚性物体对象的期望状态。顾名思义,此方法将刚性物体对象的根状态写入仿真缓冲区。

            # reset root state
            root_pose = cone_object.data.default_root_pose.torch.clone()
            # sample a random position on a cylinder around the origins
            root_pose[:, :3] += origins
            root_pose[:, :3] += math_utils.sample_cylinder(
                radius=0.1, h_range=(0.25, 0.5), size=cone_object.num_instances, device=cone_object.device
            )
            # write root state to simulation
            cone_object.write_root_pose_to_sim_index(root_pose=root_pose)
            root_vel = cone_object.data.default_root_vel.torch.clone()
            cone_object.write_root_velocity_to_sim_index(root_velocity=root_vel)
            # reset buffers
            cone_object.reset()

推进仿真#

在推进仿真之前,我们执行 assets.RigidObject.write_data_to_sim() 方法。此方法将其他数据,例如外部力,写入仿真缓冲区。在本教程中,我们没有对刚性物体施加任何外部力,因此此方法不是必需的。但是,为了完整性,我们还是加入了它。

        # apply sim data
        cone_object.write_data_to_sim()

更新状态#

在推进仿真后,我们使用 assets.RigidObject.update() 方法更新刚性物体对象的内部缓冲区,以反映其在 assets.RigidObject.data 属性中的新状态。

        # update buffers
        cone_object.update(sim_dt)

代码执行#

此脚本使用 Isaac Sim PhysX,需要安装 Isaac Sim。以下命令使用下图所示的 Isaac Sim 视口显示仿真:

uv run isaaclab -p scripts/tutorials/01_assets/run_rigid_object.py --viz kit
./isaaclab.sh -p scripts/tutorials/01_assets/run_rigid_object.py --viz kit

这应打开一个包含地面、灯光和若干绿色圆锥体的舞台。圆锥体从随机高度落下,最终停在地面。在终端按 Ctrl+C 可停止仿真。

run_rigid_object.py 的结果

本教程展示了如何生成刚性物体并将它们封装在一个 RigidObject 类中以初始化它们的物理句柄,从而允许设置和获取它们的状态。在下一个教程中,我们将看到如何与由关节连接的一系列刚性物体组成的关节物体进行交互。