I am trying to calculate the 4x4 rotation and translation matrix to align one set of 3D points with another (example shows 2D case). The code I have written works OK with rotation around (0, 0)
or when translating but not when rotating around an arbitrary pivot point.
I have read in various places (such as here and here) that you need to subtract the offset to the origin but this is not what I am after as it returns the rotated shape back to its original origin.
Can the rotation and translation for these examples be stored in a single 4x4 matrix?
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import collections
def rotation(r):
return np.asarray([np.cos(r), -np.sin(r), np.sin(r), np.cos(r)]).reshape(2, 2)
def calculate_rotation(A, B, R_=np.arange(0, 360)):
yaw_rotation = np.deg2rad(R_) # Brute force!
diff = np.zeros(len(yaw_rotation))
for i, a in enumerate(yaw_rotation):
C = B.copy()
C[:, :2] = np.dot(C[:, :2], rotation(a))
diff[i] = sum([((C[:, 0].copy() - A[:, 0])**2).mean(),
((C[:, 1].copy() - A[:, 1])**2).mean()])
return -yaw_rotation[np.where(diff == diff.min())][0]
def rotation_matrix(rot=[0, 0, 0], tra=[0, 0, 0]):
xA, yA, zA = rot
rX = np.matrix([[1, 0, 0, 0], [0, np.cos(xA), -np.sin(xA), 0], [0, np.sin(xA), np.cos(xA), 0], [0, 0, 0, 1]])
rY = np.matrix([[np.cos(yA), 0, np.sin(yA), 0], [0, 1, 0, 0], [-np.sin(yA), 0, np.cos(yA), 0], [0, 0, 0, 1]])
rZ = np.matrix([[np.cos(zA), -np.sin(zA), 0, 0], [np.sin(zA), np.cos(zA), 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])
r = np.dot(rX, np.dot(rY, rZ))
t = np.identity(4)
t[:3, 3] = tra
return np.dot(t, r)
def plot_triangle(Tri, i, color='none'):
ax = plt.subplot(1, 3, i)
pc = collections.PolyCollection((Tri[:, :2], ), facecolor=color, alpha=.5)
ax.add_collection(pc)
for i, row in enumerate(Tri):
ax.scatter(row[0], row[1])
ax.text(row[0], row[1], i)
plt.figure(figsize=(12, 4))
X = [4, 0, 0]
Y = [0, 3, 0]
Z = [0, 0, 0]
A = np.vstack([X, Y, Z, np.ones(3)]).T
for i, (r_, t_) in enumerate([[np.deg2rad(33), [0, 0]], # rotate
[0, [2, -2]], # tranlate
[np.deg2rad(33), [-2, 1]]]): # combined
Xoff, Yoff, Zoff, roll, pitch, yaw = np.zeros(6)
# offset and rotate B
B = A.copy()
B[:, :2] = np.dot(B[:, :2], rotation(r_))
B[:, 0] += t_[0]
B[:, 1] += t_[1]
C = B.copy() # so we can then apply solution to original B
################################################
# calculate distance between objects
Xoff = A[0, 0] - C[0, 0]
Yoff = A[0, 1] - C[0, 1]
Zoff = A[0, 2] - C[0, 2]
# calculate and apply offset of A to 0...
A_ = A.copy()
A_[:, 0] -= A_[0, 0]
A_[:, 1] -= A_[0, 1]
A_[:, 2] -= A_[0, 2]
# calculate offset of C to 0...
Xrot = C[0, 0]
Yrot = C[0, 1]
Zrot = C[0, 2]
C[:, 0] -= Xrot
C[:, 1] -= Yrot
C[:, 2] -= Zrot
# calculate rotation
yaw = calculate_rotation(A_, C)
# generate 4x4 matrix
M = rotation_matrix(rot=(roll, pitch, yaw),
tra=(Xoff, Yoff, Zoff))
D = np.asarray(np.dot(M, B.T).T[:, :3])
plot_triangle(A[:, :3], i+1)
plot_triangle(D, i+1, 'y')
The output of this code is where the green triangle is the starting position, the red triangle is to be aligned to and the yellow triangle is where I am at.