3

我是 python 和 Psychopy 的新手,但是我在编程和设计实验(使用 Matlab 和 EPrime)方面拥有丰富的经验。我正在运行一个 RSVP(快速视觉串行演示)实验,每 X 毫秒显示一个不同的视觉刺激(X 是一个实验变量,可以从 100 毫秒到 1000 毫秒)。由于这是一项生理实验,我需要在刺激开始时通过并行端口发送触发器。我使用示波器和光电传感器测试触发器和视觉开始之间的同步。但是,当我在 win.flip() 之前或之后发送触发器时,即使使用窗口 waitBlanking=False 参数,我仍然会得到刺激开始和代码开始之间的差异。

附上我的代码:


    im=[]
    for pic in picnames:               
        im.append(visual.ImageStim(myWin,image=pic,pos=[0,0],autoLog=True))

    myWin.flip() # to get to the next vertical blank
    while tm < and t < len(codes):                
        im[tm].draw()                                             
        parallel.setData(codes[t]) # before
        myWin.flip()                
        #parallel.setData(codes[t]) # after
        ttime.append(myClock.getTime())
        core.wait(0.01)
        parallel.setData(0)                
        dur=(myClock.getTime()-ttime[t])*1000                
        while dur < stimDur-frameDurAvg+1:
           dur=(myClock.getTime()-ttime[t])*1000
        t=t+1
        tm=tm+1            
        myWin.flip()

如何将我的刺激开始同步到触发器?我不确定这是否是显卡问题(我正在使用带有板载 Intel 显卡的 LCD ACER 屏幕)。非常感谢,
沙尼

4

3 回答 3

5

win.flip()等待下一次监视器更新。这意味着win.flip()在监视器开始绘制框架时几乎完全执行之后的下一行。那就是您要发送触发器的地方。前面的行win.flip()可能几乎提前一帧,例如 60Hz 监视器上的 16.7 毫秒,因此您的触发器会过早到达。

有两种几乎相同的方法可以做到这一点。让我们从最容易理解的开始:

win.flip()
parallel.setData(255)
core.wait(0.01)
parallel.setData(0)

...所以信号在图像被推送到监视器后立即发送。

稍微更精确的计时方法可以节省 0.01 毫秒(加减一个数量级)。在脚本早期的某个地方定义

def sendTrigger(code): 
    parallel.setData(code)
    core.wait(0.01)
    parallel.setData(0)

然后在循环中做

win.callOnFlip(sendTrigger, code=255)
win.flip()

这将在翻转之后调用该函数,在psychopy 进行一些大扫除之前。同样,与其他因素相比,这种时间上的差异是如此微不足道,以至于这实际上并不是表演的问题,而是风格偏好的问题。

于 2015-03-11T18:46:16.450 回答
0

有一个隐藏的时序变量通常会被忽略——显示器输入滞后,我认为这就是延迟的原因。简而言之,即使在从显卡获得输入后,显示器也需要一些时间来显示图像。此延迟与刷新率(屏幕切换缓冲区的次数)或显示器的响应时间无关。

在我的监视器中,当我使用 callOnFlip() 发送触发器时,我发现有 23 毫秒的延迟。我如何纠正它是:地板(23/16.667)= 1,和 23%16.667 = 6.333。所以我在第二帧调用 callOnFlip,等待 6.3 毫秒并触发端口。这行得通。我没有尝试过 WaitBlanking=True,它等待从显卡开始消隐,因为这给了我更多时间来准备下一个缓冲区。但是,我认为即使使用 WaitBlanking=True 效果也会存在。(测试后更多!)

最好的,苏达

于 2015-05-27T12:55:05.850 回答
0

至少有一个例程可用于将触发延迟标准化为屏幕刷新率。我刚刚用一个光电传感器单元对其进行了测试,我从触发器和刺激显示之间的平均延迟 13 毫秒 (sd = 3.5 ms) 变为平均延迟 4.8 毫秒 (sd = 3.1 ms)。

程序如下:

  1. 计算两个显示之间的平均持续时间。假设您的屏幕刷新率为 85.05(这是我的情况)。这意味着两次刷新之间的平均持续时间为 1000/85.05 = 11.76 毫秒。
  2. 就在你调用 win.flip() 之后,在发送触发器之前等待这个平均延迟:core.wait(0.01176)。

这并不能确保您的所有延迟现在都为零,因为您无法掌握 win.flip() 命令与屏幕当前状态之间的同步,但它会将延迟集中在零附近。至少,它对我有用。

所以代码可以更新如下:

    refr_rate = 85.05
    mean_delay_ms = (1000 / refr_rate)
    mean_delay_sec = mean_delay_ms / 1000  # Psychopy needs timing values in seconds

    def send_trigger(port, value):
        core.wait(mean_delay_sec)
        parallel.setData(value)
        core.wait(0.001)
        parallel.setData(0)

    [...]

    stimulus.draw()
    win.flip()
    send_trigger(port, value)

    [...]
于 2019-09-24T09:43:49.577 回答