How can I write a MATLAB script to plot 3D and 2D knee joint data from a CSV file with customized axes and medial–lateral connections?

조회 수: 37 (최근 30일)
The CSV file contains the following data:
  • Sensor names: sensorNames = data.Sensor(1:6,1)
  • Sensor coordinates: sensorCoordinates = data{1:6, 2:4}
  • Source number: (:,5)
  • Source coordinates: sourceCoordinates = data{:, 6:end}
I want to create a 3D plot in MATLAB that includes the following:
  1. Plot the sensor names and their coordinates.
  2. Plot the source coordinates. The source coordinates are categorized into two groups:
  • Medial: sourceCoordinates = data{7:end, 6:end}
  • Lateral: sourceCoordinates = data{1:6, 6:end}
3. Set the axis directions depending on the knee side (see figure. 1 below), using S2 as the reference sensor (0,0,0):
Figure.1
  • Right knee: X-axis positive leftward, Y-axis positive downward, Z-axis positive perpendicular to the depth of the plane.
  • Left knee: X-axis positive rightward, Y-axis positive downward, Z-axis positive perpendicular to the depth of the plane.
4. Generate three types of plots (similar to the figure.2):
  • A 3D plot with all axes.
  • A 2D plot of X vs. Y.
  • A 2D plot of X vs. Z.
5. Add a line (like the green line in the figure.2 below) that connects the medial and lateral sources. This line should represent the knee joint range in all axis directions.
Figure.2
Knee joint range determination table.
I would like to write a MATLAB script that reads the CSV file, extracts the relevant data, and generates the 3D and 2D plots including the above details.
I wrote the following code:
%% Read and Plot 3D/2D Data from CSV File
% Load the CSV file
filename = '3DPlot_KLII_Mori.csv';
data = readtable(filename);
%% Extract Sensor and Source Data
sensorNames = data.Sensor(1:6, 1); % Sensor names
sensorCoordinates = data{1:6, 2:4}; % Sensor coordinates (X, Y, Z)
sourceNumbers = data{:, 5}; % Source numbers
sourceCoordinates = data{:, 6:end}; % All source coordinates
% Categorize source coordinates
lateralSources = data{1:6, 6:end}; % Lateral sources
medialSources = data{7:end, 6:end}; % Medial sources
%% User input for knee side
kneeSide = "Right"; % Change to "Left" if left knee
% Adjust axis directions based on knee side
if strcmpi(kneeSide, "Right")
% Right knee: X positive leftward, Y downward, Z downward
sensorCoordinates(:,1) = -sensorCoordinates(:,1);
lateralSources(:,1) = -lateralSources(:,1);
medialSources(:,1) = -medialSources(:,1);
elseif strcmpi(kneeSide, "Left")
% Left knee: X positive rightward, Y downward, Z downward
% No inversion needed
else
error('Knee side must be either "Right" or "Left".');
end
%% Plotting - 3D Plot
figure;
hold on;
% Plot sensor coordinates
scatter3(sensorCoordinates(:,1), sensorCoordinates(:,2), sensorCoordinates(:,3), ...
80, 'filled', 'MarkerFaceColor', 'b');
% Add sensor name labels
for i = 1:length(sensorNames)
text(sensorCoordinates(i,1), sensorCoordinates(i,2), sensorCoordinates(i,3), ...
[' ' char(sensorNames{i})], 'FontSize', 10, 'Color', 'b');
end
% Plot lateral sources
scatter3(lateralSources(:,1), lateralSources(:,2), lateralSources(:,3), ...
60, 'o', 'MarkerEdgeColor', 'r', 'DisplayName', 'Lateral Sources');
% Plot medial sources
scatter3(medialSources(:,1), medialSources(:,2), medialSources(:,3), ...
60, 's', 'MarkerEdgeColor', 'g', 'DisplayName', 'Medial Sources');
%% Formatting for 3D plot
grid on;
xlabel('X-axis');
ylabel('Y-axis');
zlabel('Z-axis');
title(['3D Plot of Sensors and Sources (' kneeSide ' Knee)']);
legend;
view(3);
hold off;
%% Plotting - 2D Plot (X vs Y)
figure;
hold on;
scatter(sensorCoordinates(:,1), sensorCoordinates(:,2), 80, 'filled', 'b');
for i = 1:length(sensorNames)
text(sensorCoordinates(i,1), sensorCoordinates(i,2), [' ' char(sensorNames{i})], 'FontSize', 10, 'Color', 'b');
end
scatter(lateralSources(:,1), lateralSources(:,2), 60, 'ro', 'DisplayName', 'Lateral Sources');
scatter(medialSources(:,1), medialSources(:,2), 60, 'gs', 'DisplayName', 'Medial Sources');
xlabel('X-axis');
ylabel('Y-axis');
title(['2D Plot (X vs Y) - ' kneeSide ' Knee']);
legend;
grid on;
hold off;
%% Plotting - 2D Plot (X vs Z)
figure;
hold on;
scatter(sensorCoordinates(:,1), sensorCoordinates(:,3), 80, 'filled', 'b');
for i = 1:length(sensorNames)
text(sensorCoordinates(i,1), sensorCoordinates(i,3), [' ' char(sensorNames{i})], 'FontSize', 10, 'Color', 'b');
end
scatter(lateralSources(:,1), lateralSources(:,3), 60, 'ro', 'DisplayName', 'Lateral Sources');
scatter(medialSources(:,1), medialSources(:,3), 60, 'gs', 'DisplayName', 'Medial Sources');
xlabel('X-axis');
ylabel('Z-axis');
title(['2D Plot (X vs Z) - ' kneeSide ' Knee']);
legend;
grid on;
hold off;

