distribution 为 fastsim-plugin-mission,组件 URI 为 plugin://fastsim/mission。wheel 只发布 Core 要求的两组 entry point: fastsim.componentsfastsim.plugins

Task 持有的求解器在 Mission 插件私有 plugins.mission.config.solvers mapping 中声明。原生导航 backend 默认允许后退和 原地旋转,因此通常不需要调整参数:

yaml
plugins:
  mission:
    use: plugin://fastsim/mission
    config:
      solvers:
        navigation:
          backend: fastsim_navigation
          role: navigation
          config: {}

Motion 通过 key 选择该声明;目标是 captured world frame 下的绝对位姿:

yaml
scenario:
  behavior:
    task:
      - name: navigate
        type: navigate
        robot: mobile_robot
        motions:
          navigate:
            kind: navigation
            solver_profile: navigation
            pose: {x: 4.85, y: 2.30, yaw: 0.0}

不存在导航 world 配置块。Mission 通过固定运行时策略从 captured world 派生机器人 垂向碰撞区间、costmap 尺寸、栅格化、footprint、时间参数化与 debug 输出;不存在 floor、costmap 或 footprint YAML。

若要诊断 direct Motion 或验收公共合同,仍可显式开启可选 world-capture 路径:

yaml
plugins:
  mission:
    use: plugin://fastsim/mission
    config:
      world_capture:
        enabled: true
        geometry: true

开启后,每个 direct Motion 都通过 FastSim 公共 PluginClient 捕获实时关节、entity、link、 articulation 和 frame 状态。geometry: true 还会捕获几何变换,并通过公共 batch 契约 以有界 bytes 解析摘要固定的资源。提交现有 direct joint Motion 前会重新验证同一个一次性 capture; reset 或 world change 会 fail closed,不会提交旧命令。控制器收敛后,Mission 会再获取 一个 static capture,验证最终关节状态,并把观测值提交为下一 Motion 的内部状态。 该可选设置不会选择 solver,也不能关闭带 profile Motion 所必需的 capture。

带 profile 的原生导航

每个带 profile 的 Navigation Motion 规划前,Mission 都会获取一次连贯的 collision-aware planning capture(C0)。它选择能与运动机器人相互碰撞的真实 collision geometry,从障碍物集合中剔除目标机器人及其 attached payload,并 从它们完整的 captured collision assembly 派生 world-Z 查询区间。下界增加 0.1 mm contact epsilon 以剔除支撑地板,上界增加 5 cm 余量;只有与该区间严格正重叠的障碍物 才会投影并栅格化为有界二维 NavigationCostmap。目标机器人的真实 collider 与 attached payload collider 则转换到机器人局部坐标,组成 MultiPolygonFootprint。实际 moving COLLISION 集合中的每个 polygon 在投影后都会与轴对齐的 5 mm 正方形执行精确 Minkowski sum,形成固定的 5 mm L∞ 安全包络;这是 Mission 运行时策略,不是配置字段。

Mission 通过 FastSim 0.1.0a20 的公共 batch geometry resolve/read 契约获取由摘要固定 的 mesh resource。catalog 提供 identity、provenance、schema、byte size 与 digest 证据, 不提供 Mission 专用的 local_bounds 字段。Mission 校验规范化 resource bytes,解析受 支持的 mesh schema,并在自身内部计算投影和保守筛选所需的局部 bounds。校验后的原始 payload 存入有界 digest-keyed LRU。FastSim 0.1.0a20 每次 capture 仍会发布完整 coherent catalog、transforms、resource refs 与动态状态。独立的 scene.geometry@2 API 已支持按 ID 读取 transform 与有界 delta,但当前导航链没有订阅该服务。Mission 按 ID 消费 capture 事实, 而不变刚体 mesh payload 只会按缓存 resolution_key resolve/read 一次;冷缓存且被选中的障碍在同一次 decode 中完成 metadata 与栅格化,展开后的 Python mesh 不会常驻。公共 planning catalog 已描述生效后的物理几何:provider 应用的 entity-root scale 已进入 collider scale、local/compound offset 与捕获的 world pose。 Mission 因而原样转发这些已校验事实,绝不会再次乘 locked Scenario scale。locked uniform scale 事实仍作为 admission 一致性检查;无需新增导航 scale 或 bounds 字段。这些策略不会 修改 FastSim、UniRoboSim、solver 或 YAML 契约。

