general_grasp_training.py 60 KB

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