本文翻译自isaac gym中的说明文档 isaacgym/docs/programming/simsetup.html
目录
Isaac Gym 仿真流程图
模拟设置(Simulation Setup)
Isaac Gym提供了一个模拟接口,让您创建动态环境以训练智能代理。API是过程化和数据导向的,而不是面向对象的。这有助于在C++编写的核心实现和Python编写的客户端脚本之间进行高效的信息交换。例如,Isaac Gym使用平面数据数组来表示模拟的状态和控制,而不是使用体和关节对象的分层集合。这使得使用常见的Python库(如NumPy)处理数据变得容易,该库提供了操作多维数组的高效方法。还可以将模拟数据作为CPU或GPU张量访问,这些张量可以与PyTorch等常见深度学习框架共享。
核心API,包括支持的数据类型和常量,是在gymapi
模块中定义的:
from isaacgym import gymapi
所有的Gym API函数都可以作为一个单例Gym
对象的方法访问,该对象在启动时获取:
gym = gymapi.acquire_gym()
创建模拟(Creating a Simulation )
gym对象本身并没有做太多事情。它只是作为Gym API的代理。要创建一个模拟,你需要调用create_sim
方法:
sim_params = gymapi.SimParams()
sim = gym.create_sim(compute_device_id, graphics_device_id, gymapi.SIM_PHYSX, sim_params)
sim对象包含了物理和图形上下文,它们将允许您加载asset、创建环境,并与模拟进行交互。
create_sim
的第一个参数是计算设备序号,它选择用于物理模拟的GPU。
第二个参数是图形设备序号,它选择用于渲染的GPU。在多GPU系统中,您可以使用不同的设备来执行这些actor。对于headless模拟(没有显示),不需要任何传感器渲染,您可以将图形设备设置为-1,这样就不会创建图形上下文。
第三个参数指定了您希望使用的物理后端。目前,选择有SIM_PHYSX
或SIM_FLEX
。
-
PhysX后端提供了可在CPU或GPU上运行的稳健刚体和关节模拟。它目前是唯一完全支持新张量API的后端。
-
Flex后端提供了完全在GPU上运行的软体和刚体模拟,但它还不完全支持张量API。
create_sim
的最后一个参数包含了额外的模拟参数,下面将进行讨论。
模拟参数(Simulation Parameters)
模拟参数允许您配置物理模拟的细节。根据物理引擎和任务特征的不同,设置可能会有所不同。选择合适的参数对于模拟稳定性和性能很重要。下面的代码片段显示了物理参数的示例:
# get default set of parameters
sim_params = gymapi.SimParams()
# set common parameters
sim_params.dt = 1 / 60
sim_params.substeps = 2
sim_params.up_axis = gymapi.UP_AXIS_Z
sim_params.gravity = gymapi.Vec3(0.0, 0.0, -9.8)
# set PhysX-specific parameters
sim_params.physx.use_gpu = True
sim_params.physx.solver_type = 1
sim_params.physx.num_position_iterations = 6
sim_params.physx.num_velocity_iterations = 1
sim_params.physx.contact_offset = 0.01
sim_params.physx.rest_offset = 0.0
# set Flex-specific parameters
sim_params.flex.solver_type = 5
sim_params.flex.num_outer_iterations = 4
sim_params.flex.num_inner_iterations = 20
sim_params.flex.relaxation = 0.8
sim_params.flex.warm_start = 0.5
# create sim with these parameters
sim = gym.create_sim(compute_device_id, graphics_device_id, physics_engine, sim_params)
一些参数与物理引擎无关,如模拟速率、上轴或重力。其他参数只适用于特定的物理引擎(分组在.physx
和.flex
下)。如果模拟只能使用特定的物理引擎运行,那么不需要为其他引擎指定参数。为两个引擎提供参数将允许在运行时轻松选择引擎,例如根据命令行参数。有关模拟参数的更多信息,请参见:
上轴(Up Axis)
Isaac Gym支持y-up和z-up模拟。虽然z-up在机器人和研究社区中更为常见,但Gym出于历史原因默认使用y-up。这可能会在未来发生变化,但与此同时,创建z-up模拟并不困难。最重要的是,在创建模拟时配置SimParams
中的up_axis
和gravity
:
sim_params.up_axis = gymapi.UP_AXIS_Z
sim_params.gravity = gymapi.Vec3(0.0, 0.0, -9.8)
创建地面平面时,选择z-up需要特别注意,下面将进行讨论。
创建地面平面(Creating a Ground Plane)
大多数模拟都需要一个地面平面,除非它们发生在零重力的情况下。您可以像这样配置和创建地面平面:
# configure the ground plane
plane_params = gymapi.PlaneParams()
plane_params.normal = gymapi.Vec3(0, 0, 1) # z-up!
plane_params.distance = 0
plane_params.static_friction = 1
plane_params.dynamic_friction = 1
plane_params.restitution = 0
# create the ground plane[8]
gym.add_ground(sim, plane_params)
平面normal
参数定义了平面的方向,取决于上轴的选择。对于z-up,使用(0, 0, 1),对于y-up,使用(0, 1, 0)。可以指定一个不是轴对齐的法向量来获得一个倾斜的地面平面。distance
参数定义了平面距离原点的距离。static_friction
和dynamic_friction
是静摩擦系数和动摩擦系数。restitution
系数可以用来控制与地面平面碰撞的弹性(反弹量)。
加载Assets
Gym目前支持加载URDF和MJCF文件格式。加载assets文件会创建一个包含所有身体、碰撞形状、视觉附件、关节和自由度(DOFs)定义的GymAsset对象。某些格式还支持软体和粒子。
在加载Asset时,您需要指定Assets根目录和相对于根目录的Asset路径。这种分割是必要的,因为导入器有时需要在Asset目录树中搜索外部参考文件,例如网格或材质。Asset根目录可以指定为绝对路径或相对于当前工作目录的路径。在我们的Python示例中,我们这样加载Asset:
asset_root = "../../assets"
asset_file = "urdf/franka_description/robots/franka_panda.urdf"
asset = gym.load_asset(sim, asset_root, asset_file)
load_asset
方法使用文件名扩展名来确定Asset文件格式。支持的扩展名包括URDF文件的.urdf
和MJCF文件的.xml
。
有时,您可能希望向Asset导入器传递额外的信息。这可以通过指定可选的AssetOptions
参数来实现:
asset_options = gymapi.AssetOptions()
asset_options.fix_base_link = True
asset_options.armature = 0.01
asset = gym.load_asset(sim, asset_root, asset_file, asset_options)
导入选项会影响模型的物理和视觉特性,因此可能会影响模拟稳定性和性能。更多细节可以在assets章节中找到。
加载asset不会自动将其添加到模拟中。GymAsset
作为Actors的buleprint(可以用作创建某物的基础的设计或计划),可以在模拟中以不同的姿态和属性多次实例化。
环境和智能体(Environments and Actors)
examples/franka_cube_ik.py
的截图,使用PhysX在GPU上同时模拟了64个环境。1每个环境有三个actor:一个Franka手臂,一个桌子,和一个要拿起的盒子。2这些环境彼此物理上独立,但是为了方便监控,它们被一起渲染。使用PyTorch的基于张量的API来获取所有环境的状态和施加控制。
环境由一组相互模拟的actor和传感器组成。环境中的actor物理上相互作用。它们的状态由物理引擎维护,并可以使用后面讨论的控制API进行控制。放置在环境中的传感器,如摄像机,将能够捕捉到该环境中的actor。
Isaac Gym的一个重要设计方面是能够将多个环境实例打包到单个模拟中。这在诸如强化学习之类的应用领域非常重要,因为需要大量的运行来训练智能体执行某些任务。使用Isaac Gym,您可以同步运行数十个、数百个甚至数千个环境实例。您可以随机化每个环境中的初始条件,如布局、actor姿势,甚至actor本身。您可以随机化所有actor的物理、视觉和控制属性。
将多个环境打包到单个模拟中有几个优点,但整体原因是性能。许多场景实际上非常简单。例如,你可能有一个人形模型学习在平坦的地面上行走,或者一个关节式机械臂学习在柜子里拾取物品。运行模拟每个时间步骤都涉及一定的开销。通过将多个简单环境打包到单个模拟中,我们可以最小化这些开销,并增加整体的计算密度。这意味着相比于启动计算和收集结果所需的设置时间,更多的时间将花费在进行有意义的计算上。
Isaac Gym提供了一个简单的程序化API,用于创建环境并添加actor。这比仅从文件加载静态场景更有用,因为它允许你控制添加到场景中的所有actor的位置和属性。在添加actor之前,你必须创建一个环境:
spacing = 2.0
lower = gymapi.Vec3(-spacing, 0.0, -spacing)
upper = gymapi.Vec3(spacing, spacing, spacing)
env = gym.create_env(sim, lower, upper, 8)
每个环境都有自己的坐标空间,它嵌入在全局模拟空间中。创建环境时,我们指定环境的局部范围,该范围取决于环境实例之间的期望间隔。当新环境添加到模拟中时,它们将按行一次排列在一个2D网格中。create_env
的最后一个参数表示每行要打包的环境数量。
Actor只是GymAsset
的一个实例。要将actor添加到环境中,你必须指定源asset、期望位姿和其他一些细节:
pose = gymapi.Transform()
pose.p = gymapi.Vec3(0.0, 1.0, 0.0)
pose.r = gymapi.Quat(-0.707107, 0.0, 0.0, 0.707107)
actor_handle = gym.create_actor(env, asset, pose, "MyActor", 0, 1)
每个actor必须放置在一个环境中。不能有不属于环境的actor。actor的姿势在环境本地坐标系中定义,使用位置矢量p
和方向四元数r
。在上面的代码中,方向是由四元数(-0.707107,0.0,0.0,0.707107)指定的。Quat
构造函数按照(x
,y
,z
,w
)的顺序接受参数,因此该四元数表示绕x轴旋转-90度。当将使用z-up约定定义的asset加载到使用y-up约定的模拟中时,这样的旋转是必要的。Isaac Gym提供了一组方便的数学辅助工具,包括四元数工具,因此可以使用轴角形式定义四元数,如下所示:
pose.r = gymapi.Quat.from_axis_angle(gymapi.Vec3(1, 0, 0), -0.5 * math.pi)
有关数学工具的更多信息,请参阅[数学工具]
在姿势之后的create_actor
参数是可选的。你可以为actor指定一个名称,在此示例中为“MyActor”。这使得在以后通过名称查找actor成为可能。如果你想要这样做,请确保在同一环境中为所有actor分配唯一的名称。因为名称是可选的,Isaac Gym不强制唯一性。而不是通过名称查找actor,你可以保存create_actor
返回的actor句柄。该句柄在其环境中唯一标识actor,并避免了以后搜索它时的计算成本。
参数列表的最后两个整数是 collision_group 和 collision_filter。这些值在actor的物理模拟中起着重要作用。
-
collision_group 是一个整数,用于标识将actor体分配到的碰撞组。只有属于同一个碰撞组的两个体才会发生碰撞。通常情况下,每个环境有一个碰撞组,此时组ID对应于环境的索引。这可以防止不同环境中的actor在物理上相互作用。在某些情况下,您可能希望每个环境设置多个碰撞组,以便进行更细粒度的控制。值-1用于特殊的碰撞组,它与所有其他组发生碰撞。这可以用于创建可以与所有环境中的actor发生物理交互的“共享”对象。
-
collision_filter 是一个位掩码,允许您过滤掉体之间的碰撞。如果两个体的碰撞过滤器具有一位相同的设置,则它们不会发生碰撞。这个值可以用于过滤多体actor中的自碰撞,或者防止场景中的某些物体发生物理交互。
在设置模拟时,您可以使用一个循环初始化所有环境:
# 设置环境网格
num_envs = 64
envs_per_row = 8
env_spacing = 2.0
env_lower = gymapi.Vec3(-env_spacing, 0.0, -env_spacing)
env_upper = gymapi.Vec3(env_spacing, env_spacing, env_spacing)
# 缓存一些常用的句柄以供后续使用
envs = []
actor_handles = []
# 创建和填充环境
for i in range(num_envs):
env = gym.create_env(sim, env_lower, env_upper, envs_per_row)
envs.append(env)
height = random.uniform(1.0, 2.5)
pose = gymapi.Transform()
pose.p = gymapi.Vec3(0.0, height, 0.0)
actor_handle = gym.create_actor(env, asset, pose, "MyActor", i, 1)
actor_handles.append(actor_handle)
在上面的代码片段中,所有的环境都包含相同类型的actor,但是actor的生成高度是随机的。需要注意的是,每个actor的碰撞组都对应于环境的索引,这意味着来自不同环境的actor在物理上不会相互交互。在构建环境时,我们还将一些有用的信息缓存在列表中,以便在后续的代码中轻松访问。
目前,设置环境的过程API有一些限制。假设所有的环境都是按顺序创建和填充的。你创建一个环境并将所有的actor添加到其中,然后创建另一个环境并将其所有的actor添加到其中,依此类推。一旦你完成了一个环境的填充并开始填充下一个环境,你就不能再向之前的环境中添加actor了。这与内部数据的组织方式有关,以便为状态缓存实现高效的批次索引方案。这个限制可能在将来会被解除,但现在要注意这些限制。这意味着你不能在设置完一个环境之后向其添加或删除actor。有一些方法可以“伪造”一个环境中动态数量的actor,这将在另一节中讨论。
运行模拟(Running the Simulation)
设置好环境网格和其他参数后,可以开始模拟。通常情况下,可以使用循环来进行模拟,每次循环迭代对应一个时间步长:
while True:
# step the physics
gym.simulate(sim)
gym.fetch_results(sim, True)
Isaac Gym提供了多种方式来查询世界状态和应用控制。您还可以收集传感器快照并与外部学习框架进行连接。这些主题将在后续章节中讨论。
添加查看器(Adding a Viewer)
默认情况下,模拟不会创建任何可视化反馈窗口。这允许在没有连接显示器的无头工作站或集群上运行模拟。然而,在开发和测试过程中,能够可视化模拟是很有用的。Isaac Gym带有一个简单的集成查看器,让您可以看到模拟的情况。
您可以像这样创建一个查看器:
cam_props = gymapi.CameraProperties()
viewer = gym.create_viewer(sim, cam_props)
这将弹出一个具有默认尺寸的窗口。您可以通过自定义 isaacgym.gymapi.CameraProperties
来设置不同的尺寸。
为了更新查看器,在每次模拟循环迭代期间,您可以执行以下代码:
gym.step_graphics(sim)
gym.draw_viewer(viewer, sim, True)
step_graphics
方法将模拟的可视化表示与物理状态同步。draw_viewer
方法将最新的快照渲染到查看器中。它们是分开的方法,因为 step_graphics
可以在没有查看器的情况下使用,例如在渲染摄像头传感器时。
使用这段代码,查看器窗口将尽可能快地刷新。对于简单的模拟,这通常比实时更快,因为每个dt增量的处理速度将超过相应的实时经过的时间。为了将可视化更新频率与实时同步,您可以在循环迭代的最后添加以下语句:
gym.sync_frame_time(sim)
这将使模拟速率降低到实时。如果模拟速度比实时速度慢,这个语句将不起作用。
如果您希望在查看器窗口关闭时终止模拟,您可以将循环条件设置为 query_viewer_has_closed
方法,该方法在用户关闭窗口后将返回True。
一个基本的包含查看器的模拟循环如下:
while not gym.query_viewer_has_closed(viewer):
# 更新物理仿真
gym.simulate(sim)
gym.fetch_results(sim, True)
# 更新视图器
gym.step_graphics(sim);
gym.draw_viewer(viewer, sim, True)
# 等待 dt 时间过去,进行实时同步
# 这样可以将物理仿真与渲染频率同步
gym.sync_frame_time(sim)
当视图器处于焦点状态时,可以按下 F11 键切换到全屏模式。
视图器 GUI(The Viewer GUI)
创建视图器时,会在屏幕左侧显示一个简单的图形用户界面(GUI)。可以使用“Tab”键切换 GUI 的显示和隐藏。
GUI 有 4 个不同的标签:Actors(智能体)、Sim(仿真)、Viewer(视图器)和 Perf(性能)。
-
Actors 标签提供选择环境和环境中的actor的能力。当前选定的actor有三个子标签。
-
Bodies 子标签提供了关于激活actor刚体的信息。还可以更改actor体的显示颜色并切换体轴的可视化。
-
DOFs 子标签显示了激活actor的自由度的信息。可以使用用户界面编辑自由度属性,请注意这是一个实验性功能。
-
Pose Override 子标签可以使用自由度手动设置actor的姿势。当启用此功能时,所选actor的姿势和驱动目标将被用户界面中滑块设置的值覆盖。这是一个有用的工具,可以通过交互式地探索或操作actor的自由度来使用它。
-
-
Sim 标签显示了物理仿真参数。参数根据仿真类型(PhysX 或 Flex)而有所不同,并可由用户修改。
-
Viewer 标签允许自定义常见的可视化选项。值得注意的一个功能是可以在查看物体的图形表示和物理形状之间切换。这在调试物理行为时可能会有帮助。
-
Viewer选项卡允许自定义常见的可视化选项。一个值得注意的功能是能够在查看物体的图形表示和物理引擎使用的物理形状之间切换。这在调试物理行为时非常有帮助。
-
Perf选项卡显示了gym的内部性能测量结果。顶部的滑块“Performance Measurement Window”指定了测量性能的帧数。帧率报告了在先前的测量窗口内的平均帧率(FPS)。其余的性能测量结果是每帧的平均值,根据指定的帧数进行报告。
-
Frame Time是从一个步骤开始到下一个步骤开始的总时间。
-
Physics simulation time是物理求解器运行的时间。
-
Physics Data Copy是用于复制模拟结果的时间量。
-
Idle time是闲置时间,通常在
gym.sync_frame_time(sim)
中进行。 -
Viewer Rendering Time是用于渲染和显示查看器的时间。
-
Sensor Image Copy time是将传感器图像数据从GPU复制到CPU所花费的时间。
-
Sensor Image Rendering time是将相机传感器渲染到GPU缓冲区所花费的时间,不包括查看器相机。
-
自定义鼠标/键盘输入(Custom Mouse/Keyboard Input)
要从查看器获取鼠标/键盘输入,可以订阅并查询操作事件。有关如何执行此操作的示例,请参考examples/projectiles.py
:
gym.subscribe_viewer_keyboard_event(viewer, gymapi.KEY_SPACE, "space_shoot")
gym.subscribe_viewer_keyboard_event(viewer, gymapi.KEY_R, "reset")
gym.subscribe_viewer_mouse_event(viewer, gymapi.MOUSE_LEFT_BUTTON, "mouse_shoot")
...
while not gym.query_viewer_has_closed(viewer):
...
for evt in gym.query_viewer_action_events(viewer):
...
清理(Cleanup)
在退出时,应按以下方式释放sim和viewer对象:
gym.destroy_viewer(viewer)
gym.destroy_sim(sim)