Terrains

除了API提供的在仿真环境中添加平地面之外,我们还提供了用于生成不平坦地形的API和工具。可以使用gym.add_triangle_mesh()将地形添加为静态三角网格。我们在isaacgym/terrain_utils.py中提供了一些简单地形的生成工具。可以使用不同的高度地形类型生成:random_uniform_terrain()sloped_terrain()pyramid_sloped_terrain()discrete_obstacles_terrain()wave_terrain()stairs_terrain()pyramid_stairs_terrain()stepping_stones_terrain()。然后可以使用convert_heightfield_to_trimesh()将其转换为三角网格并添加到仿真中。

请参阅examples/terrain_creation.py以了解更多详细信息。

../_images/terrain.png

我们还在我们的AnymalTerrain环境中展示了使用不平坦地形的强化学习示例,可以在https://github.com/NVIDIA-Omniverse/IsaacGymEnvs找到。

在这里插入图片描述

网络接触力问题

目前已知刚体与三角网格碰撞的净接触力报告(gym.acquire_net_contact_force_tensor())不可靠。由于与三角形边缘的相互作用,报告的力可能会在某些时间步骤中被大大减小甚至完全丢失。净接触力报告将来会得到改进。作为解决方法,我们建议使用力传感器(gym.create_asset_force_sensor())或在环境中筛选报告的净接触力。

Frequently Asked Questions

Isaac Gym的范围非常广泛。它涉及物理模拟、强化学习、GPU并行化等等。在“内部”有很多内容,因此用户可能会对正在进行的具体情况或如何执行某些常见操作有疑问。在本部分中,我们回答了那些最常见的问题。

dt和substep有什么区别?

dt是模拟的时间增量。每次通过gym.simulate(...)步进模拟时,时间会按照dt前进。substep是将这个dt分成几个片段以进行模拟的步数。substep越多,时间切片越细,模拟的准确性就越高,但计算量也越大。例如:假设你的dt为1/60,substep为2。当你调用gym.simulate(...)时,物理会在1/60秒内前进两个1/120秒的时间步。你不能访问这些1/120秒时间步的结果,只能通过调用gym.fetch_results(...)获取1/60秒处的总结果。这可能是某些数值积分方法的结果。

以零阶为例,假设我们有一个函数f,我们知道f(0),并且知道一阶导数f’(0),那么我们可以的粗略估计未来一段时间的f

当 → 0时,该关系成为精确关系。然而,在现实中,永远不会等于0,并且我们始终会有某种形式的积分误差。假设 。这意味着f在f(1/2)处的值是f(0)的两倍,所以f在1处的精确结果应该是e,约为2.71828。如果我们在一个时间步长内逼近f(1)的话,我们得到1 + 1 * 1 = 2,比实际值低了25%左右。如果我们将这个时间步长分为两个子步长,我们得到

这与实际值相差约17%。改进了很多!Euler方法对dt的大小和选择的substep数敏感,即使Gym不使用Euler方法,即使是高级数值积分算法也对时间步长和所采取的子步长数敏感。你无法避免积分误差。

通常情况下,dt应远小于1,substep的数量应为小整数(2或4比较常见)。如果dt太大,模拟中会出现奇怪的行为(事物会爆炸)。如果substep太大,模拟将运行时间过长。

调用gym.simulate()时会发生什么?

当你调用gym.simulate(...)时,你是通过dt将模拟推进了一段时间。如上所述,为了精确性,这个模拟通常被划分为几个子步。为了节省内存,每个子步的模拟结果只会被临时保存,而在dt结束时的结果可以从设备中获取。

完全有可能,虚拟世界的模拟1秒所用的时间可能长于1秒的实际时间,但如果你使用的是现代硬件,除非你试图模拟整个海洋的粒子,否则这只会是代码中其他地方出现了一些问题的可能性很高。很可能,模拟1秒虚拟时间所用的实际时间会明显少于1秒。如果帧速率以尽可能快的速度渲染,那么世界中的东西看起来会比实际生活中的移动得更快。你可以使用gym.sync_frame_time(...) python绑定来减轻这个问题,它会将模拟与渲染帧率同步,并强制程序等待dt实际时间秒后继续运行。

