一个核磁的N-BACK任务

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

适用于psychopy2022.2.5版本的Nb-back任务,用于核磁收集数据

仅作为记录与备份,大部分使用AI完成,不进行解答

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from psychopy import visual, core, event, sound, gui
import random
import os
import csv
import time
import numpy as np

########## 收集被试信息 ################
info = {'编号':'', 
        '性别\n(1-女 2-男)':['1','2'],
        '年龄':''}
# title为标题,order为内容顺序
inputDlg = gui.DlgFromDict(info, title = '信息收集', order = ['编号','性别\n(1-女 2-男)','年龄'])
if inputDlg.OK: #单击OK后的操作
    pass #占位符
else:
    core.quit()

# 打开一个1920*1080分辨率的窗口,灰色背景,非全屏,单位为像素,中心坐标是(0,0)
win = visual.Window((1920,1080), color=(128,128,128), 
                    fullscr=False, units='pix',colorSpace='rgb255')
    
## 定义注视点和指导语
fix = visual.TextStim(win, text = '+', color = 'white', height = 100, bold = False)
intro_1 = visual.ImageStim(win, image='intro/1.jpg')
intro_2 = visual.ImageStim(win, image='intro/2.jpg')
intro_3 = visual.ImageStim(win, image='intro/3.jpg')
intro_4 = visual.ImageStim(win, image='intro/4.jpg')
intro_5 = visual.ImageStim(win, image='intro/5.jpg')
## 定义刺激图片和刺激音频的路径
positionFolder = 'position/'
letterFolder = 'letter/'
puretoneFolder = 'puretone/'
## 定义试次数和注视点时间变量
nTrials_2 = 17
nTrials_3 = 18
buffertime = 10.000
fixtime = 14.000

## 定义生成2-back的刺激序列的函数
def generate_2_Back_Sequence(nTrials_2):
    stimuliSequence = np.random.randint(1, 9, nTrials_2) #取值为左闭右开,即包含下限1不包含上限9
    for n in range(3, nTrials_2):
        if np.random.rand() > 0.5:
            stimuliSequence[n] = stimuliSequence[n - 2]
        # 检查当前刺激是否是刺激序列中连续出现的相同刺激,并且出现次数超过了4次。
        if n >= 4 and np.sum(stimuliSequence[n-4:n] == stimuliSequence[n]) == 4:
            differentStimulus = np.setdiff1d(np.arange(1, 9), stimuliSequence[n])
            stimuliSequence[n] = np.random.choice(differentStimulus)
    return stimuliSequence
## 定义生成3-back的刺激序列的函数
def generate_3_Back_Sequence(nTrials_3):
    stimuliSequence = np.random.randint(1, 9, nTrials_3) #取值为左闭右开,即包含下限1不包含上限9
    for n in range(4, nTrials_3):
        if np.random.rand() > 0.5:
            stimuliSequence[n] = stimuliSequence[n - 3]
        # 检查当前刺激是否是刺激序列中连续出现的相同刺激,并且出现次数超过了4次。
        if n >= 4 and np.sum(stimuliSequence[n-4:n] == stimuliSequence[n]) == 4:
            differentStimulus = np.setdiff1d(np.arange(1, 9), stimuliSequence[n])
            stimuliSequence[n] = np.random.choice(differentStimulus)
    return stimuliSequence

########### 定义数据储存的文档 ##########
date = time.strftime("_20%y_%m_%d_%H%M", time.localtime()) #获取当前时间  
file = 'Nback_data/' + info['编号'] + '_' + date + '.csv' #文档名(含路径)  
# 写入表头
with open(file, 'a', newline='') as csvfile:
    writer = csv.writer(csvfile)
    writer.writerow(['编号', '性别', '年龄', 'Trial', 'Condition', 'Load', 'BlockType', 
                      'Stimstart', 'Stimend','RESP', 'RT', 'ACC'])

