我正在尝试在 MATLAB 中进行心理物理学实验,其中刺激的时机非常关键。我有代码来生成一个、两个和三个的序列,每个序列都会触发显示不同的刺激。显示刺激 2 时,参与者有 1 秒的时间通过按下按钮(使用 ResponsePixx 按钮盒)做出响应。
我需要每个刺激在前一个刺激之后传递 0.5 秒,这意味着对刺激 2 的响应时间与下一个刺激的传递时间重叠。所以我认为我需要以某种方式跟踪后台的按钮按下情况,同时继续提供刺激序列。
这是简化版本,只是将其简化为当前的问题:
% Create sequence of numbers
sequence = %FUNCTION OUTPUTS 1XN SEQUENCE OF ONES, TWOS, AND THREES
for iSequence = 1:length(sequence)
disp(sequence(iSequence)) % Stand-in for displaying proper stimuli
if sequence(iSequence) == 2
% wait for 1 second whilst continuing display of sequence)
% During this time, other code will run to track for participant responses
disp("Time elapsed")
end
pause (0.5)
end
我用 parfeval 做了一些测试,但没有成功 - 输出显示正确,但中断了主序列传递的时间(我假设是从并行池中获取输出的延迟,但说实话,我几乎没有并行处理的经验,所以我不确定):
delete(gcp('nocreate'))
clear
background = parpool(1);
sequence = % createTrialSequence
for iSequence = 1:length(sequence)
disp(sequence(iSequence))
if iSequence > 1
if sequence(iSequence - 1) == 2
fetchOutputs(evalButton)
end
end
if sequence(iSequence) == 2
evalButton = parfeval(background, @trackInBackground, 1);
end
pause(0.5)
end
我也尝试过使用事件和侦听器,但这也会暂停序列的传递(同样,不太熟悉它们的工作原理,因此可能会丢失一些关键的东西):
clear
event = targetAppeared;
listener = trackTargetAppearance(event);
sequence = %createTrialSequence
for iSequence = 1:length(sequence)
disp(sequence(iSequence))
if sequence(iSequence) == 2
event.isTarget
end
pause(0.5)
end
classdef targetAppeared < handle
events
targetEvent
end
methods
function isTarget(obj)
notify(obj, 'targetEvent')
end
end
end
classdef trackTargetAppearance < handle
methods
function obj = trackTargetAppearance(targetObj)
addlistener(targetObj, 'targetEvent', @trackTargetAppearance.handleEvent)
end
end
methods (Static)
function handleEvent(~, ~)
startTime = GetSecs; %get system time, from PsychToolBox
while GetSecs < (startTime + 1)
continue
end
disp("Target Response Window elapsed")
end
end
end
timer
的潜在解决方案
请原谅由于为此构建了一个最小的 GUI 而导致的代码混乱,唯一需要关注的机制是:
上例代码:
function hf = TimedResponse
global sequence ;
sequence = 1:10 ;
hf = build_gui ;
end
function myTimerFcn(~,~,hfig)
global sequence ;
global responseTimer ;
persistent iseq ;
hf = guidata(hfig) ;
if isempty(iseq) ; iseq=1 ; end
if iseq>numel(sequence) ; iseq=1 ; end
hf.lbl.String = num2str(sequence(iseq)) ;
if sequence(iseq) == 2
responseTimer = tic ;
end
iseq = iseq+1 ;
end
function StartButtonPushed(src,~)
hf = guidata(src) ;
if strcmpi(hf.t.Running,'off')
start(hf.t) ;
hf.btnStart.String = 'Stop';
else
stop(hf.t) ;
hf.btnStart.String = 'Start';
end
end
function RespButtonPushed(src,~)
global responseTimer ;
hf = guidata(src) ;
ElapsedTime = toc(responseTimer) ;
if ElapsedTime < 1
hf.lblresp.ForegroundColor = 'g' ;
hf.lblresp.String = sprintf('*** PASS *** (Time to respond to stimuli: %f seconds)',ElapsedTime) ;
else
hf.lblresp.ForegroundColor = 'r' ;
hf.lblresp.String = sprintf('*** TOO LATE *** (Time to respond to stimuli: %f seconds)',ElapsedTime) ;
end
end
function h = build_gui
h.fig = figure('unit','normalized','CloseRequestFcn',@my_closereq);
h.t = timer('Period',0.5,'ExecutionMode','fixedDelay') ;
h.t.TimerFcn = @(e,v)myTimerFcn(e,v,h.fig) ;
h.btnStart = uicontrol('Style','pushbutton','String','Start','unit','normalized','Position',[0.1 0.8 0.8 0.1],'Callback',@StartButtonPushed);
h.lbl = uicontrol('Style','text','String','0','unit','normalized','Position',[0.1 0.4 0.8 0.3],'FontSize',36);
h.btnResp = uicontrol('Style','pushbutton','String','Stimuli observed','unit','normalized','Position',[0.1 0.2 0.8 0.1],'Callback',@RespButtonPushed);
h.lblresp = uicontrol('Style','text','String','','unit','normalized','Position',[0.1 0.1 0.8 0.05],'FontSize',12);
h.seq = 1:10 ;
guidata(h.fig,h)
end
function my_closereq(src,~)
hf = guidata(src) ;
disp('Deleting timer') ;
if strcmpi(hf.t.Running,'on')
stop(hf.t) ;
end
delete(hf.t) ;
delete(hf.fig) ;
end