How to obtain roll / tilt angle from gyroscope?

Imagine that the user is holding the device with both hands in landscape mode. I need to determine the roll / tilt angle. The picture below show three different angles.


This is the only kind of rotation I am interested in. So I tried using Input.gyro.attitude.eulerAngles.z. That works if the forward vector of the gyroscope is pointing at the horizon like the top image below. But if the forward vector is pointing downwards (3rd image) it fails.


So I was wondering if anyone has an idea of how to determine the angles (z rotation) shown in the first image regadles of x rotation. I know that this a typical case of gimbal lock, but there must be a work-around. How do racing games tackle this problem when using the gyro for steering??

Note: I do not want to use Input.gyro.rotationRate and apply it as a delta to some kind of starting orientation, because that will become more and more inaccurate over time. Input.gyro.attitude (which is a quaternion) is the way to go. I just don’t know how to get roll / tilt if the device is facing down (or up).

As it turns out, the solution is pretty easy. After having spent way too much time with manual calculation I came up with this few lines instead:

Quaternion referenceRotation = Quaternion.identity;
Quaternion deviceRotation = DeviceRotation.Get();
Quaternion eliminationOfXY = Quaternion.Inverse(
	Quaternion.FromToRotation(referenceRotation * Vector3.forward, 
							  deviceRotation * Vector3.forward)
Quaternion rotationZ = eliminationOfXY * deviceRotation;
float roll = rotationZ.eulerAngles.z;

Rotating the quaternion so that its forward vector points to referenceRotation * Vector3.forward eliminates x and y rotation while maintaining z.

Still, the resulting roll angle will get a little bit unstable if you turn yourself 180° around real worlds y axis while playing (e.g. if you’re sitting in a moving vehicle). That’s what referenceRotation is for, which could either be reset manually by the user or even better via compass. That’s what’s next on my to do list :slight_smile:


DeviceRotation.Get() is a wrapper for gyro input which adjusts the forward axis and takes different device orientations into account. Read more about it here.

@codingChris we are dealing with a similar situation right now where we need the X and Y axis.
You describe this working for the Z axis, do you mean the “heading” rotation as used in regular aeronautics?

much appreciated, and sorry for the gravereply!

When I use Chris’ code for the z-axis (the most upvoted post), I am able to change the referenceRotation to my own value (using DeviceRotation.Get), and measure the difference from that. It works perfectly, as I set my referenceRotation to the player’s resting hand position on startup. And I use that as my reference point.

But as soon as I try to do the same with the y-axis (using Vector3.up), it does not work and the reading goes completely crazy. This axis only seems to work with a referenceRotation of Quaternion.identity, nothing else. Can anyone please advise why this might be? I really need to do the same thing as the z-axis and I do not understand why it won’t work.

@codingChris any ideas?

EDIT: I suspect it has something to do with when he says “DeviceRotation.Get() is a wrapper for gyro input which adjusts the forward axis and takes different device orientations into account”. I suspect I need to do the same for the up axis, but unfortunately the tutorial he posted no longer works!! Does anyone know how to do this?