Mission 把这些不可变值推入选中的 fastsim_navigation session,并调用 Hybrid A* 规划器。返回的 NavigationPath 会转成经过时间参数化的多帧 ControlTrack,不会再 压缩成一个 base-joint target。Mission 固定的参考轨迹平移上限 5.0 m/s、偏航角速度 上限 3.0 rad/smotion_duration_s 共同确定最小间隔;Mission 从公共 target descriptor 读取 max_command_rate_hz,并在内部把间隔向上取整为 cadence 的整数倍, 不依赖公共 cadence helper,也不需要新增时序配置。

规划与 debug 生成完成后,Mission 获取 fresh collision-aware handoff capture(C1), 立即调用公共标准 strict revalidation。成功后得到 validated C2 capture 与一次性公共 PlanningSubmissionHandle。Mission 把 world path 重锚到真实 C2 embedded-base 状态并 保留 raw yaw winding,在原 C0 costmap 上验证完整路径,再利用 immutable-payload cache 与公共 C2 batch lease 针对实际 submission path 执行 focused C0C2 delta 检查。

focused evidence 为 current 时,Mission 将精确 ControlChunk 封闭绑定到 C2 dependency/observation,并携带一次性 handle 通过公共 scoped control session 直接提交。 导航链不使用 Easy 私有接口、自定义 revalidation callback 或借用的 validation candidate。 strict CAPTURE_STALE 或 authority admission 的纯 freshness 拒绝会在有界次数内重取 C1/C2 并复用同一 plan;只有 focused stale 才从新 C0 重建实时地图、footprint、 Hybrid A* 路径、timeline 与 debug 证据。其他 resource、contract、solver、authority、 validation 或 control failure 均 fail closed。所有 capture/resource lease 会在每条退出 路径关闭,Task 持有的 solver session 则在 run 终态关闭。规划、debug 生成、有界整轮 重试与控制器收敛共同受一个端到端 motion_timeout_s 约束。

FastSim 0.1.0a20 的 strict revalidation 会比较完整 planning publication; expected_motion_entities 不会缩小该比较范围。如果无关事实在每次 handoff 都持续超出 容差,Mission 会在有界重试后 fail closed,且不会提交控制;选择性 geometry 读取不能 替代一次性 authority seal。

每次完整规划尝试都会在请求 fresh C1 前原子写入独立 PNG,并立即发布 mission.navigation.debug.attemptfinal: false),因此 stale 或被拒绝的尝试也保留 可定位证据。提交成功后,mission.navigation.debug 为同一文件发布最终证据 (final: true)。两个事件都报告绝对路径、SHA-256、byte size、图像尺寸及 map/path revision。黑色表示 lethal obstacle occupancy;青色轮廓是沿路径抽样的机器人 footprint, 不是障碍物重影或运动历史轨迹。PNG 内嵌 metadata 包含 costmap origin、resolution、width 与 height。路径为 <tempdir>/fastsim-navigation-debug/<sha256(run_id)[:16]>/mission-navigation-ordinal-<ordinal:08d>-attempt-<attempt:04d>.png (常见 Linux 环境中 <tempdir>/tmp)。debug 在 C1 capture 之前完成,不占用 fresh C1/C2 handoff window;写图仍是无配置开关的强制、原子证据步骤。

带 profile 的 cuRobo 手臂对齐

FastSim cuRobo adapter 接纳指定 arm-role solver profile 的 JointMotionPoseMotion。solver 声明位于 plugins.mission.config.solvers;Scenario Motion 只包含 用于选择声明的 solver_profile key。带 profile 的 PoseMotion 会在 admission 时冻结 具名 candidate 集合,依据 coherent planning capture 解析 worldrobotobjectobject_linkee frame,并调用 cuRobo v2 plan_to_pose。返回的 selected-goal index 会与 Task pose binding 精确闭合。

每个带 profile 的手臂 Motion 都会获取一次完整 collision-aware capture,并从中派生 world collision set、captured joints 与 solver root。root 来自 solver model 物理根 link 在 capture 中的 world pose;G2 split model 使用 mobile_base_link,whole-body model 使用 base_link,不会假定 Scenario pose 或 entity-root pose。cuRobo 接收 robot-root 坐标下的 world,因此 solver 内部 base 原点为 (0, 0, 0)。这个原点只是 frame normalization, 不是控制命令:base axis 会从 solver trajectory 排除,也绝不下发。