Isaac Gym采用数据导向的方法,因此所有的角色和物体都是通过它们的handle访问的。目前,这些handle在每个环境中是唯一的,但这可能会在将来改变。目前,角色只“知道”它们环境中的其他角色和物体。这对传感器也是如此。如果你在环境1中渲染一个相机传感器,环境2中的所有物体都不会出现在渲染中。用户自行决定如何最好地管理这些handle,对于在单个环境中有多个角色并没有真正的后果。

访问角色、刚体和DOF的API函数调用都需要相应的角色handle、刚体handle和环境指针。例如,假设你有一个环境,其中有一个Franka Panda机械臂和一个柜子。假设你在设备上生成了几百个这样的机械臂。如果你想访问特定环境中特定Panda机器人的末端执行器,你需要末端执行器的handle、角色的handle和指向该环境的env指针。获得角色handle和env指针很容易,只需在构造时保存它们。获取末端执行器会有些棘手。我们知道末端执行器在asset文件中被称为“panda_hand”,所以我们可以使用gym.find_actor_rigid_body_handle(...)函数来获取该末端执行器的handle。

effector_handle = gym.find_actor_rigid_body_handle(env_ptr, franka_actor_handle, "panda_hand")

简而言之,在单个环境中处理多个角色是通过使用handle来实现的。

有几种方法可以实现这个目标。

首先,我们可以对关节施加扭矩来模拟。这需要几个步骤:我们需要关节的“齿轮”,功率比例尺度,要采取的行动向量,环境指针和演员句柄。通过调用gym.apply_actor_dof_efforts(...)函数来对演员施加扭矩。该函数接受环境指针,演员句柄和一个要施加的扭矩数组(efforts)。该数组的长度必须与演员中可活动关节的数量相匹配。这个力矩数组以Nm为单位,并且通常通过关节齿轮、功率比例以及一个行动向量(根据演员不同,可以是-1,1或0,1等浮点数的向量)的乘积来计算。关节的齿轮是可以施加到关节上的最大扭矩,而功率比例只是进一步缩放该值的某个因子。当调用gym.simulate(...)函数时,这些扭矩将被施加到关节上,从而导致角加速度等…

其次,我们可以为关节设置逆运动学目标。您可以使用gym.set_actor_dof_position_targets(...)来设置位置目标。当调用simulate时,演员关节将根据它们的关节约束和到目标位置的努力DOF参数移动。

第三,您可以手动设置演员的物体状态。**这可能非常危险!**为了实现这一点,您需要在目标状态中“尊重”关节约束,否则您可能会使演员爆炸。因此,在仅当您有一个想要重置到的目标状态的“缓存”时,(例如重置训练环境等)才应该使用这种方法。这可以通过调用gym.set_actor_rigid_body_states(...)来完成。此函数使用numpy结构化数组来定义目标刚体状态。这些绑定定义了可用于创建特定类型数据的numpy结构化数组的一组数据类型:GymRigidBodyState就是其中之一。它有两个字段,即“姿态”和“速度”,分别用于姿态和速度。姿态和速度实际上是GymTransform和GymVelocity,它们也是结构化数组(同样,如果可以避免的话,你永远不要手动设置这个)。该调用还需要一个gymapi状态枚举,该枚举定义了您希望使用此调用设置的确切状态数据,其中最常见的是gymapi.STATE_ALL,意味着您希望设置身体变换和速度。在调用gym.simulate(...)时,演员物体将立即传送到指定的状态。最佳的使用方法是调用numpy.copy(gym.get_actor_rigid_body_states(...))来保存状态,然后在以后的某个时间点使用“set”调用来重置到该状态。

最后,您可以调用gym.set_actor_dof_states(...)来手动设置DOFs(位置和速度)的状态。与上述方法一样,这也是非常危险的。使用此方法将关节重置为从之前复制并保存的缓存状态。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