粒子发射器#
发射器(emitter)是一个喷嘴,在仿真运行过程中把粒子注入粒子求解器。超越刚体 在构建时一次性填充固定体积的粒子,而发射器每一步都注入新粒子:像水龙头、软管或持续流动的沙流,只要你调用它,它就会一直喷出。
发射器本身不进行任何仿真。它拥有一个粒子实体,该实体用 max_particles 个槽位预分配,发射器把位置和速度写入该实体的求解器。发射器的材质决定由哪个求解器处理这些粒子:gs.materials.SPH.* 对应光滑粒子流体动力学,gs.materials.MPM.* 对应物质点法,gs.materials.PBD.* 对应基于位置的动力学。
完整脚本是 examples/coupling/water_wheel.py:
import numpy as np
import genesis as gs
gs.init()
scene = gs.Scene(
sim_options=gs.options.SimOptions(dt=4e-3, substeps=10),
sph_options=gs.options.SPHOptions(particle_size=0.02), # meters
)
scene.add_entity(gs.morphs.Plane())
emitter = scene.add_emitter(
material=gs.materials.SPH.Liquid(),
max_particles=100000,
surface=gs.surfaces.Glass(color=(0.7, 0.85, 1.0, 0.7)),
)
scene.build()
for i in range(500):
emitter.emit(
pos=(0.5, 1.0, 3.5), # nozzle position, meters, Z-up
direction=(0.0, 0.0, -1.0), # aimed straight down
speed=5.0, # m/s
droplet_shape="circle",
droplet_size=0.22, # stream diameter, meters
)
scene.step()
添加发射器#
scene.add_emitter() 必须像其他实体一样在 scene.build() 之前调用。它会分配粒子实体并返回一个 Emitter 句柄,供你每一步调用:
emitter = scene.add_emitter(
material=gs.materials.SPH.Liquid(),
max_particles=100000,
surface=gs.surfaces.Glass(color=(0.7, 0.85, 1.0, 0.7)),
)
material选择求解器和物理行为。可传入任何 MPM 或 SPH 材质(例如gs.materials.SPH.Liquid、gs.materials.MPM.Liquid或gs.materials.MPM.Sand),或gs.materials.PBD.Particle与gs.materials.PBD.Liquid之一。其他材质会在add_emitter时报错。max_particles限制发射器容纳的粒子数。一旦发射达到上限,发射器会回收最旧的粒子,因此长时间运行的流在内存上保持有界。默认值为20000。surface只控制外观。若省略,发射器使用gs.surfaces.Default(color=(0.6, 0.8, 1.0, 1.0))。发射器不支持vis_mode="visual";请使用默认的"particle",或用"recon"渲染重建的流体表面。
每步发射#
发射是逐步进行的:一次 emit() 调用注入流的一段,每一步都调用便形成连续流动。停止调用后流会中断,而已发射的粒子继续仿真。
emitter.emit(
pos=(0.5, 1.0, 3.5),
direction=(0.0, 0.0, -1.0),
speed=5.0,
droplet_shape="circle",
droplet_size=0.22,
)
pos 是世界坐标系中的喷嘴位置,单位为米(Z 轴向上)。direction 会在内部归一化,因此只有方向重要,长度无关。speed 是发射速度,单位为 m/s,同时设定每个粒子沿 direction 的初速度。
当你不设置 droplet_length 时,发射器会根据一步内的运动 speed * dt 计算每段长度。若该长度短于一个粒子,发射器会累积它,在积累足够之前不发射任何粒子,因此慢速流仍会形成完整粒子而不会被丢弃。
液滴形状#
droplet_shape 设置发射几何体的横截面;droplet_size 设置其范围,单位为米。
|
|
发射内容 |
|---|---|---|
|
|
沿 |
|
|
单个球形液滴;忽略 |
|
|
沿 |
|
|
沿 |
theta 以弧度为单位让液滴绕其发射轴旋转。p_size 覆盖采样粒子尺寸(默认为求解器的设置)。
随时间改变方向会让流弯曲。examples/coupling/sand_wheel.py 就以这种方式让沙流来回扫动:
for i in range(1000):
emitter.emit(
pos=(0.5, 0.0, 2.3),
direction=(0.0, np.sin(i / 10) * 0.35, -1.0), # oscillating aim
speed=8.0,
droplet_shape="rectangle",
droplet_size=[0.03, 0.05], # width, height in meters
)
scene.step()
全向发射#
emit_omni() 从球壳沿半径方向释放粒子,而不是从定向喷嘴喷出:这是一次爆发或喷泉,而非一股流。每个粒子的速度都以 speed 从中心指向外:
emitter.emit_omni(
pos=(0.5, 0.5, 1.0), # center of the source, meters
source_radius=0.1, # shell radius, meters
speed=2.0, # m/s, radially outward
)
多个发射器#
每个发射器拥有自己的粒子实体,因此可以按需添加多个:不同材质、位置和表面可在同一场景中共存。在同一步循环中对每个发射器调用 emit()。
emitter_a = scene.add_emitter(
material=gs.materials.MPM.Liquid(),
max_particles=500000,
surface=gs.surfaces.Rough(color=(0.0, 0.9, 0.4, 1.0)),
)
emitter_b = scene.add_emitter(
material=gs.materials.MPM.Liquid(),
max_particles=500000,
surface=gs.surfaces.Rough(color=(0.0, 0.4, 0.9, 1.0)),
)
for i in range(500):
emitter_a.emit(pos=(0.3, 0.5, 2.0), direction=(0, 0, -1), speed=3.0, droplet_shape="circle", droplet_size=0.1)
emitter_b.emit(pos=(0.7, 0.5, 2.0), direction=(0, 0, -1), speed=3.0, droplet_shape="circle", droplet_size=0.1)
scene.step()
注意事项与常见陷阱#
Warning
发射器必须位于其求解器的仿真域内。如果某次 emit() 调用会把粒子放到求解器边界之外,它会报错而不是静默裁剪。请把喷嘴对准 mpm_options / sph_options 设定的边界内部。
Note
发射器不支持可微分模式。以 requires_grad=True 构建场景后再添加发射器会抛出异常。