I need to calculate the angle between two plane. One plane is the hand plane and the second is the forearm plane. I calculated the normals of that planes and used the formula atan2(norm(cross(var.n1,var.n2)),dot(var.n1,var.n2)); in MATLAB. I want to see the flexion/extension angle of the wrist that is characterised by positive and negative peaks but with this formula I obtain only positive peaks.
%% Script to compute the angles of wrist flexion/extension and adduction/abduction based on Vicon data
% REF: Cheryl et al., March 2008
% Order of the markers: 1.WRR 2.WRU 3.FAU 4.FAR 5.CMC2 6.CMC5 7.MCP5 8.MCP2
clc
close all
clear all
%% Initialization
% dir_kinematic = input('Path of the folder containing the kinematic files: ','s');
dir_kinematic = 'C:\Users\Utente\Desktop\TESI\Vicon\test.mat';
cd(dir_kinematic);
fileList = getAllFiles(dir_kinematic,0); % Get names of all kinematic files
f=1;
%% Conversion to angles
for f = 1:length(fileList)
if ~isempty(strfind(fileList{f},'mat')) % Take only mat files
% 0. Loading
load(fileList{f});
% 1. Filtering
frameRate = kinematic.framerate;
n = 9;
Wn = 2/(frameRate/2);
ftype = 'low';
[b,a] = butter(n,Wn,ftype);
kinematic.x = filtfilt(b,a,kinematic.x);
kinematic.y = filtfilt(b,a,kinematic.y);
kinematic.z = filtfilt(b,a,kinematic.z);
% 2. Create vectors
var.n=length(kinematic.x);
% Forearm plane
var.FAU_WRU=[kinematic.x(:,2)-kinematic.x(:,3),kinematic.y(:,2)-kinematic.y(:,3),kinematic.z(:,2)-kinematic.z(:,3)];
var.WRR_WRU=[kinematic.x(:,2)-kinematic.x(:,1),kinematic.y(:,2)-kinematic.y(:,1),kinematic.z(:,2)-kinematic.z(:,1)];
% Hand plane
var.CMC5_MCP5=[kinematic.x(:,7)-kinematic.x(:,6),kinematic.y(:,7)-kinematic.y(:,6),kinematic.z(:,7)-kinematic.z(:,6)];
var.MCP2_MCP5=[kinematic.x(:,7)-kinematic.x(:,8),kinematic.y(:,7)-kinematic.y(:,8),kinematic.z(:,7)-kinematic.z(:,8)];
% Transpose
var.FAU_WRU = var.FAU_WRU';
var.WRR_WRU = var.WRR_WRU';
var.CMC5_MCP5 = var.CMC5_MCP5';
var.MCP2_MCP5 = var.MCP2_MCP5';
% 3. Calculate angle of wrist flexion/extension
% Cross vector function for all time => create normal vector plane
var.forearm_n=[];
var.hand_n=[];
var.theta_rad=[];
for i = 1:var.n % Loop through experiment
% vector x and y of the forearm plane
var.v1=var.FAU_WRU(:,i); % take x,y,z of the vector for every time
var.v2=var.WRR_WRU(:,i);
% vector x and y of the hand plane
var.v3=var.CMC5_MCP5(:,i);
var.v4=var.MCP2_MCP5(:,i);
var.forearm_n= [var.forearm_n, cross(var.v1,var.v2)];
var.hand_n=[var.hand_n, cross(var.v3,var.v4)];
end
% Calculate angle
for i = 1:var.n
var.n1=(var.forearm_n(:,i));
var.n2=var.hand_n(:,i);
var.scalar_product(i) = dot(var.n1,var.n2);
%Equation (2) of the paper
var.theta_rad=[var.theta_rad, atan2(norm(cross(var.n1,var.n2)),dot(var.n1,var.n2))]; % result in radian
angle.flex_deflex_wrist{f}=(var.theta_rad*180)/pi;
end
% 4. Calculate angle of wrist adduction/abduction
% Projection vector onto plane
var.MCP2_MCP5_forearmproj=[];
var.WRR_WRU_forearmproj=[];
var.rad_ul_angle_rad=[];
for i=1:var.n
%take x,y,z of the vector for each time
var.v1=var.MCP2_MCP5(:,i);
var.v2=var.WRR_WRU(:,i);
% vector x and y of the forearm plane
var.vfx=var.FAU_WRU(:,i); % take x,y,z of the vector for every time
var.vfy=var.WRR_WRU(:,i);
%projection of vector MCP2_MCP5 and WRR_WRU onto forearm plane
var.squNorm1=(norm(var.vfx)*norm(var.vfx));
var.squNorm2=(norm(var.vfy)*norm(var.vfy));
var.MCP2_MCP5_forearmproj=[var.MCP2_MCP5_forearmproj,((((var.v1')*var.vfx)*var.vfx/var.squNorm1)+(((var.v1')*var.vfy)*var.vfy/var.squNorm2))];
var.WRR_WRU_forearmproj=[var.WRR_WRU_forearmproj,((var.vfx*((var.v2')*var.vfx/var.squNorm1))+(var.vfy*((var.v2')*var.vfy/var.squNorm2)))];
end
% Calculate angle
for i=1:var.n
var.n1=var.MCP2_MCP5_forearmproj(:,i)';
var.n2=var.WRR_WRU_forearmproj(:,i);
var.product=var.n1*var.n2;
var.rad_ul_angle_rad=[var.rad_ul_angle_rad, atan2(norm(cross(var.n1,var.n2)),dot(var.n1,var.n2))];% en rad
angle.rad_ul_wrist{f}=(var.rad_ul_angle_rad*180)/pi;
end
end
end
I want to know why my angles are always positive? I need to see positive and negative peaks...
Thanks for the help!
Since the angle between two planes is the same as that between it's normals, I will constrain the discussion to angle between two vectors.
We know that the cross product between two vectors (a and b) is another vector, perpendicular to both of them.
We are dealing with the problem of distinguishing angles -T and +T, where T is some angle.
Using the formula you used, these two angles would give the same result, due to the fundamental formula itself that is used:
atan2 (|a x b|, a.b)
This is because while a.b is the same in both cases, a x b differs, exactly in the sign of the normal to the two vectors, and nothing else (verify yourself using hand rules). When we compute the norm of this vector, information about its sign is lost, due to which the function always returns positive values.
What you can do about it
You need to keep track of the sign of a x b, to determine if the angle is positive or negative.
Note: As I'm replying from my phone I am unable to add better formatting or code, will update the answer soon.
Related
I have a target coordinate (xyz) within a 3D rectangle. The target coordinate is set assuming the 3D rectangle is in a flat plane with x,y,z. The target coordinate is set to be on one terminal end of the rectangle, with the 'y' component set to be -2 from the top of the 3D square (Refer to image). However, in reality the 3D rectangle is not in a flat plane with x,y,z axes, it is skewed. We calculate the skew by measuring two reference points on the top surface of the rectangle and now want to recalculate the target coordinate. I do not know how to ensure I am computing the skew correctly or (more importantly) plot the old vs new 3D rectangle and target coordinate. Below is code to generate both the matrix and the target within said matrix.
For the two reference points used to calculate the skew of the 3D rectangle, we name one 'bregma' and one 'lamda'.
%% CONVENTIONS:
%1) numbers get lower when
% 1) going to left on x axis
% 2) going down y axis
% 3) going posterior (or towards you) on z axis,
% opposite of convention
%2) coordinate order is AP (anterior posterior), ML
%(medial lateral), DV
%(dorsal ventral)
%Bregma is the anterior reference point and
%Lambda is posterior reference point . The goal variables
%are the point in space I want to reach. I'm hoping the code creates new
%points for me as caused by a change in elevation between point "c" and "f"
%Note, for AP/ML/DV absolute values, the actual integers %are merely
%reference points in space. It is the delta between AP %and AP or ML and ML
%that is meaningful
%values a-f would be acquired from the stereotaxic frame
a= 43; %AP at bregma
b= 20; %ML at bregma
c= 22; %DV at bregma
d= 38.8; %AP at lambda
e= 20; %ML at Lambda
f= 21.5; %DV at lamda This variable causes the change %in which I want the rotation to correct
%Bregma =[b a c];
%Following three points are given relative to bregma, this is convention. So mathematically 'Bregma=[0 0 0];'
%These are also the coordinates I want altered from the rotation matrix
MLgoal=0; %ML to injection
APgoal=0; %AP to injection
DVgoal=-2.0; %DV to injection
DVgoal_zaxis_signconvention_correction = -DVgoal; %we want sign convention flipped for z axis[![enter image description here][1]][1]
A= a-d;
M= b-e;
D= c-f;
%Bregma =[0 0 0];
v = [A M D];
mv = norm(v);
u=[A 0 0]; %theoretical vector [ML,AP,DV] bregma to %lambda
mu = norm(u);
%angle in radians
X=acos(dot(u,v)/(mu*mv));
cu=cross(u,v);
w=cu/norm(cu); %unit vector
%rotate about this unit vector by angle X
wx=w(1);
wy=w(2);
wz=w(3);
%sustitute variables into general rotation for more %readable code
vx = 1-cos(X);
cx = cos(X);
sx = sin(X);
%rotation matrix about arbitrary unit vector [wx; wy; %wz] by angle X
r=[vx*wx*wx + cx, vx*wx*wy - wz*sx, vx*wx*wz + wy*sx;
vx*wx*wy + wz*sx, vx*wy*wy + cx, vx*wy*wz - wx*sx;
vx*wx*wz - wy*sx, vx*wy*wz + wx*sx, vx*wz*wz + cx];
%Newcoordinate output should tell you the change in %targeting coordinate
%after correcting for the output of rotational matrix. %The value for DV
%seems too large based on the subtle increase elevation %of reference point
%Bregma to reference point Lambda
Newcoordinates=r* [APgoal;MLgoal;DVgoal_zaxis_signconvention_correction]; %should give you the new coordinates for the injection %site
I frequently use discretized curves described by vectors, say x and y, meaning that each point (x(k),y(k)) lies on the curve. Note that x and y are generally not monotonically increasing.
Then I need to represent the data differently, because I need to know the y-values where x equals a set of given values, i.e. I want a vector yr for a given vector xq such that (xq(k),yr(k)) are all good approximations of the original curve. So normally, interpolation would be used, however
yr = interp1(x,y,xq)
results in an error
The grid vectors are not strictly monotonic increasing.
How can I do this (in a nice way)? Note that I want to preserve the shape (connectivity between neighboring nodes) of the curve given by x and y.
Example Problem
Say you have data representing a circle in a 2D (x,y) space. What would you do if you needed the representation of that circle on a different x-grid?
PS: I will provide my current approach as an answer, but welcome other approaches, especially if they are "nicer" or "simpler"? (forgive me these subjective terms)
The current approach that I have works as follows. Basically, I do normal interpolation steps on pieces of the curve described by x and y.
% Determine whether xq is a column or row vector. This is used to make sure
% that xr and yr have the same orientation as xq.
if iscolumn(xq)
dim = 1;
else
dim = 2;
end
% Make sure that xq is unique and ascending
xq = unique(xq);
% Initialize xr and yr as empty arrays
[xr,yr] = deal([]);
% Loop over all [xk,xkp1]-intervals
for k = 1:length(x)-1
% Choose values from xq that are in [xk,xkp1] (or [xkp1,xk] if
% decreasing)
if x(k) <= x(k+1) % x is locally increasing
xradd = xq(x(k)<=xq & xq<=x(k+1));
else % x is locally decreasing
xradd = flip(xq(x(k+1)<=xq & xq<=x(k)));
% 'flip' ensures that xradd is also decreasing (to maintain order
% of x in xr)
end
% Perform local interpolation
yradd = interp1(x(k:k+1),y(k:k+1),xradd);
% Assemble xr and yr from local interpolations
xr = cat(dim,xr,xradd);
yr = cat(dim,yr,yradd);
end
This method works, but there may be other methods.
I have a weird problem with the discrete fft. I know that the Fourier Transform of a Gauss function exp(-x^2/2) is again the same Gauss function exp(-k^2/2). I tried to test that with some simple code in MatLab and FFTW but I get strange results.
First, the imaginary part of the result is not zero (in MatLab) as it should be.
Second, the absolute value of the real part is a Gauss curve but without the absolute value half of the modes have a negative coefficient. More precisely, every second mode has a coefficient that is the negative of that what it should be.
Third, the peak of the resulting Gauss curve (after taking the absolute value of the real part) is not at one but much higher. Its height is proportional to the number of points on the x-axis. However, the proportionality factor is not 1 but nearly 1/20.
Could anyone explain me what I am doing wrong?
Here is the MatLab code that I used:
function [nooutput,M] = fourier_test
Nx = 512; % number of points in x direction
Lx = 50; % width of the window containing the Gauss curve
x = linspace(-Lx/2,Lx/2,Nx); % creating an equidistant grid on the x-axis
input_1d = exp(-x.^2/2); % Gauss function as an input
input_1d_hat = fft(input_1d); % computing the discrete FFT
input_1d_hat = fftshift(input_1d_hat); % ordering the modes such that the peak is centred
plot(real(input_1d_hat), '-')
hold on
plot(imag(input_1d_hat), 'r-')
The answer is basically what Paul R suggests in his second comment, you introduce a phase shift (linearly dependent on the frequency) because the center of the Gaussian described by input_1d_hat is effectively at k>0, where k+1 is the index into input_1d_hat. Instead if you center your data (such that input_1d_hat(1) corresponds to the center) as follows you get a phase-corrected Gaussian in the frequency domain:
Nx = 512; % number of points in x direction
Lx = 50; % width of the window containing the Gauss curve
x = linspace(-Lx/2,Lx/2,Nx); % creating an equidistant grid on the x-axis
%%%%%%%%%%%%%%%%
x=fftshift(x); % <-- center
%%%%%%%%%%%%%%%%
input_1d = exp(-x.^2/2); % Gauss function as an input
input_1d_hat = fft(input_1d); % computing the discrete FFT
input_1d_hat = fftshift(input_1d_hat); % ordering the modes such that the peak is centered
plot(real(input_1d_hat), '-')
hold on
plot(imag(input_1d_hat), 'r-')
From the definition of the DFT, if the Gaussian is not centered such that maximum occurs at k=0, you will see a phase twist. The effect off fftshift is to perform a circular shift or swapping of left and right sides of the dataset, which is equivalent to shifting the center of the peak to k=0.
As for the amplitude scaling, that is an issue with the definition of the DFT implemented in Matlab. From the documentation for the FFT:
For length N input vector x, the DFT is a length N vector X,
with elements
N
X(k) = sum x(n)*exp(-j*2*pi*(k-1)*(n-1)/N), 1 <= k <= N.
n=1
The inverse DFT (computed by IFFT) is given by
N
x(n) = (1/N) sum X(k)*exp( j*2*pi*(k-1)*(n-1)/N), 1 <= n <= N.
k=1
Note that in the forward step the summation is not normalized by N. Therefore if you increase the number of points Nx in the summation while keeping the width Lx of the Gaussian function constant you will increase X(k) proportionately.
As for signal leaking into the imaginary frequency dimension, that is due to the discrete form of the DFT, which results in truncation and other effects, as noted again by Paul R. If you reduce Lx while keeping Nx constant, you should see a reduction in the amount of signal in the imaginary dimension relative to the real dimension (compare the spectra while keeping peak intensities in the real dimension equal).
You'll find additional answers to similar questions here and here.
If I have points in a graph in matlab that are randomly sorted and when plotted produce various closed shapes. Given a specific point on the left side of one of the closed shapes how can I get all the points of that shape in a vector form keeping in mind the convention of collecting the points is moving in a clockwise direction.
A commented example:
cla
% Random data
x = rand(15,1);
y = rand(15,1);
scatter(x,y)
% Random point to the left
hold on
p = [-0.1, rand(1)*0.2 + 0.5];
plot(p(1),p(2),'*r')
% Separate points that lie above your point
idx = y >= p(2);
% Sort x then y increasing for upper half and x then y decreasing for lower half
shape = [sortrows([x( idx) y( idx)],[1 2])
sortrows([x(~idx) y(~idx)],[-1 -2])];
Check that shape contains clockwise sorted coordinates by plotting an open line:
% Plot clockwise open line
plot(shape(1:end ,1),shape(1:end,2),'k')
I'm currently frustrated by the following problem:
I've got trajectory data (i.e.: Longitude and Latitude data) which I interpolate to find a linear fitting (using polyfit and polyval in matlab).
What I'd like to do is to rotate the axes in a way that the x-axis (the Longitude one) ends up lying on the best-fit line, and therefore my data should now lie on this (rotated) axis.
What I've tried is to evaluate the rotation matrix from the slope of the line-of-fit (m in the formula for a first grade polynomial y=mx+q) as
[cos(m) -sin(m);sin(m) cos(m)]
and then multiply my original data by this matrix...to no avail!
I keep obtaining a plot where my data lay in the middle and not on the x-axis where I expect them to be.
What am I missing?
Thank you for any help!
Best Regards,
Wintermute
A couple of things:
If you have a linear function y=mx+b, the angle of that line is atan(m), not m. These are approximately the same for small m', but very different for largem`.
The linear component of a 2+ order polyfit is different than the linear component of a 1st order polyfit. You'll need to fit the data twice, once at your working level, and once with a first order fit.
Given a slope m, there are better ways of computing the rotation matrix than using trig functions (e.g. cos(atan(m))). I always try to avoid trig functions when performing geometry and replace them with linear algebra operations. This is usually faster, and leads to fewer problems with singularities. See code below.
This method is going to lead to problems for some trajectories. For example, consider a north/south trajectory. But that is a longer discussion.
Using the method described, plus the notes above, here is some sample code which implements this:
%Setup some sample data
long = linspace(1.12020, 1.2023, 1000);
lat = sin ( (long-min(long)) / (max(long)-min(long))*2*pi )*0.0001 + linspace(.2, .31, 1000);
%Perform polynomial fit
p = polyfit(long, lat, 4);
%Perform linear fit to identify rotation
pLinear = polyfit(long, lat, 1);
m = pLinear(1); %Assign a common variable for slope
angle = atan(m);
%Setup and apply rotation
% Compute rotation metrix using trig functions
rotationMatrix = [cos(angle) sin(angle); -sin(angle) cos(angle)];
% Compute same rotation metrix without trig
a = sqrt(m^2/(1+m^2)); %a, b are the solution to the system:
b = sqrt(1/(1+m^2)); % {a^2+b^2 = 1}, {m=a/b}
% %That is, the point (b,a) is on the unit
% circle, on a line with slope m
rotationMatrix = [b a; -a b]; %This matrix rotates the point (b,a) to (1,0)
% Generally you rotate data after removing the mean value
longLatRotated = rotationMatrix * [long(:)-mean(long) lat(:)-mean(lat)]';
%Plot to confirm
figure(2937623)
clf
subplot(211)
hold on
plot(long, lat, '.')
plot(long, polyval(p, long), 'k-')
axis tight
title('Initial data')
xlabel('Longitude')
ylabel('Latitude')
subplot(212)
hold on;
plot(longLatRotated(1,:), longLatRotated(2,:),'.b-');
axis tight
title('Rotated data')
xlabel('Rotated x axis')
ylabel('Rotated y axis')
The angle you are looking for in the rotation matrix is the angle of the line makes to the horizontal. This can be found as the arc-tangent of the slope since:
tan(\theta) = Opposite/Adjacent = Rise/Run = slope
so t = atan(m) and noting that you want to rotate the line back to horizontal, define the rotation matrix as:
R = [cos(-t) sin(-t)
sin(-t) cos(-t)]
Now you can rotate your points with R