######### 定义第一个Blcok的内容——2back-Position#################
def block_2_position(win,info):
    ## 生成刺序列
    stimuliSequence = generate_2_Back_Sequence(nTrials_2)
    # 定义条件判定空列表
    conditions = []
    for trials in range(len(stimuliSequence)):
        if trials >= 2:
            if stimuliSequence[trials] == stimuliSequence[trials - 2]:
                condition = 'same'
            else:
                condition = 'diff'
        else:
            condition = 'None'
        conditions.append(condition) 
    ## 刺激呈现循环
    for trials in range(len(stimuliSequence)):
        #初始化按键和反应时
        resp = None 
        rt = None   
        # 刺激呈现0.5s
        StimStart = Clock.getTime()
        position_path = positionFolder + f'position_{stimuliSequence[trials]}.jpg'
        stim = visual.ImageStim(win, position_path)
        stim.draw()
        win.flip()
        core.wait(0.5)
        # 空白屏幕1.5s
        win.flip()
        # 在整个2秒的trial时间内持续检测按键
        while Clock.getTime() - StimStart <= 2.000:
            keys = event.getKeys(keyList=['1', '4', 'escape'], timeStamped=Clock)
            for key, time in keys:
                if key in ['1','4']:
                    resp = 1 if key == '1' else 4
                    rt = time - StimStart
                elif key == 'escape':
                    win.close()
                    core.quit()            
        if resp is None:
            rt = 10
            resp = 10 
        # ACC判断
        if trials >= 2:
            if (conditions[trials] == 'same' and resp == 1) or (conditions[trials] == 'diff' and resp == 4):
                acc = 1
            else:
                acc = 0
        else:
            acc = None 
        # 记录trial结束时间
        StimEnd = Clock.getTime()
        # 将每个trial的结果写入CSV文件
        with open(file, 'a', newline='') as csvfile:
            writer = csv.writer(csvfile)
            writer.writerow([info['编号'], info['性别\n(1-女 2-男)'], info['年龄'], stimuliSequence[trials], conditions[trials], 
                            '2', 'position', StimStart, StimEnd, resp, rt, acc])


######### 定义第二个Blcok的内容——2back-Letter #################
def block_2_letter(win,info):
    ## 生成刺序列
    stimuliSequence = generate_2_Back_Sequence(nTrials_2)
    # 定义条件判定空列表
    conditions = []
    for trials in range(len(stimuliSequence)):
        if trials >= 2:
            if stimuliSequence[trials] == stimuliSequence[trials - 2]:
                condition = 'same'
            else:
                condition = 'diff'
        else:
            condition = 'None'
        conditions.append(condition) 
    ## 刺激呈现循环
    for trials in range(len(stimuliSequence)):
        #初始化按键和反应时
        resp = None 
        rt = None  
        # 刺激呈现0.5s
        StimStart = Clock.getTime()
        letter_path = letterFolder + f'letter_{stimuliSequence[trials]}.wav'
        stim = sound.Sound(letter_path)
        stim.play()
        core.wait(0.5)
        stim = None  # 将声音对象设置为None释放资源
        # 在整个2秒的trial时间内持续检测按键
        while Clock.getTime() - StimStart <= 2.000:
            keys = event.getKeys(keyList=['1', '4', 'escape'], timeStamped=Clock)
            for key, time in keys:
                if key in ['1','4']:
                    resp = 1 if key == '1' else 4
                    rt = time - StimStart
                elif key == 'escape':
                    win.close()
                    core.quit()            
        if resp is None:
            rt = 10
            resp = 10 
        # ACC判断
        if trials >= 2:
            if (conditions[trials] == 'same' and resp == 1) or (conditions[trials] == 'diff' and resp == 4):
                acc = 1
            else:
                acc = 0
        else:
            acc = None 
        # 记录trial结束时间
        StimEnd = Clock.getTime()
        # 将每个trial的结果写入CSV文件
        with open(file, 'a', newline='') as csvfile:
            writer = csv.writer(csvfile)
            writer.writerow([info['编号'], info['性别\n(1-女 2-男)'], info['年龄'], stimuliSequence[trials], conditions[trials], 
                            '2', 'letter', StimStart, StimEnd, resp, rt, acc])


######### 定义第三个Blcok的内容——2back-Puretone #################
def block_2_puretone(win,info):
    ## 生成刺序列
    stimuliSequence = generate_2_Back_Sequence(nTrials_2)  
    # 定义条件判定空列表
    conditions = []
    for trials in range(len(stimuliSequence)):
        if trials >= 2:
            if stimuliSequence[trials] == stimuliSequence[trials - 2]:
                condition = 'same'
            else:
                condition = 'diff'
        else:
            condition = 'None'
        conditions.append(condition) 
    ## 刺激呈现循环
    for trials in range(len(stimuliSequence)):
        #初始化按键和反应时
        resp = None 
        rt = None  
        # 刺激呈现0.5s
        StimStart = Clock.getTime()
        puretone_path = puretoneFolder + f'puretone_{stimuliSequence[trials]}.wav'
        stim = sound.Sound(puretone_path)
        stim.play()
        core.wait(0.5)
        stim = None  # 将声音对象设置为None释放资源
        # 在整个2秒的trial时间内持续检测按键
        while Clock.getTime() - StimStart <= 2.000:
            keys = event.getKeys(keyList=['1', '4', 'escape'], timeStamped=Clock)
            for key, time in keys:
                if key in ['1','4']:
                    resp = 1 if key == '1' else 4
                    rt = time - StimStart
                elif key == 'escape':
                    win.close()
                    core.quit()            
        if resp is None:
            rt = 10
            resp = 10 
        # ACC判断
        if trials >= 2:
            if (conditions[trials] == 'same' and resp == 1) or (conditions[trials] == 'diff' and resp == 4):
                acc = 1
            else:
                acc = 0
        else:
            acc = None 
        # 记录trial结束时间
        StimEnd = Clock.getTime()
        # 将每个trial的结果写入CSV文件
        with open(file, 'a', newline='') as csvfile:
            writer = csv.writer(csvfile)
            writer.writerow([info['编号'], info['性别\n(1-女 2-男)'], info['年龄'], stimuliSequence[trials], conditions[trials], 
                            '2', 'puretone', StimStart, StimEnd, resp, rt, acc])
                            
