Nvidia Isaac Sim 6 - 快速教程 2 - 机器人基础教程

机器人基础教程介绍如何向 Stage 添加机器人,移动机器人以及查看机器人属性。


1. GUI

1.1. 向 Stage 添加机器人

  • 新建 Stage(File > New)。
  • 从顶部菜单栏选择 Create > Robots > Franka Emika Panda Arm,将机器人添加到场景中。

1.2. 检查机器人

使用 Physics Inspector 查看机器人的关节属性。

  1. 依次点击 Tools > Physics > Physics Inspector,右侧将打开一个窗口。
  1. 选择 Franka 进行检查。窗口将默认显示关节信息,比如上下限位及默认位置。
  1. 点击右上角的汉堡图标,可查看更多选项,比如关节刚度和阻尼。
  1. 修改这些数值,机器人将随之移动,同时出现绿色对勾标记。
  1. 若要提交更改,并且将其设置为机器人的新默认值,请点击绿色对勾标记。

1.3. 控制机器人

基于 GUI 的机器人控制器位于 Omniverse 的可视化编程工具 OmniGraph 中。本教程将使用快捷工具生成图,然后在 OmniGraph 编辑器中检查该图。

  1. 依次点击 Tools > Robotics > Omnigraph Controllers > Joint Position,打开图生成器。
  1. 在随后出现的“Articulation Position Controller Inputs”弹窗中,点击“Robot Prim”字段旁边的“Add”。
  1. 选择 Franka 作为目标。
  1. 点击“OK”生成图。

要移动机器人:

  1. 在右上角的“Stage”选项卡中,选择 Graph > Position_Controller
  1. 选择 JointCommandArray 节点。可以通过 Stage 树选择该节点,也可以在图编辑器中选择该节点。
  1. 在右下角的“Property”选项卡中,可以看到关节命令值。“Construct Array Node”下的“Inputs”对应机器人上的各个关节,从基座关节开始。
  1. 点击+保持+拖拽各个数值字段,或输入不同的数值,可看到机器人手臂的位置发生变化。
  1. 点击“Play”启动仿真。

要可视化生成的图:

  1. 打开图编辑器窗口,Window > Graph Editors > Action Graph。编辑器窗口在包含机器人的 Viewport 选项卡下方的标签页中打开。
  1. 将新打开的浏览器标签页拉起。
  1. 点击图编辑器窗口中间的 Edit Action Graph
  1. 在列表中选择唯一已有的图。
  1. 选择一个数组,通过 Stage 和 Property 选项卡,查看与每个数组节点关联的值。
  1. 选择图中的 Articulation Controller 对象,查看其属性。

2. Extension

2.1. 向 Stage 添加机器人

开启新 Stage(File > New)。要向场景中添加机器人,将以下代码片段复制粘贴到 Script Editor 中,然后运行。

import carb
from isaacsim.core.prims import Articulation
from isaacsim.core.utils.stage import add_reference_to_stage
from isaacsim.storage.native import get_assets_root_path
import numpy as np

assets_root_path = get_assets_root_path()
if assets_root_path is None:
    carb.log_error("Could not find Isaac Sim assets folder")
usd_path = assets_root_path + "/Isaac/Robots/FrankaRobotics/FrankaPanda/franka.usd"
prim_path = "/World/Arm"

add_reference_to_stage(usd_path=usd_path, prim_path=prim_path)
arm_handle = Articulation(prim_paths_expr=prim_path, name="Arm")
arm_handle.set_world_poses(positions=np.array([[0, -1, 0]]))

2.2. 检查机器人

Isaac Sim Core API 提供许多可用于获取机器人信息的函数调用。下面的示例用于查找关节数量、关节名称、各种关节属性以及关节状态。

在 Script Editor 中打开新标签页,复制粘贴以下代码片段。该代码只能在前面的添加机器人步骤完成后运行,因为只有到那一步之后,arm_handle 才可用。运行代码片段前需先按下 Play。这些命令需要在物理仿真运行时才能工作。

# Get the number of joints
num_joints = arm_handle.num_joints
print("Number of joints: ", num_joints)

# Get joint names
joint_names = arm_handle.joint_names
print("Joint names: ", joint_names)

# Get joint limits
joint_limits = arm_handle.get_dof_limits()
print("Joint limits: ", joint_limits)

# Get joint positions
joint_positions = arm_handle.get_joint_positions()
print("Joint positions: ", joint_positions)

注意,当按下“Run”时,将只打印一次状态,即便仿真仍在运行。如果想查看更新的状态,必须持续按“Run”。如果想在每个物理步都打印信息,则需要将这些命令插到在每个物理步都运行的物理回调中。

要将这些命令插到物理回调中,请在 Script Editor 的单独选项卡中运行以下代码片段。

import asyncio
from isaacsim.core.api.simulation_context import SimulationContext

