From a9be8de5fb9768a11b944c93a2ac28cbe720805a Mon Sep 17 00:00:00 2001 From: "AD\\WoudaFJ" <f.j.wouda@utwente.nl> Date: Thu, 17 Aug 2023 16:34:40 +0200 Subject: [PATCH] Renamed variables to make thngs clearer. TODO: inSignals should be updated. --- +math/IntSignals.m | 3 ++- +math/calculateSteps.m | 28 ++++++++++++++++++++++++++-- +math/correctSteps.m | 29 ++++++++++++++++------------- +math/initOrien.m | 35 ++++++++++++++++------------------- +math/zvt.m | 26 +++++++++++++------------- Main.m | 2 +- getCalibration.m | 3 +-- 7 files changed, 75 insertions(+), 51 deletions(-) diff --git a/+math/IntSignals.m b/+math/IntSignals.m index 3c226f3..c4890df 100644 --- a/+math/IntSignals.m +++ b/+math/IntSignals.m @@ -1,5 +1,6 @@ function data = intSignals(data,index) +% Pre-allocate structures for rotated signals. if index == 1 data.rG = nan(data.N,3); data.vG = nan(data.N,3); @@ -10,7 +11,7 @@ end rt=eye(3); % Current part of analysis. -indexes = data.tm(index):data.tm(index+1); +indexes = data.tMiddle(index):data.tMiddle(index+1); % Rotate the gyroscope data to Foot-frame. gF = (data.Rcal*data.dgyr(indexes,:)'); diff --git a/+math/calculateSteps.m b/+math/calculateSteps.m index dfaeffd..43da98e 100644 --- a/+math/calculateSteps.m +++ b/+math/calculateSteps.m @@ -6,10 +6,34 @@ % output: % dataStruct - containing calculated FPA. -function dataStruct = calculateSteps(dataStruct) +function dataStruct = calculateSteps(dataStruct, trialName) + +% Get the trial data. +data = dataStruct.(trialName); + +% Get all calibration files. +calibrationFields = fieldnames(dataStruct.calibration); + +% By default take the first calibration trial. +calibration = dataStruct.calibration.(calibrationFields{1}); + +% cut-off frequency of the filter. +fCutoff = 2; + +% 2nd order low-pass butterworth filter to get the relevant motion data. +% Implement as filtfilt due to no realtime constraints. +[Bf, Af] = butter(2, (2 * fCutoff / data.fs)); +data.filtAcc = filtfilt(Bf, Af, data.acc); +data.filtGyr = filtfilt(Bf, Af, data.gyr); + +% Step detection using the zero velocity update. +data = math.zvt(data); + +% Correct the steps. +data = math.correctSteps(data); % Number of steps is 1 smaller as it should end with foot contact. -steps = 1:(length(dataStruct.tMiddle)-1); +steps = 1:(length(data.tMiddle)-1); % Loop over all steps. for iStep = steps diff --git a/+math/correctSteps.m b/+math/correctSteps.m index f779408..fffdb2f 100644 --- a/+math/correctSteps.m +++ b/+math/correctSteps.m @@ -9,19 +9,22 @@ function dataStruct = correctSteps(dataStruct) - -% Correct for wrong first step. -if dataStruct.tEnd(1) < dataStruct.tStart(1) - dataStruct.tEnd(1) = []; -end - -% End with a full step. -if dataStruct.tEnd(end) < dataStruct.tStart(end) - dataStruct.tStart(end) = []; +if ~isempty(dataStruct.tStart) + % Correct for wrong first step. + if dataStruct.tEnd(1) < dataStruct.tStart(1) + dataStruct.tEnd(1) = []; + end + + % End with a full step. + if dataStruct.tEnd(end) < dataStruct.tStart(end) + dataStruct.tStart(end) = []; + end + + % Get the middle of contact phases for later calculations. + dataStruct.tMiddle = floor((dataStruct.tStart(2:end) + ... + dataStruct.tEnd(1:end-1))/2); +else + disp("NO steps detected.") end -% Get the middle of contact phases for later calculations. -dataStruct.tMiddle = floor((dataStruct.tStart(2:end) + ... - dataStruct.tEnd(1:end-1))/2); - end \ No newline at end of file diff --git a/+math/initOrien.m b/+math/initOrien.m index f2fa384..f01dc45 100644 --- a/+math/initOrien.m +++ b/+math/initOrien.m @@ -13,37 +13,34 @@ % tEnd - end time of static parts. % contactVector - boolean vector containing indicators of contact. -function outStruct = initOrien(data) - -% Pre-allocate structure -outStruct = struct(); +function data = initOrien(data) % cut-off frequency of the filter. -fCutoff = 2; +data.fCutoff = 2; % 2nd order low-pass butterworth filter to get the relevant motion data. % Implement as filtfilt due to no realtime constraints. -[Bf, Af] = butter(2, (2 * fCutoff / data.fs)); -outStruct.filtAcc = filtfilt(Bf, Af, data.acc); -outStruct.filtGyr = filtfilt(Bf, Af, data.gyr); +[Bf, Af] = butter(2, (2 * data.fCutoff / data.fs)); +data.filtAcc = filtfilt(Bf, Af, data.acc); +data.filtGyr = filtfilt(Bf, Af, data.gyr); %% Step detection using the zero velocity update. -[outStruct.tStart, outStruct.tEnd, outStruct.contactVector] = math.zvt(outStruct.filtAcc,outStruct.filtGyr, data.fs); +data = math.zvt(data); % Correct the calculated steps. -outStruct = math.correctSteps(outStruct); +data = math.correctSteps(data); % Find the Static and Dynamic parts of the calibration trial. -staticPart = floor(0.2*outStruct.tStart):floor(0.8*outStruct.tStart); -steps = find(diff(outStruct.contactVector)); -if outStruct.contactVector(1) == 1 +staticPart = floor(0.2*data.tStart):floor(0.8*data.tStart); +steps = find(diff(data.contactVector)); +if data.contactVector(1) == 1 steps(1) = []; end dynamicPart = steps(1):steps(6); % Determine bias and subtract it. -outStruct.gyroBias = mean(data.gyr(staticPart,:)); -gyroNoBias = data.gyr - outStruct.gyroBias; +data.gyroBias = mean(data.gyr(staticPart,:)); +gyroNoBias = data.gyr - data.gyroBias; % Obtain the perpendicular walking direction principal component. yAxis = pca((gyroNoBias(dynamicPart,:)-mean(gyroNoBias(dynamicPart,:)))); @@ -60,16 +57,16 @@ xas = cross(normYaxis',zaxis)/norm(cross(normYaxis',zaxis)); tzt = cross(xas,normYaxis)/norm(cross(xas,normYaxis)); % Calculate the errors of the axes with respect to being perpendicular. -outStruct.rmz = sqrt((tzt-zaxis).^2); +data.rmz = sqrt((tzt-zaxis).^2); txt = cross(zaxis,xas)/norm(cross(zaxis,xas)); -outStruct.rmy = sqrt((txt-normYaxis').^2); +data.rmy = sqrt((txt-normYaxis').^2); % the rotation matrix is obtained by adding the three directions together. -outStruct.rotMatrix = inv([xas',normYaxis,tzt']); +data.rotMatrix = inv([xas',normYaxis,tzt']); % Determine acc offset (gravity) accOffset = mean(data.acc(staticPart,:)); % Rotate acc offset to foot frame. -outStruct.accOffsetR = (outStruct.rotMatrix*accOffset')'; +data.accOffsetR = (data.rotMatrix*accOffset')'; end \ No newline at end of file diff --git a/+math/zvt.m b/+math/zvt.m index 80e22ac..e09a071 100644 --- a/+math/zvt.m +++ b/+math/zvt.m @@ -8,19 +8,19 @@ % tEnd - indexes of toe-off. % contactVector - vector with indicators of contact. -function [tStart, tEnd, dynamicsVector] = zvt(acc,gyr,fs) +function data = zvt(data) % Obtain the magnitudes of acceleration and angular velocity. -mGyr=sqrt(sum(gyr.^2,2)); -mAcc=sqrt(sum(acc.^2,2)); +mGyr=sqrt(sum(data.filtGyr.^2,2)); +mAcc=sqrt(sum(data.filtAcc.^2,2)); % Initialize N, k and v. -N = length(mGyr); -dynamicsVector = ones(length(mGyr),1); +data.N = length(mGyr); +data.contactVector = ones(length(mGyr),1); varianceWindow = zeros(length(mGyr),1); % loop over all samples starting at 11 for the first window. -for i=11:N +for i=11:data.N % Take a snapshot of the acceleration data and average it. mAccWindow=mAcc(i-10:i-1); avgmAccWindow=sum(mAccWindow)/10; @@ -33,25 +33,25 @@ for i=11:N % stationary. For detailed explanation of the conditions see the % publication: https://doi.org/10.1186/s12984-021-00816-4 if varianceWindow(i-10)<0.5 && mGyr(i-10)<50 && (9<mAcc(i-10) && mAcc(i-10)<11) - dynamicsVector(i-10)=0; + data.contactVector(i-10)=0; end end % Differentiate the contact vector to find indices of the contacts and % double check that the duration of contact is sufficient (10 samples). -x=diff(dynamicsVector); +x=diff(data.contactVector); dx=find(abs(x)==1); % Correct the contact windows shorter than 100 ms to non-contact. for i=1:length(dx)-1 - if (dx(i+1)-dx(i))/fs <= 0.1 - dynamicsVector(dx(i):dx(i+1))=dynamicsVector(dx(i)-1); + if (dx(i+1)-dx(i))/data.fs <= 0.1 + data.contactVector(dx(i):dx(i+1))=data.contactVector(dx(i)-1); end end % Recalculate the moments of contact to get indices. -dx=(diff(dynamicsVector)); -tStart=find(dx==1); -tEnd=find(dx==-1); +dx=(diff(data.contactVector)); +data.tStart=find(dx==1); +data.tEnd=find(dx==-1); end diff --git a/Main.m b/Main.m index 516837f..2b08b77 100644 --- a/Main.m +++ b/Main.m @@ -29,7 +29,7 @@ for i = 3:length(subjectFolders) % calculate all values for each step. dataStruct.(subjectFolders(i).name).(trialName) = ... - math.calculateSteps(dataStruct.(subjectFolders(i).name).(trialName)); + math.calculateSteps(dataStruct.(subjectFolders(i).name), trialName); end end \ No newline at end of file diff --git a/getCalibration.m b/getCalibration.m index 1bb3dde..9375003 100644 --- a/getCalibration.m +++ b/getCalibration.m @@ -27,8 +27,7 @@ function dataStruct = getCalibration(filePath) end % Determine the calibration orientation. - dataStruct.(trialName).calibration = ... - math.initOrien(dataStruct.(trialName)); + dataStruct.(trialName) = math.initOrien(dataStruct.(trialName)); -- GitLab