如何将代码发送到并行端口,与Psychopy中的视觉刺激完全同步

时间:2015-03-11 15:44:02

标签: python-2.7 parallel-port psychopy

我是python和psychopy的新手,但是我在编程和设计实验方面拥有丰富的经验(使用Matlab和EPrime)。我正在运行RSVP(快速视觉序列演示)实验,每X ms显示不同的视觉刺激(X是实验变量,可以是100 ms到1000 ms)。由于这是一个生理学实验,我需要在刺激开始时通过并行端口发送触发器。我使用示波器和光电传感器测试触发和视觉开始之间的同步。但是,当我在win.flip()之前或之后发送我的触发器时,即使使用windowBlanking = 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屏幕)。非常感谢,
  沙尼

3 个答案:

答案 0 :(得分:5)

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

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

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()

这将在翻转之后调用该功能,然后心理状态会进行一些清理。同样,与其他因素相比,时间上的这种差异是微不足道的,这不是一个性能问题,而是风格偏好。

答案 1 :(得分:0)

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

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

最佳, Suddha

答案 2 :(得分:0)

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

过程如下:

  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)

    [...]