######### 定义第四个Blcok的内容——Position#################
def block_3_position(win,info):
    ## 生成刺序列
    stimuliSequence = generate_3_Back_Sequence(nTrials_3)
    # 定义条件判定空列表
    conditions = []
    for trials in range(len(stimuliSequence)):
        if trials >= 3:
            if stimuliSequence[trials] == stimuliSequence[trials - 3]:
                condition = 'same'
            else:
                condition = 'diff'
        else:
            condition = 'None'
        conditions.append(condition) 
    ## 刺激呈现循环
    for trials in range(len(stimuliSequence)):
        #初始化按键和反应时
        resp = None 
        rt = None  
        # 刺激呈现0.5s
        StimStart = Clock.getTime()
        position_path = positionFolder + f'position_{stimuliSequence[trials]}.jpg'
        stim = visual.ImageStim(win, position_path)
        stim.draw()
        win.flip()
        core.wait(0.5)
        # 空白屏幕1.5s
        win.flip()
        # 在整个2秒的trial时间内持续检测按键
        while Clock.getTime() - StimStart <= 2.000:
            keys = event.getKeys(keyList=['1', '4', 'escape'], timeStamped=Clock)
            for key, time in keys:
                if key in ['1','4']:
                    resp = 1 if key == '1' else 4
                    rt = time - StimStart
                elif key == 'escape':
                    win.close()
                    core.quit()            
        if resp is None:
            rt = 10
            resp = 10 
        # ACC判断
        if trials >= 3:
            if (conditions[trials] == 'same' and resp == 1) or (conditions[trials] == 'diff' and resp == 4):
                acc = 1
            else:
                acc = 0
        else:
            acc = None 
        # 记录trial结束时间
        StimEnd = Clock.getTime()
        # 将每个trial的结果写入CSV文件
        with open(file, 'a', newline='') as csvfile:
            writer = csv.writer(csvfile)
            writer.writerow([info['编号'], info['性别\n(1-女 2-男)'], info['年龄'], stimuliSequence[trials], conditions[trials], 
                            '3', 'position', StimStart, StimEnd, resp, rt, acc])
    
######### 定义第五个Blcok的内容——Letter #################
def block_3_letter(win,info):
    ## 生成刺序列
    stimuliSequence = generate_3_Back_Sequence(nTrials_3)
    # 定义条件判定空列表
    conditions = []
    for trials in range(len(stimuliSequence)):
        if trials >= 3:
            if stimuliSequence[trials] == stimuliSequence[trials - 3]:
                condition = 'same'
            else:
                condition = 'diff'
        else:
            condition = 'None'
        conditions.append(condition) 
    ## 刺激呈现循环
    for trials in range(len(stimuliSequence)):
        #初始化按键和反应时
        resp = None 
        rt = None 
        # 刺激呈现0.5s
        StimStart = Clock.getTime()
        letter_path = letterFolder + f'letter_{stimuliSequence[trials]}.wav'
        stim = sound.Sound(letter_path)
        stim.play()
        core.wait(0.5)
        stim = None  # 将声音对象设置为None释放资源
        # 在整个2秒的trial时间内持续检测按键
        while Clock.getTime() - StimStart <= 2.000:
            keys = event.getKeys(keyList=['1', '4', 'escape'], timeStamped=Clock)
            for key, time in keys:
                if key in ['1','4']:
                    resp = 1 if key == '1' else 4
                    rt = time - StimStart
                elif key == 'escape':
                    win.close()
                    core.quit()            
        if resp is None:
            rt = 10
            resp = 10 
        # ACC判断
        if trials >= 3:
            if (conditions[trials] == 'same' and resp == 1) or (conditions[trials] == 'diff' and resp == 4):
                acc = 1
            else:
                acc = 0
        else:
            acc = None 
        # 记录trial结束时间
        StimEnd = Clock.getTime()
        # 将每个trial的结果写入CSV文件
        with open(file, 'a', newline='') as csvfile:
            writer = csv.writer(csvfile)
            writer.writerow([info['编号'], info['性别\n(1-女 2-男)'], info['年龄'], stimuliSequence[trials], conditions[trials], 
                            '3', 'letter', StimStart, StimEnd, resp, rt, acc])
                            
