Recover angles after transformation - python

This seems like an easy enough task but I've failed to find a solution and I've run out of ideas.
I have two angles which I employ to define some transformation coefficients. Now, I don't actually have the values for those angles in my real data, I have the coefficients and I need to recover the angles.
I thought the arctan2 function would take care of this, but there are cases where it fails to recover the proper a1 angle and instead returns its 180 complement, which later affects the recovery of the a2 angle.
What am I doing wrong and how can I recover the a1, a2 angles properly?
import numpy as np
# Repeat 100 times
for _ in range(100):
# Define two random angles in the range [-pi, pi]. I do not have these
# angles in my actual data, I have the A,B,C coefficients shown below.
a1, a2 = np.random.uniform(-180., 180., (2,))
# Transformation coefficients using the above angles.
# This is the data I actually have.
a1_rad, a2_rad = np.deg2rad(a1), np.deg2rad(a2) # to radians
A = - np.sin(a1_rad) * np.sin(a2_rad)
B = np.cos(a1_rad) * np.sin(a2_rad)
C = np.cos(a2_rad)
# Recover a1 using 'arctan2' (returns angle in the range [-pi, pi])
a1_recover = np.arctan2(-A / B, 1.)
# Now obtain sin(a2), used below to obtain 'a2'
sin_a2 = -A / np.sin(a1_recover)
# Recover a2 using 'arctan2', where: C = cos(a2)
a2_recover = np.arctan2(sin_a2, C)
# Print differences.
a1_recover = np.rad2deg(a1_recover)
print("a1: {:.2f} = {} - {}".format(a1 - a1_recover, a1, a1_recover))
a2_recover = np.rad2deg(a2_recover)
print("a2: {:.2f} = {} - {}\n".format(a2 - a2_recover, a2, a2_recover))

When a2_rad equals 0, (A, B, C) equals (0, 0, 1) no matter what a1_rad equals. So the transformation is not 1-to-1. Therefore there is no well-defined inverse.
def ABC(a1, a2):
a1_rad, a2_rad = np.deg2rad(a1), np.deg2rad(a2) # to radians
A = - np.sin(a1_rad) * np.sin(a2_rad)
B = np.cos(a1_rad) * np.sin(a2_rad)
C = np.cos(a2_rad)
return A, B, C
print(ABC(0, 0))
# (-0.0, 0.0, 1.0)
print(90, 0)
# (-0.0, 0.0, 1.0)
print(-90, 0)
# (-0.0, 0.0, 1.0)
A similar problem happens at the opposite (South) pole. Within the limits of floating point accuracy, all these values (of the form ABC(a1, 180)) are essentially equal too:
ABC(1, 180)
# (-2.1373033680837913e-18, 1.2244602795081332e-16, -1.0)
ABC(0, 180)
# (-0.0, 1.2246467991473532e-16, -1.0)
ABC(90, 180)
# (-1.2246467991473532e-16, 7.498798913309288e-33, -1.0)
You can think of a1, a2 as coordinates on a unit sphere where a1
represents the angle away from the x-axis (more often called theta) and a2
represents the angle away from the z-axis (often called phi).
A,B,C represents the same point on the unit sphere in Cartesian coordinates.
Usually spherical coordinates restrict a1 to the range [0, 2*pi) and a2 to the range [0, pi].
Even with this restriction, the North and South poles have more than one (actually infinite number of) valid representation.

You cannot restore angle sign information because it was loosed in A,B calculation (formation).
8 possible combinations of sin/cos signs give only 4 results of A/B signs (and sign of cos(a2) cannot help here).
Note that for spherical coordinates inclination range is only 0..Pi

