TT Lab
Blog

한국어 English 日本語 中文

Get started
Blog

Blog

Coordinate Systems and Transformations: Rotation Matrices, Homogeneous Transformations, Euler Angles and Quaternions, from Camera to Table Tennis Table

Status: Draft (2026-10-18), Measured. The numbers were calculated directly using Python 3.9.6 and numpy 2.0.2 on a laptop (Apple M1 Max, macOS 26.5). Times are the medians of 7 repetitions (dropping the first run). Raw data and code: working directory measure/robot-arm-basics/'s m02_frames.py, m02b_float32.py, out/m02_frames.json. No experiments calibrated with a real camera were performed (Unmeasured). References were accessed on 2026-10-18. Writing convention: Measured data is the default, unmeasured items are marked as "Unmeasured", and unverified items are marked as "Unverified".

Table of Contents

1. Intuition: The Same Ball, Four Descriptions

A ball is hovering. The camera describes it as "3.1 m in front of my lens, slightly down and to the left." Relative to the table tennis table, it is "0.8 m from the center toward the robot, at a height of 25 cm." Relative to the robot base, it is "0.9 m forward, 0.4 m up," and relative to the racket, it is "3 cm in front of the face." All of these refer to the same point.

Many bugs in robot software stem from mixing up these four descriptions. Therefore, each coordinate system (frame) is given a name, and moving a point always requires going through a transformation. This series uses the following four names.

Name Origin and Axes Defined By
{C} Camera Lens center, z is viewing direction Camera setup
{T} Table tennis table Top surface center, x width, y length, z up ITTF specifications and DeepMind dataset convention
{B} Robot base Above the first joint axis Robot installation
{E} Racket Racket face center, z is face normal Forward kinematics (Part 3)

2. Diagram: The Coordinate System Chain

좌표계 사슬: 카메라가 본 공을 라켓 좌표로 카메라 {C} 공 위치를 잼 탁구대 {T} 원점: 윗면 가운데 로봇 바닥 {B} 팔이 서 있는 곳 라켓 {E} 정기구학 결과 보정으로 한 번 구함 설치할 때 잼 관절각 q 로 매번 동차 변환 T = [ R p ; 0 0 0 1 ] (4×4) 탁구대 좌표의 공 = ᵀT_C · (카메라 좌표의 공) 바닥 좌표의 공 = ᴮT_T · ᵀT_C · p_C 사슬은 오른쪽부터 곱하고, 거꾸로 가려면 역변환.
Diagram: Transformation chain from camera to table tennis table, robot base, and racket

3. Calculations

3.1 Rotation Matrix

A rotation matrix R is 3×3, and each column is a unit vector of the rotated coordinate system viewed from the original coordinate system. Therefore, it always satisfies the following two properties.

Rᵀ R = I        (열들이 서로 수직이고 길이가 1)
det R = +1      (오른손 좌표계를 유지, -1 이면 거울상)

A rotation by θ around the z-axis is as follows. Connecting two rotations is done by multiplying matrices, and reversing them requires only a transpose. There is no need to compute an inverse matrix separately.

Rz(θ) = [ cos θ  −sin θ  0 ]
        [ sin θ   cos θ  0 ]
        [   0       0    1 ]

3.2 Homogeneous Transformation

A 4×4 matrix is used to handle rotation and translation simultaneously. This is called a homogeneous transformation. A 1 is appended as the fourth component to points.

