OpenSesame
Rapunzel Code Editor
DataMatrix
Support forum
Python Tutorials
MindProbe
OpenSesame videos
Python videos
Supported by

计时

本页描述了与计时相关的各种问题,并提供了基准测试结果和测试您自己系统的提示。如果您在计时方面遇到问题,请花时间阅读此页面。许多问题可以通过考虑刺激准备和监视器属性等因素来解决。

OpenSesame能实现毫秒级精确计时吗?

简短的回答是:是的。详细的回答是这个页面的其余部分。

时间敏感实验的重要注意事项

检查您的计时!

OpenSesame允许您非常准确地控制实验计时。但这并不能保证在每个特定实验中都能实现精确计时!由于许多原因(其中许多原因在本页中已经描述),您可能会遇到计时问题。因此,在时间关键性实验中,您应始终检查实验中的计时是否如预期。检查OpenSesame报告的显示时间戳是最简单的方法。

每个sketchpad项目都有一个名为time_[sketchpad name]的变量,其中包含上一次显示sketchpad的时间戳。因此,如果您希望sketchpad target 显示100毫秒,然后显示sketchpad mask,您应该验证time_mask - time_target确实是100。当使用Python内联代码时,您可以利用canvas.show()返回显示时间戳的事实。

了解您的监视器

计算机显示器会周期性地刷新。例如,如果您的显示器的刷新率是100 Hz,则显示器每10毫秒刷新一次(1000毫秒/100 Hz)。这意味着视觉刺激总是以10毫秒的倍数呈现的,您将无法呈现5毫秒或37毫秒的刺激。最常见的刷新率是60 Hz(= 16.67 ms刷新周期),尽管实验系统有时会使用具有更高刷新率的显示器。

在%VidRefresh中,您可以看到慢动作中的显示器刷新是什么样子的。在CRT显示器(非平面显示器,居中)上,刷新是一个从左到右、从上到下移动的单个像素。因此,一次只会点亮一个像素,这就是为什么CRT显示器会稍微闪烁。在LCD或TFT显示器(平面显示器,左右两侧)上,刷新是从上到下的“填充”。因此,LCD和TFT显示器不会闪烁。(除非您呈现一个闪烁的刺激,当然。)

Video 1. CRT(中心)和LCD/ TFT显示器上刷新周期的慢动作视频。视频由Jarik den Hartog和VU University Amsterdam技术支持人员提供。

如果在刷新周期进行到一半时呈现新的刺激显示,您将观察到“撕裂现象”。也就是说,显示器的上半部分将显示旧的显示内容,而下半部分将显示新的显示内容。这通常被认为是不可取的,因此新的显示应该在刷新周期从顶部开始的确切时刻呈现。这称为“垂直刷新同步”或简称“v-sync”。当启用v-sync时,撕裂现象将不再可见,因为撕裂与显示器的上边缘一致。然而,v-sync并没有改变显示器不会立即刷新并且因此总是会在一段时间内同时显示旧的和新的显示内容这一事实。

另一个重要概念是"垂直重追踪阻塞"或"阻塞翻转"。通常,当您发送一个命令显示新的画面时,计算机会立即接受该命令并将要显示的画面放入队列。然而,画面实际出现在监视器上的时间可能要稍晚一些,通常是在下一个刷新周期开始时(假设启用了 v-sync)。因此,您无法确切知道显示屏何时出现,因为您的时间戳记录的是显示屏进入队列的时间,而非显示的时间。要解决这个问题,您可以使用所谓的"阻塞翻转"。这基本上意味着,当您发送一个显示新画面的命令时,计算机将冻结,直到显示屏实际出现。这使您能够获得非常准确的显示时间戳,但代价是由于计算机在等待显示画面的大部分时间都被冻结,性能损失较大。但对于实验的目的来说,阻塞翻转通常被认为是最佳策略。

最后,液晶显示器可能会遭受"输入延迟"的问题。这意味着在计算机 "认为"显示画面出现的时刻与画面实际出现的时刻之间,存在额外的、有时是可变的延迟。这种延迟来自于显示器进行的各种数字处理,例如颜色校正或图像平滑。据我所知,输入延迟不是可以通过编程解决的问题,您应该避免在时间关键的实验中使用有显著输入延迟的显示器。

相关讨论,请参阅:

达到刷新截止期限

想象一下,您在 10:30 到达火车站。您的火车在 11:00 离开,这意味着您只有 30 分钟的时间去喝一杯咖啡。然而,如果您正好用了 30 分钟喝咖啡,那么您将在火车离开的时候才回到站台,这时您只能等待下一班火车。因此,如果您有 30 分钟等待时间,您应该喝完咖啡的时间稍短一些,比如 25 分钟。

在指定视觉刺激呈现间隔时,情况与此类似。假设您有一个 100 Hz 的显示器(每 10 毫秒刷新一次),并且想要在 100 毫秒内展示一个目标刺激,然后遮盖。您可能首先会想到在目标和遮盖之间指定 100 毫秒的间隔,毕竟这是您的目标。然而,指定一个完全为 100 毫秒的间隔可能会导致遮罩 "错过刷新截止期限",遮罩将在下一个刷新周期(假设启用了 v-sync)后的 10 毫秒才呈现。因此,如果您指定了 100 毫秒的间隔,实际间隔很可能会达到 110 毫秒!