답변 (1개)

William Rose
William Rose 2025년 8월 27일 21:06
편집: William Rose 2025년 8월 27일 21:21
[Edit: Fix spelling erros in my text. Fix incorrect matrix values for x-coordinates of medial box.]
It appers to me that you want to have lines for the edges of the boxes in your 3D plot and 2D plots, and your script is not making the lines.
Your code to read the data:
% Load the CSV file
filename = '3DPlot_KLII_Mori.csv';
data = readtable(filename);
%% Extract Sensor and Source Data
sensorNames = data.Sensor(1:6, 1); % Sensor names
sensorCoordinates = data{1:6, 2:4}; % Sensor coordinates (X, Y, Z)
sourceNumbers = data{:, 5}; % Source numbers
sourceCoordinates = data{:, 6:end}; % All source coordinates
Determine the knee joint ranges from the sensor coordinates. The knee joint determination tables above indicate that the z values are 0% of the way, and 125% of the way, from S1 to S5. Then the z-values are given by
boxZ=[1,0;-0.25,1.25]*sensorCoordinates([1,5],3); % z values for boxes
The knee joint determination tables above indicate that the y values are 45% of the way, and 80% of the way, from S2 to S1. (For x and z, the percentages are from S1. Is this really what you want for the y-values? I assume it is really what you want.) Then the y-values are given by
boxY=[0.45,0.55; 0.8,0.2]*sensorCoordinates([1,2],2); % y values for boxes
The knee joint determination tables above indicate that the x values for the medial box are 0% of the way, and 40% of the way, from S1 to S3. Then the medial box x-values are given by
boxXMed=[1,0; 0.6,0.4]*sensorCoordinates([1,3],1); % x values for medial box
The knee joint determination tables above indicate that the x values for the lateral box are 60% of the way, and 100% of the way, from S1 to S3. Then the lateral box x-values are given by
boxXLat=[0.4,0.6; 0,1]*sensorCoordinates([1,3],1); % x values for lateral box
Make grid matrices with the X,Y,Z coords of the medial box corners:
[X,Y,Z] = meshgrid(boxXMed,boxY,boxZ);
Convert the grid arrays to a 8x3 matrix representing the x,y,z coords of the 8 corners of the medial box:
boxCornersMed=[X(:),Y(:),Z(:)];
Make grid matrices with the X,Y,Z coords of the lateral box corners:
[X,Y,Z] = meshgrid(boxXLat,boxY,boxZ);
Convert the grid arrays to a 8x3 matrix representing the x,y,z coords of the 8 corners of the lateral box:
boxCornersLat=[X(:),Y(:),Z(:)];
Make an array that traverses every edge. This will require visiting some corners more than once.
boxPlotMed=boxCornersMed([1,2,4,3,1,5,6,2,6,8,4,8,7,3,7,5],:);
boxPlotLat=boxCornersLat([1,2,4,3,1,5,6,2,6,8,4,8,7,3,7,5],:);
Use plot3() for the 3D plot.
plot3(boxPlotMed(:,1),boxPlotMed(:,2),boxPlotMed(:,3),'-g') % medial box
hold on
plot3(boxPlotLat(:,1),boxPlotLat(:,2),boxPlotLat(:,3),'-r') % lateral box
xlabel('X'); ylabel('Y'); zlabel('Z');
legend('Medial Range','Lateral Range')
title(['3D Plot of Sensors and Sources']);
grid on; axis equal
I used "axis equal" so that the axes display in their true relative scale.
Add other elements to the plot, using your code:
% Categorize source coordinates
lateralSources = data{1:6, 6:end}; % Lateral sources
medialSources = data{7:end, 6:end}; % Medial sources
% Plot sensor coordinates
scatter3(sensorCoordinates(:,1), sensorCoordinates(:,2), sensorCoordinates(:,3), ...
80, 'filled', 'MarkerFaceColor', 'b', 'DisplayName', 'Sensors');
% Add sensor name labels
for i = 1:length(sensorNames)
text(sensorCoordinates(i,1), sensorCoordinates(i,2), sensorCoordinates(i,3), ...
[' ' char(sensorNames{i})], 'FontSize', 10, 'Color', 'b');
end
% Plot lateral sources
scatter3(lateralSources(:,1), lateralSources(:,2), lateralSources(:,3), ...
60, 'o', 'MarkerEdgeColor', 'r', 'DisplayName', 'Lateral Sources');
% Plot medial sources
scatter3(medialSources(:,1), medialSources(:,2), medialSources(:,3), ...
60, 's', 'MarkerEdgeColor', 'g', 'DisplayName', 'Medial Sources');
Do something similar for the 2D plots.
  댓글 수: 2
