中文
Trigger 教程
把语义事件、运行时和硬件驱动拆开,让任务逻辑保持硬件无关。
触发器与 I/O 运行时
当前 PsyFlow 的 trigger 设计已经不是旧教程里那种单一 TriggerSender 思路了。
现在的职责拆分更清晰:
- 任务代码负责语义事件
TriggerRuntime负责时序与日志- driver 负责具体硬件协议
这样做的好处是,任务逻辑可以保持 hardware-agnostic。
初始化入口
from psyflow import initialize_triggers
runtime = initialize_triggers(cfg)
它会读取当前配置中的:
trigger_driver_configtrigger_policy_configtrigger_timing_config
当前支持的 driver 类型
mockcallableserial_portserial_url
另外还有公开导出的 FanoutDriver,适合需要把一个事件发往多个下游的场景。
本地开发或 QA 时,优先使用 mock。
TriggerRuntime 的职责
TriggerRuntime 现在主要负责:
when="now"或when="flip"的时序语义- planned / executed 日志
- driver capability 检查
- strict 模式下的失败策略
例如:
from psyflow import TriggerEvent
runtime.emit(
TriggerEvent(code=11, name="cue_onset"),
when="flip",
win=win,
)
为什么比旧模式更好
旧模式常见的问题是把这些东西混在一起:
- 任务阶段语义
- serial 编码
- pulse/reset 规则
- 硬件适配细节
现在把它们拆开后,任务代码更容易读,也更容易迁移到不同实验室硬件。
strict 模式
如果事件要求的能力当前 driver 不支持,比如:
pulse_width_msreset_code
那么:
- strict 模式会直接报错
- 非 strict 模式会记录 capability-missing 日志
这对 QA 非常重要,因为它能更早暴露“任务假设了某种硬件能力”的问题。
推荐写法
在任务里尽量写成语义化事件:
from psyflow import TriggerEvent
runtime.emit(TriggerEvent(code=21, name="target_onset"), when="flip", win=win)
不要把 serial bytes、端口协议或 reset 逻辑直接塞进 run_trial.py。这些都应该留给 runtime 和 driver 层。