robot YAML 与公共 CuroboConfig.locked_joints overlay 声明在 cuRobo 中固定、因而不参与 优化的不可变关节名称集合。字段中的数值只是启动/回退值,并不要求 simulator 保持该 pose。overlay 会覆盖 file 中的同名项;cspace 外名称只有在 resolved URDF 能证明它是从 configured root 可达、非 fixed、无 mimic 的独立关节时才能加入。运行时不能增加或删除 这个名称集合。G2 whole-body profile 的集合包含 mobile x/y/yaw 与所选 gripper leader, 因此得到原生十二轴 upper-body layout(五个 body 关节加所选侧七轴手臂链)。验收 layout 以 backend 解析 overlay 后的 descriptor 为准,不会把 source YAML 中 raw cspace 声明的 长度当作规划轴数。

每次带 profile 的手臂规划尝试都始于同一次包含完整 articulation 状态的 coherent simulator capture(C1)。Mission 会消费 resolved cuRobo model 声明的每根 axis:active axis 构成 planner start state;每个非 carrier locked joint 的位置绑定为 cuRobo fixed transform,随后在 materialize geometry 前重建原生 robot model 与 solver cache。这是 FastSim adapter 的内存内操作,不需要修改第三方 cuRobo。simulator 中刻意未纳入 cuRobo model 的独立关节只保留在 capture 中,不会被静默提升为 lock。当前 G2 profile 的八个车轮 转向/滚动关节属于这类 model-excluded axis;轮子由固定在 mobile_base_link 上的四个碰撞 球代理表示。embedded carrier x/y/yaw 是另一例外:其有效值始终严格为 0.0,captured position 与 velocity 也都必须严格为零。其他 captured locked joint velocity 必须有限,但只作为诊断,因为 PhysX/TGS 可能为位置稳定的关节报告非零 瞬时速度。随后 Mission 针对 C1 绑定的有效 lock map 发布 mission.curobo.locked_state.verified。sealed C2 capture 不会刷新该 map 或重建 model; 它只校验每个 locked joint 相对其 C1 值仍处于固定 5e-4 SI position 包络内,并把 观测到的 C1C2 secant drift 投影一个最小 target command interval,要求仍处于 同一包络。这证明的是 handoff envelope,而不是整段 trajectory 的物理 hold 合同。当前 production 路径仍不支持已经移动的 embedded carrier 或 periodic carrier lift。该 Mission 手臂路径不会调用通用 projection/restoration 的 periodic lift 或 turn 代码,因此其单测 不能作为 production +/-pi 或多圈能力证据。

capture 不会预先应用配置半径。它在 collision filter 与运动 robot/payload exclusion 之后,闭合 FastSim 公开 composed-Stage collision capture 暴露的完整合格 COLLISION geometry 集合;这条路径不是 legacy UsdHelper Stage 扫描。只有 cuRobo materializer 应用 强制的 world_filter_radius: 3.0:当 collider 的 conservative shape bound 与以 captured cuRobo solver root 为球心、半径 3 m 的闭合三维球相交时才接纳。随后,如果整个 collider 相对 captured native robot-entity root 的已验证最高点小于等于 +0.15 m, 则整项省略。这个固定的 15 cm 支撑地板阈值不可配置;跨过该平面的 collider 会完整 保留。只有这项 maximum-Z 判断使用 native root;obstacle pose、半径过滤、运动学和 goal 仍以 solver root 为参考。materializer 的 admitted 与 excluded ID 集合互斥,二者并集必须精确等于 captured expected set;不存在静默丢弃或 第二次半径筛选。

collision_exclusion_objects 是面向有意接触目标的显式 locked Scenario alias 列表。 只有当 cuRobo pose request 的 pose.object 命中列表中的 alias 时,Mission 才对该请求 应用这些排除;复用同一 solver 的其他 motion 仍使用完整 collision world。alias 会在 capture 时解析成 canonical entity 与 collision ID,其中包括保留的 environment alias; 无法解析或没有 collision 的选择会 fail closed。该策略只作用于 planner,不会关闭 PhysX 执行接触。

