将新机器人添加到 Isaac Lab#
模拟和训练新机器人是一个多步骤的过程,从将机器人导入 Isaac Sim 开始。这在 Isaac Sim 文档 这里 中有详细介绍。一旦机器人被导入并调整进行仿真,我们必须定义那些必要的接口,以便在多个环境中克隆机器人,驱动其关节,并正确地重置它,无论选择了哪种工作流程或训练框架。
在本教程中,我们将探讨如何将新机器人添加到 Isaac Lab。关键步骤是创建一个 AssetBaseCfg
,它定义了机器人的 USD 关节和通过 Isaac Lab 可用的学习算法之间的接口。
代码#
本教程对应于 scripts/tutorials/01_assets
目录中的 add_new_robot
脚本。
add_new_robot.py 的代码
1# Copyright (c) 2022-2025, The Isaac Lab Project Developers.
2# All rights reserved.
3#
4# SPDX-License-Identifier: BSD-3-Clause
5
6import argparse
7
8from isaaclab.app import AppLauncher
9
10# add argparse arguments
11parser = argparse.ArgumentParser(
12 description="This script demonstrates adding a custom robot to an Isaac Lab environment."
13)
14parser.add_argument("--num_envs", type=int, default=1, help="Number of environments to spawn.")
15# append AppLauncher cli args
16AppLauncher.add_app_launcher_args(parser)
17# parse the arguments
18args_cli = parser.parse_args()
19
20# launch omniverse app
21app_launcher = AppLauncher(args_cli)
22simulation_app = app_launcher.app
23
24import numpy as np
25import torch
26
27import isaaclab.sim as sim_utils
28from isaaclab.actuators import ImplicitActuatorCfg
29from isaaclab.assets import AssetBaseCfg
30from isaaclab.assets.articulation import ArticulationCfg
31from isaaclab.scene import InteractiveScene, InteractiveSceneCfg
32from isaaclab.utils.assets import ISAAC_NUCLEUS_DIR
33
34JETBOT_CONFIG = ArticulationCfg(
35 spawn=sim_utils.UsdFileCfg(usd_path=f"{ISAAC_NUCLEUS_DIR}/Robots/Jetbot/jetbot.usd"),
36 actuators={"wheel_acts": ImplicitActuatorCfg(joint_names_expr=[".*"], damping=None, stiffness=None)},
37)
38
39DOFBOT_CONFIG = ArticulationCfg(
40 spawn=sim_utils.UsdFileCfg(
41 usd_path=f"{ISAAC_NUCLEUS_DIR}/Robots/Dofbot/dofbot.usd",
42 rigid_props=sim_utils.RigidBodyPropertiesCfg(
43 disable_gravity=False,
44 max_depenetration_velocity=5.0,
45 ),
46 articulation_props=sim_utils.ArticulationRootPropertiesCfg(
47 enabled_self_collisions=True, solver_position_iteration_count=8, solver_velocity_iteration_count=0
48 ),
49 ),
50 init_state=ArticulationCfg.InitialStateCfg(
51 joint_pos={
52 "joint1": 0.0,
53 "joint2": 0.0,
54 "joint3": 0.0,
55 "joint4": 0.0,
56 },
57 pos=(0.25, -0.25, 0.0),
58 ),
59 actuators={
60 "front_joints": ImplicitActuatorCfg(
61 joint_names_expr=["joint[1-2]"],
62 effort_limit_sim=100.0,
63 velocity_limit_sim=100.0,
64 stiffness=10000.0,
65 damping=100.0,
66 ),
67 "joint3_act": ImplicitActuatorCfg(
68 joint_names_expr=["joint3"],
69 effort_limit_sim=100.0,
70 velocity_limit_sim=100.0,
71 stiffness=10000.0,
72 damping=100.0,
73 ),
74 "joint4_act": ImplicitActuatorCfg(
75 joint_names_expr=["joint4"],
76 effort_limit_sim=100.0,
77 velocity_limit_sim=100.0,
78 stiffness=10000.0,
79 damping=100.0,
80 ),
81 },
82)
83
84
85class NewRobotsSceneCfg(InteractiveSceneCfg):
86 """Designs the scene."""
87
88 # Ground-plane
89 ground = AssetBaseCfg(prim_path="/World/defaultGroundPlane", spawn=sim_utils.GroundPlaneCfg())
90
91 # lights
92 dome_light = AssetBaseCfg(
93 prim_path="/World/Light", spawn=sim_utils.DomeLightCfg(intensity=3000.0, color=(0.75, 0.75, 0.75))
94 )
95
96 # robot
97 Jetbot = JETBOT_CONFIG.replace(prim_path="{ENV_REGEX_NS}/Jetbot")
98 Dofbot = DOFBOT_CONFIG.replace(prim_path="{ENV_REGEX_NS}/Dofbot")
99
100
101def run_simulator(sim: sim_utils.SimulationContext, scene: InteractiveScene):
102 sim_dt = sim.get_physics_dt()
103 sim_time = 0.0
104 count = 0
105
106 while simulation_app.is_running():
107 # reset
108 if count % 500 == 0:
109 # reset counters
110 count = 0
111 # reset the scene entities to their initial positions offset by the environment origins
112 root_jetbot_state = scene["Jetbot"].data.default_root_state.clone()
113 root_jetbot_state[:, :3] += scene.env_origins
114 root_dofbot_state = scene["Dofbot"].data.default_root_state.clone()
115 root_dofbot_state[:, :3] += scene.env_origins
116
117 # copy the default root state to the sim for the jetbot's orientation and velocity
118 scene["Jetbot"].write_root_pose_to_sim(root_jetbot_state[:, :7])
119 scene["Jetbot"].write_root_velocity_to_sim(root_jetbot_state[:, 7:])
120 scene["Dofbot"].write_root_pose_to_sim(root_dofbot_state[:, :7])
121 scene["Dofbot"].write_root_velocity_to_sim(root_dofbot_state[:, 7:])
122
123 # copy the default joint states to the sim
124 joint_pos, joint_vel = (
125 scene["Jetbot"].data.default_joint_pos.clone(),
126 scene["Jetbot"].data.default_joint_vel.clone(),
127 )
128 scene["Jetbot"].write_joint_state_to_sim(joint_pos, joint_vel)
129 joint_pos, joint_vel = (
130 scene["Dofbot"].data.default_joint_pos.clone(),
131 scene["Dofbot"].data.default_joint_vel.clone(),
132 )
133 scene["Dofbot"].write_joint_state_to_sim(joint_pos, joint_vel)
134 # clear internal buffers
135 scene.reset()
136 print("[INFO]: Resetting Jetbot and Dofbot state...")
137
138 # drive around
139 if count % 100 < 75:
140 # Drive straight by setting equal wheel velocities
141 action = torch.Tensor([[10.0, 10.0]])
142 else:
143 # Turn by applying different velocities
144 action = torch.Tensor([[5.0, -5.0]])
145
146 scene["Jetbot"].set_joint_velocity_target(action)
147
148 # wave
149 wave_action = scene["Dofbot"].data.default_joint_pos
150 wave_action[:, 0:4] = 0.25 * np.sin(2 * np.pi * 0.5 * sim_time)
151 scene["Dofbot"].set_joint_position_target(wave_action)
152
153 scene.write_data_to_sim()
154 sim.step()
155 sim_time += sim_dt
156 count += 1
157 scene.update(sim_dt)
158
159
160def main():
161 """Main function."""
162 # Initialize the simulation context
163 sim_cfg = sim_utils.SimulationCfg(device=args_cli.device)
164 sim = sim_utils.SimulationContext(sim_cfg)
165
166 sim.set_camera_view([3.5, 0.0, 3.2], [0.0, 0.0, 0.5])
167 # design scene
168 scene_cfg = NewRobotsSceneCfg(args_cli.num_envs, env_spacing=2.0)
169 scene = InteractiveScene(scene_cfg)
170 # Play the simulator
171 sim.reset()
172 # Now we are ready!
173 print("[INFO]: Setup complete...")
174 # Run the simulator
175 run_simulator(sim, scene)
176
177
178if __name__ == "__main__":
179 main()
180 simulation_app.close()
解释代码#
从根本上说,机器人是一个具有关节驱动的关节。要在仿真中移动一个机器人,我们必须对其驱动应用目标,并使仿真向前推进。然而,严格通过关节驱动来控制机器人是很繁琐的,特别是如果您想控制任何复杂的东西,而且如果您想在多个环境中克隆机器人,就更加繁琐了。
为了简化这个过程,Isaac Lab 提供了一系列 配置
类,定义了需要被克隆的 USD 的哪些部分,哪些部分是需要被代理控制的执行器,如何进行重置等等… 根据资产需要进行多少精细调整,您可以通过许多方式配置单个机器人资产以用于 Isaac Lab。为了演示,教程脚本导入了两个机器人:第一个机器人,Jetbot,配置最小,而第二个机器人,Dofbot,附加参数进行了配置。
Jetbot 是一个简单的双轮差动底座,顶部带有摄像头。该资产用于 Isaac Sim 中的许多演示和教程,因此我们知道它非常适合!要将其带入 Isaac lab,我们必须首先定义其中一个这些配置。因为一个机器人是一个具有关节驱动的关节,我们定义了一个 ArticulationCfg
来描述机器人。
import isaaclab.sim as sim_utils
from isaaclab.actuators import ImplicitActuatorCfg
from isaaclab.assets import AssetBaseCfg
from isaaclab.assets.articulation import ArticulationCfg
from isaaclab.scene import InteractiveScene, InteractiveSceneCfg
from isaaclab.utils.assets import ISAAC_NUCLEUS_DIR
JETBOT_CONFIG = ArticulationCfg(
spawn=sim_utils.UsdFileCfg(usd_path=f"{ISAAC_NUCLEUS_DIR}/Robots/Jetbot/jetbot.usd"),
actuators={"wheel_acts": ImplicitActuatorCfg(joint_names_expr=[".*"], damping=None, stiffness=None)},
)
这是在 Isaac Lab 中机器人的最小配置。只有两个必需参数: spawn
和 actuators
。
spawn
参数正在寻找一个 SpawnerCfg
,用于指定在仿真中定义机器人的 USD 资产。Isaac Lab 仿真实用程序, isaaclab.sim
,为我们提供了一个 USDFileCfg
类,它接受我们的 USD 资产路径,并生成我们需要的 SpawnerCfg
。在这种情况下, jetbot.usd
位于 Isaac Assets 下的 Robots/Jetbot/jetbot.usd
。
actuators
参数是一个执行器配置的字典,并定义我们打算用代理控制的机器人的哪些部分。有许多不同的方式可以更新关节的状态以达到某个目标。Isaac Lab 提供了一系列可以用于匹配常见执行器模型或甚至实现自己的执行器类。在这种情况下,我们使用 ImplicitActuatorCfg
类来指定机器人的执行器,因为它们是简单的轮子并且默认值是合适的。
为这个字典指定关节名键可以具有不同的特定级别。jetbot 只有几个关节,我们将只使用在 USD 资产中指定的默认值,因此我们可以使用简单的正则表达式, .*
来指定所有关节。其他正则表达式也可以用于分组关节和相关配置。
备注
在隐式执行器中必须指定刚度和阻尼,但 None
值将使用在 USD 资产中定义的默认值。
虽然这是最小配置,但我们可以指定许多其他参数
DOFBOT_CONFIG = ArticulationCfg(
spawn=sim_utils.UsdFileCfg(
usd_path=f"{ISAAC_NUCLEUS_DIR}/Robots/Dofbot/dofbot.usd",
rigid_props=sim_utils.RigidBodyPropertiesCfg(
disable_gravity=False,
max_depenetration_velocity=5.0,
),
articulation_props=sim_utils.ArticulationRootPropertiesCfg(
enabled_self_collisions=True, solver_position_iteration_count=8, solver_velocity_iteration_count=0
),
),
init_state=ArticulationCfg.InitialStateCfg(
joint_pos={
"joint1": 0.0,
"joint2": 0.0,
"joint3": 0.0,
"joint4": 0.0,
},
pos=(0.25, -0.25, 0.0),
),
actuators={
"front_joints": ImplicitActuatorCfg(
joint_names_expr=["joint[1-2]"],
effort_limit_sim=100.0,
velocity_limit_sim=100.0,
stiffness=10000.0,
damping=100.0,
),
"joint3_act": ImplicitActuatorCfg(
joint_names_expr=["joint3"],
effort_limit_sim=100.0,
velocity_limit_sim=100.0,
stiffness=10000.0,
damping=100.0,
),
"joint4_act": ImplicitActuatorCfg(
joint_names_expr=["joint4"],
effort_limit_sim=100.0,
velocity_limit_sim=100.0,
stiffness=10000.0,
damping=100.0,
),
},
)
该配置可用于向场景中添加一个 Dofbot,并包含其中一些参数。Dofbot 是一个带有多个关节的业余机械臂机器人,因此我们有更多可用于配置的选项。最显著的两个区别是为物理属性添加配置和机器人的初始状态 init_state
。
USDFileCfg
对刚性体和机器人等具有特殊参数。 rigid_props
参数需要一个 RigidBodyPropertiesCfg
,允许您指定机器人被生成时关于其行为作为仿真中 “物理对象” 的身体链接属性。而 articulation_props
管理与用于通过时间的求解器推进关节相关的属性,因此它期望配置一个 ArticulationRootPropertiesCfg
。提供的其他物理属性和参数可以通过 isaaclab.sim.schemas
提供的配置指定。
ArticulationCfg
可选地包括 init_state
参数,它定义了关节的初始状态。关节的初始状态是一个特殊的,用户定义的状态,当机器人由 Isaac Lab 生成或重置时使用。初始关节状态 joint_pos
由 USD 关节名称作为键指定的浮点数字典来指定( 不是 执行器名称)。这里值得注意的另一点是初始位置 pos
的坐标系,即环境的坐标系。在这种情况下,通过指定一个位置 (0.25, -0.25, 0.0)
,我们偏移了机器人的生成位置 从环境的原点 ,而不是世界的原点。
通过这些机器人的配置,我们现在可以将它们添加到场景中,并以直接工作流程的常规方式与它们进行交互:通过定义一个包含机器人的关节配置的 InteractiveSceneCfg
…
class NewRobotsSceneCfg(InteractiveSceneCfg):
"""Designs the scene."""
# Ground-plane
ground = AssetBaseCfg(prim_path="/World/defaultGroundPlane", spawn=sim_utils.GroundPlaneCfg())
# lights
dome_light = AssetBaseCfg(
prim_path="/World/Light", spawn=sim_utils.DomeLightCfg(intensity=3000.0, color=(0.75, 0.75, 0.75))
)
# robot
Jetbot = JETBOT_CONFIG.replace(prim_path="{ENV_REGEX_NS}/Jetbot")
Dofbot = DOFBOT_CONFIG.replace(prim_path="{ENV_REGEX_NS}/Dofbot")
…然后推进仿真同时适当地更新场景实体。
def run_simulator(sim: sim_utils.SimulationContext, scene: InteractiveScene):
sim_dt = sim.get_physics_dt()
sim_time = 0.0
count = 0
while simulation_app.is_running():
# reset
if count % 500 == 0:
# reset counters
count = 0
# reset the scene entities to their initial positions offset by the environment origins
root_jetbot_state = scene["Jetbot"].data.default_root_state.clone()
root_jetbot_state[:, :3] += scene.env_origins
root_dofbot_state = scene["Dofbot"].data.default_root_state.clone()
root_dofbot_state[:, :3] += scene.env_origins
# copy the default root state to the sim for the jetbot's orientation and velocity
scene["Jetbot"].write_root_pose_to_sim(root_jetbot_state[:, :7])
scene["Jetbot"].write_root_velocity_to_sim(root_jetbot_state[:, 7:])
scene["Dofbot"].write_root_pose_to_sim(root_dofbot_state[:, :7])
scene["Dofbot"].write_root_velocity_to_sim(root_dofbot_state[:, 7:])
# copy the default joint states to the sim
joint_pos, joint_vel = (
scene["Jetbot"].data.default_joint_pos.clone(),
scene["Jetbot"].data.default_joint_vel.clone(),
)
scene["Jetbot"].write_joint_state_to_sim(joint_pos, joint_vel)
joint_pos, joint_vel = (
scene["Dofbot"].data.default_joint_pos.clone(),
scene["Dofbot"].data.default_joint_vel.clone(),
)
scene["Dofbot"].write_joint_state_to_sim(joint_pos, joint_vel)
# clear internal buffers
scene.reset()
print("[INFO]: Resetting Jetbot and Dofbot state...")
# drive around
if count % 100 < 75:
# Drive straight by setting equal wheel velocities
action = torch.Tensor([[10.0, 10.0]])
else:
# Turn by applying different velocities
action = torch.Tensor([[5.0, -5.0]])
scene["Jetbot"].set_joint_velocity_target(action)
# wave
wave_action = scene["Dofbot"].data.default_joint_pos
wave_action[:, 0:4] = 0.25 * np.sin(2 * np.pi * 0.5 * sim_time)
scene["Dofbot"].set_joint_position_target(wave_action)
scene.write_data_to_sim()
sim.step()
sim_time += sim_dt
count += 1
scene.update(sim_dt)
备注
您可能会看到一个警告,表示未配置所有执行器!这是预期的,因为我们在本教程中未处理夹爪。
