NVIDIA ISAAC 软件开发工具包 的模块化和易于使用的感知堆栈继续加速各种移动机器人的发展。 ISAAC sdk2020.1 引入了 Python API ,使那些熟悉 Python 的人更容易构建机器人应用程序。
在这篇文章中,我们将探讨这个特性,并分享如何使用 Python 构建您自己的 ISAAC 应用程序的分步指南。我们在 ISAAC SDK 中介绍 Python 编程,并举例说明如何创建应用程序;如何使用代码、模块和数据流;以及如何处理不同的数据类型。我们用示例来总结文章,将子图添加到同一个应用程序中,并将其部署到 Jetson 上。我们还将向您展示如何使用 Jupyter 笔记本电脑,这是一个面向 Python 开发人员的强大 UI 工具。对于更高级的 Python 开发人员,我们还提供了在移动机器人上部署 ISAAC 应用程序以及在 ISAAC Sim 中部署协作机器人手臂的例子。
图 1 。使用 Jupyter 笔记本和 ISAAC SDK Python API 在 ISAAC Sim 中控制虚拟机器人。
ISAAC SDK 中 Python 编程入门
为了指导您使用 Python 创建一个 ISAAC 应用程序,请创建一个 mybot.py 应用程序文件。从 apps 文件夹下的新文件夹开始, //apps/mybot 。用下面的代码创建一个构建文件,并将其保存在 //apps/mybot/BUILD 下,以便 Bazel 能够识别它。
为了指导您使用 Python 创建一个 ISAAC 应用程序,请创建一个 mybot.py
应用程序文件。从 apps 文件夹下的新文件夹开始, //apps/mybot
。用下面的代码创建一个构建文件,并将其保存在 //apps/mybot/BUILD
下,以便 Bazel 能够识别它。
load("//engine/build:isaac.bzl", "isaac_py_app") isaac_py_app( name = "mybot", srcs = ["mybot.py"], data = [], modules=[], deps = [ "//engine/pyalice", ], )
在构建文件中, //engine/pyalice
是 Python API 的支持代码, mybot.py
是用 Python 编写的 robot 应用程序。将下面的代码放入 //apps/mybot/mybot.py
并用 bazel run apps/mybot:mybot
运行它。您可以通过控制台 spew 判断它正在运行,您可以使用经典的 CTR L-C 随时停止它。
from engine.pyalice import Application app = Application(name="mybot") app.run()
当应用程序运行时,将浏览器指向 http://localhost:3000 ,您将在 视力 中看不到任何内容,这是 ISAAC SDK 中的可视化工具。
ISAAC SDK 为机器人应用程序提供了许多构建块,称为 代码 。
其中一些代码可以按原样提供,比如 录音机 。其他模块打包为模块,必须在代码集可用之前显式加载这些模块。将以下模块添加到刚刚创建的 Bazel 构建文件中,以便加载它们。
modules = [ "message_generators", "viewers", ],
现在可以使用在 app.run
之前创建的应用程序实例从 Python 加载它们:
app.load_module('message_generators') app.load_module('viewers')
在 ISAAC SDK 中,数据由传感器生成,并在代码实例之间流动,直到它们被执行器消耗。要从加载的 codelet 创建实例,请在调用之前将以下代码添加到 Python 应用程序应用程序运行.
node_src = app.add('src') component_src = \ node_src.add(app.registry.isaac.message_generators.ImageLoader, 'ImageLoader')
这里创建了一个名为 src
的节点。消息生成器模块提供的 ImageLoader
的组件以 ImageLoader
的名称创建,并附加到 src
的节点上。它将指定 PNG 文件中的图像数据作为 ColorCameraProto
消息发布,就像它们来自真实的相机一样。您可以指定要从中加载数据的图像以及其他几个参数(尽管在本示例中这些参数并不重要)以及消息发布的频率。有关详细信息,请参阅 ISAAC 消息_ generators . ImageLoader 。
component_src.config['color_filename'] = '/home/bob/Pictures/panda.png' component_src.config['focal_length'] = [35.0, 35.0] component_src.config['optical_center'] = [300.0, 400.0] component_src.config['tick_period'] = '1hz'
为了可视化摄像机图像, 彩色摄影机 代码将有所帮助。类似地,您可以使用以下代码创建它的实例:
node_sink = app.add('sink') component_sink = \ node_sink.add(app.registry.isaac.viewers.ColorCameraViewer, 'ColorCameraViewer')
数据应该在代码之间流动。但是,必须在它们之间建立连接才能使数据流动。从文档中可以看出, CameraGenerator
有三个输出通道。选择 ColorCameraProto
消息发布到的 color _ left 频道。类似地,您可以看到 ColorCameraViewer
代码从 color_listener
通道读取消息。连接它们(称为“边缘”):
app.connect(component_src, 'color', component_sink, 'color_listener')
再次运行应用程序( mybot.py
),并检查 Sight 是否有图像。
ISAAC SDK 允许您在应用程序中使用多个数据源:
在本节中,我们将解释如何在 Python 应用程序中使用这些数据类型。
ISAAC SDK 支持许多带有 V4L2Camera
codelet 的 USB 摄像头。
在本节中,您将使用 Realsense 摄像头,它由 realsense
模块中的 RealsenseCamera
代码集支持。类似地,你可以从视线中看到摄像机镜头,就像之前的图像一样。因为这是对 ImageLoader
的直接替换,所以可以使用命令行参数在它们之间进行切换。有关如何在 Python 中处理命令行参数的更多信息, argparse- 命令行选项、参数和子命令的解析器 。您将得到如下代码示例:
parser = argparse.ArgumentParser(description='Sample Python API app') parser.add_argument('--source', type=str, dest='source', help='The source to get data from', choices=['camera', 'image'], default='camera') args, _ = parser.parse_known_args() if args.source == 'image': app.load_module('message_generators') component_src = \ node_src.add(app.registry.isaac.message_generators.ImageLoader, 'src') component_src.config['color_filename'] = '/home/bob/Pictures/panda.png' component_src.config['focal_length'] = [35.0, 35.0] component_src.config['optical_center'] = [300.0, 400.0] component_src.config['tick_period'] = '1hz' app.connect(component_src, 'color', viewer_component, 'color_listener') elif args.source == 'camera': app.load_module('realsense') camera = app.add("cam").add(app.registry.isaac.RealsenseCamera) camera.config.rows = 480 camera.config.cols = 640 camera.config.color_framerate = 30 camera.config.depth_framerate = 30 app.connect(camera, 'color', viewer_component, 'color_listener') app.run()
如前所示, PythonAPI 提供了处理不同环境的灵活性。
使用真实的传感器数据是直观的,但是这样做并不总是实际可行的。木桶能帮上忙。 Cask 是用于记录 ISAAC SDK 中的消息的格式。在 ISAAC SDK 中,可以记录一个消息流,并在以后回放以用于调试或分析。要从 Realsense 摄像头录制图像流,请尝试在 //apps/samples/camera:record_realsense
运行示例应用程序。
有了录音桶,你可以在没有真正的传感器的情况下随时随地重放和播放消息流。假设记录的容器位于 /home/bob/cask/
的文件夹中。您可以使用 Replay
代码来检索消息:
player_node = app.add('player') player_component = player_node.add(app.registry.isaac.alice.Replay) player_component.config['cask_directory'] = '/home/bob/cask' app.connect(player_component, 'color', viewer_component, 'color_listener')
类似地,您可以向 Python 应用程序添加一个 cask 作为一个可能的选项,就像您使用 realseness camera 一样。这里,名称 color
是用于记录彩色相机图像流的通道名称。然后你就可以查看视频流,就好像它来自真实的摄像机一样。
如前所述,您已经有了一个 Python 应用程序,可以在记录的传感器数据( cask )和真实传感器数据( camera )之间切换。现在再添加一个可能的数据源:来自 ISAAC Sim Unity3D 的模拟传感器。将 Sim 选项添加到命令行参数源,并将模拟相机消息流连接到查看器以进行可视化,如以下代码示例所示:
parser.add_argument('--source', type=str, dest='source', help='The source to get data from', choices=['cask', 'camera', 'image', 'sim'], default='sim') … if args.source == 'image': ... elif args.source == 'cask': ... elif args.source == 'camera': ... elif args.source == 'sim': app.load('packages/navsim/apps/navsim_tcp.subgraph.json') app.connect('interface/output', 'color', 'viewer/ColorCameraViewer', 'color_listener') app.run()
检索 ISAAC 是 Unity3D 。有关详细信息,请参阅 ISAAC Sim Unity3D 。
启动 ISAAC 是 Unity3D :
./build/sample.x86_64 --scene medium_warehouse
默认情况下, Python 应用程序尝试与同一主机上的模拟对话。如果应用程序正在另一台主机上运行,请为组件 interface/output
配置相应的主机参数。有关详细信息,请参阅 isaac.alice.TcpSubscriber 。运行该应用程序,模拟摄像机的镜头可以看到。
镜头来自安装在模拟机器人上的模拟摄像机。试着在模拟图形用户界面( GUI )上玩一些可移动的物体,比如纳米盒子,看看模拟相机的工作原理和真相机一样。
正如您所注意到的,组件(从代码创建的实例)和连接它们的边组成了一个图形。这样的图可以从 JSON 文件加载,也可以根据需要从 Python 应用程序加载。有关详细信息,请参阅 MIG 。
例如,模拟通信子图 packages/navsim/apps/navsim_tcp.subgraph.json
封装了用于使用 TCP 与 ISAAC Sim Unity3D 或 NVIDIA Omniverse 通信的节点、组件和边。要使其对应用程序可用,请将以下 Bazel 数据依赖项添加到您先前创建的生成文件中:
data = [ "//packages/navsim/apps:navsim_tcp_subgraph", ],
在 Python 应用程序中,可以使用以下命令加载它:
app.load('packages/navsim/apps/navsim_tcp.subgraph.json')
子图组件更像是由一组节点组成的配方,而不是容器。加载子图更像是按照配方创建节点和组件。在视图中,您可以看到从子图(图 4 )创建的所有节点(场景管理器、接口)。通过将它们与其他节点连接,可以使用 app.connect
创建更复杂的应用程序。
当加载多个子图时,命名冲突 MIG ht 会发生,因为在任何应用程序中,节点都需要具有唯一的名称。若要避免此类冲突,请使用另一个参数加载子图:
app.load( 'packages/navsim/apps/navsim_tcp.subgraph.json', 'simulation', )
这里,第二个参数是 JSON 文件中指定的所有节点的“ node name prefix ”。例如, packages/navsim/apps/navsim_tcp.subgraph.json
文件指定一个名为 interface
的节点。前面的语句将创建一个名为 simulation.interface
的节点,而不是 interface
。
现在有了一个 Python 应用程序。在真正的 Jetson 板上运行只需要一个命令:
./engine/build/deploy.sh -h -p //apps/mybot:mybot-pkg -d jetpack43
有关将应用程序部署到 Jetson 的更多信息,请参阅 入门 和 在 Jetson 上部署和运行 。
使用 SSH 连接到您的 Jetson 板或从 GUI 打开一个终端并检查文件夹 /home/nvidia/deploy/bob/mybot-pkg
。如果您在 Jetson 和开发设置上使用不同的用户名,请将 nvidia
替换为 Jetson 板上的用户名,将 bob 替换为开发设置上的用户名。
使用以下命令在 Jetson 上运行应用程序:
nvidia@Jetson:~/deploy/bob/mybot-pkg$ ./run apps/mybot/mybot.py
如果您正在使用存储库之外的资源,请考虑将它们添加到应用程序的 Bazel 依赖项中,以便可以使用 deploy.sh
将它们与应用程序一起自动部署到 deploy.sh
。
如果您在 PC 机上使用 ISAAC Sim Unity3D ,则可以正常通信。
因为这里有 pythonapi ,所以 Jupyter 笔记本肯定可以工作。将以下生成文件与空的 mybot.ipynb
一起使用:
isaac_jupyter_app( name = "mybot", modules = [ "message_generators", "viewers", ], notebook = "mybot.ipynb", )
以类似于 Jetson 板或 X86 工作站的方式部署应用程序,并使用以下命令启动 Jupyter :
jupyter notebook apps/mybot/mybot.ipynb
你现在可以走了。 run 函数被阻塞,只有在 robotic 应用程序停止时才返回。要以交互方式使用 robotics 应用程序,请相应地使用 start 和 stop 函数。
在使用模拟传感器工作的部分,有一个模拟机器人,上面安装了模拟摄像机。 ISAAC Sim Unity3D 是用来模拟移动机器人的。对于在 ISAAC Sim Unity3D 中控制带有差分基座的模拟机器人的示例 robot 应用程序,请检查 //apps/navsim:navsim_navigate
中的应用程序。组件和子图可以在 JSON 文件 apps/navsim/navsim_navigate.app.json
中找到。有关详细信息,请参见 ISAAC Sim Unity3D 。
除了移动机器人, 用 Jupyter 笔记本电脑进行简单的联合控制 SDK 也可以用于构建机器人手臂的应用程序。有了 Omniverse ISAAC ,你可以在没有真正硬件的情况下使用模拟机械手臂。有关更多信息,请按照 用 Jupyter 笔记本电脑进行简单的联合控制 上的“ UR10 in 用 Jupyter 笔记本电脑进行简单的联合控制 用 Jupyter 笔记本电脑进行简单的联合控制 Sim ”会话说明操作。应用程序将在模拟中控制机械臂,如图 5 所示。
以下是应用程序中发生的情况。第一件事是加载一个子图,允许通过 TCP 与模拟器通信。
app.load(filename="packages/navsim/apps/navsim_tcp.subgraph.json", prefix="simulation")
若要为关节生成平滑运动,请为节点加载另一个子图:
app.load( filename="packages/planner/apps/multi_joint_lqr_control.subgraph.json", prefix="lqr")
该子图封装 LQR 规划器的节点,生成当前关节状态和目标关节位置的命令。将仿真节点与规划器的节点连接起来,以使机械臂关节状态消息和命令消息在它们之间流动:
app.connect(simulation_node["output"], "joint_state", lqr_interface, "joint_state") app.connect(lqr_interface, "joint_command", simulation_node["input"], "joint_position")
用 Python 编码的代码集 PyCodelet JointPositionControl
从滑块读取目标关节位置值,并将这些值作为 CompositeProto
消息发布:
class JointPositionControl(Codelet): def start(self): self.tx = self.isaac_proto_tx("CompositeProto", "command") self._widget = CompositeWidget(self.config.joints "position", self.config.limits) def tick(self): self.tx._msg = self._widget.composite self.tx.publish()
有关详细信息,请参见 创建 Python 代码 。
然后, JointPositionControl
代码集连接到一个节点,并连接到 LQR planner 的目标输入通道:
widget_node = app.add("command_generator") joint_commander = widget_node.add(JointPositionControl) app.connect(joint_commander, "command", lqr_interface, "joint_target")
启动应用程序应用程序启动你可以随意使用手臂。
在 Omniverse ISAAC Sim 中,要有一个带摄像头的机械臂,请加载 stage omni:/Isaac/Samples/Isaac_SDK/Scenario/sortbot_sim.usd
。在 Omniverse ISAAC Sim 中启动仿真和机器人引擎桥,并将视口从透视切换到腕部摄影机,如图 6 和 7 所示。
与之前的应用程序一样,您可以将模拟摄影机通道连接到 ColorCameraViewer
代码板以可视化画面,并将 DepthCameraViewer
连接到可视化模拟深度传感器数据。
app.load_module("viewers") viewers = app.add("viewers") color_viewer = viewers.add(app.registry.isaac.viewers.ColorCameraViewer, "ColorViewer") app.connect(simulation_node["output"], "color", color_viewer, "color_listener") depth_viewer = viewers.add(app.registry.isaac.viewers.DepthCameraViewer, "DepthViewer") app.connect(simulation_node["output"], "depth", depth_viewer, "depth_listener") depth_viewer.config.max_visualization_depth = 3
启动应用程序,模拟的摄像机镜头应该会出现在眼前:
在本文中,您使用 pythonapi 从头创建了一个机器人应用程序。您使应用程序可以使用真实的摄影机、录制的摄影机数据和模拟摄影机。我们还向您展示了如何使用 pythonapi 处理模拟移动机器人和模拟机械臂。做机器人玩得开心!
关于作者
Yang Liu 是一名软件工程师,负责开发 NVIDIA ISAAC SDK 的各个部分。他获得了达拉斯德克萨斯大学计算机科学博士学位。
审核编辑:郭婷
全部0条评论
快来发表一下你的评论吧 !