You should use np.arctan2(-A , B) instead of np.arctan2(-A / B, 1.). With the latter you are losing information: A = -1 and B = 1 will give the same result as A - 1 and B = -1, hence the 180 mismatch sometimes.
If you restrict a2 to be in (0,180) then you can recover the angles. Note that with this restriction a2 can be recovered as acos(C). (I've tried this but since my program is in C it might not be helpful)

Related

Distance in arrays

So I have to code a function that calculates the distance between two points - p1 and p2 - from an array with several points. Each point represents a square with 20 m on each side.
the distance should be something like:
d = sqrt(w(r1 −r2))^2 + (w(c1 −c2))^2 + (a1 −a2)^2 )
Where w is 20, the side of the square, r1 and r2, the row's index, c1 and c2 the column's index, and a1 and a2 the value of each point.
the array of distances is:
test = [ [206,205,204,190,208], [190,194,206,197,203], [196,196,205,201,193], [194,199,199,206,205], [192,196,195,201,193], [194,199,200,200,205], [196,196,195,200,193] ]
Can someone help on this easy one?
first you need to import sqrt from the math libary
squares can either be calculated by multiplying the value with itself (r2-r1)(r2-r1) or by using pow from the math libary as well. (r2-r1)^2 does not work.
strictly speaking A is not an array but a list (rows) of lists (columns).
But you can thing of it as a kind of array anyway. You get values of it by using two indices A[row_index][column_index]
from math import sqrt, pow
A = [[206,205,204,190,208],
[190,194,206,197,203],
[196,196,205,201,193],
[194,199,199,206,205],
[192,196,195,201,193],
[194,199,200,200,205],
[196,196,195,200,193]]
W = 20
def distance(r1, c1, r2, c2):
# get a values for point 1 and 2
a1 = A[r1][c1]
a2 = A[r2][c2]
# calculate the distance
d = sqrt(pow(W*(r2-r1), 2) + pow(W*(c2-c1), 2) + pow(a2-a1, 2))
return d
print(distance(0,0,4,4))
>>> 113.88

Euler Angles and Rotation Matrix from two 3D points