######### 定义第六个Blcok的内容——Puretone #################
def block_3_puretone(win,info):
    ## 生成刺序列
    stimuliSequence = generate_3_Back_Sequence(nTrials_3)  
    # 定义条件判定空列表
    conditions = []
    for trials in range(len(stimuliSequence)):
        if trials >= 3:
            if stimuliSequence[trials] == stimuliSequence[trials - 3]:
                condition = 'same'
            else:
                condition = 'diff'
        else:
            condition = 'None'
        conditions.append(condition) 
    ## 刺激呈现循环
    for trials in range(len(stimuliSequence)):
        #初始化按键和反应时
        resp = None 
        rt = None 
        # 刺激呈现0.5s
        StimStart = Clock.getTime()
        puretone_path = puretoneFolder + f'puretone_{stimuliSequence[trials]}.wav'
        stim = sound.Sound(puretone_path)
        stim.play()
        core.wait(0.5)
        stim = None  # 将声音对象设置为None释放资源
        # 在整个2秒的trial时间内持续检测按键
        while Clock.getTime() - StimStart <= 2.000:
            keys = event.getKeys(keyList=['1', '4', 'escape'], timeStamped=Clock)
            for key, time in keys:
                if key in ['1','4']:
                    resp = 1 if key == '1' else 4
                    rt = time - StimStart
                elif key == 'escape':
                    win.close()
                    core.quit()            
        if resp is None:
            rt = 10
            resp = 10 
        # ACC判断
        if trials >= 3:
            if (conditions[trials] == 'same' and resp == 1) or (conditions[trials] == 'diff' and resp == 4):
                acc = 1
            else:
                acc = 0
        else:
            acc = None 
        # 记录trial结束时间
        StimEnd = Clock.getTime()
        # 将每个trial的结果写入CSV文件
        with open(file, 'a', newline='') as csvfile:
            writer = csv.writer(csvfile)
            writer.writerow([info['编号'], info['性别\n(1-女 2-男)'], info['年龄'], stimuliSequence[trials], conditions[trials], 
                            '3', 'puretone', StimStart, StimEnd, resp, rt, acc])


#################### 实验开始,调用函数 ################
# 呈现指导语1
intro_1.draw()
win.flip()
event.waitKeys(keyList='s')
# 呈现指导语2
intro_2.draw()
win.flip()
event.waitKeys( keyList='s')

## 为整个实验创建一个时钟
Clock = core.Clock()

# 创建一个包含这些block函数的列表
blocks_2 = [block_2_position, block_2_letter, block_2_puretone]
blocks_3 = [block_3_position, block_3_letter, block_3_puretone]
# 将每种 block 复制两次,形成 6 个 block 的列表
blocks_2 = blocks_2 * 3
blocks_3 = blocks_3 * 3

## 2-BACK-实验循环-其中9个block,每个block有15个trial
# 每次循环前重新打乱 blocks_2 列表
random.shuffle(blocks_2)
# 清屏
win.flip()
# 重置时钟,呈现10s缓冲
Clock.reset()
while Clock.getTime() < buffertime:
    pass
# 第一个基线时长维持14s
fix.draw()
win.flip()
while Clock.getTime() < fixtime + buffertime:
    pass
win.flip()
# 依次执行打乱后的block函数
for block in blocks_2:
    # 调用block函数
    block(win, info)  
    # block之后的基线时长,维持14s
    FixStart = Clock.getTime()
    fix.draw()
    win.flip()
    while Clock.getTime() - FixStart < fixtime:
        pass
    win.flip()

# 呈现中间语,告诉被试即将进入3-BACK
intro_3.draw()
win.flip()
event.waitKeys(keyList='s')  

## 3-BACK-实验循环-其中9个block,每个block有15个trial
# 每次循环前重新打乱 blocks_3 列表
random.shuffle(blocks_3)
# 重置时钟
Clock.reset()
# 清屏,呈现10s缓冲
win.flip()
while Clock.getTime() < buffertime:
    pass
# 第一个基线时长维持14s
fix.draw()
win.flip()
while Clock.getTime() < fixtime + buffertime:
    pass
win.flip()
# 依次执行打乱后的block函数
for block in blocks_3:
    # 调用block函数
    block(win, info)  
    # block之后的基线时长,维持14s
    FixStart = Clock.getTime()
    fix.draw()
    win.flip()
    while Clock.getTime() - FixStart < fixtime:
        pass
    win.flip()

# 呈现结束语
intro_4.draw()
win.flip()
core.wait(1)

# 关闭窗口
win.close()    
print('Succeed!')

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值