cuRobo v2 不会在这条 Scene 路径中加载 analytic primitive bucket。因此 cuboid 保持原生 cuboid,sphere、cylinder 与 capsule 则以确定性的保守外包三角 mesh 提交,而不是会缩小几何的内接近似。native-mesh signature 把每个 collision_id 与 shape 内容、scale 和 source identity 绑定;任一项变化都会重建 cuRobo 按名称键控的 Warp mesh cache,而只改 pose 可以复用缓存。Mission 会先同步并 attest 这份 materialization,再由 Mission 私有、版本锁定的 checker-data adapter 冻结同一 solver 的 active collision slot 与 Warp BVH,并立即转换成 Mission 自有的 WorldMeshProjection。 任何 cuRobo snapshot 类型都不会越过 backend 边界,CollisionWorld 仍是规划中心数据结构。

payload collision 同样只由中心 CollisionWorldRobotAttachmentState 事实驱动。 对于一个已 attach 的 payload,Mission 保留其完整 captured collision geometry,将其从 environment obstacle 集合排除,并调用 cuRobo 公共 fit_spheres_to_mesh 与 attachment-manager API。attached_object_sphere_budget 是正整数的显式拟合预算与 native slot 配额;robot 文件声明 attached_object extra link 时默认值为 16。cuRobo 实际返回的 active sphere 可以少于预算。Mission 校验精确实际数量、有限正半径、link-local readback、 公共 FK 与 sphere-set digest;coverage、surface gap、protrusion 和 volume metric 只作为 证据记录,不设置全覆盖阈值。实际数量超过容量时会拒绝,不会截断。

每次调用 planner 前,Mission 都执行 fail-closed 的固定 alignment gate。它比较精确的 model/layout 与 joint-state digest、原生 FK link pose、全部配置 collision sphere,以及 每个 admitted mesh 的 world 坐标。不可配置的阈值为:root 1 µm/1 µrad,link 2 mm/0.5 度,sphere center/radius 2 mm/1 µm,obstacle vertex 1 mm;obstacle ID 与 source-asset digest 必须精确相等;collision geometry 先在 materializer 与 loaded checker 之间以每坐标 1 µm 的边界闭合 oriented float32 surface。loaded pose 的平移对每个 obstacle 独立使用 L∞ 上界 32 * float32_ulp(max(1 m, max(abs(expected_position)))),姿态角仍限制为 1 µrad。 alignment event 会逐 sample 记录平移误差的 p50/p95/p99/max、各轴/L2/角度最大值、 阈值范围,以及最差 object 的 delta、position scale、float32 ULP、multiplier 和最终阈值。 vertex ID、face 顺序、同向重复面以及 cuRobo process=True 的 vertex 合并不影响该 identity;反向 winding 仍不同。world-space 比较再使用对称 1 mm nearest-surface gate。loaded world content digest 是证据;它与 source-world digest 不等及 raw indexed-topology 差异只是 diagnostics。G2 左、右验收 profile 各包含 154 个机器人基础 collision sphere 和八个规划 axis(七个手臂关节加对应 inner-gripper joint);alignment sample 还会包含实际 active 的 attached-payload sphere。Mission 通过公共 visualization API 发布每个 sample 及其 overlay,并发布仅在状态转换时触发的 alarm:首次拒绝立即告警, 连续三个 accepted sample 后才报告恢复。

FastSim 公开 composed-Stage collision capture 与 materialize 给 cuRobo 的 world 都保留 完整的索引三角 surface;送入 solver 的几何没有被转成线。最终证据来自实际 loaded collision checker,不是用 materializer 输入重建。公共 overlay 直接使用 resource-backed mesh instance 显示 admitted 碰撞体,并以共享 unit-sphere resource 实例化机器人和 attached payload 的实际 collision sphere;不再绘制 mesh/球线框、 root 坐标轴或 link-error 线段。world_filter_radius 的闭区间与对象保守 bound 有交集时 保留对象;完全位于 bound 外的对象不会进入 loaded checker、数值校验或实体 overlay。 Mission 另行从 loaded collision checker 原子发布完整 binary PLY、object manifest 和填充 PNG。 observer 流式读取每个 PLY 顶点与面、重算 oriented triangle-surface 的 object/集合 content digest,并以该填充 PNG 作为验收图;raw PLY topology 只用于 diagnostics。

