2
0

general_grasp_training.py 61 KB

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