composable-core-motion icon indicating copy to clipboard operation
composable-core-motion copied to clipboard

CMRotationMatrix (Attitude) is broken

Open barksten opened this issue 4 years ago • 5 comments

When replacing vanilla CMMotionManager I get some flipped axes of the rotation matrix

I've stopped at a breakpoint in Attitude.init(_ attitude: CMAttitude) and let it assign self.quaternion Comparing the rotation matrices yields the following results:

(lldb) po attitude.rotationMatrix
▿ CMRotationMatrix
  - m11 : -0.1969096064567566
  - m12 : 0.9799075722694397
  - m13 : -0.03173968195915222
  - m21 : -0.7388630509376526
  - m22 : -0.12703722715377808
  - m23 : 0.6617723703384399
  - m31 : 0.6444437503814697
  - m32 : 0.15376073122024536
  - m33 : 0.7490326166152954

(lldb) po self.rotationMatrix
▿ CMRotationMatrix
  - m11 : -0.19690976161972862
  - m12 : -0.7388631294669954
  - m13 : 0.6444438080182784
  - m21 : 0.979907719073764
  - m22 : -0.12703738138275833
  - m23 : 0.1537607421647839
  - m31 : -0.031739689326065
  - m32 : 0.6617724530725418
  - m33 : 0.7490325843885801

(lldb) 

Expected behavior To have the same axes would be nice..

Environment

  • Xcode 12.4
  • Swift 5.3
  • OS iOS 14.4

barksten avatar Mar 22 '21 13:03 barksten

Temporary workaround is to flip the matrix:

extension CMRotationMatrix {
    var flipped: Self {
        var flipped = CMRotationMatrix()
        flipped.m11 = self.m11
        flipped.m12 = self.m21
        flipped.m13 = self.m31
        flipped.m21 = self.m12
        flipped.m22 = self.m22
        flipped.m23 = self.m32
        flipped.m31 = self.m13
        flipped.m32 = self.m23
        flipped.m33 = self.m33
        return flipped
    }
}

barksten avatar Mar 22 '21 13:03 barksten

Good find! Some of the math here file must be wrong or CoreMotion has a different basis than what we assume :/

Will look into it soon, but if the fix really is to transpose the matrix we'd be happy to except a PR to update that computed property.

mbrandonw avatar Mar 22 '21 13:03 mbrandonw

I'll give it a try later today. The hardest part is to write tests since the (core motion) framework types don't have any useful public constructors (I guess that's why you wrote your own implementations in the first place...)

barksten avatar Mar 22 '21 14:03 barksten

Sorry, I have not worked on this in a while... Anyway, today I have some new findings:

I put a breakpoint inside DeviceMotion.init(_ deviceMotion: CMDeviceMotion):

po deviceMotion

QuaternionX -0.000882 QuaternionY -0.005251 QuaternionZ 0.932196 QuaternionW -0.361915 UserAccelX 0.001116 UserAccelY 0.000844 UserAccelZ -0.003047 RotationRateX -0.000685 RotationRateY 0.003429 RotationRateZ 0.000225 MagneticFieldX -15.539566 MagneticFieldY 8.030884 MagneticFieldZ -36.237579 MagneticFieldAccuracy 2 Heading 47.562225 SensorLocation 0 @ 667686.919423

po self.attitude
▿ Attitude
  ▿ quaternion : CMQuaternion
    - x : -0.000882195988422291
    - y : -0.005250768163730715
    - z : 0.9321958498359307
    - w : -0.36191515007517006

So far, so good.. if we continue:

po deviceMotion.attitude
CMAttitude Pitch: -0.524317, Roll: 0.312014, Yaw: -137.562226

po self.attitude.pitch * 180 / Double.pi
0.3120013533421466

po self.attitude.roll * 180 / Double.pi
-0.52432466754302

po self.attitude.yaw * 180 / Double.pi
-137.56508141514132

My conclusion is that the bases on CoreMotion is different than your assumptions and it's not just the rotation matrix that is broken.

For reference

I'll prepare a PR later tonight.

barksten avatar Feb 24 '22 13:02 barksten

I've created a new PR with just the rotation matrix fix. The pitch/roll is still an issue. I've searched around the we in my quest to understand the quirks of CMQuaternion and have found this old SO post (unexpected results from glkquaternion conversion from cmquaternion)

barksten avatar Mar 01 '22 09:03 barksten