同状态请求通过 gate 后仍会真实调用 cuRobo v2 planner;Mission 校验返回的每个 waypoint 都保持 captured state,并在不提交控制的情况下记录成功。对于非同状态 cspace 或 pose plan,Mission 会严格 revalidate planning capture,把精确多帧手臂 ControlChunk 与所得 one-shot PlanningSubmissionHandle 绑定,通过 scoped control session 提交并要求 controller convergence。frozen profile 不提交物理 base control。 当 backend 的 effective model 自身把完整 manifest (x, y, yaw) carrier 作为有序前缀时, 这些轴无需额外 Mission 权限开关即为 active。Mission 会把 captured 物理底盘重锚成 cuRobo 请求中的 (0, 0, 0),再把返回的 SE(2) 采样恢复为物理关节坐标,并把底盘与其余 model axis 放进 同一个原子 ControlTrack。stale handoff evidence 只在固定界限内重试;其他 authority、alignment、trajectory 或 control failure 均 fail closed。 mobile-manipulation 与 navigation group 的重叠轴只支持在一个 Mission execution 内串行 复用;Run 不得再把这些物理别名轴授予另一个并发 control producer。

配套 G2 验收矩阵精确包含四份配置:split_left.ymlsplit_right.ymlwhole_body_left.ymlwhole_body_right.yml。split profile 使用原生八轴 group; whole-body profile 使用上述最终有效的原生十二轴 upper-body group,并把 mobile x/y/yaw 锁为零、把所选 inner-gripper leader 锁为 -0.7;Mission resource group 与最终原生 descriptor 精确一致。四个验收 profile 都保持 frozen carrier,且不下发任何物理 base control。G2 资产矩阵另外加载 base_navigation.ymlmobile_manipulation_left.yml / mobile_manipulation_right.yml,分别暴露三轴活动底盘 和十轴底盘加单臂 layout。只有 resource group 与 backend effective descriptor 精确 匹配时,Mission 才接纳它们;不再需要 planned_carrier 配置。部分、乱序、被锁定、非 SI 或非前缀 carrier 均 fail closed。当前 production 路径也拒绝未经认证的 yaw branch 跨越与完整转圈, 不声称支持通用 periodic projection。现有 run.yaml 仍是导航 demo,特意保持不变。当前 checkout 中的 第三方 cuRobo 已在 Warp 1.13 兼容边界更新:旧的 wp.torch.device_from_torch 调用已改用公共 wp.device_from_torch API,CPU 与真实 CUDA mesh-cache 回归均已通过。这已 解除当前 checkout 中该项具体依赖 blocker,但不等于声称 G2 十四个 matrix case 已全部端到端通过。把 Scene 转为 OBB、关闭 mesh collision 或静默丢弃 mesh 仍是 不可接受的降级,因为它们都会削弱 collision 与 alignment contract。

唯一 Scenario 结构

task 本身就是有序 Action 列表。behavior 内不再出现 schemaactions、 外部 Task 文档或任何兼容别名。

yaml
scenario:
  behavior:
    task:
      - name: close
        type: grasp
        robot: arm
        motions:
          close:
            kind: joints
            arm: arm
            joints:
              joint1: 0.0
              joint2: 0.0

FastSim 要求 behavior 是 mapping,并把它锁进 ExecutionPlan。Mission 会拒绝 未知的 behavior 字段和空 Task 列表。

物理 attachment 与 cuRobo attachment 是两个独立 Action。它们严格在 Task 中作者写下的 位置执行,Mission 不强制两份状态同步:

yaml
scenario:
  behavior:
    task:
      - name: physically-attach-dish
        type: physical_attachment
        robot: g2
        motions:
          change:
            kind: physical_attachment
            arm: left_gripper
            operation: attach
            object: dish
            link: ee
      - name: add-dish-to-curobo
        type: curobo_attachment
        robot: g2
        motions:
          change:
            kind: curobo_attachment
            solver_profile: left_curobo
            operation: attach
            object: dish