async def test():
    def print_state(dt):
        joint_positions = arm_handle.get_joint_positions()
        print("Joint positions: ", joint_positions)
    simulation_context = SimulationContext()
    await simulation_context.initialize_simulation_context_async()
    await simulation_context.reset_async()
    simulation_context.add_physics_callback("printing_state", print_state)

asyncio.ensure_future(test())

按下 Play 启动仿真,然后运行该代码片段。可以看到信息在每个物理步都被打印到终端中。

如果不再需要每个物理步都打印,可以通过运行以下代码片段移除物理回调。

simulation_context = SimulationContext()
simulation_context.remove_physics_callback("printing_state")

2.3. 控制机器人

在 Isaac Sim 中,可以通过多种方法控制机器人。最底层的方法是直接发送关节命令设置位置、速度和力矩。下面是使用 Articulation API 在关节级别控制机器人的示例。

Script Editor 中打开新标签页,复制粘贴下面的代码片段。该代码只能在前面的添加机器人步骤完成后运行,因为只有到那一步之后,arm_handle 才可用。运行代码片段前请先按下 Play 按钮。必须启动物理仿真,这些命令才能生效。下面提供两个位置,可以在它们之间切换。如果已经将上面的打印状态代码片段添加到每个物理步中,应该能看到随着机器人移动,打印出的关节数值发生变化。

# Set joint position randomly
arm_handle.set_joint_positions([[-1.5, 0.0, 0.0, -1.5, 0.0, 1.5, 0.5, 0.04, 0.04]])
# Set all joints to 0
arm_handle.set_joint_positions([[0.0, 0.0, 0.0 , 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]])

与上面的 get_joint_positions 函数类似,这里的 set_joint_positions 只在按下“Run”时执行一次。如果希望在每个物理步都发送命令,则需要将这些命令插到在每个物理步都运行的物理回调中。


3. Standalone Python

运行本教程的脚本位于 standalone_examples/tutorials/getting_started_robot.py。要运行该脚本,请打开终端,切换到 Isaac Sim 安装目录的根目录,然后运行以下命令:

./python.sh standalone_examples/tutorials/getting_started_robot.py

3.1. 代码解析

getting_started_robot.py 脚本中的第 14 到 50 行用于设置场景,以及将机器人添加到 Stage 中。脚本首先导入必要的模块、添加地平面、设置相机角度,并且向场景中添加两个机器人:一个机械臂机器人和一个移动机器人。这里使用的 API 与 Extension 工作流中使用的相同。

Extension 工作流中的 Python 与 Standalone Python 的主要区别包括:

3.1.1. 从顶部启动仿真器

独立脚本启动仿真应用并且设置 Stage(创建新 Stage、地平面,添加 Franka 和 Carter)。以下代码片段展示该模式:先启动应用,然后使用实验性 API 创建 Stage 及添加机器人。

from isaacsim import SimulationApp

simulation_app = SimulationApp({"headless": False})

import isaacsim.core.experimental.utils.stage as stage_utils
from isaacsim.core.experimental.objects import GroundPlane
from isaacsim.core.experimental.prims import Articulation, XformPrim
from isaacsim.storage.native import get_assets_root_path

assets_root_path = get_assets_root_path()
stage_utils.create_new_stage()
GroundPlane("/World/GroundPlane", positions=[0, 0, 0])

asset_path = assets_root_path + "/Isaac/Robots/FrankaRobotics/FrankaPanda/franka.usd"
stage_utils.add_reference_to_stage(usd_path=asset_path, path="/World/Arm")
arm_transform = XformPrim("/World/Arm")
arm_transform.set_world_poses(positions=[0.0, 1.0, 0.0])
arm = Articulation("/World/Arm")

3.1.2. 显式地步进仿真

在脚本底部的循环中,每次迭代都调用步进函数 my_world.step()。在这个步进函数内部,它向前推进固定次数的渲染和物理计算。

该脚本将运行 4 个周期;在每个周期中,机械臂和小车将移动或停止,并且在最后一个周期中,在每个物理步都打印小车的关节位置。

    for i in range(4):
        print("running cycle: ", i)
        if i == 1 or i == 3:
            print("moving")
            # move the arm
            arm.set_joint_positions([[-1.5, 0.0, 0.0, -1.5, 0.0, 1.5, 0.5, 0.04, 0.04]])
            # move the car
            car.set_joint_velocities([[1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]])
        if i == 2:
            print("stopping")
            # reset the arm
            arm.set_joint_positions([[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]])
            # stop the car
            car.set_joint_velocities([[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]])

        for j in range(100):
            # step the simulation, both rendering and physics
            my_world.step(render=True)
            # print the joint positions of the car at every physics step
            if i == 3:
                car_joint_positions = car.get_joint_positions()
                print("car joint positions:", car_joint_positions)

get_dof_positions 和 set_dof_positions 函数与 Extension 工作流中使用的相同。因为在 Standalone 中步进是显式的,所以这些命令位于循环内部,并且默认在每个物理步运行。这是 Extension 和 Standalone Python 工作流之间的主要区别。