general_grasp_training.py 60 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. """
  4. This experiment was created using PsychoPy3 Experiment Builder (v2023.2.3),
  5. on Wed Dec 6 17:50:50 2023
  6. If you publish work using this script the most relevant publication is:
  7. Peirce J, Gray JR, Simpson S, MacAskill M, Höchenberger R, Sogo H, Kastman E, Lindeløv JK. (2019)
  8. PsychoPy2: Experiments in behavior made easy Behav Res 51: 195.
  9. https://doi.org/10.3758/s13428-018-01193-y
  10. """
  11. # --- Import packages ---
  12. from psychopy import locale_setup
  13. from psychopy import prefs
  14. from psychopy import plugins
  15. plugins.activatePlugins()
  16. prefs.hardware['audioLib'] = 'ptb'
  17. prefs.hardware['audioLatencyMode'] = '3'
  18. from psychopy import sound, gui, visual, core, data, event, logging, clock, colors, layout
  19. from psychopy.tools import environmenttools
  20. from psychopy.constants import (NOT_STARTED, STARTED, PLAYING, PAUSED,
  21. STOPPED, FINISHED, PRESSED, RELEASED, FOREVER, priority)
  22. import numpy as np # whole numpy lib is available, prepend 'np.'
  23. from numpy import (sin, cos, tan, log, log10, pi, average,
  24. sqrt, std, deg2rad, rad2deg, linspace, asarray)
  25. from numpy.random import random, randint, normal, shuffle, choice as randchoice
  26. import os # handy system and path functions
  27. import sys # to get file system encoding
  28. from psychopy.hardware import keyboard
  29. # Run 'Before Experiment' code from config
  30. import os
  31. import datetime
  32. from time import sleep
  33. import argparse
  34. from device.data_client import NeuracleDataClient
  35. from device.trigger_box import TriggerNeuracle
  36. from device.fubo_pneumatic_finger import FuboPneumaticFingerClient
  37. from settings.config import settings
  38. from bci_core.online import Controller
  39. from settings.config import settings
  40. config_info = settings.CONFIG_INFO
  41. # get train params
  42. def parse_args():
  43. parser = argparse.ArgumentParser(
  44. description='Hand gesture train'
  45. )
  46. parser.add_argument(
  47. '--subj',
  48. dest='subj',
  49. help='Subject name',
  50. default=None,
  51. type=str
  52. )
  53. parser.add_argument(
  54. '--n-trials',
  55. dest='n_trials',
  56. help='Trial number',
  57. type=int,
  58. )
  59. parser.add_argument(
  60. '--hand-feedback',
  61. dest='hand_feedback',
  62. action='store_true',
  63. )
  64. parser.add_argument(
  65. '--com',
  66. dest='com',
  67. help='Peripheral serial port',
  68. type=str
  69. )
  70. parser.add_argument(
  71. '--finger-model',
  72. '-fm',
  73. dest='finger_model',
  74. help='Gesture to train',
  75. type=str
  76. )
  77. parser.add_argument(
  78. '--virtual-feedback-rate',
  79. '-vfr',
  80. dest='virtual_feedback_rate',
  81. help='Virtual feedback rate',
  82. type=float
  83. )
  84. parser.add_argument(
  85. '--difficulty',
  86. help='Task difficultys',
  87. type=str
  88. )
  89. parser.add_argument(
  90. '--model-path',
  91. dest='model_path',
  92. help='Path to model file',
  93. default=None,
  94. type=str
  95. )
  96. return parser.parse_args()
  97. args = parse_args()
  98. # connect neo
  99. receiver = NeuracleDataClient(n_channel=len(config_info['channel_labels']),
  100. samplerate=config_info['sample_rate'],
  101. host=config_info['host'],
  102. port=config_info['port'],
  103. buffer_len=config_info['buffer_length'])
  104. # connect to trigger box
  105. trigger = TriggerNeuracle()
  106. if args.hand_feedback:
  107. # connect to mechanical hand
  108. hand_device = FuboPneumaticFingerClient({'port': args.com})
  109. # build bci controller
  110. controller = Controller(args.virtual_feedback_rate,
  111. args.model_path)
  112. # Run 'Before Experiment' code from decision
  113. cnt_threshold_table = {
  114. 'easy': 3,
  115. 'mid': 4,
  116. 'hard': 5
  117. }
  118. cnt_threshold = cnt_threshold_table[args.difficulty]
  119. correct_cnt = 0
  120. # --- Setup global variables (available in all functions) ---
  121. # Ensure that relative paths start from the same directory as this script
  122. _thisDir = os.path.dirname(os.path.abspath(__file__))
  123. # Store info about the experiment session
  124. psychopyVersion = '2023.2.3'
  125. expName = 'train' # from the Builder filename that created this script
  126. expInfo = {
  127. 'participant': f"{randint(0, 999999):06.0f}",
  128. 'session': '001',
  129. 'date': data.getDateStr(), # add a simple timestamp
  130. 'expName': expName,
  131. 'psychopyVersion': psychopyVersion,
  132. }
  133. def showExpInfoDlg(expInfo):
  134. """
  135. Show participant info dialog.
  136. Parameters
  137. ==========
  138. expInfo : dict
  139. Information about this experiment, created by the `setupExpInfo` function.
  140. Returns
  141. ==========
  142. dict
  143. Information about this experiment.
  144. """
  145. # temporarily remove keys which the dialog doesn't need to show
  146. poppedKeys = {
  147. 'date': expInfo.pop('date', data.getDateStr()),
  148. 'expName': expInfo.pop('expName', expName),
  149. 'psychopyVersion': expInfo.pop('psychopyVersion', psychopyVersion),
  150. }
  151. # show participant info dialog
  152. dlg = gui.DlgFromDict(dictionary=expInfo, sortKeys=False, title=expName)
  153. if dlg.OK == False:
  154. core.quit() # user pressed cancel
  155. # restore hidden keys
  156. expInfo.update(poppedKeys)
  157. # return expInfo
  158. return expInfo
  159. def setupData(expInfo, dataDir=None):
  160. """
  161. Make an ExperimentHandler to handle trials and saving.
  162. Parameters
  163. ==========
  164. expInfo : dict
  165. Information about this experiment, created by the `setupExpInfo` function.
  166. dataDir : Path, str or None
  167. Folder to save the data to, leave as None to create a folder in the current directory.
  168. Returns
  169. ==========
  170. psychopy.data.ExperimentHandler
  171. Handler object for this experiment, contains the data to save and information about
  172. where to save it to.
  173. """
  174. # data file name stem = absolute path + name; later add .psyexp, .csv, .log, etc
  175. if dataDir is None:
  176. dataDir = _thisDir
  177. filename = u'data/%s_%s_%s' % (expInfo['participant'], expName, expInfo['date'])
  178. # make sure filename is relative to dataDir
  179. if os.path.isabs(filename):
  180. dataDir = os.path.commonprefix([dataDir, filename])
  181. filename = os.path.relpath(filename, dataDir)
  182. # an ExperimentHandler isn't essential but helps with data saving
  183. thisExp = data.ExperimentHandler(
  184. name=expName, version='',
  185. extraInfo=expInfo, runtimeInfo=None,
  186. originPath='/Users/dingkunliu/Projects/MI-BCI-Proj/kraken/backend/general_grasp_training.py',
  187. savePickle=True, saveWideText=True,
  188. dataFileName=dataDir + os.sep + filename, sortColumns='time'
  189. )
  190. thisExp.setPriority('thisRow.t', priority.CRITICAL)
  191. thisExp.setPriority('expName', priority.LOW)
  192. # return experiment handler
  193. return thisExp
  194. def setupLogging(filename):
  195. """
  196. Setup a log file and tell it what level to log at.
  197. Parameters
  198. ==========
  199. filename : str or pathlib.Path
  200. Filename to save log file and data files as, doesn't need an extension.
  201. Returns
  202. ==========
  203. psychopy.logging.LogFile
  204. Text stream to receive inputs from the logging system.
  205. """
  206. # this outputs to the screen, not a file
  207. logging.console.setLevel(logging.DEBUG)
  208. # save a log file for detail verbose info
  209. logFile = logging.LogFile(filename+'.log', level=logging.DEBUG)
  210. return logFile
  211. def setupWindow(expInfo=None, win=None):
  212. """
  213. Setup the Window
  214. Parameters
  215. ==========
  216. expInfo : dict
  217. Information about this experiment, created by the `setupExpInfo` function.
  218. win : psychopy.visual.Window
  219. Window to setup - leave as None to create a new window.
  220. Returns
  221. ==========
  222. psychopy.visual.Window
  223. Window in which to run this experiment.
  224. """
  225. if win is None:
  226. # if not given a window to setup, make one
  227. win = visual.Window(
  228. size=[1440, 900], fullscr=True, screen=0,
  229. winType='pyglet', allowStencil=False,
  230. monitor='testMonitor', color=[1,1,1], colorSpace='rgb',
  231. backgroundImage='', backgroundFit='none',
  232. blendMode='avg', useFBO=True,
  233. units='height'
  234. )
  235. if expInfo is not None:
  236. # store frame rate of monitor if we can measure it
  237. expInfo['frameRate'] = win.getActualFrameRate()
  238. else:
  239. # if we have a window, just set the attributes which are safe to set
  240. win.color = [1,1,1]
  241. win.colorSpace = 'rgb'
  242. win.backgroundImage = ''
  243. win.backgroundFit = 'none'
  244. win.units = 'height'
  245. win.mouseVisible = False
  246. win.hideMessage()
  247. return win
  248. def setupInputs(expInfo, thisExp, win):
  249. """
  250. Setup whatever inputs are available (mouse, keyboard, eyetracker, etc.)
  251. Parameters
  252. ==========
  253. expInfo : dict
  254. Information about this experiment, created by the `setupExpInfo` function.
  255. thisExp : psychopy.data.ExperimentHandler
  256. Handler object for this experiment, contains the data to save and information about
  257. where to save it to.
  258. win : psychopy.visual.Window
  259. Window in which to run this experiment.
  260. Returns
  261. ==========
  262. dict
  263. Dictionary of input devices by name.
  264. """
  265. # --- Setup input devices ---
  266. inputs = {}
  267. ioConfig = {}
  268. ioSession = ioServer = eyetracker = None
  269. # create a default keyboard (e.g. to check for escape)
  270. defaultKeyboard = keyboard.Keyboard(backend='ptb')
  271. # return inputs dict
  272. return {
  273. 'ioServer': ioServer,
  274. 'defaultKeyboard': defaultKeyboard,
  275. 'eyetracker': eyetracker,
  276. }
  277. def pauseExperiment(thisExp, inputs=None, win=None, timers=[], playbackComponents=[]):
  278. """
  279. Pause this experiment, preventing the flow from advancing to the next routine until resumed.
  280. Parameters
  281. ==========
  282. thisExp : psychopy.data.ExperimentHandler
  283. Handler object for this experiment, contains the data to save and information about
  284. where to save it to.
  285. inputs : dict
  286. Dictionary of input devices by name.
  287. win : psychopy.visual.Window
  288. Window for this experiment.
  289. timers : list, tuple
  290. List of timers to reset once pausing is finished.
  291. playbackComponents : list, tuple
  292. List of any components with a `pause` method which need to be paused.
  293. """
  294. # if we are not paused, do nothing
  295. if thisExp.status != PAUSED:
  296. return
  297. # pause any playback components
  298. for comp in playbackComponents:
  299. comp.pause()
  300. # prevent components from auto-drawing
  301. win.stashAutoDraw()
  302. # run a while loop while we wait to unpause
  303. while thisExp.status == PAUSED:
  304. # make sure we have a keyboard
  305. if inputs is None:
  306. inputs = {
  307. 'defaultKeyboard': keyboard.Keyboard(backend='PsychToolbox')
  308. }
  309. # check for quit (typically the Esc key)
  310. if inputs['defaultKeyboard'].getKeys(keyList=['escape']):
  311. endExperiment(thisExp, win=win, inputs=inputs)
  312. # flip the screen
  313. win.flip()
  314. # if stop was requested while paused, quit
  315. if thisExp.status == FINISHED:
  316. endExperiment(thisExp, inputs=inputs, win=win)
  317. # resume any playback components
  318. for comp in playbackComponents:
  319. comp.play()
  320. # restore auto-drawn components
  321. win.retrieveAutoDraw()
  322. # reset any timers
  323. for timer in timers:
  324. timer.reset()
  325. def run(expInfo, thisExp, win, inputs, globalClock=None, thisSession=None):
  326. """
  327. Run the experiment flow.
  328. Parameters
  329. ==========
  330. expInfo : dict
  331. Information about this experiment, created by the `setupExpInfo` function.
  332. thisExp : psychopy.data.ExperimentHandler
  333. Handler object for this experiment, contains the data to save and information about
  334. where to save it to.
  335. psychopy.visual.Window
  336. Window in which to run this experiment.
  337. inputs : dict
  338. Dictionary of input devices by name.
  339. globalClock : psychopy.core.clock.Clock or None
  340. Clock to get global time from - supply None to make a new one.
  341. thisSession : psychopy.session.Session or None
  342. Handle of the Session object this experiment is being run from, if any.
  343. """
  344. # mark experiment as started
  345. thisExp.status = STARTED
  346. # make sure variables created by exec are available globally
  347. exec = environmenttools.setExecEnvironment(globals())
  348. # get device handles from dict of input devices
  349. ioServer = inputs['ioServer']
  350. defaultKeyboard = inputs['defaultKeyboard']
  351. eyetracker = inputs['eyetracker']
  352. # make sure we're running in the directory for this experiment
  353. os.chdir(_thisDir)
  354. # get filename from ExperimentHandler for convenience
  355. filename = thisExp.dataFileName
  356. frameTolerance = 0.001 # how close to onset before 'same' frame
  357. endExpNow = False # flag for 'escape' or other condition => quit the exp
  358. # get frame duration from frame rate in expInfo
  359. if 'frameRate' in expInfo and expInfo['frameRate'] is not None:
  360. frameDur = 1.0 / round(expInfo['frameRate'])
  361. else:
  362. frameDur = 1.0 / 60.0 # could not measure, so guess
  363. # Start Code - component code to be run after the window creation
  364. # --- Initialize components for Routine "before_mi" ---
  365. train_position = visual.TextStim(win=win, name='train_position',
  366. text='训练部位:右手',
  367. font='Open Sans',
  368. pos=(0, 0), height=0.05, wrapWidth=None, ori=0.0,
  369. color='black', colorSpace='rgb', opacity=None,
  370. languageStyle='LTR',
  371. depth=0.0);
  372. instruction = visual.TextStim(win=win, name='instruction',
  373. text='准备进行一般抓握训练,\n按空格键继续',
  374. font='Open Sans',
  375. pos=(0, 0), height=0.05, wrapWidth=None, ori=0.0,
  376. color='black', colorSpace='rgb', opacity=None,
  377. languageStyle='LTR',
  378. depth=-1.0);
  379. key_resp = keyboard.Keyboard()
  380. # --- Initialize components for Routine "mi_prepare" ---
  381. text = visual.TextStim(win=win, name='text',
  382. text='请准备',
  383. font='Open Sans',
  384. pos=(0, 0), height=0.05, wrapWidth=None, ori=0.0,
  385. color='black', colorSpace='rgb', opacity=None,
  386. languageStyle='LTR',
  387. depth=-1.0);
  388. # --- Initialize components for Routine "mi_begin" ---
  389. img_right = visual.ImageStim(
  390. win=win,
  391. name='img_right',
  392. image='static/images/hand_move.png', mask=None, anchor='center',
  393. ori=0.0, pos=(0, 0), size=None,
  394. color=[1,1,1], colorSpace='rgb', opacity=None,
  395. flipHoriz=False, flipVert=False,
  396. texRes=128.0, interpolate=True, depth=0.0)
  397. # --- Initialize components for Routine "decision" ---
  398. # --- Initialize components for Routine "mi_feedback" ---
  399. feedback = visual.TextStim(win=win, name='feedback',
  400. text=None,
  401. font='Open Sans',
  402. pos=(0, 0), height=0.05, wrapWidth=None, ori=0.0,
  403. color='black', colorSpace='rgb', opacity=None,
  404. languageStyle='LTR',
  405. depth=0.0);
  406. # --- Initialize components for Routine "mi_feedback_2" ---
  407. feedback_2 = visual.TextStim(win=win, name='feedback_2',
  408. text=None,
  409. font='Open Sans',
  410. pos=(0, 0), height=0.05, wrapWidth=None, ori=0.0,
  411. color='white', colorSpace='rgb', opacity=None,
  412. languageStyle='LTR',
  413. depth=0.0);
  414. # --- Initialize components for Routine "mi_rest" ---
  415. img_rest = visual.ImageStim(
  416. win=win,
  417. name='img_rest',
  418. image='static/images/rest.png', mask=None, anchor='center',
  419. ori=0.0, pos=(0, 0), size=None,
  420. color=[1,1,1], colorSpace='rgb', opacity=None,
  421. flipHoriz=False, flipVert=False,
  422. texRes=128.0, interpolate=True, depth=0.0)
  423. # --- Initialize components for Routine "end" ---
  424. mi_end = visual.TextStim(win=win, name='mi_end',
  425. text='结束实验',
  426. font='Open Sans',
  427. pos=(0, 0), height=0.05, wrapWidth=None, ori=0.0,
  428. color='black', colorSpace='rgb', opacity=None,
  429. languageStyle='LTR',
  430. depth=0.0);
  431. # create some handy timers
  432. if globalClock is None:
  433. globalClock = core.Clock() # to track the time since experiment started
  434. if ioServer is not None:
  435. ioServer.syncClock(globalClock)
  436. logging.setDefaultClock(globalClock)
  437. routineTimer = core.Clock() # to track time remaining of each (possibly non-slip) routine
  438. win.flip() # flip window to reset last flip timer
  439. # store the exact time the global clock started
  440. expInfo['expStart'] = data.getDateStr(format='%Y-%m-%d %Hh%M.%S.%f %z', fractionalSecondDigits=6)
  441. # --- Prepare to start Routine "before_mi" ---
  442. continueRoutine = True
  443. # update component parameters for each repeat
  444. thisExp.addData('before_mi.started', globalClock.getTime())
  445. key_resp.keys = []
  446. key_resp.rt = []
  447. _key_resp_allKeys = []
  448. # keep track of which components have finished
  449. before_miComponents = [train_position, instruction, key_resp]
  450. for thisComponent in before_miComponents:
  451. thisComponent.tStart = None
  452. thisComponent.tStop = None
  453. thisComponent.tStartRefresh = None
  454. thisComponent.tStopRefresh = None
  455. if hasattr(thisComponent, 'status'):
  456. thisComponent.status = NOT_STARTED
  457. # reset timers
  458. t = 0
  459. _timeToFirstFrame = win.getFutureFlipTime(clock="now")
  460. frameN = -1
  461. # --- Run Routine "before_mi" ---
  462. routineForceEnded = not continueRoutine
  463. while continueRoutine:
  464. # get current time
  465. t = routineTimer.getTime()
  466. tThisFlip = win.getFutureFlipTime(clock=routineTimer)
  467. tThisFlipGlobal = win.getFutureFlipTime(clock=None)
  468. frameN = frameN + 1 # number of completed frames (so 0 is the first frame)
  469. # update/draw components on each frame
  470. # *train_position* updates
  471. # if train_position is starting this frame...
  472. if train_position.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
  473. # keep track of start time/frame for later
  474. train_position.frameNStart = frameN # exact frame index
  475. train_position.tStart = t # local t and not account for scr refresh
  476. train_position.tStartRefresh = tThisFlipGlobal # on global time
  477. win.timeOnFlip(train_position, 'tStartRefresh') # time at next scr refresh
  478. # add timestamp to datafile
  479. thisExp.timestampOnFlip(win, 'train_position.started')
  480. # update status
  481. train_position.status = STARTED
  482. train_position.setAutoDraw(True)
  483. # if train_position is active this frame...
  484. if train_position.status == STARTED:
  485. # update params
  486. pass
  487. # if train_position is stopping this frame...
  488. if train_position.status == STARTED:
  489. # is it time to stop? (based on global clock, using actual start)
  490. if tThisFlipGlobal > train_position.tStartRefresh + 2-frameTolerance:
  491. # keep track of stop time/frame for later
  492. train_position.tStop = t # not accounting for scr refresh
  493. train_position.frameNStop = frameN # exact frame index
  494. # add timestamp to datafile
  495. thisExp.timestampOnFlip(win, 'train_position.stopped')
  496. # update status
  497. train_position.status = FINISHED
  498. train_position.setAutoDraw(False)
  499. # *instruction* updates
  500. # if instruction is starting this frame...
  501. if instruction.status == NOT_STARTED and tThisFlip >= 2-frameTolerance:
  502. # keep track of start time/frame for later
  503. instruction.frameNStart = frameN # exact frame index
  504. instruction.tStart = t # local t and not account for scr refresh
  505. instruction.tStartRefresh = tThisFlipGlobal # on global time
  506. win.timeOnFlip(instruction, 'tStartRefresh') # time at next scr refresh
  507. # add timestamp to datafile
  508. thisExp.timestampOnFlip(win, 'instruction.started')
  509. # update status
  510. instruction.status = STARTED
  511. instruction.setAutoDraw(True)
  512. # if instruction is active this frame...
  513. if instruction.status == STARTED:
  514. # update params
  515. pass
  516. # *key_resp* updates
  517. waitOnFlip = False
  518. # if key_resp is starting this frame...
  519. if key_resp.status == NOT_STARTED and tThisFlip >= 2-frameTolerance:
  520. # keep track of start time/frame for later
  521. key_resp.frameNStart = frameN # exact frame index
  522. key_resp.tStart = t # local t and not account for scr refresh
  523. key_resp.tStartRefresh = tThisFlipGlobal # on global time
  524. win.timeOnFlip(key_resp, 'tStartRefresh') # time at next scr refresh
  525. # add timestamp to datafile
  526. thisExp.timestampOnFlip(win, 'key_resp.started')
  527. # update status
  528. key_resp.status = STARTED
  529. # keyboard checking is just starting
  530. waitOnFlip = True
  531. win.callOnFlip(key_resp.clock.reset) # t=0 on next screen flip
  532. win.callOnFlip(key_resp.clearEvents, eventType='keyboard') # clear events on next screen flip
  533. if key_resp.status == STARTED and not waitOnFlip:
  534. theseKeys = key_resp.getKeys(keyList=['space'], ignoreKeys=["escape"], waitRelease=False)
  535. _key_resp_allKeys.extend(theseKeys)
  536. if len(_key_resp_allKeys):
  537. key_resp.keys = _key_resp_allKeys[-1].name # just the last key pressed
  538. key_resp.rt = _key_resp_allKeys[-1].rt
  539. key_resp.duration = _key_resp_allKeys[-1].duration
  540. # a response ends the routine
  541. continueRoutine = False
  542. # check for quit (typically the Esc key)
  543. if defaultKeyboard.getKeys(keyList=["escape"]):
  544. thisExp.status = FINISHED
  545. if thisExp.status == FINISHED or endExpNow:
  546. endExperiment(thisExp, inputs=inputs, win=win)
  547. return
  548. # check if all components have finished
  549. if not continueRoutine: # a component has requested a forced-end of Routine
  550. routineForceEnded = True
  551. break
  552. continueRoutine = False # will revert to True if at least one component still running
  553. for thisComponent in before_miComponents:
  554. if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
  555. continueRoutine = True
  556. break # at least one component has not yet finished
  557. # refresh the screen
  558. if continueRoutine: # don't flip if this routine is over or we'll get a blank screen
  559. win.flip()
  560. # --- Ending Routine "before_mi" ---
  561. for thisComponent in before_miComponents:
  562. if hasattr(thisComponent, "setAutoDraw"):
  563. thisComponent.setAutoDraw(False)
  564. thisExp.addData('before_mi.stopped', globalClock.getTime())
  565. # check responses
  566. if key_resp.keys in ['', [], None]: # No response was made
  567. key_resp.keys = None
  568. thisExp.addData('key_resp.keys',key_resp.keys)
  569. if key_resp.keys != None: # we had a response
  570. thisExp.addData('key_resp.rt', key_resp.rt)
  571. thisExp.addData('key_resp.duration', key_resp.duration)
  572. thisExp.nextEntry()
  573. # the Routine "before_mi" was not non-slip safe, so reset the non-slip timer
  574. routineTimer.reset()
  575. # set up handler to look after randomisation of conditions etc
  576. trials = data.TrialHandler(nReps=args.n_trials, method='sequential',
  577. extraInfo=expInfo, originPath=-1,
  578. trialList=[None],
  579. seed=None, name='trials')
  580. thisExp.addLoop(trials) # add the loop to the experiment
  581. thisTrial = trials.trialList[0] # so we can initialise stimuli with some values
  582. # abbreviate parameter names if possible (e.g. rgb = thisTrial.rgb)
  583. if thisTrial != None:
  584. for paramName in thisTrial:
  585. globals()[paramName] = thisTrial[paramName]
  586. for thisTrial in trials:
  587. currentLoop = trials
  588. thisExp.timestampOnFlip(win, 'thisRow.t')
  589. # pause experiment here if requested
  590. if thisExp.status == PAUSED:
  591. pauseExperiment(
  592. thisExp=thisExp,
  593. inputs=inputs,
  594. win=win,
  595. timers=[routineTimer],
  596. playbackComponents=[]
  597. )
  598. # abbreviate parameter names if possible (e.g. rgb = thisTrial.rgb)
  599. if thisTrial != None:
  600. for paramName in thisTrial:
  601. globals()[paramName] = thisTrial[paramName]
  602. # --- Prepare to start Routine "mi_prepare" ---
  603. continueRoutine = True
  604. # update component parameters for each repeat
  605. thisExp.addData('mi_prepare.started', globalClock.getTime())
  606. # Run 'Begin Routine' code from initialize_buffer
  607. decision_buffer = []
  608. # keep track of which components have finished
  609. mi_prepareComponents = [text]
  610. for thisComponent in mi_prepareComponents:
  611. thisComponent.tStart = None
  612. thisComponent.tStop = None
  613. thisComponent.tStartRefresh = None
  614. thisComponent.tStopRefresh = None
  615. if hasattr(thisComponent, 'status'):
  616. thisComponent.status = NOT_STARTED
  617. # reset timers
  618. t = 0
  619. _timeToFirstFrame = win.getFutureFlipTime(clock="now")
  620. frameN = -1
  621. # --- Run Routine "mi_prepare" ---
  622. routineForceEnded = not continueRoutine
  623. while continueRoutine and routineTimer.getTime() < 1.5:
  624. # get current time
  625. t = routineTimer.getTime()
  626. tThisFlip = win.getFutureFlipTime(clock=routineTimer)
  627. tThisFlipGlobal = win.getFutureFlipTime(clock=None)
  628. frameN = frameN + 1 # number of completed frames (so 0 is the first frame)
  629. # update/draw components on each frame
  630. # *text* updates
  631. # if text is starting this frame...
  632. if text.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
  633. # keep track of start time/frame for later
  634. text.frameNStart = frameN # exact frame index
  635. text.tStart = t # local t and not account for scr refresh
  636. text.tStartRefresh = tThisFlipGlobal # on global time
  637. win.timeOnFlip(text, 'tStartRefresh') # time at next scr refresh
  638. # add timestamp to datafile
  639. thisExp.timestampOnFlip(win, 'text.started')
  640. # update status
  641. text.status = STARTED
  642. text.setAutoDraw(True)
  643. # if text is active this frame...
  644. if text.status == STARTED:
  645. # update params
  646. pass
  647. # if text is stopping this frame...
  648. if text.status == STARTED:
  649. # is it time to stop? (based on global clock, using actual start)
  650. if tThisFlipGlobal > text.tStartRefresh + 1.5-frameTolerance:
  651. # keep track of stop time/frame for later
  652. text.tStop = t # not accounting for scr refresh
  653. text.frameNStop = frameN # exact frame index
  654. # add timestamp to datafile
  655. thisExp.timestampOnFlip(win, 'text.stopped')
  656. # update status
  657. text.status = FINISHED
  658. text.setAutoDraw(False)
  659. # check for quit (typically the Esc key)
  660. if defaultKeyboard.getKeys(keyList=["escape"]):
  661. thisExp.status = FINISHED
  662. if thisExp.status == FINISHED or endExpNow:
  663. endExperiment(thisExp, inputs=inputs, win=win)
  664. return
  665. # check if all components have finished
  666. if not continueRoutine: # a component has requested a forced-end of Routine
  667. routineForceEnded = True
  668. break
  669. continueRoutine = False # will revert to True if at least one component still running
  670. for thisComponent in mi_prepareComponents:
  671. if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
  672. continueRoutine = True
  673. break # at least one component has not yet finished
  674. # refresh the screen
  675. if continueRoutine: # don't flip if this routine is over or we'll get a blank screen
  676. win.flip()
  677. # --- Ending Routine "mi_prepare" ---
  678. for thisComponent in mi_prepareComponents:
  679. if hasattr(thisComponent, "setAutoDraw"):
  680. thisComponent.setAutoDraw(False)
  681. thisExp.addData('mi_prepare.stopped', globalClock.getTime())
  682. # using non-slip timing so subtract the expected duration of this Routine (unless ended on request)
  683. if routineForceEnded:
  684. routineTimer.reset()
  685. else:
  686. routineTimer.addTime(-1.500000)
  687. # set up handler to look after randomisation of conditions etc
  688. classification = data.TrialHandler(nReps=5.0, method='sequential',
  689. extraInfo=expInfo, originPath=-1,
  690. trialList=[None],
  691. seed=None, name='classification')
  692. thisExp.addLoop(classification) # add the loop to the experiment
  693. thisClassification = classification.trialList[0] # so we can initialise stimuli with some values
  694. # abbreviate parameter names if possible (e.g. rgb = thisClassification.rgb)
  695. if thisClassification != None:
  696. for paramName in thisClassification:
  697. globals()[paramName] = thisClassification[paramName]
  698. for thisClassification in classification:
  699. currentLoop = classification
  700. thisExp.timestampOnFlip(win, 'thisRow.t')
  701. # pause experiment here if requested
  702. if thisExp.status == PAUSED:
  703. pauseExperiment(
  704. thisExp=thisExp,
  705. inputs=inputs,
  706. win=win,
  707. timers=[routineTimer],
  708. playbackComponents=[]
  709. )
  710. # abbreviate parameter names if possible (e.g. rgb = thisClassification.rgb)
  711. if thisClassification != None:
  712. for paramName in thisClassification:
  713. globals()[paramName] = thisClassification[paramName]
  714. # --- Prepare to start Routine "mi_begin" ---
  715. continueRoutine = True
  716. # update component parameters for each repeat
  717. thisExp.addData('mi_begin.started', globalClock.getTime())
  718. # Run 'Begin Routine' code from algo
  719. # send trigger
  720. current_true_label = settings.FINGERMODEL_IDS[args.finger_model]
  721. win.callOnFlip(trigger.send_trigger, current_true_label)
  722. # keep track of which components have finished
  723. mi_beginComponents = [img_right]
  724. for thisComponent in mi_beginComponents:
  725. thisComponent.tStart = None
  726. thisComponent.tStop = None
  727. thisComponent.tStartRefresh = None
  728. thisComponent.tStopRefresh = None
  729. if hasattr(thisComponent, 'status'):
  730. thisComponent.status = NOT_STARTED
  731. # reset timers
  732. t = 0
  733. _timeToFirstFrame = win.getFutureFlipTime(clock="now")
  734. frameN = -1
  735. # --- Run Routine "mi_begin" ---
  736. routineForceEnded = not continueRoutine
  737. while continueRoutine and routineTimer.getTime() < 1.0:
  738. # get current time
  739. t = routineTimer.getTime()
  740. tThisFlip = win.getFutureFlipTime(clock=routineTimer)
  741. tThisFlipGlobal = win.getFutureFlipTime(clock=None)
  742. frameN = frameN + 1 # number of completed frames (so 0 is the first frame)
  743. # update/draw components on each frame
  744. # *img_right* updates
  745. # if img_right is starting this frame...
  746. if img_right.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
  747. # keep track of start time/frame for later
  748. img_right.frameNStart = frameN # exact frame index
  749. img_right.tStart = t # local t and not account for scr refresh
  750. img_right.tStartRefresh = tThisFlipGlobal # on global time
  751. win.timeOnFlip(img_right, 'tStartRefresh') # time at next scr refresh
  752. # add timestamp to datafile
  753. thisExp.timestampOnFlip(win, 'img_right.started')
  754. # update status
  755. img_right.status = STARTED
  756. img_right.setAutoDraw(True)
  757. # if img_right is active this frame...
  758. if img_right.status == STARTED:
  759. # update params
  760. pass
  761. # if img_right is stopping this frame...
  762. if img_right.status == STARTED:
  763. # is it time to stop? (based on global clock, using actual start)
  764. if tThisFlipGlobal > img_right.tStartRefresh + 1-frameTolerance:
  765. # keep track of stop time/frame for later
  766. img_right.tStop = t # not accounting for scr refresh
  767. img_right.frameNStop = frameN # exact frame index
  768. # add timestamp to datafile
  769. thisExp.timestampOnFlip(win, 'img_right.stopped')
  770. # update status
  771. img_right.status = FINISHED
  772. img_right.setAutoDraw(False)
  773. # check for quit (typically the Esc key)
  774. if defaultKeyboard.getKeys(keyList=["escape"]):
  775. thisExp.status = FINISHED
  776. if thisExp.status == FINISHED or endExpNow:
  777. endExperiment(thisExp, inputs=inputs, win=win)
  778. return
  779. # check if all components have finished
  780. if not continueRoutine: # a component has requested a forced-end of Routine
  781. routineForceEnded = True
  782. break
  783. continueRoutine = False # will revert to True if at least one component still running
  784. for thisComponent in mi_beginComponents:
  785. if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
  786. continueRoutine = True
  787. break # at least one component has not yet finished
  788. # refresh the screen
  789. if continueRoutine: # don't flip if this routine is over or we'll get a blank screen
  790. win.flip()
  791. # --- Ending Routine "mi_begin" ---
  792. for thisComponent in mi_beginComponents:
  793. if hasattr(thisComponent, "setAutoDraw"):
  794. thisComponent.setAutoDraw(False)
  795. thisExp.addData('mi_begin.stopped', globalClock.getTime())
  796. # Run 'End Routine' code from algo
  797. data_from_buffer = receiver.get_trial_data(clear=False)
  798. decision = controller.step_decision(data_from_buffer, current_true_label)
  799. decision_buffer.append(decision)
  800. # write decision to data
  801. logging.exp('decision: {}'.format(decision))
  802. # using non-slip timing so subtract the expected duration of this Routine (unless ended on request)
  803. if routineForceEnded:
  804. routineTimer.reset()
  805. else:
  806. routineTimer.addTime(-1.000000)
  807. # completed 5.0 repeats of 'classification'
  808. # --- Prepare to start Routine "decision" ---
  809. continueRoutine = True
  810. # update component parameters for each repeat
  811. thisExp.addData('decision.started', globalClock.getTime())
  812. # Run 'Begin Routine' code from decision
  813. cnt = 0
  814. for d in decision_buffer:
  815. if d == current_true_label:
  816. cnt += 1
  817. success = False
  818. feedback_time = 2
  819. global correct_cnt
  820. if cnt >= cnt_threshold:
  821. success = True
  822. correct_cnt += 1 # count all correct trials
  823. if args.hand_feedback:
  824. feedback_time = 10
  825. # keep track of which components have finished
  826. decisionComponents = []
  827. for thisComponent in decisionComponents:
  828. thisComponent.tStart = None
  829. thisComponent.tStop = None
  830. thisComponent.tStartRefresh = None
  831. thisComponent.tStopRefresh = None
  832. if hasattr(thisComponent, 'status'):
  833. thisComponent.status = NOT_STARTED
  834. # reset timers
  835. t = 0
  836. _timeToFirstFrame = win.getFutureFlipTime(clock="now")
  837. frameN = -1
  838. # --- Run Routine "decision" ---
  839. routineForceEnded = not continueRoutine
  840. while continueRoutine:
  841. # get current time
  842. t = routineTimer.getTime()
  843. tThisFlip = win.getFutureFlipTime(clock=routineTimer)
  844. tThisFlipGlobal = win.getFutureFlipTime(clock=None)
  845. frameN = frameN + 1 # number of completed frames (so 0 is the first frame)
  846. # update/draw components on each frame
  847. # check for quit (typically the Esc key)
  848. if defaultKeyboard.getKeys(keyList=["escape"]):
  849. thisExp.status = FINISHED
  850. if thisExp.status == FINISHED or endExpNow:
  851. endExperiment(thisExp, inputs=inputs, win=win)
  852. return
  853. # check if all components have finished
  854. if not continueRoutine: # a component has requested a forced-end of Routine
  855. routineForceEnded = True
  856. break
  857. continueRoutine = False # will revert to True if at least one component still running
  858. for thisComponent in decisionComponents:
  859. if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
  860. continueRoutine = True
  861. break # at least one component has not yet finished
  862. # refresh the screen
  863. if continueRoutine: # don't flip if this routine is over or we'll get a blank screen
  864. win.flip()
  865. # --- Ending Routine "decision" ---
  866. for thisComponent in decisionComponents:
  867. if hasattr(thisComponent, "setAutoDraw"):
  868. thisComponent.setAutoDraw(False)
  869. thisExp.addData('decision.stopped', globalClock.getTime())
  870. # the Routine "decision" was not non-slip safe, so reset the non-slip timer
  871. routineTimer.reset()
  872. # --- Prepare to start Routine "mi_feedback" ---
  873. continueRoutine = True
  874. # update component parameters for each repeat
  875. thisExp.addData('mi_feedback.started', globalClock.getTime())
  876. # Run 'Begin Routine' code from code
  877. if success:
  878. feedback.text = '恭喜!'
  879. if args.hand_feedback:
  880. hand_device.start(args.finger_model)
  881. else:
  882. feedback.text = '继续努力!'
  883. # keep track of which components have finished
  884. mi_feedbackComponents = [feedback]
  885. for thisComponent in mi_feedbackComponents:
  886. thisComponent.tStart = None
  887. thisComponent.tStop = None
  888. thisComponent.tStartRefresh = None
  889. thisComponent.tStopRefresh = None
  890. if hasattr(thisComponent, 'status'):
  891. thisComponent.status = NOT_STARTED
  892. # reset timers
  893. t = 0
  894. _timeToFirstFrame = win.getFutureFlipTime(clock="now")
  895. frameN = -1
  896. # --- Run Routine "mi_feedback" ---
  897. routineForceEnded = not continueRoutine
  898. while continueRoutine:
  899. # get current time
  900. t = routineTimer.getTime()
  901. tThisFlip = win.getFutureFlipTime(clock=routineTimer)
  902. tThisFlipGlobal = win.getFutureFlipTime(clock=None)
  903. frameN = frameN + 1 # number of completed frames (so 0 is the first frame)
  904. # update/draw components on each frame
  905. # *feedback* updates
  906. # if feedback is starting this frame...
  907. if feedback.status == NOT_STARTED and tThisFlip >= 0-frameTolerance:
  908. # keep track of start time/frame for later
  909. feedback.frameNStart = frameN # exact frame index
  910. feedback.tStart = t # local t and not account for scr refresh
  911. feedback.tStartRefresh = tThisFlipGlobal # on global time
  912. win.timeOnFlip(feedback, 'tStartRefresh') # time at next scr refresh
  913. # add timestamp to datafile
  914. thisExp.timestampOnFlip(win, 'feedback.started')
  915. # update status
  916. feedback.status = STARTED
  917. feedback.setAutoDraw(True)
  918. # if feedback is active this frame...
  919. if feedback.status == STARTED:
  920. # update params
  921. pass
  922. # if feedback is stopping this frame...
  923. if feedback.status == STARTED:
  924. # is it time to stop? (based on global clock, using actual start)
  925. if tThisFlipGlobal > feedback.tStartRefresh + feedback_time / 2-frameTolerance:
  926. # keep track of stop time/frame for later
  927. feedback.tStop = t # not accounting for scr refresh
  928. feedback.frameNStop = frameN # exact frame index
  929. # add timestamp to datafile
  930. thisExp.timestampOnFlip(win, 'feedback.stopped')
  931. # update status
  932. feedback.status = FINISHED
  933. feedback.setAutoDraw(False)
  934. # check for quit (typically the Esc key)
  935. if defaultKeyboard.getKeys(keyList=["escape"]):
  936. thisExp.status = FINISHED
  937. if thisExp.status == FINISHED or endExpNow:
  938. endExperiment(thisExp, inputs=inputs, win=win)
  939. return
  940. # check if all components have finished
  941. if not continueRoutine: # a component has requested a forced-end of Routine
  942. routineForceEnded = True
  943. break
  944. continueRoutine = False # will revert to True if at least one component still running
  945. for thisComponent in mi_feedbackComponents:
  946. if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
  947. continueRoutine = True
  948. break # at least one component has not yet finished
  949. # refresh the screen
  950. if continueRoutine: # don't flip if this routine is over or we'll get a blank screen
  951. win.flip()
  952. # --- Ending Routine "mi_feedback" ---
  953. for thisComponent in mi_feedbackComponents:
  954. if hasattr(thisComponent, "setAutoDraw"):
  955. thisComponent.setAutoDraw(False)
  956. thisExp.addData('mi_feedback.stopped', globalClock.getTime())
  957. # the Routine "mi_feedback" was not non-slip safe, so reset the non-slip timer
  958. routineTimer.reset()
  959. # --- Prepare to start Routine "mi_feedback_2" ---
  960. continueRoutine = True
  961. # update component parameters for each repeat
  962. thisExp.addData('mi_feedback_2.started', globalClock.getTime())
  963. # Run 'Begin Routine' code from code_2
  964. if success:
  965. feedback_2.text = '恭喜!'
  966. if args.hand_feedback:
  967. hand_device.extend()
  968. else:
  969. feedback_2.text = '继续努力!'
  970. # keep track of which components have finished
  971. mi_feedback_2Components = [feedback_2]
  972. for thisComponent in mi_feedback_2Components:
  973. thisComponent.tStart = None
  974. thisComponent.tStop = None
  975. thisComponent.tStartRefresh = None
  976. thisComponent.tStopRefresh = None
  977. if hasattr(thisComponent, 'status'):
  978. thisComponent.status = NOT_STARTED
  979. # reset timers
  980. t = 0
  981. _timeToFirstFrame = win.getFutureFlipTime(clock="now")
  982. frameN = -1
  983. # --- Run Routine "mi_feedback_2" ---
  984. routineForceEnded = not continueRoutine
  985. while continueRoutine:
  986. # get current time
  987. t = routineTimer.getTime()
  988. tThisFlip = win.getFutureFlipTime(clock=routineTimer)
  989. tThisFlipGlobal = win.getFutureFlipTime(clock=None)
  990. frameN = frameN + 1 # number of completed frames (so 0 is the first frame)
  991. # update/draw components on each frame
  992. # *feedback_2* updates
  993. # if feedback_2 is starting this frame...
  994. if feedback_2.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
  995. # keep track of start time/frame for later
  996. feedback_2.frameNStart = frameN # exact frame index
  997. feedback_2.tStart = t # local t and not account for scr refresh
  998. feedback_2.tStartRefresh = tThisFlipGlobal # on global time
  999. win.timeOnFlip(feedback_2, 'tStartRefresh') # time at next scr refresh
  1000. # add timestamp to datafile
  1001. thisExp.timestampOnFlip(win, 'feedback_2.started')
  1002. # update status
  1003. feedback_2.status = STARTED
  1004. feedback_2.setAutoDraw(True)
  1005. # if feedback_2 is active this frame...
  1006. if feedback_2.status == STARTED:
  1007. # update params
  1008. pass
  1009. # if feedback_2 is stopping this frame...
  1010. if feedback_2.status == STARTED:
  1011. # is it time to stop? (based on global clock, using actual start)
  1012. if tThisFlipGlobal > feedback_2.tStartRefresh + feedback_time / 2-frameTolerance:
  1013. # keep track of stop time/frame for later
  1014. feedback_2.tStop = t # not accounting for scr refresh
  1015. feedback_2.frameNStop = frameN # exact frame index
  1016. # add timestamp to datafile
  1017. thisExp.timestampOnFlip(win, 'feedback_2.stopped')
  1018. # update status
  1019. feedback_2.status = FINISHED
  1020. feedback_2.setAutoDraw(False)
  1021. # check for quit (typically the Esc key)
  1022. if defaultKeyboard.getKeys(keyList=["escape"]):
  1023. thisExp.status = FINISHED
  1024. if thisExp.status == FINISHED or endExpNow:
  1025. endExperiment(thisExp, inputs=inputs, win=win)
  1026. return
  1027. # check if all components have finished
  1028. if not continueRoutine: # a component has requested a forced-end of Routine
  1029. routineForceEnded = True
  1030. break
  1031. continueRoutine = False # will revert to True if at least one component still running
  1032. for thisComponent in mi_feedback_2Components:
  1033. if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
  1034. continueRoutine = True
  1035. break # at least one component has not yet finished
  1036. # refresh the screen
  1037. if continueRoutine: # don't flip if this routine is over or we'll get a blank screen
  1038. win.flip()
  1039. # --- Ending Routine "mi_feedback_2" ---
  1040. for thisComponent in mi_feedback_2Components:
  1041. if hasattr(thisComponent, "setAutoDraw"):
  1042. thisComponent.setAutoDraw(False)
  1043. thisExp.addData('mi_feedback_2.stopped', globalClock.getTime())
  1044. # the Routine "mi_feedback_2" was not non-slip safe, so reset the non-slip timer
  1045. routineTimer.reset()
  1046. # --- Prepare to start Routine "mi_rest" ---
  1047. continueRoutine = True
  1048. # update component parameters for each repeat
  1049. thisExp.addData('mi_rest.started', globalClock.getTime())
  1050. # Run 'Begin Routine' code from trigger_rest
  1051. # send trigger
  1052. win.callOnFlip(trigger.send_trigger, 0)
  1053. # keep track of which components have finished
  1054. mi_restComponents = [img_rest]
  1055. for thisComponent in mi_restComponents:
  1056. thisComponent.tStart = None
  1057. thisComponent.tStop = None
  1058. thisComponent.tStartRefresh = None
  1059. thisComponent.tStopRefresh = None
  1060. if hasattr(thisComponent, 'status'):
  1061. thisComponent.status = NOT_STARTED
  1062. # reset timers
  1063. t = 0
  1064. _timeToFirstFrame = win.getFutureFlipTime(clock="now")
  1065. frameN = -1
  1066. # --- Run Routine "mi_rest" ---
  1067. routineForceEnded = not continueRoutine
  1068. while continueRoutine and routineTimer.getTime() < 5.0:
  1069. # get current time
  1070. t = routineTimer.getTime()
  1071. tThisFlip = win.getFutureFlipTime(clock=routineTimer)
  1072. tThisFlipGlobal = win.getFutureFlipTime(clock=None)
  1073. frameN = frameN + 1 # number of completed frames (so 0 is the first frame)
  1074. # update/draw components on each frame
  1075. # *img_rest* updates
  1076. # if img_rest is starting this frame...
  1077. if img_rest.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
  1078. # keep track of start time/frame for later
  1079. img_rest.frameNStart = frameN # exact frame index
  1080. img_rest.tStart = t # local t and not account for scr refresh
  1081. img_rest.tStartRefresh = tThisFlipGlobal # on global time
  1082. win.timeOnFlip(img_rest, 'tStartRefresh') # time at next scr refresh
  1083. # add timestamp to datafile
  1084. thisExp.timestampOnFlip(win, 'img_rest.started')
  1085. # update status
  1086. img_rest.status = STARTED
  1087. img_rest.setAutoDraw(True)
  1088. # if img_rest is active this frame...
  1089. if img_rest.status == STARTED:
  1090. # update params
  1091. pass
  1092. # if img_rest is stopping this frame...
  1093. if img_rest.status == STARTED:
  1094. # is it time to stop? (based on global clock, using actual start)
  1095. if tThisFlipGlobal > img_rest.tStartRefresh + 5-frameTolerance:
  1096. # keep track of stop time/frame for later
  1097. img_rest.tStop = t # not accounting for scr refresh
  1098. img_rest.frameNStop = frameN # exact frame index
  1099. # add timestamp to datafile
  1100. thisExp.timestampOnFlip(win, 'img_rest.stopped')
  1101. # update status
  1102. img_rest.status = FINISHED
  1103. img_rest.setAutoDraw(False)
  1104. # check for quit (typically the Esc key)
  1105. if defaultKeyboard.getKeys(keyList=["escape"]):
  1106. thisExp.status = FINISHED
  1107. if thisExp.status == FINISHED or endExpNow:
  1108. endExperiment(thisExp, inputs=inputs, win=win)
  1109. return
  1110. # check if all components have finished
  1111. if not continueRoutine: # a component has requested a forced-end of Routine
  1112. routineForceEnded = True
  1113. break
  1114. continueRoutine = False # will revert to True if at least one component still running
  1115. for thisComponent in mi_restComponents:
  1116. if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
  1117. continueRoutine = True
  1118. break # at least one component has not yet finished
  1119. # refresh the screen
  1120. if continueRoutine: # don't flip if this routine is over or we'll get a blank screen
  1121. win.flip()
  1122. # --- Ending Routine "mi_rest" ---
  1123. for thisComponent in mi_restComponents:
  1124. if hasattr(thisComponent, "setAutoDraw"):
  1125. thisComponent.setAutoDraw(False)
  1126. thisExp.addData('mi_rest.stopped', globalClock.getTime())
  1127. # using non-slip timing so subtract the expected duration of this Routine (unless ended on request)
  1128. if routineForceEnded:
  1129. routineTimer.reset()
  1130. else:
  1131. routineTimer.addTime(-5.000000)
  1132. thisExp.nextEntry()
  1133. if thisSession is not None:
  1134. # if running in a Session with a Liaison client, send data up to now
  1135. thisSession.sendExperimentData()
  1136. # completed args.n_trials repeats of 'trials'
  1137. # --- Prepare to start Routine "end" ---
  1138. continueRoutine = True
  1139. # update component parameters for each repeat
  1140. thisExp.addData('end.started', globalClock.getTime())
  1141. # Run 'Begin Routine' code from score
  1142. mi_end.text = f"实验结束,\n得分:{int(correct_cnt / args.n_trials * 100)}"
  1143. # keep track of which components have finished
  1144. endComponents = [mi_end]
  1145. for thisComponent in endComponents:
  1146. thisComponent.tStart = None
  1147. thisComponent.tStop = None
  1148. thisComponent.tStartRefresh = None
  1149. thisComponent.tStopRefresh = None
  1150. if hasattr(thisComponent, 'status'):
  1151. thisComponent.status = NOT_STARTED
  1152. # reset timers
  1153. t = 0
  1154. _timeToFirstFrame = win.getFutureFlipTime(clock="now")
  1155. frameN = -1
  1156. # --- Run Routine "end" ---
  1157. routineForceEnded = not continueRoutine
  1158. while continueRoutine and routineTimer.getTime() < 5.0:
  1159. # get current time
  1160. t = routineTimer.getTime()
  1161. tThisFlip = win.getFutureFlipTime(clock=routineTimer)
  1162. tThisFlipGlobal = win.getFutureFlipTime(clock=None)
  1163. frameN = frameN + 1 # number of completed frames (so 0 is the first frame)
  1164. # update/draw components on each frame
  1165. # *mi_end* updates
  1166. # if mi_end is starting this frame...
  1167. if mi_end.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
  1168. # keep track of start time/frame for later
  1169. mi_end.frameNStart = frameN # exact frame index
  1170. mi_end.tStart = t # local t and not account for scr refresh
  1171. mi_end.tStartRefresh = tThisFlipGlobal # on global time
  1172. win.timeOnFlip(mi_end, 'tStartRefresh') # time at next scr refresh
  1173. # add timestamp to datafile
  1174. thisExp.timestampOnFlip(win, 'mi_end.started')
  1175. # update status
  1176. mi_end.status = STARTED
  1177. mi_end.setAutoDraw(True)
  1178. # if mi_end is active this frame...
  1179. if mi_end.status == STARTED:
  1180. # update params
  1181. pass
  1182. # if mi_end is stopping this frame...
  1183. if mi_end.status == STARTED:
  1184. # is it time to stop? (based on global clock, using actual start)
  1185. if tThisFlipGlobal > mi_end.tStartRefresh + 5-frameTolerance:
  1186. # keep track of stop time/frame for later
  1187. mi_end.tStop = t # not accounting for scr refresh
  1188. mi_end.frameNStop = frameN # exact frame index
  1189. # add timestamp to datafile
  1190. thisExp.timestampOnFlip(win, 'mi_end.stopped')
  1191. # update status
  1192. mi_end.status = FINISHED
  1193. mi_end.setAutoDraw(False)
  1194. # check for quit (typically the Esc key)
  1195. if defaultKeyboard.getKeys(keyList=["escape"]):
  1196. thisExp.status = FINISHED
  1197. if thisExp.status == FINISHED or endExpNow:
  1198. endExperiment(thisExp, inputs=inputs, win=win)
  1199. return
  1200. # check if all components have finished
  1201. if not continueRoutine: # a component has requested a forced-end of Routine
  1202. routineForceEnded = True
  1203. break
  1204. continueRoutine = False # will revert to True if at least one component still running
  1205. for thisComponent in endComponents:
  1206. if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
  1207. continueRoutine = True
  1208. break # at least one component has not yet finished
  1209. # refresh the screen
  1210. if continueRoutine: # don't flip if this routine is over or we'll get a blank screen
  1211. win.flip()
  1212. # --- Ending Routine "end" ---
  1213. for thisComponent in endComponents:
  1214. if hasattr(thisComponent, "setAutoDraw"):
  1215. thisComponent.setAutoDraw(False)
  1216. thisExp.addData('end.stopped', globalClock.getTime())
  1217. # using non-slip timing so subtract the expected duration of this Routine (unless ended on request)
  1218. if routineForceEnded:
  1219. routineTimer.reset()
  1220. else:
  1221. routineTimer.addTime(-5.000000)
  1222. # Run 'End Experiment' code from config
  1223. receiver.close()
  1224. # mark experiment as finished
  1225. endExperiment(thisExp, win=win, inputs=inputs)
  1226. def saveData(thisExp):
  1227. """
  1228. Save data from this experiment
  1229. Parameters
  1230. ==========
  1231. thisExp : psychopy.data.ExperimentHandler
  1232. Handler object for this experiment, contains the data to save and information about
  1233. where to save it to.
  1234. """
  1235. filename = thisExp.dataFileName
  1236. # these shouldn't be strictly necessary (should auto-save)
  1237. thisExp.saveAsWideText(filename + '.csv', delim='auto')
  1238. thisExp.saveAsPickle(filename)
  1239. def endExperiment(thisExp, inputs=None, win=None):
  1240. """
  1241. End this experiment, performing final shut down operations.
  1242. This function does NOT close the window or end the Python process - use `quit` for this.
  1243. Parameters
  1244. ==========
  1245. thisExp : psychopy.data.ExperimentHandler
  1246. Handler object for this experiment, contains the data to save and information about
  1247. where to save it to.
  1248. inputs : dict
  1249. Dictionary of input devices by name.
  1250. win : psychopy.visual.Window
  1251. Window for this experiment.
  1252. """
  1253. if win is not None:
  1254. # remove autodraw from all current components
  1255. win.clearAutoDraw()
  1256. # Flip one final time so any remaining win.callOnFlip()
  1257. # and win.timeOnFlip() tasks get executed
  1258. win.flip()
  1259. # mark experiment handler as finished
  1260. thisExp.status = FINISHED
  1261. # shut down eyetracker, if there is one
  1262. if inputs is not None:
  1263. if 'eyetracker' in inputs and inputs['eyetracker'] is not None:
  1264. inputs['eyetracker'].setConnectionState(False)
  1265. logging.flush()
  1266. def quit(thisExp, win=None, inputs=None, thisSession=None):
  1267. """
  1268. Fully quit, closing the window and ending the Python process.
  1269. Parameters
  1270. ==========
  1271. win : psychopy.visual.Window
  1272. Window to close.
  1273. inputs : dict
  1274. Dictionary of input devices by name.
  1275. thisSession : psychopy.session.Session or None
  1276. Handle of the Session object this experiment is being run from, if any.
  1277. """
  1278. thisExp.abort() # or data files will save again on exit
  1279. # make sure everything is closed down
  1280. if win is not None:
  1281. # Flip one final time so any remaining win.callOnFlip()
  1282. # and win.timeOnFlip() tasks get executed before quitting
  1283. win.flip()
  1284. win.close()
  1285. if inputs is not None:
  1286. if 'eyetracker' in inputs and inputs['eyetracker'] is not None:
  1287. inputs['eyetracker'].setConnectionState(False)
  1288. logging.flush()
  1289. if thisSession is not None:
  1290. thisSession.stop()
  1291. # terminate Python process
  1292. core.quit()
  1293. # if running this experiment as a script...
  1294. if __name__ == '__main__':
  1295. # call all functions in order
  1296. expInfo = showExpInfoDlg(expInfo=expInfo)
  1297. thisExp = setupData(expInfo=expInfo)
  1298. logFile = setupLogging(filename=thisExp.dataFileName)
  1299. win = setupWindow(expInfo=expInfo)
  1300. inputs = setupInputs(expInfo=expInfo, thisExp=thisExp, win=win)
  1301. run(
  1302. expInfo=expInfo,
  1303. thisExp=thisExp,
  1304. win=win,
  1305. inputs=inputs
  1306. )
  1307. saveData(thisExp=thisExp)
  1308. quit(thisExp=thisExp, win=win, inputs=inputs)