What is wrong with my ankle joint moment estimation?

8 views (last 30 days)
Hi all,
I collected human motion data (kinematics and ground reaction forces) during walking. I am trying to estimate ankle joint moments (3D) and later powers. I segmented (per stance phase on a force plate) 3D ankle joint angles, GRFs, ankle joint centes, foot and shank center of mass postions and accelerations, foot and shank angulat velocities and accelerations. I do not have center of pressure data, I approximate this. Below is my code that I use. However, I am not sure whether ankle moments are calculated correctly beacuse of the atypical spike in the sagital plane moment in the first 20 frames that you can see in tha image below. Can you help please?
% MATLAB Code for 3D Ankle Joint Torques Calculation with Segmented Data Structure
load Data_structure.mat
% Constants
g = 9.81; % m/s^2, gravitational acceleration
%Subject weight/height
mass = 84.6;
height = 1.55;
% segment mass (kg)
massThigh = 0.142*mass;
massShank = 0.043*mass;
massFoot = 0.013*mass;
% segment lenght (meters)
L_thigh = 0.240*height;
L_shank = 0.262*height;
L_foot = 0.144*height;
% Moment of inertia
I_foot = diag(data.FootMoI); % 3x3 matrix
I_shank = diag(data.ShankMoI);
I_thigh = diag(data.ThighMoI);
% Initialize moment arrays
nFrames = 101;
forcePlates = {'fp1', 'fp3', 'fp4'};
limbs = {'L', 'R'};
ankleMoments = struct();
% Loop through force plates
for fp = 1:length(forcePlates)
fpName = forcePlates{fp};
for limb = 1:length(limbs)
limbName = limbs{limb};
% Initialize moment data for each plate and limb
ankleMoments.(fpName).(limbName) = zeros(3, nFrames);
% Check if data exists for the current force plate and limb
if isfield(data.Segmented_Angles, fpName) && isfield(data.Segmented_Angles.(fpName), limbName)
if ~isempty(data.Segmented_Angles.(fpName).(limbName)) && isfield(data.Segmented_Angles.(fpName).(limbName){1, 1}, 'ankle')
% Extract kinematic and GRF data
ankleAngles = data.Segmented_Angles.(fpName).(limbName){1, 1}.ankle;
ankleJC = [data.Segmented_Jointcenteres.(fpName).(limbName){1, 1}.ankle.ap;
data.Segmented_Jointcenteres.(fpName).(limbName){1, 1}.ankle.ml;
data.Segmented_Jointcenteres.(fpName).(limbName){1, 1}.ankle.ver];
% COM positions and accelerations
comFoot = data.Segmented_CoMpos.(fpName).(limbName){1, 1}.foot';
foot_comAcc = data.Segmented_CoMacc.(fpName).(limbName){1, 1}.foot';
% Angular velocities and accelerations
foot_aVel = data.Segmented_aVel.(fpName).(limbName){1, 1}.foot';
foot_aAcc = data.Segmented_aAcc.(fpName).(limbName){1, 1}.foot';
% GRF data
try
GRF_fp = [data.(['Kistler' num2str(fp) '_fx_stance']);
data.(['Kistler' num2str(fp) '_fy_stance']);
data.(['Kistler' num2str(fp) '_fz_stance'])];
catch
GRF_fp = zeros(3, nFrames);
end
% Approximate COP position
r_ankle_to_COP = zeros(3, nFrames);
for i = 1:nFrames
% Get GRF vertical force to detect stance
grfZ = GRF_fp(3, :);
stanceFrames = find(grfZ > 10);
% Skip if not in stance
if isempty(stanceFrames) || ~ismember(i, stanceFrames)
r_ankle_to_COP(:, i) = [0; 0; 0];
continue;
end
% Use alpha based on stance progression
alpha = (i - stanceFrames(1)) / (stanceFrames(end) - stanceFrames(1));
alpha = min(max(alpha, 0), 1); % clamp to [0, 1]
% Assume foot runs anterior along Z (adjust if needed)
heel_to_toe_vector = [0; 0; 1] * L_foot;
% CoP vector from ankle = moving from heel to toe
r_ankle_to_COP(:, i) = -0.5 * heel_to_toe_vector + alpha * heel_to_toe_vector;
end
% Calculate Ankle Moments
for i = 1:nFrames
% Inertial and gravitational forces
footWeight = [0; 0; -massFoot * g];
footInertialForce = -massFoot * foot_comAcc(:, i);
% Dynamic vector from ankle joint to CoM
r_ankle_to_CoM = comFoot(:, i) - ankleJC(:, i);
% Compute Ankle Moments
ankleMoments.(fpName).(limbName)(:, i) = cross(r_ankle_to_COP(:, i), GRF_fp(:, i)) + ...
cross(r_ankle_to_CoM, footWeight + footInertialForce) + ...
I_foot * foot_aAcc(:, i) + ...
cross(foot_aVel(:, i), I_foot * foot_aVel(:, i));
end
end
end
end
end
% Plotting Ankle Moments in the Sagittal Plane from one force plate
figure
plot(ankleMoments.fp1.R(1,:))
xlabel('Frame');
ylabel('Ankle sagital Moment (Nm)');
  9 Comments
William Rose
William Rose on 31 May 2025
Recently you answered a question about design of a feedback block in a control system, root locus analysis, etc. It was a really good answer and it made me wish I remembered more from my one controls course. Your answer prompted me to go back and spend an hour or so with my 1980s copy of Design of Feedback Control Systems by Hostetter.

Sign in to comment.

Accepted Answer

William Rose
William Rose on 29 May 2025
Edited: William Rose on 29 May 2025
I agree with you that the early peak in your calculated ankle moment is anomalous and probably indicates a problem in the data or the calculations. My initial reaction is that this is probably a consequence of the fact that you do not measure the center of pressure (COP) location. Errors in the estimated COP location could easily cause significant errors in ankle joint moment.
Is plantarflexion positive in your plot? The broad late peak makes me think it is.
Here are published examples of sagittal plane ankle moment.
In the plot above, plantar flexion is positive, moment is normalized by body weight, and a full stride is shown.
In the plot above, dorsiflexion is positive, moment is normalized by body weight, and the stance phase is shown. Focus on the normal traces.
In the plot above, dorsiflexion is positive, moment is normalized by body weight, and the stance phase is shown. The trace for walking is thicker and has a negative peak that occurs later.
In the plot above, dorsiflexion is positive, moment is not normalized by body weight, and the stance phase is shown.
Your plot says the units are N-m, but I wonder if you have normalized the moment by body weight. If N-m is correct (i.e. you have not normalized), then the broad peak in plantar flexion in the second half of stance, in your plot, is much smaller than what others have reported.
Hansen et al. (2004) J Biomech use a simpler method to estimate ankle moment - see their Fig 2. They say that RP Wells (1981) found that the simpler method gave very similar results to a full inverse dynamics approach.
I have not checked your inverse dynamics calculations.
  5 Comments
Tomaszzz
Tomaszzz on 30 May 2025
@William Rose; Dear William, many thanks for spending time familirazing yourself with the problem and writing back. Your answers have helped me better understand what I am doing. I am yet to cross check all the matters you have raised but I have just quickly modified the CoP estimations based on your input and indeed it seems the problem was here. Many thanks!
William Rose
William Rose on 31 May 2025
That shape looks a lot better! And now that you have changed the units to N-m/kg, the magnitude also looks reasonable. Good luck with your research.

Sign in to comment.

More Answers (0)

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!