Psychopy online experiment programming notes
科技的进步从来不是靠自我封闭,这也是为什么我将笔记保存在Github Pages上公开,而不是保存在个人网站或社交平台上。你可以从仓库里面直接保存原版的md文件,也可以随时访问这个页面。
在此记录Psychopy的编程笔记,一方面是给自己做一个备忘录,另一方面是希望通过这个方式抛砖引玉,让各位能够在解决类似问题时有一些思路,同时也可以给出更好的解决方案。如果恰好笔记有所帮助,那也给这篇笔记增加了不小的意义。
对于科研人而言,分享不图任何报酬、利益,但希望获得尊重。如果这个笔记有所帮助,你想告诉更多的人,那非常欢迎。当你进行转载或搬运的时候,请注明出处(贴个网址就行),最重要的:请不要进行有偿分享。
Psychopy本身是一个开源软件,它的出现是为了让我们更好、更方便的进行实验,其作者的初衷和Steven Luck一样,后者将ERPLab的功能进行界面化、简单化,让EEG的分析变得更简单,为更多科研者所用。所以,请让科研变得更简单,不要让它变得更复杂。
Online and Offline Experiment
这一步只能单纯的实现,被试达到一定正确率后通过练习。按照上面链接中的设置,可以出现的效果是:达到设定的正确率后,被试可以通过练习,但无法进行再次练习,只能进行正式实验。对于练习较少的实验,被试可能会没有完全掌握实验操作方式,且无法再次进行练习。这就需要添加达到正确率后,既可以选择通过练习,也可以选择返回重新练习
设置被试主动重复练习的按键,即达到正确率后仍然想再次练习。在计算练习正确率code组件中的begin routine
和end routine
部分中增加代码。
Begin routine部分:
|
|
End routine部分:
|
|
注意:因为在没有达到要求的正确率时,程序出现的指导语是:练习未通过,请按空格重新练习。通过练习时也是按空格进入正式实验,所以这里会存在一个问题:在end routine部分如果不加第一句限制正确率的条件代码,同时,如果被试没有达到要求的正确率(如这里设置的0.8,即80%),按空格键也会跳出练习的循环。if number_correct/(practice_trials.nTotal + 1) >= 0.80
这句代码在end routine部分必须加上,不是多余的。
方法:单个trial的组件一般放在同一个routine中(如图中practice_stroop
),反馈就单独新建一个routine放在trial routine后,即被试按键后判断后(如图中feedback_stroop
)。在feedback_stroop
中有两个组件,首先是code_3
组件,用于判断正确错误,然后是text文字组件fb_2
,用于呈现反馈的文字,文字组件中的内容部分输入$fb_text
,这样code_3
组件生成的反馈内容就在文字组件中呈现。如下图:
在这个feedback_stroop
Routine中增加一个code_3
组件(名称随意),将组件的语言方式设置为both,在其中的begin routine部分增加如下代码:
左侧的python窗口输入:
|
|
右侧的js窗口输入:
|
|
两部分代码唯一的区别在于:无反应在python中的表示为None
,在js中的表示为undefined
,这里为了本地端和网页端都能正常运行,就将代码模式设置为both
,这样python代码和js代码就可以不一样,这样两端都可以正常运行程序。
更新:None
和undefined
从 psychopy2022.2.3版本以后似乎不存在这个问题了,code组件的code type直接用Auto->Js
模式即可。
select rows
位置填入代码:$random(10)*108
,然后condition文件选择正式实验的excel文件即可。(不推荐这个方法,建议另外创建练习的condition表格文件,尽量把每个条件都选1到2个试次进行练习,这样更平衡。)str()
转换为字符即可(括号内为刺激条件名称)Psychopy的默认单位是height,这个单位的好处是能够根据屏幕的大小自动适配刺激大小,不会因屏幕分辨率不同造成刺激大小不同。
以我的实验程序为例,需要刺激大小在3°以内,屏幕分辨率是$1920pix \times 1080pix$
-
视角计算参数:屏幕长:
40cm
宽:30cm
,距离屏幕:60cm
-
用
height
为单位,默认字体大小为0.05
,如果高度为1的话就是全屏高度,即像素的1080pix
,那0.05
就是5%
。 -
所以默认的$0.05height \approx 1080pix \times 5$ % $= 54pix $,这根据视角计算工具得出大约为不到1.1度视角
-
根据视角计算器,
3°
视角大约为151pix
. -
因此,把
151pix
换算到height
,$height=151pix/1080pix\approx0.14$,即3°
视角大小的刺激。 -
乘数和被乘数上下移动0.5度错开位置,答案保持中心不变。 移动位置为:$height=25pix/1080pix\approx0.023$。
⭐stroop正式实验刺激大小为2度(100.5pix
,即0.093height
),反馈字体大小为1.5度,掩蔽刺激大小6.4度
⭐乘法口诀正式实验刺激大小为0.093height
(即2°
)
在单个试次的loop
下,新增一个routine
,在这个routine
中添加一个code组件,在里面的begin routine
下增加如下代码:
|
|
⚠️trials.thisTrialN
中的trials要根据loop的名称对应修改,因为我设置的loop名称为trials
,如果loop名称为trials_stroop
,那就要修改为trials_stroop.thisTrialN
。
代码解释:如果试次数达到191或383,则运行该Routine,其他试次则不运行。运行该Routine时,则弹出文字“请休息一下,按空格键继续”。如下图。
Confused about indices used in selected rows
假如你想在loop_1
选择第1行到第8行进行循环,loop_2
选择第9行到第16行进行循环,loop_3
选择第17行到第24行进行循环。
那在添加loop后,需要分别在Selected rows部分填入:0:8、8:17、17:25
⚠️可以看到,python的选择方式是[
左开 右闭)
的方式
Online Experiment
同步本地和线上程序时,如果直接同步不可用,可以搜索在线程序来同步(注意,同步要同步两次,同步一次可能不会生效)。方法如下:
通过build界面在线搜索
功能解决无法同步程序到网页端的方法:https://psychopy.org/online/sharingExperiments.html
在这里先打开浏览器的开发者选项查看报错是什么,Chrome和Edge浏览器是按住Ctrl+Shift
再按I
。如果报错是connection相关的,那就可以参考下方的解决方法,如果报错是syntax相关的,那就参考这个解决方法
原因: 导致程序卡在白屏位置是因为Psychopy的online实验网站Pavlovia在线加载程序时需要依赖nmp开源代码(具体是什么我也不清楚,理解为网页端需要的资源即可)。其中涉及到js和CSS代码,都是在线程序打开需要用的基础代码,且这些代码是开源的。而Pavlovia使用的这个代码是jsdeliver网站下的,这个网站由于网站证书到期,没有续期,所以被国内屏蔽,就导致在线程序需要的网页资源无法加载,所以就卡在白屏位置。
解决方法:
-
在上传实验后,在你电脑上程序文件夹里应该有一个
index.html
网页文件,这个文件在你每次修改并上传程序后都会被覆盖,且里面的内容都会被修改为默认内容。这个是需要注意的。 -
在你的在线仓库里(不知道这是什么的自己查一下),同样也会有
index.html
这个文件。打开这个文件,修改下图中的第8、14、15、16行网址,修改内容如下:将
https://cdn.jsdelivr.net/npm/
改为https://unpkg.com/
,其他部分保持不变,或者直接用后面这个网址替换上述几行网址代码也行:https://unpkg.com/jquery-ui-dist@1.12.1/jquery-ui.min.css
-
这个的原理就是换一个开源代码的源,其他源可以参考这个知乎回答:jsdelivr cdn报错无法访问
在线实验存在一个被试重复多次参加的情况,为了避免这种问题造成数据质量下降,可以增加获取IP地址来对数据进行检查,辅助判别是不是有重复参加实验的被试。
在程序的开头,增加一个Code组件,然后在Begin Experiment加上如下代码,代码的模式设置为both
,代码只放右边JS部分,左边python部分为空(参考下图):
|
|
Offline Experiment
-
在程序指导语处加一个code组件
-
在code组件的 Before Experiment选项卡 中放入代码:
1
import re # 导入re模块
-
在code组件的 Begin Experiment选项卡 中放入代码:
1 2 3 4 5 6 7
sub_name = expInfo['participant'] sub = re.findall(r"\d+\.?\d*", sub_name) sub_num = int(sub[0]) if (sub_num % 2) == 0: keybalance = "2" else: keybalance = "1"
第3步代码解释:
以字符串和数字组合为例,如被试号:S20
|
|
分离字符S
和数字20
|
|
然后用 sub_num % 2 == 0
的表达方式判断奇偶,进行按键平衡
|
|
参考:
Counterbalancing across participants
这个方法没有在线上实验程序中尝试,因为本地运行时,代码import re
在自动转换成js代码时会报错。因为编写的程序是线下版,就没有测试线上程序。后续这个功能会加入到线上程序中,然后更新本文内容。
在帖子回答中用ctrl+F
搜索 Independent Randomisation 这个 Demo,下载查看程序就知道怎么回事。
本Tip来源:wyh_task
本Tip需要结合本部分的Tip2才能明白什么回事。
例如:加载了一个List:list_motivation_cue_4 = []
,然后在循环中对这个List写入数据:list_motivation_cue_4.append(motivation)
,写入的数据就是excel条件中motivation
这个变量,然后打乱这个List:random.shuffle(list_motivation_cue_4)
,传给后面的程序使用。参考下面代码:
|
|
参考内容
-
Psychopy论坛:https://discourse.psychopy.org/
-
代码仓库地址:https://gitlab.pavlovia.org/
-
在线程序地址:https://pavlovia.org/#main
-
视角计算工具网站:https://www.sr-research.com/visual-angle-calculator/
-
网页端已经支持组件列表:https://psychopy.org/online/status.html
-
python代码和js代码转换文档:https://discourse.psychopy.org/t/psychopy-python-to-javascript-crib-sheet/14601
-
网页端运行时产生“Unknown Resource” issue报错的原因:
-
⭐参考书籍推荐:
《实验编程:PsychoPy从入门到精通》