与关节交互#
本教程展示了如何与模拟中的关节机器人进行交互。这是 与刚性物体交互 教程的延续,其中我们学习了如何与刚体对象交互。除了设置根状态外,我们还将看到如何设置关节状态并向关节机器人应用命令。
代码#
该教程对应于 source/standalone/tutorials/01_assets
目录中的 run_articulation.py
脚本。
代码 run_articulation.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 a cart-pole and interact with it.
7
8.. code-block:: bash
9
10 # Usage
11 ./isaaclab.sh -p source/standalone/tutorials/01_assets/run_articulation.py
12
13"""
14
15"""Launch Isaac Sim Simulator first."""
16
17
18import argparse
19
20from omni.isaac.lab.app import AppLauncher
21
22# add argparse arguments
23parser = argparse.ArgumentParser(description="Tutorial on spawning and interacting with an articulation.")
24# append AppLauncher cli args
25AppLauncher.add_app_launcher_args(parser)
26# parse the arguments
27args_cli = parser.parse_args()
28
29# launch omniverse app
30app_launcher = AppLauncher(args_cli)
31simulation_app = app_launcher.app
32
33"""Rest everything follows."""
34
35import torch
36
37import omni.isaac.core.utils.prims as prim_utils
38
39import omni.isaac.lab.sim as sim_utils
40from omni.isaac.lab.assets import Articulation
41from omni.isaac.lab.sim import SimulationContext
42
43##
44# Pre-defined configs
45##
46from omni.isaac.lab_assets import CARTPOLE_CFG # isort:skip
47
48
49def design_scene() -> tuple[dict, list[list[float]]]:
50 """Designs the scene."""
51 # Ground-plane
52 cfg = sim_utils.GroundPlaneCfg()
53 cfg.func("/World/defaultGroundPlane", cfg)
54 # Lights
55 cfg = sim_utils.DomeLightCfg(intensity=3000.0, color=(0.75, 0.75, 0.75))
56 cfg.func("/World/Light", cfg)
57
58 # Create separate groups called "Origin1", "Origin2"
59 # Each group will have a robot in it
60 origins = [[0.0, 0.0, 0.0], [-1.0, 0.0, 0.0]]
61 # Origin 1
62 prim_utils.create_prim("/World/Origin1", "Xform", translation=origins[0])
63 # Origin 2
64 prim_utils.create_prim("/World/Origin2", "Xform", translation=origins[1])
65
66 # Articulation
67 cartpole_cfg = CARTPOLE_CFG.copy()
68 cartpole_cfg.prim_path = "/World/Origin.*/Robot"
69 cartpole = Articulation(cfg=cartpole_cfg)
70
71 # return the scene information
72 scene_entities = {"cartpole": cartpole}
73 return scene_entities, origins
74
75
76def run_simulator(sim: sim_utils.SimulationContext, entities: dict[str, Articulation], origins: torch.Tensor):
77 """Runs the simulation loop."""
78 # Extract scene entities
79 # note: we only do this here for readability. In general, it is better to access the entities directly from
80 # the dictionary. This dictionary is replaced by the InteractiveScene class in the next tutorial.
81 robot = entities["cartpole"]
82 # Define simulation stepping
83 sim_dt = sim.get_physics_dt()
84 count = 0
85 # Simulation loop
86 while simulation_app.is_running():
87 # Reset
88 if count % 500 == 0:
89 # reset counter
90 count = 0
91 # reset the scene entities
92 # root state
93 # we offset the root state by the origin since the states are written in simulation world frame
94 # if this is not done, then the robots will be spawned at the (0, 0, 0) of the simulation world
95 root_state = robot.data.default_root_state.clone()
96 root_state[:, :3] += origins
97 robot.write_root_state_to_sim(root_state)
98 # set joint positions with some noise
99 joint_pos, joint_vel = robot.data.default_joint_pos.clone(), robot.data.default_joint_vel.clone()
100 joint_pos += torch.rand_like(joint_pos) * 0.1
101 robot.write_joint_state_to_sim(joint_pos, joint_vel)
102 # clear internal buffers
103 robot.reset()
104 print("[INFO]: Resetting robot state...")
105 # Apply random action
106 # -- generate random joint efforts
107 efforts = torch.randn_like(robot.data.joint_pos) * 5.0
108 # -- apply action to the robot
109 robot.set_joint_effort_target(efforts)
110 # -- write data to sim
111 robot.write_data_to_sim()
112 # Perform step
113 sim.step()
114 # Increment counter
115 count += 1
116 # Update buffers
117 robot.update(sim_dt)
118
119
120def main():
121 """Main function."""
122 # Load kit helper
123 sim_cfg = sim_utils.SimulationCfg(device=args_cli.device)
124 sim = SimulationContext(sim_cfg)
125 # Set main camera
126 sim.set_camera_view([2.5, 0.0, 4.0], [0.0, 0.0, 2.0])
127 # Design scene
128 scene_entities, scene_origins = design_scene()
129 scene_origins = torch.tensor(scene_origins, device=sim.device)
130 # Play the simulator
131 sim.reset()
132 # Now we are ready!
133 print("[INFO]: Setup complete...")
134 # Run the simulator
135 run_simulator(sim, scene_entities, scene_origins)
136
137
138if __name__ == "__main__":
139 # run the main function
140 main()
141 # close sim app
142 simulation_app.close()
代码解析#
场景设计#
与上一个教程相似,我们在场景中添加了地面平面和远程光线。现在,我们不再产生刚性物体,而是从其 USD 文件中生成一个车杆关节。车杆是一个简单的机器人,由一个小车和一个连接到它的杆组成。小车可以沿 x 轴自由移动,而杆可以围绕小车自由旋转。车杆的 USD 文件包含了机器人的几何形状、关节和其他物理性质。
对于车杆,我们使用其预定义的配置对象,这是 assets.ArticulationCfg
类的一个实例。这个类包含关于关节产生策略、默认初始状态、不同关节的执行器模型和其他元信息的信息。如何创建这个配置对象的更深入介绍在 编写资产配置 教程中提供。
正如在上一个教程中看到的,我们可以通过创建 assets.Articulation
类的实例并将配置对象传递给它的构造函数的方式类似地将关节放入场景中。
# Create separate groups called "Origin1", "Origin2"
# Each group will have a robot in it
origins = [[0.0, 0.0, 0.0], [-1.0, 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
cartpole_cfg = CARTPOLE_CFG.copy()
cartpole_cfg.prim_path = "/World/Origin.*/Robot"
cartpole = Articulation(cfg=cartpole_cfg)
运行模拟循环#
从上一个教程中继续,我们定期重置模拟,对关节机器人设置命令,推进模拟,并更新关节机器人的内部缓冲区。
重置模拟#
与刚体物体类似,关节机器人也有一个根状态。这个状态对应于关节机器人树中的根体。在根状态之上,关节机器人还有关节状态。这些状态对应于关节的位置和速度。
为了重置关节机器人,我们首先通过调用 Articulation.write_root_state_to_sim()
方法设置根状态。类似地,我们通过调用 Articulation.write_joint_state_to_sim()
方法设置关节状态。最后,我们调用 Articulation.reset()
方法重置任何内部缓冲区和缓存。
# 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] += 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
robot.reset()
推进模拟#
向关节机器人应用命令涉及两个步骤:
设置关节目标: 这为关节机器人设置期望的关节位置、速度或努力目标。
将数据写入模拟: 根据关节机器人的配置,这一步处理任何 动作转换 并将转换后的值写入 PhysX 缓冲区。
在本教程中,我们使用关节努力命令来控制关节机器人。为此工作,我们需要将关节机器人的刚度和阻尼参数设置为零。这是在车杆的预定义配置对象内部预先完成的。
在每一步中,我们随机采样关节努力并通过调用 Articulation.set_joint_effort_target()
方法将它们设置给关节机器人。在设置好目标后,我们通过调用 Articulation.write_data_to_sim()
方法将数据写入 PhysX 缓冲区。最后,我们推进模拟。
# 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
robot.write_data_to_sim()
更新状态#
每个关节机器人类包含一个 assets.ArticulationData
对象。这个对象保存了关节机器人的状态。为了更新缓冲区内的状态,我们调用 assets.Articulation.update()
方法。
# Update buffers
robot.update(sim_dt)
代码执行#
要运行代码并查看结果,让我们从终端运行脚本:
./isaaclab.sh -p source/standalone/tutorials/01_assets/run_articulation.py
这个命令应该会打开一个包含地面平面、灯光和两个随机移动的cart-poles的场景。要停止模拟,您可以关闭窗口,或在终端中按 Ctrl+C
。
在本教程中,我们学会了如何创建和交互一个简单的关节机器人。我们看到了如何设置关节机器人的状态(其根和关节状态),以及如何对其应用命令。我们还看到了如何更新其缓冲区,以从模拟中读取最新状态。
除了这个教程之外,我们还提供了一些生成不同机器人的其他脚本。这些包括在 source/standalone/demos
目录中。您可以运行这些脚本,比如:
# Spawn many different single-arm manipulators
./isaaclab.sh -p source/standalone/demos/arms.py
# Spawn many different quadrupeds
./isaaclab.sh -p source/standalone/demos/quadrupeds.py