Nvidia Isaac Sim 8 - Hello World
NVIDIA Omniverse™ Kit 是 NVIDIA Isaac Sim 用于构建其应用程序的工具包,它提供用于脚本编写的 Python 解释器。这意味着每个 GUI 命令以及许多额外功能都可以通过 Python API 使用。不过,使用 Pixar USD Python API 与 Omniverse Kit 交互的学习曲线很陡,而且相关步骤通常比较繁琐。因此,NVIDIA Isaac Sim 提供一组专为机器人应用而设计的 API。这些 API 封装 USD API 的复杂性,并且将常用任务中需要多步完成的操作整合为一步。
本教程将介绍 Core API 的概念及其使用方法。先从向空 Stage 中添加立方体开始,然后在此基础上逐步构建场景,其中包含多个同时执行多个任务的机器人。如图所示:core_api_tutorials_6_2.webp。
1. 学习目标
本系列教程将介绍 Core API。完成本教程后,可以了解:
- 按照 Core API 的定义创建 World 和 Scene。
- 如何向 Stage 中添加刚体,以及在 NVIDIA Isaac Sim 中使用 Python 对其进行仿真。
- 在 Extension 工作流、Standalone 工作流以及 Jupyter Notebook 中运行 Python 的区别。
2. 开始
先决条件
- 本教程要求具备 Python 和异步编程的中级知识。
首先打开 Hello World 示例。激活 Windows > Examples > Robotics Examples,这将打开 Robotics Examples 选项卡。
- 点击 Robotics Examples > General > Hello World。
- 确认 Hello World 示例扩展的窗口在工作区中可见。
这个文件夹包含三个文件:hello_world.py、hello_world_extension.py 和 __init__.py。
hello_world.py 脚本是应用程序逻辑所在的地方,而应用程序的 UI 元素则在 hello_world_extension.py 脚本中添加,由此与逻辑进行关联。
- 点击 LOAD 按钮,加载 World。
- 点击
File > New From Stage Template > Empty创建新 Stage;当系统提示是否保存当前 Stage 时,点击 Don’t Save。
- 再次点击 LOAD 按钮,重新加载 World。
- 打开
hello_world.py,按 Ctrl+S 使用热重载功能。可以看到,菜单从工作区中消失(因为它已重新启动)。
- 再次打开示例菜单,点击 LOAD 按钮。
现在可以开始向这个示例添加内容。
3. 代码概览
该示例继承自 BaseSample。BaseSample 是 Extension 应用程序的模版,用于为机器人 Extension 应用程序搭建基础功能。下面是 BaseSample 执行的一些操作示例:
- 通过按钮加载 World 及其对应资源。
- 在创建新 Stage 时清空 World。
- 将 World 中的对象重置为默认状态。
- 处理热重载。
World 是能够以简单且模块化的方式与仿真器交互的核心类。它处理许多与时间相关的事件,比如添加回调、推进物理步、重置场景、添加任务等。
World 包含 Scene 实例。Scene 类用于管理 USD Stage 中需要关注的仿真资源。它提供简单易用的 API,用于在 Stage 中添加、操作、检查和重置不同的 USD 资源。
from isaacsim.examples.interactive.base_sample import BaseSample #boiler plate of a robotics extension application
class HelloWorld(BaseSample):
def __init__(self) -> None:
super().__init__()
return
# This function is called to setup the assets in the scene for the first time
# Class variables should not be assigned here, since this function is not called
# after a hot-reload, its only called to load the world starting from an EMPTY stage
def setup_scene(self):
# A world is defined in the BaseSample, can be accessed everywhere EXCEPT __init__
world = self.get_world()
world.scene.add_default_ground_plane() # adds a default ground plane to the scene
return3.1. 单例 World
World 是单例,这意味着在运行 NVIDIA Isaac Sim 时只能存在一个 World。下面的代码演示如何在不同文件和 Extension 中获取当前 World 实例。
from isaacsim.examples.interactive.base_sample import BaseSample
from isaacsim.core.api import World
class HelloWorld(BaseSample):
def __init__(self) -> None:
super().__init__()
return
def setup_scene(self):
world = World.instance()
world.scene.add_default_ground_plane()
return4. 向 Scene 添加对象
使用 Python API 将立方体作为刚体添加到场景中。
from isaacsim.examples.interactive.base_sample import BaseSample
import numpy as np
# Can be used to create a new cube or to point to an already existing cube in stage.
from isaacsim.core.api.objects import DynamicCuboid
class HelloWorld(BaseSample):
def __init__(self) -> None:
super().__init__()
return
def setup_scene(self):
world = self.get_world()
world.scene.add_default_ground_plane()
fancy_cube = world.scene.add(
DynamicCuboid(
prim_path="/World/random_cube", # The prim path of the cube in the USD stage
name="fancy_cube", # The unique name used to retrieve the object from the scene later on
position=np.array([0, 0, 1.0]), # Using the current stage units which is in meters by default.
scale=np.array([0.5015, 0.5015, 0.5015]), # most arguments accept mainly numpy arrays.
color=np.array([0, 0, 1.0]), # RGB channels, going from 0-1
))
return- 按 Ctrl+S 保存代码,热重载 NVIDIA Isaac Sim。
- 再次打开菜单。
- 点击
File > New From Stage Template > Empty,然后点击 LOAD 按钮。如已对setup_scene中的内容进行修改,则需要执行该操作。否则,只需点击 LOAD 按钮即可。
- 点击 PLAY 按钮开始仿真动态立方体,观察其下落。
5. 检查对象属性
打印立方体的世界位姿和速度。高亮行展示如何通过名称获取对象及查询其属性。
from isaacsim.examples.interactive.base_sample import BaseSample
import numpy as np
from isaacsim.core.api.objects import DynamicCuboid
class HelloWorld(BaseSample):
def __init__(self) -> None:
super().__init__()
return
def setup_scene(self):
world = self.get_world()
world.scene.add_default_ground_plane()
fancy_cube = world.scene.add(
DynamicCuboid(
prim_path="/World/random_cube",
name="fancy_cube",
position=np.array([0, 0, 1.0]),
scale=np.array([0.5015, 0.5015, 0.5015]),
color=np.array([0, 0, 1.0]),
))
return
# Here we assign the class's variables
# this function is called after load button is pressed
# regardless starting from an empty stage or not
# this is called after setup_scene and after
# one physics time step to propagate appropriate
# physics handles which are needed to retrieve
# many physical properties of the different objects
async def setup_post_load(self):
self._world = self.get_world()
self._cube = self._world.scene.get_object("fancy_cube")
position, orientation = self._cube.get_world_pose()
linear_velocity = self._cube.get_linear_velocity()
# will be shown on terminal
print("Cube position is : " + str(position))
print("Cube's orientation is : " + str(orientation))
print("Cube's linear velocity is : " + str(linear_velocity))
return5.1. 在仿真期间持续检查对象属性
仿真过程中,在每个物理步打印立方体的世界位姿和速度。在该工作流中,应用程序是异步运行的,无法控制何时推进物理步。不过,可以添加回调,确保某些操作在特定事件之前发生。
按如下方式添加物理回调:
from isaacsim.examples.interactive.base_sample import BaseSample
import numpy as np
from isaacsim.core.api.objects import DynamicCuboid
class HelloWorld(BaseSample):
def __init__(self) -> None:
super().__init__()
return
def setup_scene(self):
world = self.get_world()
world.scene.add_default_ground_plane()
fancy_cube = world.scene.add(
DynamicCuboid(
prim_path="/World/random_cube",
name="fancy_cube",
position=np.array([0, 0, 1.0]),
scale=np.array([0.5015, 0.5015, 0.5015]),
color=np.array([0, 0, 1.0]),
))
return
async def setup_post_load(self):
self._world = self.get_world()
self._cube = self._world.scene.get_object("fancy_cube")
self._world.add_physics_callback("sim_step", callback_fn=self.print_cube_info) #callback names have to be unique
return
# here we define the physics callback to be called before each physics step, all physics callbacks must take
# step_size as an argument
def print_cube_info(self, step_size):
position, orientation = self._cube.get_world_pose()
linear_velocity = self._cube.get_linear_velocity()
# will be shown on terminal
print("Cube position is : " + str(position))
print("Cube's orientation is : " + str(orientation))
print("Cube's linear velocity is : " + str(linear_velocity))6. 将示例转换为 Standalone 应用程序
注意
- 在 Windows 上,使用
python.bat代替python.sh。
- 下面关于
python.sh工作方式的说明,同样适用于python.bat。
- 在 Windows 上,使用
在此工作流中,从 Python 启动后,机器人应用立即开始运行,并且可以控制何时推进物理步和渲染步。
打开新 my_application.py 文件,并且添加以下内容:
#launch Isaac Sim before any other imports
#default first two lines in any standalone application
from isaacsim import SimulationApp
simulation_app = SimulationApp({"headless": False}) # we can also run as headless.
from isaacsim.core.api import World
from isaacsim.core.api.objects import DynamicCuboid
import numpy as np
world = World()
world.scene.add_default_ground_plane()
fancy_cube = world.scene.add(
DynamicCuboid(
prim_path="/World/random_cube",
name="fancy_cube",
position=np.array([0, 0, 1.0]),
scale=np.array([0.5015, 0.5015, 0.5015]),
color=np.array([0, 0, 1.0]),
))
# Resetting the world needs to be called before querying anything related to an articulation specifically.
# Its recommended to always do a reset after adding your assets, for physics handles to be propagated properly
world.reset()
for i in range(500):
position, orientation = fancy_cube.get_world_pose()
linear_velocity = fancy_cube.get_linear_velocity()
# will be shown on terminal
print("Cube position is : " + str(position))
print("Cube's orientation is : " + str(orientation))
print("Cube's linear velocity is : " + str(linear_velocity))
# we have control over stepping physics and rendering in this workflow
# things run in sync
world.step(render=True) # execute one physics step and one rendering step
simulation_app.close() # close Isaac Sim使用 ./python.sh ./exts/isaacsim.examples.interactive/isaacsim/examples/interactive/user_examples/my_application.py 运行该脚本。