What is wrong with my ankle joint moment estimation?
8 views (last 30 days)
Show older comments
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
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.
Accepted Answer
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
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.
More Answers (0)
See Also
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!