object 名称只能通过锁定的 Scenario alias map 解析。物理 link 同时支持声明过的语义 frame 名称和直接 authored link 名称。Mission 派生确定性的 command 与 attachment ID,调用已授权的公共 scene.command 服务,再用更新的 coherent capture 校验已 settle 的 scene/geometry attachment record;物理 detach 不写 link。cuRobo Motion 没有 armlink,原生 attachment link 由所选 solver 配置持有。运动学 Motion 与 FluidMotion 都会拒绝 attachment 字段。

用水杯视觉打水是独立的 fill Action。精确配方为 pose:pre_fill? -> pose:fill -> fluid:dispense -> pose:retreat?。必需的 fill Pose 负责定位;必需的 dispense FluidMotion 随后作为独立有序步骤执行, 再进入已声明的 retreat

yaml
- name: fill-cup
  type: fill
  robot: mobile_manipulator
  motions:
    pre_fill: null
    fill:
      kind: pose
      arm: left_curobo
      solver_profile: left_curobo
      pose: {source: inline, frame: world, data: [...]}
    dispense:
      kind: fluid
      verification: visual_only
      renderer: visualization_overlay
      source:
        kind: entity
        entity: faucet
        position_m: [0.0, 0.0, 0.8]
        direction: [0.0, 0.0, -1.0]
        inherit_velocity: false
      fluid: water
      rate_ml_s: 40.0
      volume_ml: 24.0
      seed: 23
    retreat: null

物理 particle_emitter 的出口和全部物理参数属于 FastSim Core 的锁定 Scene 声明; Mission 的 dispense FluidMotion 除通用的 verificationrenderer 外,只保留精确的 fluid/emitter 引用:

yaml
scenario:
  scene:
    fluids:
      water:
        use: fluid://water
        emitters:
          fill_cup:
            bind: {entity: faucet}
            position_m: [0.0, 0.0, 0.8]
            direction: [0.0, 0.0, -1.0]
            flow:
              rate_ml_s: 40.0
              speed_m_s: 0.35
              spread_radius_m: 0.006
              spray_half_angle_deg: 6.0
            limit: {volume_ml: 24.0}
            trigger: {mode: manual}
            enabled: false
            seed: 23
  behavior:
    task:
      - name: fill-cup
        type: fill
        robot: mobile_manipulator
        motions:
          pre_fill: null
          fill:
            kind: pose
            arm: left_curobo
            solver_profile: left_curobo
            pose: {source: inline, frame: world, data: [...]}
          dispense:
            kind: fluid
            verification: visual_only
            renderer: particle_emitter
            fluid: water
            emitter: fill_cup
          retreat: null

FluidMotion 的 effect 字段与 kind 同级,不存在 effect 包装层,也不再接受 special.liquid。FluidMotion 没有 armsolver_profileattachment;流体发射既不 提交运动学 target,也不改变 attachment 状态,因此封闭 codec 会拒绝这三个字段。在 fill recipe 中,adapter 从必需且先执行的 fill Pose 派生内部 capture target;该 Pose 仍照常声明自己的 arm。旧 receiver 字段不属于任何 renderer 的封闭 schema,必须 拒绝。renderer 必须显式 填写,不存在隐式回退。visualization_overlay 通过 FastSim 公共 visualization 服务显示纯视觉水流;particle_emitter 则使用公共 fluid.emitters。 overlay 只接受 volume_mlduration_s,不接受 particle_count;volume 会按 volume_ml / rate_ml_s 换算为显示时长。Mission 会在 prepare 阶段编译并检查该计划的 预算;显示时长大于或等于 motion_timeout_s 时,会在任何 control 或 effect-service 副作用之前拒绝。其 fluid: water 只是语义标签,不是 Scenario 液体 alias。Mission 只从锁定 Scenario 解析可选的 entity/link source。由于 overlay 是静态点层,entity/link source 必须显式设置 inherit_velocity: false;作者配置的 spray_half_angle_deg 会在固定边界内扩大水流的横向散射。

overlay 会在新鲜 capture 的静态 source frame 中发布一条有界、由请求摘要确定的点流, 要求 publish 回执完整且无丢失,在有界时钟内保持显示,再凭严格 clear 回执清除完全相同的 primitive。它不会申请 fluid.emitters、创建粒子、消耗 reservoir,也不声称流体落点或 任何容器的接收量。取消或失败会先 drain 正在执行的公共调用,并尝试清除同一 primitive,然后 继续传播原始异常。