T = [ R  p ]        점 변환:   [x']   = T [x]
    [ 0  1 ]                   [1 ]       [1]

역변환:  T⁻¹ = [ Rᵀ  −Rᵀ p ]
               [ 0     1   ]

Inverse transformations are also calculated using the formula above rather than a general inverse matrix. It is numerically safer and faster.

3.3 Euler Angles

A human-readable pose representation is Euler angles. The ZYX order (yaw, pitch, roll), which is common in robotics and aviation, is R = Rz(요)·Ry(피치)·Rx(롤) . The following formula is used to extract them back from a rotation matrix (r_ij is row i, column j).

피치 = asin(−r31)
요   = atan2(r21, r11)
롤   = atan2(r32, r33)

When the pitch reaches ±90°, r11, r21, r32, and r33 all become 0, making it impossible to determine yaw and roll independently. This means two rotation axes have aligned, causing the loss of one degree of freedom, which is called gimbal lock. What is actually more important than the problem occurring only at exactly 90° is that noise swells significantly near it. We measure this in Section 4.3.

3.4 Quaternion

A unit quaternion q = (w, x, y, z) is expressed in terms of the rotation axis k and rotation angle θ as q = (cos θ/2, k·sin θ/2) . It represents rotations with four numbers, has no gimbal lock, and makes it easy to smoothly interpolate between two rotations. However, one must always remember that q and −q represent the same rotation (double cover). If signs are not aligned when calculating pose error, two close poses will appear to be nearly 360° apart.

In the code for this series, quaternion multiplication is implemented as a single pure Python function.

def qmul(a, b):
    aw, ax, ay, az = a
    bw, bx, by, bz = b
    return (aw*bw - ax*bx - ay*by - az*bz,
            aw*bx + ax*bw + ay*bz - az*by,
            aw*by - ax*bz + ay*bw + az*bx,
            aw*bz + ax*by - ay*bx + az*bw)

3.5 From Camera to Table Tennis Table: Determining Pose via Marker Points

Triangulation with two cameras yields 3D points in camera coordinates {C}. If N points whose table tennis table coordinates {T} are known—such as table corners—are also measured by the camera, R and p that best align the two point sets can be found. This method involves subtracting the centroids of the two sets and performing Singular Value Decomposition (SVD) on the covariance matrix, commonly known as the Kabsch algorithm.

def kabsch(P, Q):                       # Q ≈ R·P + t 가 되는 R, t (P, Q 는 N×3)
    pc, qc = P.mean(0), Q.mean(0)
    U, _, Vt = np.linalg.svd((P - pc).T @ (Q - qc))
    d = np.sign(np.linalg.det(Vt.T @ U.T))      # 거울상(det = -1) 방지
    R = Vt.T @ np.diag([1, 1, d]) @ U.T
    return R, qc - R @ pc

d If the line correcting the sign to is omitted, a reflection matrix may result when the points are nearly coplanar (which the table tennis surface is).

4. Measurements

4.1 Does Orthogonality Breakdown When Multiplying Rotations Repeatedly?

Like gyro integration, we measured how much RᵀR deviates from the identity matrix while repeatedly multiplying small rotations (0.06~0.6°). The same rotation sequence was also multiplied using quaternions.

Number of Multiplications float64 matrix ‖RᵀR − I‖ float64 quaternion |‖q‖ − 1| float32 matrix float32 quaternion
1,000 - - 7.8×10⁻⁶ 2.8×10⁻⁶
10,000 - - 2.9×10⁻⁵ 5.6×10⁻⁶
100,000 4.7×10⁻¹³ 2.5×10⁻¹⁴ 2.6×10⁻⁴ 8.6×10⁻⁵
1,000,000 4.4×10⁻¹² 3.1×10⁻¹⁴ (Not measured) (Not measured)

When float64 matrices were reset to the nearest rotation matrix via SVD every 1,000 operations, the error remained at 1.2×10⁻¹⁵ even after one million times.

There are two interpretations. First, drift is hardly a concern with float64 on PCs. One million times corresponds to 17 minutes at 1 kHz control. Second, the story is different for microcontrollers using float32. An error of 2.6×10⁻⁴ after 100,000 times (100 seconds at 1 kHz) is comparable to an axis skew of about 0.015° and continues to grow. Code that accumulates rotations in float32, such as motor controllers or IMU firmware, must periodically re-normalize. Quaternions make this easier than matrices because only a single length needs to be divided by 1.

4.2 Computation Time

Operation Time per operation (µs)
numpy 3×3 matrix multiplication 0.68
numpy 4×4 matrix multiplication 0.66
numpy 3×3 matrix × vector 0.60
Pure Python quaternion multiplication 0.55
Vector rotation using pure Python quaternion 1.33

Numpy takes almost the same time, 0.6~0.7 µs, for both 3×3 and 4×4 matrices. This is because function call overhead dominates over the multiplication itself at this size. Therefore, speed should not be the criterion when choosing a representation in Python. They should be chosen based on properties such as gimbal lock, interpolation, and ease of normalization.

4.3 Noise Amplification Near Gimbal Lock

After creating a ZYX pose and adding a small random rotation noise of 0.0057° (10⁻⁴ rad), angles were re-extracted using the formula in Section 3.3. This was attempted 2,000 times for each pitch as it approached 90°.

피치가 90°에 가까울수록 오일러 각이 흔들린다 30 10 3 1 0.3 0.1 0.03 0.01 0.1 1 10 피치와 90° 사이 간격(도, 로그) 요·롤 오차(도, 로그) 넣은 자세 잡음 0.0057° 중앙값(실선) 95백분위(점선) ZYX 오일러 각, 각 점 2,000번 시도. 잡음은 그대로인데 뽑아낸 각의 오차가 커진다.
Figure: Yaw and roll errors increasing as the pitch approaches 90 degrees
Difference between pitch and 90° Median yaw/roll error 95th percentile
30° 0.007° 0.011°
10° 0.017° 0.032°
3° 0.058° 0.10°
1° 0.17° 0.31°
0.3° 0.54° 1.0°
0.1° 1.6° 3.1°
0.03° 5.4° 10.5°

Since the slope on a logarithmic axis is nearly 1, the error grows roughly as "noise ÷ angle remaining until 90° (in radians)." Because table tennis rackets are frequently tilted from nearly vertical to horizontal, they often pass near a pitch of 90°. Rather than storing or filtering racket poses using Euler angles, it is safer to keep the internal representation as a rotation matrix or quaternion and convert to Euler angles only when displaying to humans.

4.4 Determining Camera Pose via Marker Points

The camera was placed 2 m behind the robot-side endline at a height of 1.6 m, pointing down at the center of the table tennis table. The four corners of the table, two points on top of the net posts, and both ends of the center line were used as basic markers, and arbitrary points on the table were added when more points were needed. Gaussian noise σ was added to each point in camera coordinates, and the pose was calculated using the function in Section 3.5. These are the medians of 500 trial runs for each condition.

Number of Points σ Rotation Error Camera Position Error Opposite Endline Point Error (Median / 95%)
4 2 mm 0.073° 3.7 mm 1.9 / 3.6 mm
4 5 mm 0.20° 9.4 mm 4.9 / 8.5 mm
8 2 mm 0.061° 2.9 mm 1.4 / 2.6 mm
8 5 mm 0.15° 7.2 mm 3.6 / 6.9 mm
8 10 mm 0.31° 15.1 mm 7.1 / 13.7 mm
32 5 mm 0.10° 4.7 mm 2.1 / 3.8 mm

5. Limitations

6. Conclusion

A word on TT. The coordinates of the same ball vary depending on who is viewing it. When tilting the racket up and down, you must hold tight to quaternions instead of Euler angles!

References

All materials were accessed on 2026-10-18.

Sign in to like

Comments

No comments yet.

Sign in to leave a comment