解决办法很简单:您应该指定一个比您期望的稍短的间隔,比如 95 毫秒。不要担心间隔太短,因为在 100 Hz 的显示器上,两个刺激显示之间的间隔必然是 10 毫秒的倍数。因此,95 毫秒将变成 100 毫秒(10 帧),1 毫秒将变成 10 毫秒(1 帧),等等。换句话说,间隔会向上取整(永远不会向下取整!)以符合显示器的刷新率。

禁用桌面效果

许多现代操作系统使用图形化的桌面效果。例如,这些效果提供了透明效果和在大多数现代操作系统上看到的平滑窗口最小化和最大化效果。尽管这些效果底层的软件因系统而异,但它们通常在应用程序与显示屏之间增加了一层。这层额外的图层可能会阻止 OpenSesame 同步到垂直刷新和/或实现阻塞翻转。

尽管桌面特效可能会导致问题,但通常不会出现问题。这似乎因系统和显卡的不同而有所不同。然而,当操作系统允许时,在用于实验测试的系统上最好禁用桌面特效。

以下是针对各种操作系统关于桌面特效的一些建议:

  • Windows XP下,没有任何桌面特效。
  • Windows 7下,通过在“个性化”部分选择“基本和高对比度主题”下列出的任何主题即可禁用桌面特效。
  • Windows 10下,没有办法完全禁用桌面特效。
  • Ubuntu 和其他使用 Gnome 3的 Linux 发行版下,无法完全禁用桌面特效。
  • 使用 KDE 的 Linux 发行版下,您可以在“系统设置”中的“桌面特效”部分禁用桌面特效。
  • Mac OS下,似乎无法完全禁用桌面特效。

考虑刺激准备时间/准备-运行结构

如果您在呈现视觉刺激过程中关注精确计时,那么您应该提前准备刺激。这样,您在实验的时间关键部分不会因刺激准备而出现不可预测的延迟。

首先我们考虑一个包含间隔 canvas1canvas2 之间的刺激准备时间的脚本(您可以将此粘贴到 inline_script 项目中)(%LstStimPrepBad)。指定的间隔是 95 毫秒,所以 - 考虑到[刷新截止时间]中描述的“向上取整”规则 - 您可以期望在我的 60 Hz 显示器上得到 100 毫秒的间隔。然而,在我的测试系统上,下面的脚本导致了 150 毫秒的间隔,这在 60 Hz 的显示器上对应于 9 帧。这是由于canvas2 的准备导致的 50 毫秒(3 帧)的意外延迟。

%-- 代码: id: LstStimPrepBad 语法:python source: stimulus-preparation-bad.py caption: “在这个脚本中,canvas1canvas2 之间的持续时间受到刺激准备时间的混淆。” --%

现在让我们考虑上面脚本的一个简单变体(%LstStimPrepGood)。这次,我们先准备 canvas1canvas2, 然后再展示它们。在我的测试系统上,这导致一个稳定的 100 毫秒间隔,就像应该的那样!

%-- 代码: id: LstStimPrepGood 语法:python source: stimulus-preparation-good.py caption: “在这个脚本中,canvas1canvas2 之间的持续时间不受刺激准备时间的混淆。” --%

在使用图形界面时,同样的考虑因素适用,但 OpenSesame 通过提前自动处理大部分刺激准备来帮助您。然而,您必须考虑到这种准备是在 sequence 项目的层级上进行的,而不是在 loop 项目的层级上进行的。 实际上,这意味着 sequence 内部的计时不受刺激准备时间的混淆。但在 sequence 之间的计时是。

为了使这更具体,让我们考虑下面显示的结构(%FigStimPrepBad)。假设 sketchpad 项目的持续时间设为 95 毫秒,这样就会产生 100 毫秒的持续时间,或在 60 Hz 显示器上执行 6 帧。在我的测试系统上,实际持续时间为 133 毫秒,或 8 帧,因为计时受到每次执行序列时 sketchpad 项目的准备混淆。所以这是一个关于您应该如何 实现实验中的时间关键部分的例子。

现在让我们考虑下面所示的结构(Figure 1)。假设sketchpad1的持续时间设置为95 ms,从而在sketchpad1sketchpad2之间实现100 ms的间隔。在这种情况下,这两个项目都显示为相同sequence的一部分,时间将不会受到刺激准备时间的影响。因此,在我测试系统上,sketchpad1sketchpad2之间的实际间隔确实是100 ms,或者在60 Hz显示器上为6帧。

请注意,这只适用于sketchpad1sketchpad2之间的间隔,因为它们是按顺序作为相同序列执行的。在运行i上的sketchpad2和运行i+1上的sketchpad1之间的间隔再次受到刺激准备时间的影响。