使用 particle_emitter 时,Mission 在 prepare 阶段只从锁定的 scenario.scene.fluids 声明解析 fluid + emitter;Action 中内联的 source、flow、limit、 trigger 与 seed 会被拒绝。被引用 emitter 必须位于启用的 fluid 上,且为 disabled、manual、 不绑定 reservoir、使用毫升流率,并且恰好声明一个有限的 volume、duration 或 particle limit。一个 Task 最多引用同一 configured emitter 一次;同一 Run 内的 Mission 实例还会 持有进程内排他 claim。稳定的 Mission operation_id 与 Core 物理 emitter_id 分离,其 摘要同时固定 Plan/Scenario identity 与 canonical declaration。

emitter: fill_cupscenario.scene.fluids.water.emitters 下的声明 ID,不是触发时间。 configured emitter 保持 trigger.mode: manualenabled: false;Mission 只有在有序执行 到 dispense Motion 时才调用 set_enabled(true)。如果按时间限制出液,应在 Scene 声明 中填写 limit.duration_s;也可以改用 limit.volume_ml 或粒子数量限制,但必须且只能有 一个有限 limit。

Core 在 generation 启动时创建 configured emitter,因此 Mission 依次执行 status、 使用必需 fill Pose 派生的内部 target 执行 fresh static source identity/frame capture、 set_enabled(true) 和 status polling,绝不会 在该路径调用 start。首次接管必须看到零计数、不出水的 PAUSED。新鲜 capture 只证明 configured outlet 的 entity/link source 有唯一 authored identity 和稳定 source frame;Core emitter 的 bind 表示该出口 source,不是 receiver。Mission 在发布 mission.liquid.requested 并同步激活同一物理 emitter 时保持 capture lease 打开。 Core reset 后只允许把新的零计数 PAUSED generation 重新接管;其他未拥有或 identity 不一致的状态会被拒绝,且不会被修改。

requested、started、settled、failed 与 settlement-unknown 事件保留 renderer/evidence mode、Action identity/ordinal、FluidMotion identity/global ordinal、请求摘要、capture identity、generation/tick 和 source identity,不携带 receiver 或 attachment identity。 每个 particle-emitter 事件都显式设置 claims_particle_state: true,因为公共 emitter 状态与计数构成粒子状态证据;同时保持 claims_reservoir_state: falseclaims_received_volume: false。overlay 的 started/settled 还保留完整 publish 回执, settled 保留 clear 回执,且 overlay 事件的三项 claim flag 均为 false。对 particle renderer,取消或失败会先重新核对 configured identity,再调用 set_enabled(..., false)。已验证的 PAUSED、不出水状态会保留计数,使同一 generation 仍可重试;只有同一已验证 emitter 在 pause 后仍出水时,才把 stop 用作二级 containment。 identity 或 generation 证据未知时不会继续修改。失败事件会携带物理 emitter_id 与最佳 可信计数;失败事件发布是 best effort,绝不会覆盖原始取消或执行异常。

当前公共 capture 与 fluid-emitter 服务没有可传给 set_enabled 的共享 authority token。 保持 fresh capture lease 打开并同步激活,可关闭 Mission 层面的 await 级 TOCTOU 窗口, 但无法从形式上排除另一个独立写入者在 capture 校验与激活之间并发修改 source identity;真正 的跨服务原子性仍需要未来的 Core authority-token 扩展。Mission 的进程内 claim 也不能 替代 Core claim/lease;当非 Mission 消费者可能控制同一 emitter 时仍需扩展 Core 契约。

visual_only 是有意限定:overlay 只证明 developer visualization 已被接受并清除, emitter 计数也只证明有界视觉出水;二者都不能证明流体落点、容器关联或接收体积,这些 均不属于 FluidMotion 契约。原有 pour 仍只是 pose 配方,不是 FluidMotion 的别名。

