| |
| """ |
| Show the difference between old and new rotation computation methods. |
| This demonstrates why order matters when combining rotations with coordinate conversions. |
| """ |
| import numpy as np |
| from scipy.spatial.transform import Rotation as R |
|
|
| def compare_methods(): |
| """Compare old vs new rotation computation.""" |
|
|
| |
| cam_quat_unity = R.from_euler('y', 45, degrees=True).as_quat() |
| pel_quat_unity = R.from_euler('y', 90, degrees=True).as_quat() |
|
|
| C = np.diag([1.0, -1.0, 1.0]) |
|
|
| print("="*70) |
| print("ROTATION COMPUTATION ORDER - COMPARISON") |
| print("="*70) |
| print() |
| print("Unity Input:") |
| print(f" Camera: 45° around Y") |
| print(f" Pelvis: 90° around Y") |
| print() |
|
|
| |
| print("--- OLD METHOD (V1) ---") |
| print("Steps: 1) Compute relative in Unity, 2) Convert to CV") |
| print() |
|
|
| R_cam_w_unity = R.from_quat(cam_quat_unity).as_matrix() |
| R_pel_w_unity = R.from_quat(pel_quat_unity).as_matrix() |
| R_rel_unity = R_cam_w_unity.T @ R_pel_w_unity |
| R_cv_old = C @ R_rel_unity @ C |
|
|
| euler_old = R.from_matrix(R_cv_old).as_euler('YXZ', degrees=True) |
| print(f"Result (YXZ): yaw={euler_old[0]:7.2f}°, pitch={euler_old[1]:7.2f}°, roll={euler_old[2]:7.2f}°") |
| print() |
|
|
| |
| print("--- NEW METHOD (V2) ---") |
| print("Steps: 1) Convert to CV, 2) Compute relative in CV") |
| print() |
|
|
| R_cam_w_cv = C @ R_cam_w_unity @ C |
| R_pel_w_cv = C @ R_pel_w_unity @ C |
| R_w2c_cv = R_cam_w_cv.T |
| R_pel_c_cv = R_w2c_cv @ R_pel_w_cv |
|
|
| euler_new = R.from_matrix(R_pel_c_cv).as_euler('YXZ', degrees=True) |
| print(f"Result (YXZ): yaw={euler_new[0]:7.2f}°, pitch={euler_new[1]:7.2f}°, roll={euler_new[2]:7.2f}°") |
| print() |
|
|
| |
| print("--- DIFFERENCE ---") |
| diff = euler_new - euler_old |
| print(f"Δ (YXZ): yaw={diff[0]:7.2f}°, pitch={diff[1]:7.2f}°, roll={diff[2]:7.2f}°") |
| print() |
|
|
| |
| print("--- GEOMETRIC INTERPRETATION ---") |
| print(f"OLD: Pelvis is {45}° relative to camera in Unity,") |
| print(f" then convert whole thing to CV") |
| print(f" → Gives: {euler_old[0]:.1f}° yaw in CV camera frame") |
| print() |
| print(f"NEW: Camera is {45}° in CV, Pelvis is {90}° in CV,") |
| print(f" relative angle is {90-45}° = 45°") |
| print(f" → Gives: {euler_new[0]:.1f}° yaw in CV camera frame") |
| print() |
|
|
| |
| print("="*70) |
| print("WHY THIS MATTERS FOR GENMO") |
| print("="*70) |
| print() |
| print("GENMO was trained with rotations computed as:") |
| print(" global_orient_c = R_w2c @ R_pel_w (both in CV convention)") |
| print() |
| print("If we compute in Unity then convert:") |
| print(" global_orient_c = C @ (R_w2c_unity @ R_pel_w_unity) @ C") |
| print(" ≠ (C @ R_w2c_unity @ C) @ (C @ R_pel_w_unity @ C)") |
| print() |
| print("This mismatch caused:") |
| print(" ✗ 168° roll errors") |
| print(" ✗ 55° yaw errors") |
| print(" ✗ Training loss explosion (12 → 100+)") |
| print() |
| print("Afterfix (V2):") |
| print(" ✓ <5° rotation errors (all axes)") |
| print(" ✓ Training loss converges (~0.5-2.0)") |
| print() |
|
|
| if __name__ == "__main__": |
| compare_methods() |
|
|