赞
踩
近期使用isaac gym,所以对一些使用进行汇总。
在isaac gym中,存在几个基础且常见的概念:gym, sim, env, actor, rigid
Gym: 在Isaac Gym中,“Gym”通常指的是整个仿真平台或框架本身。它提供了创建和管理多个仿真环境的接口。
Sim: “Sim”是指模拟器(simulator),是仿真的核心组件。它负责处理物理计算和仿真的所有细节,如动力学、碰撞检测和其他物理交互。Isaac Gym使用NVIDIA PhysX作为其后端物理引擎,可以高效地在GPU上运行。在代码中的体现是,调用sim可以完成对模拟器的step。
Env: “Env”是指环境(environment),是智能体(agent)进行学习和互动的场所。每个环境包含了特定的任务或场景设置,智能体需要在这些环境中执行操作以获取奖励并学习策略。
Actor: “Actor”是在仿真中表示具有物理属性的对象,如机器人、物体等。每个Actor包括了用于描述其形状、质量、动力学属性等的各种参数。Actors是智能体与环境互动的主体,例如一个机器人的手臂或车辆。
Rigid: “Rigid”,刚体,是一种物理对象,其形状在仿真过程中不会发生变化。在Isaac Gym中,刚体用来表示那些不需要弹性或变形特性的实体。刚体动力学是计算这些对象如何在力和碰撞作用下移动和反应的基础。
Index / indcies: 这是一个很容易混淆的概念,特别是在多env多actor,每个actor拥有1个以上rigid时。理解index的索引获取和它代表的对象非常重要。
由此,可以得到isaac gym仿真环境的基本框架,即基于gym,使用sim进行仿真,在sim中可以生成多个env,每个env中可以生成多个actor作为交互对象。对于每个actor,可以由rigid或其他非刚体关系组成。
基础环境包括gym和sim,viewer的初始化,以下是添加ground plane和设置sim参数的代码:
- # initialize gym
- gym = gymapi.acquire_gym()
-
- # parse arguments
- args = gymutil.parse_arguments(description="Joint control Methods Example")
-
- # create a simulator
- sim_params = gymapi.SimParams()
- sim_params.substeps = 1
- sim_params.dt = opt.dt
-
- sim_params.physx.solver_type = 1
- sim_params.physx.num_position_iterations = 4
- sim_params.physx.num_velocity_iterations = 1
-
- sim_params.physx.num_threads = args.num_threads
- sim_params.physx.use_gpu = args.use_gpu
- sim_params.use_gpu_pipeline = args.use_gpu_pipeline
- if args.sim_device_type == 'cuda' and sim_params.use_gpu_pipeline:
- device = args.sim_device
- else:
- device = 'cpu'
-
- sim = gym.create_sim(args.compute_device_id, args.graphics_device_id, args.physics_engine, sim_params)
-
- if sim is None:
- print("*** Failed to create sim")
- quit()
-
- # create viewer using the default camera properties
- viewer = gym.create_viewer(sim, gymapi.CameraProperties())
- if viewer is None:
- raise ValueError('*** Failed to create viewer')
-
- # add ground plane
- plane_params = gymapi.PlaneParams()
- gym.add_ground(sim, gymapi.PlaneParams())
sim搭建好后就是声明env或envs。
使用gym.create_env声明env并获得env_handle,如果使用多个环境可添加handle到envs。
如:
- num_envs = 16
- num_per_row = int(np.sqrt(num_envs))
- spacing = 1.5 # actor 之间的间隔
- env_lower = gymapi.Vec3(-spacing, 0.0, -spacing)
- env_upper = gymapi.Vec3(spacing, 0.0, spacing)
-
- envs = []
- for i in range(num_envs):
- # create env
- env_handle = gym.create_env(sim, env_lower, env_upper, int(np.sqrt(num_envs)))
- envs.append(env_handle)
创建完成env后可以创建viewer:
- # create viewer
- mid = 0.5 * spacing * (num_per_row - 1)
- cam_pos = gymapi.Vec3(0, spacing, 0)
- cam_target = gymapi.Vec3(mid, 0, mid)
- gym.viewer_camera_look_at(viewer, None, cam_pos, cam_target)
这里的环境参数是以上内容中设定环境时可能使用的参数,调用gymutil和gymapi。
# parse arguments 这个函数句柄没有在handbook中找到,待议 args = gymutil.parse_arguments(description="Joint control Methods Example") 其中gymapi.SimParams()返回一个Gym Simulation Parameters的类,其中会包括FlexParams和PhysXParams两个子类用于设定详细参数。 sim_params.dt = 0.01 # 环境step时间为10ms sim_params.substeps = 2 # 模拟的子步数,暂不确定有什么用 sim_params.gravity = [0., 0., -9.81] sim_params.enable_actor_creation_warning sim_params.num_client_threads sim_params.flex # 更详细的仿真设置,参考FlexParams sim_params.up_axis = 1 # 设定坐标系的垂直坐标轴,0为y,1为z sim_params.use_gpu_pipeline sim_params.stress_visualization sim_params.stress_visualization_min sim_params.stress_visualization_max sim_params.physx.solver_type = 1 # sim_params.physx.num_position_iterations = 4 # sim_params.physx.num_velocity_iterations = 1 # sim_params.physx.num_threads = args.num_threads sim_params.physx.use_gpu = args.use_gpu
请参考https://github.com/leggedrobotics/legged_gym
举例,生成一个球体,需要用到代码获得球体预设,并将球体句柄加入到actor中。(此时为多环境)
- sphere_asset = self.gym.create_sphere(self.sim, radius, asset_options)
- # (self: Gym, env: Env, asset: Asset, pose: Transform, name: str = None, group: int = - 1, filter: int = - 1, segmentationId: int = 0)
- sphere_handle = self.gym.create_actor(env, sphere_asset, pos, "sphere", filter, 1) # in filter i
- actor_handles.append(sphere_handle)
其中,使用gymapi.AssetOptions(),对球体的部分参数进行设定,如:
- # Create the sphere asset with specific physical properties
- asset_options = gymapi.AssetOptions()
- asset_options.density = 530 # Set the density for the tennis ball
以及,使用gymapi.Transform(),对球体的位姿进行设定,如:
- initial_pose = np.random.uniform(-1, 1, size=3)
- initial_pose = torch.tensor(initial_pose, device=device)
- pos = gymapi.Transform()
- pos.p = gymapi.Vec3(*initial_pose)
- pos.r = gymapi.Quat(-0.707107, 0.0, 0.0, 0.707107)
以及,使用gymapi.RigidShapeProperties(),对于部分刚体参数的设置,如:
- # Set the restitution using RigidShapeProperties
- properties = gymapi.RigidShapeProperties() # 具有filter参数
- properties.restitution = 0.85 # Set the restitution for the tennis ball
- properties.filter = 0
- gym.set_actor_rigid_shape_properties(env, sphere_handle, [properties])
注意,以上的几个参数相互存在互补和重复,正确设定即可。
也可以调整球体的颜色:
- color = gymapi.Vec3(random.random(), random.random(), random.random())
- gym.set_rigid_body_color(env, actor_handle, 0, gymapi.MESH_VISUAL_AND_COLLISION, color)
在导入模型时,我们需要对模型的handle和index进行记录,以便于后续的数据索引,并避免重复检查索引,加速程序运算。
- self.actor_handles = []
- self.actor_idxs = []
- self.rigid_body_idxs = []
- self.envs = []
使用以下api获取index和handle。如果发现返回的idx或handle为-1,则代表未检索到对象。
- actor_handle = self.gym.create_actor(env_handle, robot_asset, start_pose, self.cfg.asset.name, i,self.cfg.asset.self_collisions, 0)
- self.envs.append(env_handle)
- self.actor_handles.append(actor_handle)
- actor_idx = self.gym.find_actor_index(env_handle, self.cfg.asset.name, gymapi.DOMAIN_SIM)
- self.actor_idxs.append(actor_idx)
Isaac gym手册中有大量的句柄搜索方法,此处我只列举了当前选出的较为合适的方法。由于这些概念较为抽象,请实际debug时进行查看以理解。
sim声明后的运行主要为以下流程:
- # after init and before loop
- gym.prepare_sim(sim)
-
- # start loop
- while not gym.query_viewer_has_closed(viewer):
- # step the physics
- gym.simulate(sim)
- gym.fetch_results(sim, True)
-
- # refresh tensors
-
- # take actions
-
- # update viewer
- gym.step_graphics(sim)
- gym.draw_viewer(viewer, sim, True)
- gym.sync_frame_time(sim)
-
-
- # end and clean up
- gym.destroy_viewer(viewer)
- gym.destroy_sim(myball.sim)
sim中的信息以isaac gym自有的tensor类型存储,不同于pytorch。为了确保获取实时的信息,使用以下句柄在读取tensor前对tensor进行刷新和维护:
- # refresh tensors
- gym.refresh_rigid_body_state_tensor(sim)
- gym.refresh_dof_state_tensor(sim)
- gym.refresh_jacobian_tensors(sim)
- gym.refresh_mass_matrix_tensors(sim)
Isaac gym中的状态tensor大致有几类:_root_tensor, rigid_body_tensor, contact_force_tensor. dof_tensor暂时不提及。
以上的tensor可以通过以下api获取:
- self._root_tensor = self.gym.acquire_actor_root_state_tensor(self.sim)
- _rb_states = self.gym.acquire_rigid_body_state_tensor(self.sim)
- _net_cf = self.gym.acquire_net_contact_force_tensor(self.sim)
获取tensor后,需要通过wrap_tensor操作来将isaac gym类型的tensor转化为torch类型的tensor。
- self.root_states = gymtorch.wrap_tensor(self._root_tensor)
- self.rb_states = gymtorch.wrap_tensor(_rb_states)
- self.contact_forces = gymtorch.wrap_tensor(_net_cf).view(self.num_envs, -1, 3)
从调用的sim句柄可以看出,此处获取的tensor索引(index)应该是sim的全局索引。
由于一次获取的是全部环境的rigid tensor,所以可以按照需要使用env对rigid进行数据切片:
- positions = []
- velocities = []
-
- for i, (env, ball_handle) in enumerate(zip(envs, actor_handles)):
- num_bodies = gym.get_actor_rigid_body_count(env, ball_handle)
- if num_bodies == 0:
- raise ValueError("The specified ball handle has no rigid bodies.")
- # body_index = gym.get_actor_rigid_body_handle(env, ball_handle, 0)
-
- pos = rb_states[i, 0:3].cpu().numpy()
- vel = rb_states[i, 7:10].cpu().numpy()
-
- positions.append(pos)
- velocities.append(vel)
注:以上代码仅适用与多环境单actor单rigid的情况。后续更新
对于gym.acquire_rigid_body_state_tensor的参数定义如下:
Retrieves buffer for Rigid body states.
The buffer has shape (num_rigid_bodies, 13).
State for each rigid body contains position([0:3]), rotation([3:7]), linear velocity([7:10]), and angular velocity([10:13]).
参考isaac gym的例程franka_ cube_ik_osc.py
其中,涉及使用函数
gym.get_actor_rigid_body_index,DOMAIN_SIM表示返回sim中的句柄,DOMAIN_ENV则是返回env中的句柄。
- box_idx = gym.get_actor_rigid_body_index(env, box_handle, 0, gymapi.DOMAIN_SIM)
- box_idxs.append(box_idx)
gym.find_actor_rigid_body_handle,查找给定名称的actor刚体句柄。
- hand_handle = gym.find_actor_rigid_body_handle(env, franka_handle, "panda_hand")
- hand_pose = gym.get_rigid_transform(env, hand_handle)
gym.find_actor_rigid_body_index,使用此函数可查找状态缓冲区中刚体的索引。
- hand_idx = gym.find_actor_rigid_body_index(env, franka_handle, "panda_hand", gymapi.DOMAIN_SIM)
- hand_idxs.append(hand_idx)
以上带有DOMAIN_SIM一类参数函数是部分查找函数的集合,所以有更多不同名称的调用方法,具体见handbook。
获取index后即可切片读取信息:
- box_pos = rb_states[box_idxs, :3]
- box_rot = rb_states[box_idxs, 3:7]
-
- hand_pos = rb_states[hand_idxs, :3]
- hand_rot = rb_states[hand_idxs, 3:7]
- hand_vel = rb_states[hand_idxs, 7:]
这里提及的碰撞检测使用
- net_contact_forces = self.gym.acquire_net_contact_force_tensor(self.sim)
- self.contact_forces = gymtorch.wrap_tensor(net_contact_forces).view(self.num_envs, -1, 3) # shape: num_envs, num_bodies, xyz axis
刷新使用
self.gym.refresh_net_contact_force_tensor(self.sim)
刷新后可以检测
base_force = torch.norm(self.contact_forces[:, self.termination_contact_indices, :], dim=-1)
在这里提一个很恶心的问题,文档的说明不多。
在CPU状态下,我们可以通过以下方法来设定全局的刚体状态,比如需要重置状态的时候。
- # Unwrap the tensor before setting it
- rb_states = gymtorch.unwrap_tensor(rb_states)
- ii = self.gym.set_rigid_body_state_tensor(self.sim, rb_states)
- print("vel", ii)
但是该方法不能在GPU环境下生效,(会返回false,但是程序运行不会报错)需要改为另一个方法:
-
- indices = torch.tensor(actor_idxs, device=self.device, dtype=torch.int32)
- self.gym.set_actor_root_state_tensor_indexed(self.sim,
- gymtorch.unwrap_tensor(rb_states),
- gymtorch.unwrap_tensor(indices), len(indices))
以上代码可以设定特定index的root_state状态,以下代码设定全部的状态:
self.gym.set_actor_root_state_tensor(self.sim, gymtorch.unwrap_tensor(self.rb_states))
在设定状态时,可能在gpu pipline遇见以下问题。
部分错误请详细阅读用户文档的:NVIDIA_Omniverse/ISAAC_Gym/isaacgym/docs/programming/tensors.html?highlight=set_dof_state_tensor_indexed
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。