I am trying to find the Euler angles that allow the transformation from point A to point B in 3D space.
Consider the normalized vectors A = [1, 0, 0] and B = [0.32 0.88 -0.34].
I understand that by computing the cross product A × B I get the rotation axis. The angle between A and B is given by tan⁻¹(||cross||, A·B), where A·B is the dot product between A and B.
This gives me the rotation vector rotvec = [0 0.36 0.93 1.24359531111], which is rotvec = [A × B; angle] (the cross product is normalized).
Now my question is: How do I move from here to get the Euler angles that correspond to the transformation from A to B?
In MATLAB the function vrrotvec2mat receives as input a rotation vector and outputs a rotation matrix. Then the function rotm2eul should return the corresponding Euler angles. I get the following result (in radians): [0.2456 0.3490 1.2216], according to the XYZ convention. Yet, this is not the expected result.
The correct answer is [0 0.3490 1.2216] that corresponds to a rotation of 20° and 70° in Y and Z, respectively.
When I use eul2rot([0 0.3490 1.2216]) (with eul2rot taken from here) to verify the resulting rotation matrix, this one is different from the one I obtain when using vrrotvec2mat(rotvec).
I also have a Python spinet that yields the exactly same results as described above.
--- Python (2.7) using transform3d ---
import numpy as np
import transforms3d
cross = np.cross(A, B)
dot = np.dot(A, B.transpose())
angle = math.atan2(np.linalg.norm(cross), dot)
rotation_axes = sklearn.preprocessing.normalize(cross)
rotation_m = transforms3d.axangles.axangle2mat(rotation_axes[0], angle, True)
rotation_angles = transforms3d.euler.mat2euler(rotation_m, 'sxyz')
What I am missing here? What should I be doing instead?
Thank you
A rotation matrix has 3 degrees of freedom but the constraints of your problem only constrain 2 of those degrees.
This can be made more concrete by considering the case where we have a rotation matrix R which rotates from A to B so R*A == B. If we then construct another rotation matrix RB which rotates about vector B then applying this rotation to R*A won't have any effect, i.e. B == R*A == RB*R*A. It will, however, produce a different rotation matrix RB*R with different Euler angles.
Here's an example in MATLAB:
A = [1; 0; 0];
B = [0.32; 0.88; -0.34];
A = A / norm(A);
B = B / norm(B);
ax = cross(A, B);
ang = atan2(norm(ax), dot(A, B)); % ang = acos(dot(A, B)) works too
R = axang2rotm([ax; ang].');
ang_arbitrary = rand()*2*pi;
RB = axang2rotm([B; ang_arbitrary].');
R*A - B
RB*R*A - B
rotm2eul(R)
rotm2eul(RB*R)
Result
ans =
1.0e-15 *
-0.0555
0.1110
0
ans =
1.0e-15 *
0.2220
0.7772
-0.2776
ans =
1.2220 0.3483 0.2452
ans =
1.2220 0.3483 0.7549
I will give you a solution based on Euler's rotation theorem.
This solution gives you only the one angle, but the other angles can be derived.
import numpy as np
a_vec = np.array([1, 0, 0])/np.linalg.norm(np.array([1, 0, 0]))
b_vec = np.array([0.32, 0.88, -0.34])/np.linalg.norm(np.array([0.32, 0.88, -0.34]))
cross = np.cross(a_vec, b_vec)
ab_angle = np.arccos(np.dot(a_vec,b_vec))
vx = np.array([[0,-cross[2],cross[1]],[cross[2],0,-cross[0]],[-cross[1],cross[0],0]])
R = np.identity(3)*np.cos(ab_angle) + (1-np.cos(ab_angle))*np.outer(cross,cross) + np.sin(ab_angle)*vx
validation=np.matmul(R,a_vec)
This uses the common axis of rotation (eigenvector in this case), as the cross product.
The matrix R is then the rotation matrix.
This is a general way of doing it, and very simple.

Intersection of circle with two line segments not detecting all intersection points

I am using SymPy's geometry module to intersect line segments and circles. It seems only some intersection points are being counted, while many others are ignored.
Here is some test code for finding the intersection points:
from sympy.geometry import Point2D, Segment2D, Circle
# Point A, B and C
A = Point2D(1, 1)
B = Point2D(3, -1)
C = Point2D(-2, -2)
# Segment from A to B
f_0 = Segment2D(A, B)
# Segment from A to C
f_1 = Segment2D(A, C)
# Circle with center A and radius 0.8
c = Circle(A, .8)
i_0 = c.intersection(f_0)
i_1 = c.intersection(f_1)
print(i_0)
print(i_1)
This should work, and does catch all intersection-points when doing line-circle intersection or circle-circle intersection, but not segment-circle nor ray-circle intersection. Here is the output:
[]
[Point2D(217157287525381/500000000000000, 217157287525381/500000000000000)]
It's obviously not working as intended. I don't know what causes this, and I'd like to know how to fix it or find any alternatives (preferably still using SymPy).
I still do not know why my previous method did not work, but i know one that will. After messing around in Wolfram|Alpha i realized the coordinates of intersection where all irrational. Seeing the output of the program being a fraction, something was obviously wrong. It turns out that the radius of the circle, 0.8, caused all of the trouble.
Instead of giving a float as an argument, you need to sympify it first. It is important to remember two things:
The argument needs to be a string and not a float.
The 'rational' flag needs to be True.
With this in mind, the new code becomes:
from sympy import sympify
from sympy.geometry import Point2D, Segment2D, Circle
# Point A, B and C
A = Point2D(1, 1)
B = Point2D(3, -1)
C = Point2D(-2, -2)
# Segment from A to B
f_0 = Segment2D(A, B)
# Segment from A to C
f_1 = Segment2D(A, C)
# Circle with center A and radius 0.8
c = Circle(A, sympify('.8', rational=True))
i_0 = c.intersection(f_0)
i_1 = c.intersection(f_1)
print(i_0)
print(i_1)
The output then becomes:
[Point2D(2*sqrt(2)/5 + 1, -2*sqrt(2)/5 + 1)]
[Point2D(-2*sqrt(2)/5 + 1, -2*sqrt(2)/5 + 1)]

passing a float to a function with a bounded domain

I am trying to calculate the angle between some vectors in python2.7.
I am using the following identity to find the angle.
theta = acos(v . w / |v||w|)
For a particular instance my code is:
v = numpy.array([1.0, 1.0, 1.0])
w = numpy.array([1.0, 1.0, 1.0])
a = numpy.dot(v, w) / (numpy.linalg.norm(v) * numpy.linalg.norm(w))
theta = math.acos(a)
When I run this I get the error ValueError: math domain error
I assume this is because acos is only defined on the domain [-1,1] and my value 'a' is a float which is very close to 1 but actually a little bit bigger. I can confirm this with print Decimal(a) and I get 1.0000000000000002220446...
What is the best way to work around this problem?
All I can think of is to check for any values of 'a' being bigger than 1 (or less than -1) and round them to precisely 1. This seems like a tacky work around. Is there a neater / more conventional way to solve this problem?
numpy.clip:
was used in Angles between two n-dimensional vectors in Python
numpy.nan_to_num: also looks like a good patch if you re-arrange the math
and could be used with your code modified with my theta = atan2(b,a) formulation to avoid 1 + eps trouble with acos (which was my my 1st pass with: b = np.nan_to_num(np.sqrt(1 - a ** 2)))
But I have issues with the near universal use of the dot product alone with acos for the angle between vectors problem, particularly in 2 and 3 D where we have a np.cross product
I prefer forming the cross product b "sine" term, passing both the unnormalized a "cosine" term and my b to atan2:
import numpy as np
v = np.array([1.0, 1.0, 1.0])
w = np.array([1.0, 1.0, 1.0])
a = np.dot(v, w)
c = np.cross(v, w)
b = np.sqrt(np.dot(c,c))
theta = np.arctan2(b,a)
the atan2(b, a) formulation won't throw an exception with 1 + eps float errors from linalg.norm floating point tolerance if you use the normed args - and atan2 doesn't need normed args anyway
I believe it is more numerically robust with the cross product b term and atan2 giving better accuracy overall than just using the information in the a dot product "cosine" term with acos
edit: (a bit of a Math explaination, not the same a, b, c as in the code above, somewhat mixed up vector math typed in text since MathJax doesn't seem to be enabled on this forum )
a * b = |a||b| cos(w)
c = a x b = |a||b| sin(w) c_unit_vector
sqrt(c * c) = |a||b| sin(w) since c_unit_vector * c_unit_vector = 1
so we end up with atan( |a||b| sin(w), |a||b| cos(w) ) and the |a||b| cancel out in the ratio calculation internal to atan

calculating angle between three points but only anticlockwise in python

So I'm trying to calculate the angle between three points. for example
a = [14, 140]
b = [13, 120]
c = [12, 130]
d = [11, 110]
|
| c a
|
| d b
|____________
say i want to calculate the angle between ABC i use the follow code
#create vectors
ba = a - b
bc = c- b
# calculate angle
cosine_angle = numpy.dot(ba,bc) / (numpy.linalg.norm(ba) * numpy.linalg.norm(bc))
angle = numpy.arccos(cosine_angle)
pAngle = numpy.degrees(angle)
My script runs and the output angle works, my issue though is when i want to calculate BCD I want the angle on the outside not the inside so say instead of the angle being 120 degrees i want the angle 240. So i only want the anti-clockwise angles.
Not sure how to get this value, can anyone point me in the right direction?
*edit: in other terms i want to identify angles that are over 180 degrees anticlockwise
*edit2: the duplicate answer does not answer the question for me as i have not used alot of java so not sure how to code that in python
Now using angular cosine distance to calculate the angle between two vectors is quite good, but in your case it might be better to use arc tangent as mentioned in the comments.
Now assuming you want to calculate the counterclockwise angle between BCD, you can do this by using the numpy's atan2 function. atan2(x, y) will give the angle between the origin point y to x.
import numpy as np
def calculate_angle(point_a, point_b):
""" Calculate angle between two points """
ang_a = np.arctan2(*point_a[::-1])
ang_b = np.arctan2(*point_b[::-1])
return np.rad2deg((ang_a - ang_b) % (2 * np.pi))
a = np.array([14, 140])
b = np.array([13, 120])
c = np.array([12, 130])
d = np.array([11, 110])
# create vectors
ba = a - b
bc = c - b
cd = d - c
# calculate angle
cosine_angle = np.dot(ba, bc) / (np.linalg.norm(ba) * np.linalg.norm(bc))
angle = np.arccos(cosine_angle)
inner_angle = np.degrees(angle)
print inner_angle # 8.57299836361
# see how changing the direction changes the angle
print calculate_angle(bc, cd) # 188.572998364
print calculate_angle(cd, bc) # 171.427001636
You are able to calculate the inner angle, but you want the reversed angle? why not just calculating 360 - angle

Categories

Resources