与表面夹爪互动#

本教程展示了如何在仿真中与连接到其末端执行器的关节机器人进行交互。这是 与关节交互 教程的延续,我们在那里学习了如何与关节机器人进行交互。请注意,截至 IsaacSim 5.0,仅支持在cpu后端上的表面夹爪。

代码#

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

run_surface_gripper.py 的代码
  1# Copyright (c) 2022-2025, 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"""This script demonstrates how to spawn a pick-and-place robot equipped with a surface gripper and interact with it.
  7
  8.. code-block:: bash
  9
 10    # Usage
 11    ./isaaclab.sh -p scripts/tutorials/01_assets/run_surface_gripper.py --device=cpu
 12
 13When running this script make sure the --device flag is set to cpu. This is because the surface gripper is
 14currently only supported on the CPU.
 15"""
 16
 17"""Launch Isaac Sim Simulator first."""
 18
 19import argparse
 20
 21from isaaclab.app import AppLauncher
 22
 23# add argparse arguments
 24parser = argparse.ArgumentParser(description="Tutorial on spawning and interacting with a Surface Gripper.")
 25# append AppLauncher cli args
 26AppLauncher.add_app_launcher_args(parser)
 27# parse the arguments
 28args_cli = parser.parse_args()
 29
 30# launch omniverse app
 31app_launcher = AppLauncher(args_cli)
 32simulation_app = app_launcher.app
 33
 34"""Rest everything follows."""
 35
 36import torch
 37
 38import isaacsim.core.utils.prims as prim_utils
 39
 40import isaaclab.sim as sim_utils
 41from isaaclab.assets import Articulation, SurfaceGripper, SurfaceGripperCfg
 42from isaaclab.sim import SimulationContext
 43
 44##
 45# Pre-defined configs
 46##
 47from isaaclab_assets import PICK_AND_PLACE_CFG  # isort:skip
 48
 49
 50def design_scene():
 51    """Designs the scene."""
 52    # Ground-plane
 53    cfg = sim_utils.GroundPlaneCfg()
 54    cfg.func("/World/defaultGroundPlane", cfg)
 55    # Lights
 56    cfg = sim_utils.DomeLightCfg(intensity=3000.0, color=(0.75, 0.75, 0.75))
 57    cfg.func("/World/Light", cfg)
 58
 59    # Create separate groups called "Origin1", "Origin2"
 60    # Each group will have a robot in it
 61    origins = [[2.75, 0.0, 0.0], [-2.75, 0.0, 0.0]]
 62    # Origin 1
 63    prim_utils.create_prim("/World/Origin1", "Xform", translation=origins[0])
 64    # Origin 2
 65    prim_utils.create_prim("/World/Origin2", "Xform", translation=origins[1])
 66
 67    # Articulation: First we define the robot config
 68    pick_and_place_robot_cfg = PICK_AND_PLACE_CFG.copy()
 69    pick_and_place_robot_cfg.prim_path = "/World/Origin.*/Robot"
 70    pick_and_place_robot = Articulation(cfg=pick_and_place_robot_cfg)
 71
 72    # Surface Gripper: Next we define the surface gripper config
 73    surface_gripper_cfg = SurfaceGripperCfg()
 74    # We need to tell the View which prim to use for the surface gripper
 75    surface_gripper_cfg.prim_expr = "/World/Origin.*/Robot/picker_head/SurfaceGripper"
 76    # We can then set different parameters for the surface gripper, note that if these parameters are not set,
 77    # the View will try to read them from the prim.
 78    surface_gripper_cfg.max_grip_distance = 0.1  # [m] (Maximum distance at which the gripper can grasp an object)
 79    surface_gripper_cfg.shear_force_limit = 500.0  # [N] (Force limit in the direction perpendicular direction)
 80    surface_gripper_cfg.coaxial_force_limit = 500.0  # [N] (Force limit in the direction of the gripper's axis)
 81    surface_gripper_cfg.retry_interval = 0.1  # seconds (Time the gripper will stay in a grasping state)
 82    # We can now spawn the surface gripper
 83    surface_gripper = SurfaceGripper(cfg=surface_gripper_cfg)
 84
 85    # return the scene information
 86    scene_entities = {"pick_and_place_robot": pick_and_place_robot, "surface_gripper": surface_gripper}
 87    return scene_entities, origins
 88
 89
 90def run_simulator(
 91    sim: sim_utils.SimulationContext, entities: dict[str, Articulation | SurfaceGripper], origins: torch.Tensor
 92):
 93    """Runs the simulation loop."""
 94    # Extract scene entities
 95    robot: Articulation = entities["pick_and_place_robot"]
 96    surface_gripper: SurfaceGripper = entities["surface_gripper"]
 97
 98    # Define simulation stepping
 99    sim_dt = sim.get_physics_dt()
100    count = 0
101    # Simulation loop
102    while simulation_app.is_running():
103        # Reset
104        if count % 500 == 0:
105            # reset counter
106            count = 0
107            # reset the scene entities
108            # root state
109            # we offset the root state by the origin since the states are written in simulation world frame
110            # if this is not done, then the robots will be spawned at the (0, 0, 0) of the simulation world
111            root_state = robot.data.default_root_state.clone()
112            root_state[:, :3] += origins
113            robot.write_root_pose_to_sim(root_state[:, :7])
114            robot.write_root_velocity_to_sim(root_state[:, 7:])
115            # set joint positions with some noise
116            joint_pos, joint_vel = robot.data.default_joint_pos.clone(), robot.data.default_joint_vel.clone()
117            joint_pos += torch.rand_like(joint_pos) * 0.1
118            robot.write_joint_state_to_sim(joint_pos, joint_vel)
119            # clear internal buffers
120            robot.reset()
121            print("[INFO]: Resetting robot state...")
122            # Opens the gripper and makes sure the gripper is in the open state
123            surface_gripper.reset()
124            print("[INFO]: Resetting gripper state...")
125
126        # Sample a random command between -1 and 1.
127        gripper_commands = torch.rand(surface_gripper.num_instances) * 2.0 - 1.0
128        # The gripper behavior is as follows:
129        # -1 < command < -0.3 --> Gripper is Opening
130        # -0.3 < command < 0.3 --> Gripper is Idle
131        # 0.3 < command < 1 --> Gripper is Closing
132        print(f"[INFO]: Gripper commands: {gripper_commands}")
133        mapped_commands = [
134            "Opening" if command < -0.3 else "Closing" if command > 0.3 else "Idle" for command in gripper_commands
135        ]
136        print(f"[INFO]: Mapped commands: {mapped_commands}")
137        # Set the gripper command
138        surface_gripper.set_grippers_command(gripper_commands)
139        # Write data to sim
140        surface_gripper.write_data_to_sim()
141        # Perform step
142        sim.step()
143        # Increment counter
144        count += 1
145        # Read the gripper state from the simulation
146        surface_gripper.update(sim_dt)
147        # Read the gripper state from the buffer
148        surface_gripper_state = surface_gripper.state
149        # The gripper state is a list of integers that can be mapped to the following:
150        # -1 --> Open
151        # 0 --> Closing
152        # 1 --> Closed
153        # Print the gripper state
154        print(f"[INFO]: Gripper state: {surface_gripper_state}")
155        mapped_commands = [
156            "Open" if state == -1 else "Closing" if state == 0 else "Closed" for state in surface_gripper_state.tolist()
157        ]
158        print(f"[INFO]: Mapped commands: {mapped_commands}")
159
160
161def main():
162    """Main function."""
163    # Load kit helper
164    sim_cfg = sim_utils.SimulationCfg(device=args_cli.device)
165    sim = SimulationContext(sim_cfg)
166    # Set main camera
167    sim.set_camera_view([2.75, 7.5, 10.0], [2.75, 0.0, 0.0])
168    # Design scene
169    scene_entities, scene_origins = design_scene()
170    scene_origins = torch.tensor(scene_origins, device=sim.device)
171    # Play the simulator
172    sim.reset()
173    # Now we are ready!
174    print("[INFO]: Setup complete...")
175    # Run the simulator
176    run_simulator(sim, scene_entities, scene_origins)
177
178
179if __name__ == "__main__":
180    # run the main function
181    main()
182    # close sim app
183    simulation_app.close()

代码解释#

设计场景#

类似于上一个教程,我们在场景中添加了地面平面和一个远处的光源。然后,我们从其 USD 文件中生成一个关节。这次生成了一个拾取-放置机器人。拾取-放置机器人是一个简单的机器人,有 3 个受驱动的轴,其龙门使其可以沿 x 和 y 轴移动,以及沿 z 轴上下移动。此外,机器人末端执行器配备了一个表面夹爪。拾取放置机器人的 USD 文件包含了机器人的几何形状、关节和其他物理属性以及表面夹爪。在自己的机器人上实现类似的夹爪之前,我们建议查看 Isaaclab 的 Nucleus 上找到的夹爪的 USD 文件。

对于抓取和放置机器人,我们使用它的预定义配置对象,在 编写资产配置 教程中可以找到更多信息。对于表面夹爪,我们还需要创建一个配置对象。这可以通过实例化一个 assets.SurfaceGripperCfg 对象并传递相关参数来实现。

可用的参数包括:

  • max_grip_distance: 夹爪能够抓取物体的最大距离。

  • shear_force_limit: 夹爪能在与夹爪轴线垂直的方向上施加的最大力。

  • coaxial_force_limit: 夹爪沿夹爪轴的方向可以施加的最大力。

  • retry_interval: 夹爪将保持在抓取状态的时间。

如前面的教程中所见,我们可以通过创建一个 assets.Articulation 类的实例,并将配置对象传递给其构造函数,以类似的方式将关节添加到场景中。同样的原则也适用于表面夹爪。通过将配置对象传递给 assets.SurfaceGripper 构造函数,就可以创建表面夹爪并将其添加到场景中。实际上,当按下播放按钮时,对象将只被初始化。

    # Create separate groups called "Origin1", "Origin2"
    # Each group will have a robot in it
    origins = [[2.75, 0.0, 0.0], [-2.75, 0.0, 0.0]]
    # Origin 1
    prim_utils.create_prim("/World/Origin1", "Xform", translation=origins[0])
    # Origin 2
    prim_utils.create_prim("/World/Origin2", "Xform", translation=origins[1])

    # Articulation: First we define the robot config
    pick_and_place_robot_cfg = PICK_AND_PLACE_CFG.copy()
    pick_and_place_robot_cfg.prim_path = "/World/Origin.*/Robot"
    pick_and_place_robot = Articulation(cfg=pick_and_place_robot_cfg)

    # Surface Gripper: Next we define the surface gripper config
    surface_gripper_cfg = SurfaceGripperCfg()
    # We need to tell the View which prim to use for the surface gripper
    surface_gripper_cfg.prim_expr = "/World/Origin.*/Robot/picker_head/SurfaceGripper"
    # We can then set different parameters for the surface gripper, note that if these parameters are not set,
    # the View will try to read them from the prim.
    surface_gripper_cfg.max_grip_distance = 0.1  # [m] (Maximum distance at which the gripper can grasp an object)
    surface_gripper_cfg.shear_force_limit = 500.0  # [N] (Force limit in the direction perpendicular direction)
    surface_gripper_cfg.coaxial_force_limit = 500.0  # [N] (Force limit in the direction of the gripper's axis)
    surface_gripper_cfg.retry_interval = 0.1  # seconds (Time the gripper will stay in a grasping state)
    # We can now spawn the surface gripper
    surface_gripper = SurfaceGripper(cfg=surface_gripper_cfg)

运行仿真循环#

继续从前一教程开始,我们定期重置仿真,为关节设置指令,执行仿真步骤,并更新关节的内部缓冲区。

重置仿真#

要重置表面夹爪,我们只需要调用 :meth:`SurfaceGripper.reset 方法,这将重置内部缓冲区和缓存。

            # Opens the gripper and makes sure the gripper is in the open state
            surface_gripper.reset()

步进仿真#

将命令应用到表面夹爪涉及两个步骤:

  1. 设置所需命令: 这将设置所需的夹爪命令(打开、关闭或空闲)。

  2. 将数据写入仿真: 基于表面夹爪的配置,此步骤将转换后的值写入PhysX缓冲区。

在本教程中,我们使用一个随机命令来设置夹爪的指令。夹爪的行为如下:

  • -1 < command < -0.3 –> 夹爪打开中

  • -0.3 < command < 0.3` –> 夹爪空闲中

  • 0.3 < command < 1 –> 夹爪关闭中

在每一步中,我们通过调用 SurfaceGripper.set_grippers_command() 方法随机采样命令并将其设置到夹爪中。设置完命令后,我们调用 SurfaceGripper.write_data_to_sim() 方法将数据写入到PhysX缓冲区中。最后,我们进行仿真步进。

        # Sample a random command between -1 and 1.
        gripper_commands = torch.rand(surface_gripper.num_instances) * 2.0 - 1.0
        # The gripper behavior is as follows:
        # -1 < command < -0.3 --> Gripper is Opening
        # -0.3 < command < 0.3 --> Gripper is Idle
        # 0.3 < command < 1 --> Gripper is Closing
        print(f"[INFO]: Gripper commands: {gripper_commands}")
        mapped_commands = [
            "Opening" if command < -0.3 else "Closing" if command > 0.3 else "Idle" for command in gripper_commands
        ]
        print(f"[INFO]: Mapped commands: {mapped_commands}")
        # Set the gripper command
        surface_gripper.set_grippers_command(gripper_commands)
        # Write data to sim
        surface_gripper.write_data_to_sim()

更新状态#

要了解表面夹爪的当前状态,我们可以查询 assets.SurfaceGripper.state() 属性。这个属性返回一个大小为 [num_envs] 的张量,其中每个元素都是 -101 ,分别对应夹爪的状态。这个属性在每次调用 assets.SurfaceGripper.update() 方法时都会更新。

  • -1 –> 机械手爪是打开的

  • 0 –> 夹爪正在闭合

  • 1 –> 夹爪已关闭

        # Read the gripper state from the simulation
        surface_gripper.update(sim_dt)
        # Read the gripper state from the buffer
        surface_gripper_state = surface_gripper.state

代码执行#

要运行代码并查看结果,请从终端运行脚本:

./isaaclab.sh -p scripts/tutorials/01_assets/run_surface_gripper.py --device cpu

这个命令应该打开一个带有地面平面、灯光和两个夹取放置机器人的场景。在终端中,您应该看到夹爪的状态以及打印出的命令。要停止仿真,您可以关闭窗口,或在终端中按 Ctrl+C

run_surface_gripper.py 的结果

在本教程中,我们学习了如何创建和与表面夹爪进行交互。我们看到如何设置命令并查询夹爪的状态。我们还看到如何更新其缓冲区以从仿真中读取最新状态。

除了本教程之外,我们还提供了一些生成不同机器人的其他脚本。这些脚本包含在 scripts/demos 目录中。您可以这样运行这些脚本:

# Spawn many pick-and-place robots and perform a pick-and-place task
./isaaclab.sh -p scripts/demos/pick_and_place.py

请注意,实践中,用户应该将他们的 assets.SurfaceGripper 实例注册到 isaaclab.InteractiveScene 对象中,在那里会自动处理对 assets.SurfaceGripper.write_data_to_simassets.SurfaceGripper.update 方法的调用。

# Create a scene
scene = InteractiveScene()

# Register the surface gripper
scene.surface_grippers["gripper"] = surface_gripper