determine whether two points with known normals are facing each other or not (matlab) - matlab

I am trying to find a solution to the following problem but it is not clear how to solve it. Imagine that I have the following points in the space as in the image below:
If I consider that my only known information is the point positions and their normals I would like to determine whether two points (considering as a reference the position of the first point) are facing each other or not. For example from the above image for points a, b, c, d and e I have:
Point a faces points c and e but not points b and d.
Point b faces points d and e but not points a and c.
Point c faces point a but not points b, d and e.
Point d faces points b and e but not points a and c.
and finaly
Point e faces points a, b and d but not point c.
My first though was to play with the signed angles between the two normal vectors of each pair by using the solutions proposed here but this works for some pairs while not for others. The idea regarding what two point are facing each other is that if we consider a point as the origin then it faces another point if the other point is within the origin point's 180 degrees field of view and its normal vector is going inwards (kind of "towards") the origin point.
Any ideas what could help.
Thanks.
Update:
to try to be a bit more clear and answer some of the comments below. In principle its point in the space corresponds to the centroid of a face. However, I do not have this information beforehand (i.e. that each point corresponds to the center of a face, or the list of faces and their vertices). So in a higher level, if we were dealing with faces the problem would be how to determine if two faces are visible to each other or not but as I said the only information that I have now are the actual points in the space and their normals.
Sample points:
a = [26415.3720833199 11986.0504166605 739];
na = [0 0 1];
b = [27263.8100000023 11103.1983333336 1512.50000000021];
nb = [0.102791963903622 -0.994702876318771 0];
c = [28059.5700000001 11185.4316666667 962.499999999998];
nc = [-0.102791963903623 0.994702876318771 -9.06557542353252e-16];
d = [26606.7112499615 10390.7487916521 739];
nd = [0 0 1];
e = [27792.4499999996 9225.36499999984 2782];
ne = [0 0 -1];

