I have an array of coordinates in x and y, relative to the movement of an object in a constantly rotating circular environment (10 rpm). How can I disentangle between the movement of the object from that of the environment?
I tried polar coordinates, speed and movement vectors, and I get results, but I'd like to know if someone knows the right way to go. Mainly, I derotate every point according to the distance from the center of rotation, converting to polar coordinates, creating an array of derotated positions. Then I calculate the movement vector between each original_points[idx] and derotated_points[idx+1]. Then, I take the first original point as the starting point for a new array, and to that I add the first vector, storing the new location. Which will be the starting point for adding the second vector, and so on...
Seems reasonable but I'd like to know if there are other methods.
This is a really nice area and a good question about matrix transformations. If you wish to know about this, do read this course: https://graphics.stanford.edu/courses/cs248-01/
Now, Let's start by creating some sample data:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
rpm = 10
omega = rpm * 2 * np.pi / 60
time = np.linspace(0, 60, 60)
speed = 0.1
x_linear = speed * time
y_linear = np.zeros_like(time)
x_rotated = x_linear * np.cos(omega * time) - y_linear * np.sin(omega * time)
y_rotated = x_linear * np.sin(omega * time) + y_linear * np.cos(omega * time)
df = pd.DataFrame({
'time': time,
'x': x_rotated,
'y': y_rotated
})
print(df)
Here, I assume that rpm = 10
, the angular velocity, in rad(s, is given as omega = rpm * 2 * np.pi / 60
and that the speed of the obejct is speed = 0.1
units/sec.
This gives you
time x y
0 0.000000 0.000000 0.000000e+00
1 1.016949 0.049276 8.895896e-02
2 2.033898 -0.107882 1.724206e-01
3 3.050847 -0.304652 -1.623727e-02
4 4.067797 -0.177888 -3.658219e-01
5 5.084746 0.292265 -4.160862e-01
6 6.101695 0.606713 6.485704e-02
7 7.118644 0.276790 6.558492e-01
8 8.135593 -0.502393 6.399064e-01
9 9.152542 -0.903602 -1.455835e-01
10 10.169492 -0.344989 -9.566443e-01
11 11.186441 0.736640 -8.418588e-01
12 12.203390 1.192763 2.579585e-01
13 13.220339 0.381661 1.265744e+00
...
56 56.949153 -5.686844 3.030958e-01
57 57.966102 -3.074643 -4.913986e+00
58 58.983051 2.858029 -5.159620e+00
59 60.000000 6.000000 -5.732833e-14
Now, to derotate (transform the above matrix); you need to define the following function (for your reference, Gilbert Strand's Linear Algebra and Its Applications is a perfect book.
def derotate_coordinates(df, omega):
df['x_derotated'] = df['x']*np.cos(-omega*df['time']) - df['y']*np.sin(-omega*df['time'])
df['y_derotated'] = df['x']*np.sin(-omega*df['time']) + df['y']*np.cos(-omega*df['time'])
return df
which applied
df_derotated = derotate_coordinates(df.copy(), omega)
print(df_derotated)
will give you
time x y x_derotated y_derotated
0 0.000000 0.000000 0.000000e+00 0.000000 0.000000e+00
1 1.016949 0.049276 8.895896e-02 0.101695 0.000000e+00
2 2.033898 -0.107882 1.724206e-01 0.203390 -1.387779e-17
3 3.050847 -0.304652 -1.623727e-02 0.305085 0.000000e+00
4 4.067797 -0.177888 -3.658219e-01 0.406780 0.000000e+00
5 5.084746 0.292265 -4.160862e-01 0.508475 0.000000e+00
6 6.101695 0.606713 6.485704e-02 0.610169 0.000000e+00
7 7.118644 0.276790 6.558492e-01 0.711864 0.000000e+00
8 8.135593 -0.502393 6.399064e-01 0.813559 0.000000e+00
9 9.152542 -0.903602 -1.455835e-01 0.915254 2.775558e-17
10 10.169492 -0.344989 -9.566443e-01 1.016949 -5.551115e-17
11 11.186441 0.736640 -8.418588e-01 1.118644 0.000000e+00
12 12.203390 1.192763 2.579585e-01 1.220339 0.000000e+00
13 13.220339 0.381661 1.265744e+00 1.322034 -5.551115e-17
...
56 56.949153 -5.686844 3.030958e-01 5.694915 0.000000e+00
57 57.966102 -3.074643 -4.913986e+00 5.796610 0.000000e+00
58 58.983051 2.858029 -5.159620e+00 5.898305 -4.440892e-16
59 60.000000 6.000000 -5.732833e-14 6.000000 0.000000e+00
If you want to visualize this
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
plt.plot(df['x'], df['y'], 'ro-')
plt.title('Path in Rotating Frame')
plt.xlabel('X')
plt.ylabel('Y')
plt.axis('equal')
plt.subplot(1, 2, 2)
plt.plot(df_derotated['x_derotated'], df_derotated['y_derotated'], 'bo-')
plt.title('Path After Derotation')
plt.xlabel('X')
plt.ylabel('Y')
plt.axis('equal')
plt.show()
which gives
Update: In 3D, just because this is a fascinating topic
In this case, let's define a rotating spiral and work in polar coordinates:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
t = np.linspace(0, 4 * np.pi, 100)
x = t * np.sin(t)
y = t * np.cos(t)
z = t
original_points = np.vstack([x, y, z])
def rotation_matrix_y(theta):
cos_theta, sin_theta = np.cos(theta), np.sin(theta)
return np.array([
[cos_theta, 0, sin_theta],
[0, 1, 0],
[-sin_theta, 0, cos_theta]
])
def rotation_matrix_z(theta):
cos_theta, sin_theta = np.cos(theta), np.sin(theta)
return np.array([
[cos_theta, -sin_theta, 0],
[sin_theta, cos_theta, 0],
[0, 0, 1]
])
theta_z = np.radians(45)
theta_y = np.radians(30)
rot_matrix_z = rotation_matrix_z(theta_z)
rot_matrix_y = rotation_matrix_y(theta_y)
combined_rot_matrix = rot_matrix_y @ rot_matrix_z
rotated_points = combined_rot_matrix @ original_points
inverse_combined_rot_matrix = np.transpose(combined_rot_matrix)
derotated_points = inverse_combined_rot_matrix @ rotated_points
fig = plt.figure(figsize=(18, 6))
ax1 = fig.add_subplot(131, projection='3d')
ax1.plot(*original_points, 'r')
ax1.set_title('Original Spiral')
ax1.set_xlim([-20, 20])
ax1.set_ylim([-20, 20])
ax1.set_zlim([0, 40])
ax2 = fig.add_subplot(132, projection='3d')
ax2.plot(*rotated_points, 'b')
ax2.set_title('Rotated Spiral')
ax2.set_xlim([-20, 20])
ax2.set_ylim([-20, 20])
ax2.set_zlim([0, 40])
ax3 = fig.add_subplot(133, projection='3d')
ax3.plot(*derotated_points, 'g')
ax3.set_title('Derotated Spiral')
ax3.set_xlim([-20, 20])
ax3.set_ylim([-20, 20])
ax3.set_zlim([0, 40])
plt.show()
which gives you