已完成的 FastSim 对接

  • 静态 fastsim-component/2 catalog 与摘要校验的 component index;
  • 符合 fastsim-plugin-runtime/1 的 installed PluginFactory
  • 正式 prepare/start/stop/close 生命周期;
  • 精确校验锁定的 Scenario identity,并读取 scene/behavior
  • 使用编译期生成的动态 robot/articulation binding 与 scoped control grant;
  • 必需的 controlcontrol.targetsevent.publishrun.lifecyclescenario.read 公共服务,以及 catalog 中可选、但在 Motion 选择 solver 后按需 必须可用的 fluid.emittersplanning.capturescene.commandvisualization 依赖(particle_emitter FluidMotion 需要 fluid.emittersvisualization_overlay FluidMotion 与带 profile 的 cuRobo 手臂 alignment 需要 visualization);
  • 从锁定后的 Scenario pose 与初始关节状态建立确定性的直接 Motion 状态,并用权威 control-session context 提交每一个 joint.position@1
  • 每个运动学 Motion 必须到达控制器收敛终态并完成 postprocess;每个 FluidMotion 必须取得有界 renderer settlement,之后才能显式完成 Motion/Action 并进入 Mission 终态收尾;
  • 公共 planning capture:包含实时 joints、scene/link/articulation 状态、frames、 geometry transforms、有界资源读取、SHA-256 校验、revalidation 和带 planning authority 的 control submission;direct Motion 可选,带 profile 的导航或手臂求解 则强制使用 collision-aware capture;
  • 实时 collision projection、原生 Hybrid A* 规划、确定性 debug evidence 与多帧 navigation control;
  • 带 profile 的 cuRobo JointMotionPoseMotion:执行原生 v2 cspace/pose 规划、 固定 world alignment gate、公共 overlay、strict planning revalidation,以及针对非静止 结果的 sealed 多帧手臂 control;
  • 通过公共 scene.command 执行独立的物理 attach/detach Motion,并在 mission.motion.completed 前用权威 collision-aware capture 校验完整 attachment record;
  • 执行由 fill 组合的类型化纯视觉 FluidMotion emission,并显式选择公共 renderer: 严格 publish/hold/clear 的 visualization overlay,或先 PAUSED 预留、再通过静态 source capture gate 激活的 fluid.emitters 操作;两种模式都在可选 retreat 或 Action 完成前结束;
  • 发布有界的进度、capture 和终态事件,供应用通过公共接口验证完成状态;
  • 通过 run.lifecycle 精确提交一次终态请求:所有 Action 与资源清理成功后提交成功, 执行无法完成时提交失败或取消;
  • 由 Host 追踪的后台执行、有界 timeout、取消与清理。

joint 与 navigation Motion 都可以在不带 solver profile 时直接到达 control。直接导航 会把 world x/y/yaw 目标映射到唯一一个由 Manifest 声明的 holonomic embedded-base group,不执行避障规划。带 profile 的导航则执行上面的 collision-aware Hybrid A* 链路。只有控制器已收敛并通过后处理的 segment 才会推进 Mission 内部状态。 无法支持的 Task 内容会在 prepare 阶段明确失败,绝不静默降级。

FastSim Core 仍缺的接口

当前公共插件接口仍未提供:

  • 插件 checkpoint/restore;
  • planner 发现与锁定后的 planner grant;
  • 通用 Task/pose resource grant;
  • dynamic plugin 的 object binding。

Mission 会把这些剩余情况报告成明确的 admission failure。inline 带 profile pose 规划、 sealed cuRobo 手臂提交与 attachment mutation 不依赖上述缺口:Mission 持有所声明的 UniSolver session,使用 Core 标准 planning submission handle,并且只通过已授权的 scene.command 服务执行物理 attachment Motion。原生带 profile 导航路径保持不变,也不依赖 Core planner grant。

安装与验证

使用 Python 3.11 或 3.12,并安装 FastSim >=0.1.0a27,<0.2 与 Producer Kit >=0.2.8,<0.3

bash
python -m pip install .

# 一并安装 fastsim-navigation >= 0.1.3,以使用原生 Hybrid A* 规划。
python -m pip install '.[navigation]'

fastsim plugins list examples/scenario.yaml \
  --registry examples/robot-index.yaml --offline --json
fastsim plugins diagnose examples/scenario.yaml \
  --registry examples/robot-index.yaml --offline --json

源码验证:

bash
python -m pip install -e '.[dev]'
python -m pytest -q
python -m ruff check src tests
python -m mypy --config-file pyproject.toml src/fastsim_plugin_mission
python -m build

examples/ 中的 robot 只有用于发现与编译的 metadata,不是 backend 可加载的物理资产。