验证等级
可见原生 Isaac。 窗口中第一次运动执行旧 chunk 的 40 帧中的 20 帧后,机械臂 转而跟随新的运动。
前置条件
- 同一 Python 3.12 环境中的 FastSim
0.1.0a6、UniRoboSim Core0.10.0和unirobosim-isaaclab==0.10.1。 - Isaac Lab 3.0 / Isaac Sim 6.0、NVIDIA GPU 和可用的显示环境。
为什么先暂停 runtime
仿真速度不保证与墙钟时间一致,因此 sleep 半秒不代表“执行了 20 个控制帧”。程序
暂停连续物理并调用 single_step(),让快慢机器得到同样结果。物理和控制都是
60 Hz,所以每次 step 恰好跨过一个控制边界。
代码流程
FRAME_DT = 1.0 / 60.0与配置控制频率一致。linear_path(...)生成恰好frames行的双轴目标,包含起点和终点;它不依赖仿真器。- 应用启动后 pause,但世界仍保留。
old_chunk = submit_joints(..., preempt=True)立即返回 operation handle,并把 当前 incumbent chunk 标记成允许后续替换。- 循环读取
status().progress.applied_frames,single-step 到确实应用至少 20 个旧帧。 new_chunk = submit_joints(...)从同一 session 到达,在下一个安全控制边界替换 允许抢占的 incumbent。- 继续 single-step,直到新 operation 进入终态。
result()证明旧状态为preempted,新状态为succeeded。release()显式清除两个已完成 operation 的保留记录。
新 path 第一行接近旧 path 执行 20 帧时的预期位置。真实 model plugin 应使用最新 observation 作为新起点,避免不期望的跳变。
preempt 参数描述的是“正在提交的这个 chunk”:True 表示以后到达的 chunk 可以
抢占它。因此必须给旧/incumbent chunk 设置,而不是给新/challenger chunk 设置。
希望始终可被新推理结果替换的 model-servo chunk 都应设置它。
运行
bash
cd demo/fundamentals/05_servo_preemption
fastsim config validate run.yaml --json
python main.py
预期结果:原生 Isaac 窗口打开;旧运动执行 20 帧后,在安全边界切换到新运动。终端类似:
text
Old chunk: preempted, applied=20/40
New chunk: succeeded, applied=40/40
如果 provider 记账在同一个安全边界跨过,旧 applied 数可能略高于 20,但必须小于
40,且状态必须为 preempted。
常见错误
- 新 chunk 排队而不是替换:旧 incumbent 提交时没有
preempt=True,所以它正确拒绝替换。 - 旧 chunk 显示 succeeded:runtime 连续运行,提交新 chunk 前已消耗完 40 帧。
- 自定义代码无限等待:外部 model/device 流程必须配置 timeout 或最大步数策略。
下一步
继续阅读 06 — RGB 场景相机截图。