/pages/zh/manual/img/timing/stimulus-preparation-correct.png

Figure 1. 一个实验结构的示例,其中sketchpad1sketchpad2之间的时间不受刺激准备时间的影响。在这种情况下,事件的顺序如下:准备sketchpad1(2帧),准备sketchpad2(2帧),显示sketchpad1(6帧),显示sketchpad2(6帧),准备sketchpad1(2帧),准备sketchpad2(2帧),显示sketchpad1(6帧),显示sketchpad2(6帧),等等。

有关更多信息,请参阅:

后端之间的差异

OpenSesame不局限于特定方式的显示控制、系统计时器等。因此,OpenSesame 本身没有特定的时间特性,因为这些取决于所使用的后端。各种后端的性能特征不是完全相关的:在某些系统上,psycho后端可能最好用,而在其他系统上,xpyriment后端可能最好用。因此,关于OpenSesame的一大优点是,你可以选择哪个后端最适合你!

一般来说,xpyrimentpsycho后端更适用于对时间要求严格的实验,因为它们使用阻塞翻转。另一方面,legacy后端略微稳定且在使用[表单]时速度明显更快。

在正常情况下,当前三个OpenSesame后端具有如Table 1所示的属性。

backend V-sync Blocking flip
legacy yes no
xpyriment yes yes
psycho yes yes

Table 1. 后端属性。

另请参阅:

基准测试结果和测试自己系统的技巧

检查是否启用了垂直同步

如[了解您的显示器]中所述,新显示的呈现应该理想地与新的刷新周期开始同步(即"垂直同步")。您可以通过快速交替呈现不同颜色的显示来测试是否是这种情况。如果未启用垂直同步,您将清楚地观察到在监视器上运行的水平线(即"撕裂")。为了进行此测试,请运行一个实验,其中inline_script项中包含以下脚本(Listing 1):

# Create a blue and a yellow canvas
blue_canvas = Canvas(bgcolor='blue')
yellow_canvas = Canvas(bgcolor='yellow')
# Create a keyboard object
my_keyboard = Keyboard(timeout=0)
# Alternately present the blue and yellow canvas until
# a key is pressed.
while my_keyboard.get_key()[0] is None:
    blue_canvas.show()
    clock.sleep(95)
    yellow_canvas.show()
    clock.sleep(95)

测试计时精度和准确性

当您可以反复用相同的时间呈现视觉刺激时,时间定位就是精确的或一致的。当时间戳准确地反映了视觉刺激在监视器上出现的时间时,它们就是准确的。下面的脚本展示了如何检查定时的精度和准确性。这个测试可以在有和没有外部光电二极管的情况下进行,尽管使用光电二极管可以提供额外的验证。

为了简单起见,假设您的显示器频率为100 Hz,这意味着单个帧需要10 ms。然后,脚本为1帧(10 ms)呈现一个白色画布。接下来,脚本为9帧(90 ms)呈现一个黑色画布。请注意,在[如何满足刷新期限]中解释的那样,我们已经指定了85的持续时间,实际上已经取了整数。因此,我们预期两个连续白色显示的开始时间之间的间隔将是10帧或100 ms(= 10 ms + 90 ms)。

我们可以用以下两种方法验证两个白色显示之间的间隔是否确实是100 ms:

  1. 使用OpenSesame报告的时间戳。这是最容易的方法,当后端使用阻塞翻转时通常是准确的。
  2. 使用对白色显示的开始做出反应并将这些开始的时间戳记录到外部计算机的光电二极管。这是验证定时的最佳方法,因为它不依赖于软件的内省。某些问题,如之前讨论的TFT输入延迟,只能通过外部光电二极管测量得出。

我在Windows XP上%LstIntervalBenchmark,使用了所有三个后端。我还用连接到第二台计算机的光电二极管记录下来了白色显示的开始。我在%TblBenchmarkResults中总结了实验结果。

如您所见,xpyrimentpsycho 后端始终显示100 ms 的间隔。这是好的,正如我们所期望的那样。然而,legacy 后端显示的间隔为90ms。这种差异是由于legacy后端没有使用阻塞翻转(请参见[了解您的监视器]),这导致显示时间的一些不可预测性。还要注意,外部光电二极管记录的时间戳和OpenSesame报告的时间戳之间的一致性。这个一致性表明OpenSesame的时间戳是可靠的,尽管再次说明,由于缺乏阻塞翻转,legacy后端的可靠性略有不足。

Expyriment基准测试和测试套件

Expyriment网站上提供了一套非常好的基准测试。这些信息适用于使用xpyriment后端的OpenSesame实验。

Expyriment包括一个非常有用的测试套件。您可以通过运行test_suite.opensesame示例实验或在您的实验中添加一个简单的inline_script来启动这个测试套件,示例如下(%LstExpyrimentTestSuite):

要了解更多信息,请访问:

PsychoPy基准和与时间相关的信息

关于计时的一些信息可以在 PsychoPy 文档网站上找到。这些信息适用于使用 psycho 后端的 OpenSesame 实验。

Supported by