MD MEHEDI HASSAN
MD MEHEDI HASSAN 2025년 8월 29일 8:09
You're right — I want to have lines for the edges of the boxes in my 3D and 2D plots, but my script wasn’t generating them. Thank you very much for your attempt to solve the problem.
However, one issue still remains, as I mentioned in point 3: I could not set the axis directions. In my 3D plot (and in your plot as well), the axes are currently:
  • Y-axis → horizontal
  • Z-axis → vertical
  • X-axis → depth
But what I want is:
  • X-axis → horizontal
  • Y-axis → vertical
  • Z-axis → depth
which is exactly the same as Figure 2 in my question section.
Please refer to point 3 and Figure 2 in my question for clarification.
William Rose
William Rose 2025년 8월 29일 15:14
편집: William Rose 대략 20시간 전
[Edit: Add left-handed plot for right knee. Add titles to plots.]
[Edit: Adjust my comments about the use of left-handed and right-handed coordinate systems.]
Use camup() and campos() to rotate the viewpoint. (Matlab's view() command will not do what you want, because it always keeps the +Z axis pointing up.)
Use ax=gca and set(ax,"XDir","reverse") to plot with a left-handed coordinate system.
Before rotation
subplot(211)
plot3([0,1],[0,0],[0,0],'-r',[0,0],[0,1],[0,0],'-g',[0,0],[0,0],[0,1],'-b')
hold on
scatter3([1,0,0,0],[0,1,0,0],[0,0,1,0],50,[1,0,0;0,1,0;0,0,1;0,0,0],'filled')
xlabel('X'); ylabel('Y'); zlabel('Z')
axis equal
title('Default 3D View')
Same plot with rotation
subplot(224)
plot3([0,1],[0,0],[0,0],'-r',[0,0],[0,1],[0,0],'-g',[0,0],[0,0],[0,1],'-b')
hold on
scatter3([1,0,0,0],[0,1,0,0],[0,0,1,0],50,[1,0,0;0,1,0;0,0,1;0,0,0],'filled')
xlabel('X'); ylabel('Y'); zlabel('Z')
axis equal
camup([0,-1,0]) % display +Y axis pointing down
campos([-0.2,-0.5,-2]) % move camera to a new position
title('Left Knee')
Same plot with rotation and reflection
subplot(223)
plot3([0,1],[0,0],[0,0],'-r',[0,0],[0,1],[0,0],'-g',[0,0],[0,0],[0,1],'-b')
hold on
scatter3([1,0,0,0],[0,1,0,0],[0,0,1,0],50,[1,0,0;0,1,0;0,0,1;0,0,0],'filled')
xlabel('X'); ylabel('Y'); zlabel('Z')
axis equal
camup([0,-1,0]) % display +Y axis pointing down
campos([-0.2,-0.5,-2]) % move camera to a new position
ax=gca;
set(ax, 'XDir', 'reverse'); % reverse the X-axis
title('Right Knee')
The two lower plots show the X,Y,Z unit vectors (in colors R,G,B respectively) with the orientation you requested for the left and right knees.
Your figure shows a left-handed coordinate system for the right knee. I realize that using a different handed-ness for the left and right sides can make it easier to aggregate results and compute statistics from knees on both sides. The use of a left-handed system may be a good idea for that reason. The International Society of Biomechanics recommends a right handed coordinate sytem for both left and right sides. See Wu et al., "ISB recommendation on definitions of joint coordinate system of various joints for the reporting of human joint motion—part I: ankle, hip, and spine", J. Biomechanics 2002. This assures that the right hand rule works, for torque calculations, and so on.

댓글을 달려면 로그인하십시오.

카테고리

Help CenterFile Exchange에서 Biomechanics에 대해 자세히 알아보기

제품


릴리스

R2021b

Community Treasure Hunt

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

Start Hunting!

Translated by