场景中有两路相互独立的液体:
- 左侧悬空管道在世界坐标系中持续释放浅咖啡色的成簇连续流;
- 三连杆铰接臂带动水壶倾倒,绑定在
kettle_link壶口上的出口喷洒近透明水。
两路液体都是真实 Isaac Lab 粒子流体,录像来自场景内 1280×720 RGB 相机。
这个案例包含什么
| 能力 | 对应位置 |
|---|---|
| 固定容量粒子池 | run.yaml 中的 scenario.scene.fluids |
| 每种液体独立的物理与外观材质 | fluid_18000.yaml、fluid_8000.yaml |
| 世界坐标固定出口 | main.py 中的 pipe-coffee |
| 绑定铰接 Link 的出口 | kettle-spout 与 FluidAttachment |
| 有限储液量 | FluidReservoirSpec |
| 倾斜触发出液 | FluidTrigger.gravity_pour |
| 成簇连续流 | spray_half_angle_rad=0.0 |
| 锥形喷洒 | spray_half_angle_rad=math.radians(40.0) |
| 实时粒子状态查询 | simulation.particle_fluid(...) |
| 不依赖插件的纯配置储液与出口 | declarative.yaml |
场景配置
run.yaml 负责搭建环境、铰接水壶机械臂、两组液体粒子池和 RGB 相机。两组粒子池分开声明,
因为它们的容量和外观不同:
fluids.pipe_coffee:浅咖啡色,18,000 个粒子;fluids.kettle_water:近透明水,8,000 个粒子。
color_rgba 是每个液体 component 自己的属性。修改水壶里的水不会改变管道咖啡的颜色,
也不会改变密度、黏度和表面张力等物理属性。
declarative.yaml 展示更短的纯配置路径。reservoirs 和 emitters 是每个 fluid
实体下可选的 Core 字段;Runtime 在 Run prepare 时创建它们,并在 reset 时恢复。
整个过程不需要安装液体插件,也不需要 Python 初始化代码。
成簇连续流与喷洒流
管道中所有粒子的速度方向沿同一根轴,因此形成连续的成簇流:
FluidEmitterRequest(
emitter_id="pipe-coffee", # 发射器的唯一 ID。
fluid_id="fluids.pipe_coffee", # 使用哪一个粒子池。
position_m=(0.0, 0.0, 0.52), # 世界坐标系中的出口位置。
direction=(0.0, 0.0, -1.0), # 竖直向下。
rate_particles_s=2000.0, # 每秒释放的粒子数。
particle_count=18_000, # 最多释放多少粒子。
spray_half_angle_rad=0.0, # 零角度表示不喷洒。
)
水壶出口跟随铰接物体的 kettle_link。每个粒子的速度方向会确定性地分布在 40° 半角锥体内:
FluidEmitterRequest(
emitter_id="kettle-spout", # 壶口发射器 ID。
fluid_id="fluids.kettle_water", # 近透明水粒子池。
attachment=FluidAttachment(
parent="robots.pour_arm", # 绑定到哪个场景实体。
link_name="kettle_link", # 跟随水壶 Link,而不是机器人根节点。
position_m=(0.405, 0.0, 0.085), # 壶口相对 Link 的位置。
direction_local=(1.0, 0.0, 0.0), # 壶口局部出水方向。
),
reservoir_id="kettle-reservoir", # 从有限储液容器扣减水量。
flow_rate_ml_s=22.0, # 每秒流量,单位 ml。
spray_half_angle_rad=math.radians(40.0), # 40° 半角锥形喷洒。
trigger=FluidTrigger.gravity_pour(
start_angle_deg=28.0, # 到达该倾角后开始出液。
stop_angle_deg=38.0, # 回正时使用滞回阈值避免抖动。
),
)
spread_radius_m 表示出口本身的半径,单纯增大它不会变成喷洒。
spray_half_angle_rad 控制速度方向的锥形扩散;设为零就是成簇连续流。
喷洒使用确定性随机数。同一份 Run 和相同 seed 会生成相同的初始粒子方向,方便复现。
Reservoir 与触发逻辑
水壶的 reservoir 容量是 150 ml,初始有 120 ml。发射器启动后先处于 armed 状态。
机械臂驱动 pour_joint 倾倒水壶,壶口方向达到重力倾倒阈值后才开始喷洒;剩余水量不足以
生成下一个粒子时自动结束。
运行
cd demo/fundamentals/17_liquid
fastsim config validate run.yaml --project ../.fastsim/project.yaml --offline
fastsim config validate declarative.yaml --project ../.fastsim/project.yaml --offline
# 不安装液体插件,直接运行纯配置版本:
fastsim run declarative.yaml --project ../.fastsim/project.yaml
python main.py
程序会打开可见的 Isaac Lab 窗口,并生成:
output/17-liquid-stream-and-spray-1280x720.mp4
MP4 来自仿真场景内相机,不是桌面或窗口录屏。
Record → Replay
FastSim Core 可以通过按需启用的 fluid.audit 服务发布每个已经被后端接受的非空发射
Batch。Record 0.3.8 把这些紧凑的确定性输入写入 FSR 的 fluid.emissions Stream,
不会在每一帧复制全部活动粒子。Replay 0.2.15 在 Prepare 阶段通过 fluid.replay
加载完整 Stream,关闭配置中的实时发射器,并在记录的 Tick、Physics 之前注入重建后的
Batch;之后的粒子运动仍由物理仿真继续计算。
本案例的离线 Project 目前只包含共享 Component Catalog,并显式设置
installed_packages: false。因此,在 Project Catalog 能解析这两个精确插件版本之前,
这里不会提交无法运行的 Record/Replay Run 文件。
与规划几何的关系
粒子流体永远不会进入 planning.scene。规划器能够看到管道、水壶、机械臂和地面的碰撞几何,
但不会看到成千上万个短生命周期液体粒子。需要液体状态时,应通过专门的 particle-fluid
查询接口读取。
Backend 说明
这个案例需要原生粒子流体能力,目前已经在 Isaac Lab adapter 上验收。其他 backend 必须先 声明并实现相同的液体 capability,才能被这份 Run 选择。
验证等级
真实可见 Isaac 已通过。 合并后的案例实际释放管道粒子 17,600 个、水壶粒子 7,600 个; 水壶发射器正常完成,逻辑 reservoir 剩余 1.25 ml。场景相机生成 1280×720、24 FPS、310 帧 MP4。