130 lines
4.7 KiB
Python
130 lines
4.7 KiB
Python
import omni.isaac.core.utils.prims as prim_utils
|
||
from omni.isaac.core.simulation_context import SimulationContext
|
||
from omni.isaac.core.utils.stage import get_current_stage
|
||
from pxr import UsdGeom, UsdPhysics, PhysxSchema, Gf, Sdf
|
||
|
||
# 引入 PhysX 粒子工具 (Omniverse 内置库)
|
||
import omni.physx.scripts.utils as physxUtils
|
||
import omni.physx.scripts.particleUtils as particleUtils
|
||
|
||
def create_fluid_scene():
|
||
stage = get_current_stage()
|
||
|
||
# 1. 初始化仿真环境
|
||
sim = SimulationContext()
|
||
# 确保物理频率适合流体 (流体通常需要更小的步长或更多的子步)
|
||
sim.set_simulation_dt(physics_dt=1.0 / 60.0, rendering_dt=1.0 / 60.0)
|
||
|
||
# 2. 创建粒子系统 (Particle System)
|
||
# 这是管理所有流体粒子的容器
|
||
particle_system_path = "/World/FluidSystem"
|
||
particleUtils.add_physx_particle_system(
|
||
stage=stage,
|
||
particle_system_path=particle_system_path,
|
||
simulation_owner=None,
|
||
radius=0.02, # 粒子半径(决定流体分辨率)
|
||
contact_offset=0.03,
|
||
solid_rest_offset=0.02,
|
||
fluid_rest_offset=0.02,
|
||
mode="fluid" # 设置为流体模式
|
||
)
|
||
|
||
# 3. 创建流体材质 (定义粘度)
|
||
# 对于像蜂蜜或洗发水这样的"带流性"液体,需要高粘度
|
||
material_path = "/World/FluidMaterial"
|
||
particleUtils.add_pbd_particle_material(
|
||
stage=stage,
|
||
path=material_path,
|
||
cohesion=0.02, # 内聚力 (让液体聚在一起)
|
||
viscosity=0.5, # 粘度 (关键参数:数值越大越粘稠,如蜂蜜)
|
||
surface_tension=0.01, # 表面张力
|
||
friction=0.1, # 粒子间摩擦
|
||
damping=0.1 # 阻尼
|
||
)
|
||
|
||
# 4. 创建流体几何体 (在试管初始位置生成一堆粒子)
|
||
# 假设我们在 (0, 0, 0.5) 的位置生成一团液体
|
||
fluid_path = "/World/Liquid"
|
||
# 使用网格采样创建粒子点集
|
||
particleUtils.add_physx_particleset_pointinstancer(
|
||
stage=stage,
|
||
path=fluid_path,
|
||
particle_system_path=particle_system_path,
|
||
self_collision=True,
|
||
fluid=True,
|
||
particle_group=0,
|
||
particle_mass=0.001,
|
||
density=1000.0 # 水的密度
|
||
)
|
||
|
||
# 这里的关键是将材质绑定到几何体
|
||
physxUtils.add_physics_material_to_prim(stage, stage.GetPrimAtPath(fluid_path), material_path)
|
||
|
||
# 这里的逻辑通常需要编写一个简单的生成器,在空间中填满粒子
|
||
# 为了演示简单,我们假设通过 create_grid_particles 生成位置
|
||
# 在实际 Isaac Lab 中,你通常会加载一个预先保存好的带粒子的 USD,或者使用 SamplingAPI
|
||
points = []
|
||
for x in range(5):
|
||
for y in range(5):
|
||
for z in range(10):
|
||
points.append(Gf.Vec3f(x*0.04, y*0.04, 0.5 + z*0.04))
|
||
|
||
# 将坐标赋予粒子系统
|
||
points_attr = stage.GetPrimAtPath(fluid_path).GetAttribute("positions")
|
||
points_attr.Set(points)
|
||
|
||
# 5. 创建容器 (烧杯和试管)
|
||
# 这里使用简单的圆柱管演示,实际应用需加载 .usd 模型
|
||
|
||
# 烧杯 (底部接收)
|
||
beaker_path = "/World/Beaker"
|
||
prim_utils.create_cyl(
|
||
prim_path=beaker_path,
|
||
radius=0.15,
|
||
height=0.2,
|
||
position=Gf.Vec3d(0, 0, 0.1)
|
||
)
|
||
# 注意:PrimUtils创建的是实心凸包,流体进不去。
|
||
# 关键步骤:必须将容器碰撞改为 Mesh (SDF) 或手动用多个面拼成空心杯子。
|
||
# 在代码中通常加载自定义 USD 资产,这里仅做说明:
|
||
# setup_concave_collision(beaker_path)
|
||
|
||
# 试管 (上方倒水)
|
||
tube_path = "/World/TestTube"
|
||
tube = prim_utils.create_cyl(
|
||
prim_path=tube_path,
|
||
radius=0.05,
|
||
height=0.2,
|
||
position=Gf.Vec3d(0.2, 0, 0.6) # 位于烧杯上方侧面
|
||
)
|
||
# 为试管添加刚体属性以便旋转
|
||
physxUtils.set_rigid_body_properties(stage, tube_path)
|
||
|
||
return sim, tube_path
|
||
|
||
def run_simulation(sim, tube_path):
|
||
# 6. 开始循环并执行“倒水”动作
|
||
sim.initialize_physics()
|
||
sim.play()
|
||
|
||
stage = get_current_stage()
|
||
tube_prim = stage.GetPrimAtPath(tube_path)
|
||
xform = UsdGeom.Xformable(tube_prim)
|
||
|
||
# 模拟 500 帧
|
||
for i in range(500):
|
||
# 简单的运动学控制:慢慢旋转试管
|
||
if i < 200:
|
||
# 旋转以倒出液体 (欧拉角或四元数)
|
||
rotation = Gf.Rotation(Gf.Vec3d(0, 1, 0), i * 0.5) # 绕Y轴旋转
|
||
# 注意:实际代码需处理完整的 Transform 设置
|
||
current_xform = xform.GetLocalTransformation()
|
||
# 这里简化处理,直接更新姿态逻辑需根据具体场景编写
|
||
|
||
sim.step()
|
||
|
||
# 执行
|
||
if __name__ == "__main__":
|
||
sim, tube = create_fluid_scene()
|
||
run_simulation(sim, tube)
|