系列文章目录
前言
一、安装
SAPIEN 通过 PyPI 发布
1.1 依赖关系
Python 版本:
Python 3.8、3.9、3.10、3.11、3.12
操作系统: Linux
Linux Ubuntu 18.04+, Centos 7+
Windows (实验版): Windows 10,11
硬件
渲染: 英伟达或 AMD GPU
光线追踪 英伟达™(NVIDIA®)RTX GPU 或 AMD 同等产品
光线追踪去噪 英伟达™(NVIDIA®)图形处理器
GPU 模拟 英伟达™(NVIDIA®)图形处理器
软件
光线追踪 英伟达™(NVIDIA®)驱动程序 >= 470
去噪 (OIDN) 英伟达驱动 >= 520
1.2 通过 Pip(PyPI) 或 Conda 安装
pip install sapien
1.3 安装夜间版本
可以通过 haosulab/SAPIEN 访问 SAPIEN 的最新开发版本。可以使用 pip install {url} 安装构建版,其中 url 是与您的 Python 版本相对应的 .whl 文件链接。
1.4 从源代码构建
由于最新的SAPIEN可通过每晚发布的版本获取,因此只有在为不支持的平台或开发平台构建时,才需要从源代码构建SAPIEN。
1.4.1 克隆 SAPIEN
git clone --recursive https://github.com/haosulab/SAPIEN.git
cd SAPIEN
1.4.2 在 Docker 中构建
虽然可以在 Linux 上构建 SAPIEN,但我们强烈建议使用 Docker 构建。我们强烈建议使用 Docker 构建。
./docker_build_wheels.sh
注意
docker_build_wheels.sh 默认为所有 Python 版本构建。要为特定版本构建,请输入版本号(例如,./docker_build_wheels.sh 310 用于 Python 3.10)
本地构建
对于 Windows 用户,应直接构建 SAPIEN。所需的环境包括最新的 Visual Studio 和所需 Python 版本的 Conda 环境。
python setup.py bdist_wheel
1.5 验证安装
服务器(无显示)
警告
此脚本将在当前工作目录下生成 sapien_offscreen.png。
您可以使用以下命令测试 SAPIEN 的屏幕外渲染效果
python -m sapien.example.offscreen
在没有显示屏的服务器上。它可能会生成有关显示的错误。您可以忽略这些警告。
如果 SAPIEN 安装正确。将在当前工作目录下生成以下图片,命名为 sapien_offscreen.png。
桌面(带显示屏)
您可以使用以下命令测试 SAPIEN 的屏幕渲染效果
python -m sapien.example.hello_world
该命令将打开一个查看器窗口,显示地面上的红色立方体。
二、Hello World
SAPIEN 提供用于构建物理仿真环境的 API。
在本教程中,您将学习以下内容:
- 创建仿真场景
- 设置可视化
- 运行仿真循环
完整脚本可在此处下载 hello_world.py
2.1 模拟场景
要使用 SAPIEN 进行模拟,首先需要创建一个场景。
scene = sapien.Scene() # Create an instance of simulation world (aka scene)scene.set_timestep(1 / 100.0) # Set the simulation frequency
场景是模拟世界的一个实例。调用构造函数 sapien.Scene 可以创建多个场景,并且这些场景是独立的。
默认情况下,SAPIEN 使用 PhysX 5 模拟刚体。scene.set_timestep 设置调用 scene.step 时物理模拟器前进的秒数。
2.2 添加刚体
到目前为止,我们的场景是空的。
在 SAPIEN 中,场景包含一个实体列表。实体的行为由附加到实体上的组件列表决定。在 SAPIEN 中,我们将具有物理行为的实体称为演员。例如,一个附有刚体组件的实体就是一个演员。让我们在场景中添加两个角色:地面和盒子。演员的创建将在 “创建演员 ”中详细说明。
scene.add_ground(altitude=0) # Add a groundactor_builder = scene.create_actor_builder()actor_builder.add_box_collision(half_size=[0.5, 0.5, 0.5])actor_builder.add_box_visual(half_size=[0.5, 0.5, 0.5], material=[1.0, 0.0, 0.0])box = actor_builder.build(name="box") # Add a boxbox.set_pose(sapien.Pose(p=[0, 0, 0.5]))
2.3 查看器
查看器创建一个窗口(图形用户界面)来渲染模拟世界。只有连接显示器后才能使用。显示器可以是物理的(如显示器),也可以是虚拟的(如 VNC)。图形用户界面的使用方法将在 Viewer 中详细说明。
viewer = scene.create_viewer() # Create a viewer (window)# The coordinate frame in Sapien is: x(forward), y(left), z(upward)# The principle axis of the camera is the x-axisviewer.set_camera_xyz(x=-4, y=0, z=2)# The rotation of the free camera is represented as [roll(x), pitch(-y), yaw(-z)]# The camera now looks at the originviewer.set_camera_rpy(r=0, p=-np.arctan2(2, 4), y=0)viewer.window.set_camera_parameters(near=0.05, far=100, fovy=1)
注释
如果只需要模拟和渲染,如收集政策学习经验,则不需要图形用户界面。
2.4 模拟循环
建立模拟世界后,实际模拟将在循环中进行。每迭代一次,场景模拟一步,并向渲染器更新世界。查看器调用渲染器更新屏幕上的结果。
while not viewer.closed: # Press key q to quitscene.step() # Simulate the worldscene.update_render() # Update the world to the rendererviewer.render()
2.5 完整脚本
import sapien as sapien
from sapien.utils import Viewer
import numpy as npdef main():scene = sapien.Scene() # Create an instance of simulation world (aka scene)scene.set_timestep(1 / 100.0) # Set the simulation frequency# NOTE: How to build (rigid bodies) is elaborated in create_actors.pyscene.add_ground(altitude=0) # Add a groundactor_builder = scene.create_actor_builder()actor_builder.add_box_collision(half_size=[0.5, 0.5, 0.5])actor_builder.add_box_visual(half_size=[0.5, 0.5, 0.5], material=[1.0, 0.0, 0.0])box = actor_builder.build(name="box") # Add a boxbox.set_pose(sapien.Pose(p=[0, 0, 0.5]))# Add some lights so that you can observe the scenescene.set_ambient_light([0.5, 0.5, 0.5])scene.add_directional_light([0, 1, -1], [0.5, 0.5, 0.5])viewer = scene.create_viewer() # Create a viewer (window)# The coordinate frame in Sapien is: x(forward), y(left), z(upward)# The principle axis of the camera is the x-axisviewer.set_camera_xyz(x=-4, y=0, z=2)# The rotation of the free camera is represented as [roll(x), pitch(-y), yaw(-z)]# The camera now looks at the originviewer.set_camera_rpy(r=0, p=-np.arctan2(2, 4), y=0)viewer.window.set_camera_parameters(near=0.05, far=100, fovy=1)while not viewer.closed: # Press key q to quitscene.step() # Simulate the worldscene.update_render() # Update the world to the rendererviewer.render()if __name__ == "__main__":main()
三、创建角色
添加到 SAPIEN 场景中的每个对象都是一个实体。具有物理属性的实体通常称为演员。
在本教程中,您将学习以下内容:
- 创建实体并添加组件
- 使用基元(方框、球体、胶囊)创建刚体角色
- 使用网格文件创建刚体角色
- 使用姿势设置实体的姿势
完整的脚本可在此处下载 create_actors.py。香蕉的碰撞网格和可视化网格可在 collision.obj 和 visual.glb 中找到。
3.1 创建实体
实体的唯一属性包括姿势(位置和旋转)和名称。实体的行为完全由其附加组件决定。PhysxRigidDynamicComponent 可使实体在 PhysX 模拟器中遵循刚体动力学。渲染体组件(RenderBodyComponent)可让相机渲染该实体。
以下代码展示了我们如何创建实体、设置其姿势和名称、附加物理组件和渲染组件并将其添加到场景中。
def create_box(scene: sapien.Scene,pose: sapien.Pose,half_size,color=None,name="",
) -> sapien.Entity:"""Create a box.Args:scene: sapien.Scene to create a box.pose: 6D pose of the box.half_size: [3], half size along x, y, z axes.color: [4], rgbaname: name of the actor.Returns:sapien.Entity"""entity = sapien.Entity()entity.set_name(name)entity.set_pose(pose)# create PhysX dynamic rigid bodyrigid_component = sapien.physx.PhysxRigidDynamicComponent()rigid_component.attach(sapien.physx.PhysxCollisionShapeBox(half_size=half_size, material=sapien.physx.get_default_material()))# create render body for visualizationrender_component = sapien.render.RenderBodyComponent()render_component.attach(# add a box visual shape with given size and rendering materialsapien.render.RenderShapeBox(half_size, sapien.render.RenderMaterial(base_color=[*color[:3], 1])))entity.add_component(rigid_component)entity.add_component(render_component)entity.set_pose(pose)# in general, entity should only be added to scene after it is fully builtscene.add_entity(entity)# name and pose may be changed after added to scene# entity.set_name(name)# entity.set_pose(pose)return entity
注意
碰撞形状并不一定与视觉形状相对应。例如,您可能需要一个简单的碰撞形状来进行快速模拟,但却需要一个复杂的视觉形状来进行逼真的渲染。
世界坐标系中方块的姿态可由 Pose 指定。Pose 描述了一个 6 维姿势,由一个 3 维位置矢量 p 和一个 4 维四元数 q(按 wxyz 约定表示旋转)组成。
3.2 用演员生成器创建演员
使用底层实体 API 创建角色似乎有点繁琐,因此我们提供了一个方便的 ActorBuilder 类,用于创建角色。用下面的代码就可以创建同一个盒子。
def create_box_v2(scene: sapien.Scene,pose: sapien.Pose,half_size,color=None,name="",
) -> sapien.Entity:"""Create a box.Args:scene: sapien.Scene to create a box.pose: 6D pose of the box.half_size: [3], half size along x, y, z axes.color: [3] or [4], rgb or rgbaname: name of the actor.Returns:sapien.Entity"""half_size = np.array(half_size)builder: sapien.ActorBuilder = scene.create_actor_builder()builder.add_box_collision(half_size=half_size) # Add collision shapebuilder.add_box_visual(half_size=half_size, material=color) # Add visual shapebox: sapien.Entity = builder.build(name=name)box.set_pose(pose)return box
除方框外,SAPIEN 支持的原始形状还包括球体、胶囊和圆柱体。代码中包含创建球体和胶囊的示例代码。圆柱体是一个特殊的基元,因为 PhysX 本身不支持圆柱体碰撞基元。我们使用凸网格来实现圆柱体碰撞。
3.3 使用多个基元创建演员
接下来,我们将举例说明如何通过多个方块(带四条腿的桌面)创建一个角色(桌子)。
def create_table(scene: sapien.Scene,pose: sapien.Pose,size,height,thickness=0.1,color=(0.8, 0.6, 0.4),name="table",
) -> sapien.Entity:"""Create a table (a collection of collision and visual shapes)."""builder = scene.create_actor_builder()# Tabletoptabletop_pose = sapien.Pose([0.0, 0.0, -thickness / 2]) # Make the top surface's z equal to 0tabletop_half_size = [size / 2, size / 2, thickness / 2]builder.add_box_collision(pose=tabletop_pose, half_size=tabletop_half_size)builder.add_box_visual(pose=tabletop_pose, half_size=tabletop_half_size, material=color)# Table legs (x4)for i in [-1, 1]:for j in [-1, 1]:x = i * (size - thickness) / 2y = j * (size - thickness) / 2table_leg_pose = sapien.Pose([x, y, -height / 2])table_leg_half_size = [thickness / 2, thickness / 2, height / 2]builder.add_box_collision(pose=table_leg_pose, half_size=table_leg_half_size)builder.add_box_visual(pose=table_leg_pose, half_size=table_leg_half_size, material=color)table = builder.build(name=name)table.set_pose(pose)return table
我们可以调用 add_box_collision(pose=Pose(...),...)来设置演员坐标系中碰撞形状的姿势。同样,我们可以调用 add_box_visual(pose=Pose(...),...)来设置视觉形状。请注意,table.set_pose(pose) 设置的是演员在世界帧中的坐标系姿势。
3.4 从网格文件创建角色
除了基元,还可以通过网格文件创建角色。
builder = scene.create_actor_builder()builder.add_convex_collision_from_file(filename="../assets/banana/collision_meshes/collision.obj")builder.add_visual_from_file(filename="../assets/banana/visual_meshes/visual.glb")mesh = builder.build(name="mesh")mesh.set_pose(sapien.Pose(p=[-0.2, 0, 1.0 + 0.05]))
注意事项
SAPIEN 中动态刚体的任何碰撞形状都必须是凸形。为此,在模拟中使用之前,网格将被 “烹制 ”为凸网格。
3.5 删除实体
手动或通过演员生成器将实体添加到场景后,可以调用 scene.remove_entity(entity) 或 entity.remove_from_scene() 将其移除。移除的实体可以再次添加到场景中,但已在场景中的实体则不能再次添加。
四、物理学
由于 SAPIEN 是一个物理仿真框架,我们希望展示如何改变物理属性,从而产生不同的行为。
在本教程中,您将学习以下内容:
- 使用 PhysxSceneConfig 初始化默认物理属性
- 使用 PhysxMaterial 设置不同的物理材料
- 创建运动角色
- 为动作体启用阻尼
- 获取刚体的运动学量(姿态、速度、角速度
该示例展示了一个从斜坡上滑下的物体。计算姿势需要 transforms3d,可通过 pip install transforms3d 安装。
完整脚本可在此处下载 physics.py
4.1 设置默认物理属性
创建场景时可以指定默认的物理属性。这些属性包括重力、静摩擦力、动摩擦力以及回复力(碰撞弹性)。
scene_config = sapien.physx.PhysxSceneConfig()# The default physical properties can be modified through sapien.SceneConfigprint(scene_config.gravity)scene_config.gravity = np.array([0.0, 0.0, args.gravity])sapien.physx.set_scene_config(scene_config)# SAPIEN's default physical material for PhysX can be modified at any time# It is not bound to a scenedefault_material = sapien.physx.get_default_material()print(default_material.static_friction)print(default_material.dynamic_friction)print(default_material.restitution)sapien.physx.set_default_material(static_friction=0.3, dynamic_friction=0.3, restitution=0.0)# by default, SAPIEN scene consists of PhysxSystem and RenderSystem# The PhysxSystem can take a PhysxSceneConfig to custom its behaviorscene = sapien.Scene([sapien.physx.PhysxCpuSystem(),sapien.render.RenderSystem(),
注意 PhysxSceneConfig 描述了场景的全局物理属性。但是,默认的物理材料是整个 SAPIEN 流程的全局材料。
4.2 设置物理材料
PhysxMaterial 描述 PhysX 刚体材料的物理(接触)属性(摩擦力和回复力)。可以在创建角色时在 ActorBuilder 中指定。如果没有提供,将使用由场景默认物理属性生成的默认物理材料。
# It is not bound to a scenedefault_material = sapien.physx.get_default_material()print(default_material.static_friction)print(default_material.dynamic_friction)print(default_material.restitution)sapien.physx.set_default_material(
其他一些物理属性,如密度,会直接提供给碰撞形状。我们将更新 Create Actors 中的 create_sphere 函数。
def create_sphere(scene: sapien.Scene,pose: sapien.Pose,radius,color=None,density=1000.0,physical_material: sapien.physx.PhysxMaterial = None,name='',
) -> sapien.Entity:"""Create a sphere."""builder = scene.create_actor_builder()builder.add_sphere_collision(radius=radius, material=physical_material, density=density)builder.add_sphere_visual(radius=radius, material=color)sphere = builder.build(name=name)sphere.set_pose(pose)return sphere
备注
SAPIEN 目前未对滚动阻力(摩擦)进行建模。
4.3 创建运动角色
现在,让我们创建一个斜坡。斜坡应该是一个运动对象,而不是动态对象。换句话说,它不能受外力影响。我们可以在创建角色时设置 is_kinematic=True。
def create_box(scene: sapien.Scene,pose: sapien.Pose,half_size,color=None,is_kinematic=False,density=1000.0,physical_material: sapien.physx.PhysxMaterial = None,name='',
) -> sapien.Entity:"""Create a box.Args:scene: sapien.Scene to create a box.pose: 6D pose of the box.half_size: [3], half size along x, y, z axes.color: [3] or [4], rgb or rgba.is_kinematic: whether an object is kinematic (can not be affected by forces).density: float, the density of the box.physical_material: physical material of the actor.name: name of the actor.Returns:sapien.Entity"""half_size = np.array(half_size)builder = scene.create_actor_builder()builder.add_box_collision(half_size=half_size, material=physical_material, density=density) # Add collision shapebuilder.add_box_visual(half_size=half_size, material=color) # Add visual shapeif is_kinematic:box = builder.build_kinematic(name=name)else:box = builder.build(name=name)box.set_pose(pose)return box
4.4 为演员设置阻尼
有时,您可能会模拟一些与(线性或角度)速度成正比的阻力,例如空气阻力。这可以通过设置角色的阻尼来实现。
rigid_component = actor.find_component_by_type(sapien.physx.PhysxRigidBodyComponent)rigid_component.set_linear_damping(args.linear_damping)
4.5 获取演员的运动量(姿势、速度
我们可以通过 get_pose()、get_linear_velocity() 和 get_angular_velocity()获取演员的运动量(姿势、线速度、角速度)。
print('step:', steps)print('Pose', actor.get_pose())print('Velocity', rigid_component.get_linear_velocity())
五、创建衔接
衔接是一组用特殊关节连接在一起的链接(每个链接的行为都类似于一个刚体)。例如,抽屉可以通过棱柱关节(滑块)与桌子连接,门可以通过反卷关节(铰链)与坐标系连接。机器人也是关节的一个实例。关节通常从 URDF XML 中加载,我们将在其他示例中看到这一点。本教程展示了如何通过 SAPIEN 编程创建关节。
关节衔接可以通过在关节上施加扭矩来控制。为了使关节运动达到预期状态,用户可以应用控制器,根据实际与预期之间的差异计算修正扭矩。
在本教程中,您将学习以下内容:
- 创建衔接
- 使用内置控制器控制衔接
- 获取衔接的运动学量
本示例说明了如何从零开始制作一辆可控玩具车。
完整脚本可在此处下载 create_articulations.py
创建根链接
在 SAPIEN 中,衔接被表示为一棵树。每个节点都是一个链接,每条边都表示子链接通过关节与父链接相连。要制作一辆玩具车,我们先从车身开始。