我是python和psychopy的新手,但是我在编程和设计实验(使用Matlab和EPrime)方面拥有丰富的经验。我正在运行一个RSVP(快速视觉连续演示)实验,每X毫秒显示一次不同的视觉刺激(X是一个实验变量,可以从100毫秒到1000毫秒)。由于这是一个生理实验,我需要在刺激开始时通过并行端口发送触发器。我使用示波器和光电传感器测试触发器和视觉开始之间的同步。但是,当我在 win.flip() 之前或之后发送触发器时,即使使用 window 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()
如何将刺激开始与触发同步?我不确定这是否是显卡问题(我使用的是带有板载英特尔显卡的 LCD ACER 屏幕)。非常感谢,
沙尼
win.flip()
等待下一次显示器更新。这意味着win.flip()
之后的下一行几乎完全在监视器开始绘制帧时执行。这是您要发送触发器的位置。win.flip()
之前的线路可能早了几乎一帧,例如在 60Hz 显示器上为 16.7 毫秒,因此您的触发器会过早到达。
有两种几乎相同的方法可以做到这一点。让我们从最明确的开始:
for i in range(10):
win.flip()
# On the first flip
if i == 0:
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)
for i in range(10):
win.flip()
这将在第一次翻转之后调用该函数,在精神病做一些清理之前。因此,该函数本可以调用win.callOnNextFlip
,因为它仅在随后的第一次翻转时执行。
同样,与其他因素相比,这种时间差异是如此之小,以至于这不是一个性能问题,而是风格偏好的问题。
有一个隐藏的时序变量通常被忽略 - 监视器输入滞后,我认为这就是延迟的原因。简而言之,即使在从显卡获得输入后,显示器也需要一些时间来显示图像。此延迟与刷新率(屏幕切换缓冲区的次数)或监视器的响应时间无关。
在我的显示器中,当我使用 callOnFlip() 发送触发器时,我发现延迟为 23 毫秒。我如何更正它是:floor(23/16.667) = 1,23%16.667 = 6.333。所以我在第二帧上调用 callOnFlip,等待 6.3 毫秒并触发端口。这行得通。我还没有尝试过 WaitBlanking=True,它从显卡等待消隐开始,因为这给了我更多的时间来准备下一个缓冲区。但是,我认为即使使用WaitBlanking=True,效果也会存在。(测试后更多!
最好苏达
至少有一个例程可用于将触发延迟规范化为屏幕刷新率。我刚刚用光电传感器单元对其进行了测试,从触发和刺激显示之间的平均延迟 13 毫秒(sd = 3.5 ms)变为 4.8 毫秒的平均延迟(sd = 3.1 ms)。
程序如下:
- 计算两个显示器之间的平均持续时间。假设您的屏幕刷新率为 85.05(这是我的情况)。这意味着两次刷新之间的平均持续时间为 1000/85.05 = 11.76 毫秒。
- 在你调用 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)
[...]