You can solve your problem with a few simple dot products...
Based on your description, a point b is within the field of view (FOV) of another point a if the angle between the normal of a (i.e. na) and a vector going from a to b is less than or equal to 90 degrees. As described here, the angle can be found by taking the dot product of b-a and na, dividing by the length of b-a (and assuming the length of na is already 1), and taking the inverse cosine of the result. Putting it into an anonymous function, you have:
isInFOV = #(b, a, na) (acosd(dot(b-a, na)./norm(b-a)) <= 90);
You can then define a point b as "pointing toward" another point a if the component of nb (the normal of b) running along the vector going from b to a is positive. As described here, the component can be found by taking the dot product of a-b and nb and dividing by the length of a-b (and assuming the length of nb is already 1). Putting it into an anonymous function, you have:
isPointingToward = #(b, nb, a) (dot(a-b, nb)./norm(a-b) > 0);
We can then define whether a point a is "facing" another point b as:
isFacing = #(a, na, b, nb) (isInFOV(b, a, na) && isPointingToward(b, nb, a));
Note that I used the logical short circuit AND operator && since isPointingToward doesn't need to be evaluated if isInFOV already evaluates to false.
Vectorizing
You can reformulate the above equations to vectorize the operations, using functions like bsxfun or replacing a call to dot with standard matrix operations. This will allow you to check which points in a set a given point is facing. A vectorized version of the function isFacing is given below:
function index = isFacing(a, na, b, nb)
V = bsxfun(#minus, b, a); % Compute b-a for all b
V = bsxfun(#rdivide, V, sqrt(sum(V.^2, 2))); % Normalize each row
index = (acosd(V*na.') <= 90); % Find points in FOV of a
index(index) = (sum(V(index, :).*nb(index, :), 2) < 0); % Of those points in FOV,
% find those pointing
% towards a
end
Example
Using the sample data in the question:
pointMat = [26415.3720833199 11986.0504166605 739; ... % Point a
27263.8100000023 11103.1983333336 1512.50000000021; ... % Point b
28059.5700000001 11185.4316666667 962.499999999998; ... % Point c
26606.7112499615 10390.7487916521 739]; % Point d
normalMat = [0 0 1; ...
0.102791963903622 -0.994702876318771 0; ...
-0.102791963903623 0.994702876318771 -9.06557542353252e-16; ...
0 0 1];
p = [27792.4499999996 9225.36499999984 2782]; % Point e
np = [0 0 -1];
>> isFacing(p, np, pointMat, normalMat)
ans =
4×1 logical array
1 % Facing a
1 % Facing b
0 % Not facing c
1 % Facing d

Related

How should the factorGraph object function factorTwoPoseSE3 in matlab use relative measurements correctly?

I tried to add relative pose measurements of 2 adjacent nodes using factorGraph's object function factorTwoPoseSE3, but no matter how much I should modify the relative angle “relT” variable(i.e. the number of quaternions), the optimized result graph remains the same, which is apparently a big problem?
If this is not the case, how do I modify the code to achieve the correct optimisation result that I want?
Design a plane rectangle, its observed 4 absolute coordinate points (0,0), (1,0), (1,1), (0,1), (0,1), node (node) number 1,2,3,4, respectively, in the last node 4 observed node 1 relative coordinates for (0.8,0), that is, closed loop , you can see that the last node error is 0.2. that is, this pose graph contains 4 edges, The first 3 of these are odometers and the last one is a closed loop edge.
% Since factorTwoPoseSE2 cannot specify a starting point for the time being, se3 is used for this purpose.
abspos = [0,0,0,eul2quat([0,0,0],"ZYX"); % rotate 0 about Z axis
1,0,0,eul2quat([pi/2,0,0],"ZYX"); % rotate 90*pi/180 about Z axis
1,1,0,eul2quat([pi,0,0],"ZYX"); % rotate 180*pi/180 about Z axis
0,1,0,eul2quat([3*pi/2,0,0],"ZYX")]; % rotate 270*pi/180 about Z axis
relT = pi/2; % no matter how much I should modify this value, the result does't change???
relPose = [1,0,0,eul2quat([relT,0,0]);
1,0,0,eul2quat([relT,0,0]);
1,0,0,eul2quat([relT,0,0]);
0.8,0,0,eul2quat([relT,0,0])];
fg = factorGraph();
pf = factorPoseSE3Prior(1,Measurement=[0,0,0, 1,0,0,0]);
addFactor(fg,pf);
nodeID = [1 2];
f = factorTwoPoseSE3(nodeID,Measurement=relPose(1,:));% odometry
addFactor(fg,f);
nodeID = [2 3];
f = factorTwoPoseSE3(nodeID,Measurement=relPose(2,:));% odometry
addFactor(fg,f);
nodeID = [3 4];
f = factorTwoPoseSE3(nodeID,Measurement=relPose(3,:));% odometry
addFactor(fg,f);
nodeID = [4 1];
f = factorTwoPoseSE3(nodeID,Measurement=relPose(end,:));% loop closure
addFactor(fg,f);
% optimize
optns = factorGraphSolverOptions();
optimize(fg,optns);
newNodes = [fg.nodeState(1);
fg.nodeState(2);
fg.nodeState(3);
fg.nodeState(4)]
figure;
plot(newNodes(:,1),newNodes(:,2),'b-',Marker='.')
hold on;grid on;
plot(newNodes([4,1],1),newNodes([4,1],2),'r-',Marker='.')
text(newNodes(:,1),newNodes(:,2),string(1:4))
title('after factorGraph(3D) optimize')
Additionly, according to the factorTwoPoseSE3 official documentation "Measured relative pose, specified as a seven-element row vector of the form [dx dy dz dqw dqx dqy dqz]. dx, dy, and dz are the change in position in x, y, and z respectively. dqw, dqx, dqy, and dqz are the change in quaternion rotation in w, x, y, and z respectively."
So, I have made the following changes to the relpos and it seems that the optimisation graphs are not correct, which is very confusing!
relPose = diff(abspos);
relPose = [relPose;
abspos(1,:)-abspos(end,:)];
run in MATLAB R2022b.

How to find intersection point of a line in a plane in 3D space using MATLAB

I have a plain with known four co-ordinates and a line with two known co-ordinates as shown in figure.
The four co-ordinates of plane are
A = (-5 -5 -8)
B = ( 15 15 -8)
C = ( 15 15 12)
D = ( -5 -5 12)
The co-ordinates of line are
M = (1.3978,40,6.1149)
N = 4.3943, 4.8078,0.3551)
In this case line and plain intersects,then how can I find point of intersection of line and plane in 3D space by using MATLAB?
or How can I check both are intersecting or not?
I have tried to find solution by following video tutorial to find equation of plane from three points and tutorial for finding point where line intersects a plain
But in my case, equation of plane is zero. So I am confused. Can anyone help me?
Thanks in advance,
Manu
I would use simple linear algebra to find the intersection point.
Let n be normal to the plain (you can calculate it as a vector product of say N = cross(AB, AD), then unit n = N / |N| where |N| = sqrt(dot(N, N)) is length of vector N.
You can use the following function from matlabcentral which covers all the corner cases as well (such as when the line is parallel to the plane) and describes them in the comments.
Example from comment:
A =[ -6.8756 39.9090 10.0000],B =[ -6.0096 40.4090 10.0000],C =[ -6.0096 40.4090 11.0000],D=[ -6.8756 39.9090 11.0000];
P0 =[ 1.3978 40.0000 6.1149],P1 =[ 4.3943 -4.8078 0.3551];
I don't know where you made a mistake, but I am pretty sure there is an intersection point which is outside your segment. So you should have got check=3. Here is the output of step by step operation:
>> AB = B-A
AB = 0.8660 0.5000 0
>> AD = D-A
AD = 0 0 1
>> n = cross(AB,AD)/sqrt(dot(cross(AB,AD),cross(AB,AD)))
n = 0.5000 -0.8660 0
>> [I,check]=plane_line_intersect(n,A,P0,P1)
I = 1.0961 44.5116 6.6948
check = 3
It produces the same results with any other point (B, C or D) passed in. check=3 means there is an intersection point I, which is outside of the P01 segment.
As a verification step, notice that normal n has Nz = 0 which means that it's perpendicular to the Z axis. The only way a line wouldn't intersect with it is if it would be parallel to Z axis (and therefore vector P01 would be parallel to Z and have zero Z component).
Your P01 is not aligned with Z:
>> P01 = P1 - P0
P01 = 2.9965 -44.8078 -5.7598

Mahalanobis distance in Matlab

I would like to calculate the mahalanobis distance of input feature vector Y (1x14) to all feature vectors in matrix X (18x14). Each 6 vectors of X represent one class (So I have 3 classes). Then based on mahalanobis distances I will choose the vector that is the nearest to the input and classify it to one of the three classes as well.
My problem is when I use the following code I got only one value. How can I get mahalanobis distance between the input Y and every vector in X. So at the end I have 18 values and then I choose the smallest one. Any help will be appreciated. Thank you.
Note: I know that mahalanobis distance is a measure of the distance between a point P and a distribution D, but I don't how could this be applied in my situation.
Y = test1; % Y: 1x14 vector
S = cov(X); % X: 18x14 matrix
mu = mean(X,1);
d = ((Y-mu)/S)*(Y-mu)'
I also tried to separate the matrix X into 3; so each one represent the feature vectors of one class. This is the code, but it doesn't work properly and I got 3 distances and some have negative value!
Y = test1;
X1 = Action1;
S1 = cov(X1);
mu1 = mean(X1,1);
d1 = ((Y-mu1)/S1)*(Y-mu1)'
X2 = Action2;
S2 = cov(X2);
mu2 = mean(X2,1);
d2 = ((Y-mu2)/S2)*(Y-mu2)'
X3= Action3;
S3 = cov(X3);
mu3 = mean(X3,1);
d3 = ((Y-mu3)/S3)*(Y-mu3)'
d= [d1,d2,d3];
MahalanobisDist= min(d)
One last thing, when I used mahal function provided by Matlab I got this error:
Warning: Matrix is close to singular or badly scaled. Results may be inaccurate.
If you have to implement the distance yourself (school assignment for instance) this is of absolutely no use to you, but if you just need to calculate the distance as an intermediate step for other calculations I highly recommend d = Pdist2(a,b, distance_measure) the documentation is on matlabs site
It computes the pairwise distance between a vector (or even a matrix) b and all elements in a and stores them in vector d where the columns correspond to entries in b and the rows are entries from a. So d(i,j) is the distance between row j in b and row i in a (hope that made sense). If you want it could even parameters to find the k nearest neighbors, it's a great function.
in your case you would use the following code and you'd end up with the distance between elements, and the index as well
%number of neighbors
K = 1;
% X=18x14, Y=1x14, dist=18x1
[dist, iidx] = pdist2(X,Y,'mahalanobis','smallest',K);
%to find the class, you can do something like this
num_samples_per_class = 6;
matching_class = ceil(iidx/ num_samples_per_class);

Finding coordinates of a point on a line

This should be an easy one. I am trying to find the coordinates of a point on a straight line. I am implementing in MATLAB. I know, the coordinates of the endpoints and the distance from one of the point.
I am using the following formula for calculating the coordinates (please note, I cannot use mid-point formula, as the distance can vary).
I am getting the wrong results when the slope is negative. Can you please suggest, what are the conditions, that needs to be considered for using this formula? Am not aware of any other formula as well.
That's too complicated solution for such a simple task. Use direct vector computations:
function P = point_on_line(A, B, AP)
D = B - A;
P = A + D / norm(D) * AP;
end
Call like this:
P = point_on_line([x1 y1], [x2 y2], len);
x = P(1);
y = P(2);
Ask if you need any clarifications.
Nothing wrong with your solution, but you need to take care of quadrant ambiguities when you take the arctangent to compute the angle θ.
There is a nice solution for that in most programming languages: atan2. Thus:
%// Your points (fill in any values)
A = [-10 0];
B = [-1 -1];
%// Use atan2!
th = atan2( B(2)-A(2) , B(1)-A(1) );
%// Distance from A to the point of interest
AP = sqrt( (B(2)-A(2))^2 + (B(1)-A(1))^2 ) / 2;
%// The point of interest
C = [
A(1) + AP*cos( th )
A(2) + AP*sin( th )];
%// Verify correctness with plots
figure(1), clf, hold on
line([A(1); B(1)], [A(2); B(2)])
plot(...
A(1), A(2), 'r.',...
B(1), B(2), 'b.',...
C(1), C(2), 'k.', 'markersize', 20)
In general, whenever and wherever you need to take an arctangent, use atan2 and not atan. The normal atan is only for cases where you don't know the individual components of the division y/x.
Note that your solution is not extensible to 3D, whereas the vector solutions proposed by the others here are. So in general I would indeed advise you to start working with vectors. Not only is it a lot simpler in many circumstances, it is also more versatile.
This is a wrong approach to the solution, as the solution is not unique. There are two points on you line with the same distance AP from the point A: one going left and another one going right.
The are infinite approaches to solve this, I prefer vector notation.
vector ab is a 2x1 matlab matrix:
ab = B-A
abN is the normalized vector
abN = ab/norm(ab)
stepping from A in the abN direction a distance of d (in your case AP) is:
A + abN*d
hope it helped.
Ohad

How to generate random cartesian coordinates given distance constraint in Matlab

I need to generate N random coordinates for a 2D plane. The distance between any two points are given (number of distance is N(N - 1) / 2). For example, say I need to generate 3 points i.e. A, B, C. I have the distance between pair of them i.e. distAB, distAC and distBC.
Is there any built-in function in MATLAB that can do this? Basically, I'm looking for something that is the reverse of pdist() function.
My initial idea was to choose a point (say A is the origin). Then, I can randomly find B and C being on two different circles with radii distAB and distAC. But then the distance between B and C might not satisfy distBC and I'm not sure how to proceed if this happens. And I think this approach will get very complicated if N is a large number.
Elaborating on Ansaris answer I produced the following. It assumes a valid distance matrix provided, calculates positions in 2D based on cmdscale, does a random rotation (random translation could be added also), and visualizes the results:
%Distance matrix
D = [0 2 3; ...
2 0 4; ...
3 4 0];
%Generate point coordinates based on distance matrix
Y = cmdscale(D);
[nPoints dim] = size(Y);
%Add random rotation
randTheta = 2*pi*rand(1);
Rot = [cos(randTheta) -sin(randTheta); sin(randTheta) cos(randTheta) ];
Y = Y*Rot;
%Visualization
figure(1);clf;
plot(Y(:,1),Y(:,2),'.','markersize',20)
hold on;t=0:.01:2*pi;
for r = 1 : nPoints - 1
for c = r+1 : nPoints
plot(Y(r,1)+D(r,c)*sin(t),Y(r,2)+D(r,c)*cos(t));
plot(Y(c,1)+D(r,c)*sin(t),Y(c,2)+D(r,c)*cos(t));
end
end
You want to use a technique called classical multidimensional scaling. It will work fine and losslessly if the distances you have correspond to distances between valid points in 2-D. Luckily there is a function in MATLAB that does exactly this: cmdscale. Once you run this function on your distance matrix, you can treat the first two columns in the first output argument as the points you need.