将新机器人添加到 Isaac Lab

将新机器人添加到 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 中机器人的最小配置。只有两个必需参数: spawnactuators

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)

备注

您可能会看到一个警告,表示未配置所有执行器!这是预期的,因为我们在本教程中未处理夹爪。

新机器人向您问好!