最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

device orientation - The simplest way to solve gimbal lock when using DeviceOrientation events in javascript - Is a perfect spir

programmeradmin1浏览0评论
To observe the problem in action with an Android device you may visit HERE and scroll down to "Show orientation angles". Once you turn it on and start getting live numbers, try holding your device as « upright » as you can, then slightly tilt it left or right. To make it more tangible scroll further down and find "Turn the Gyro-Cube ON". Finally view the behavior of the cube while doing the same upright positioning and the same leaning.

NOTE: You must view the page with https and NOT http

DISCLAIMER: We are not affiliated with any of the parties found on the Gyro-Cube demo page.

The [now-solved] problem starts with...

window.addEventListener("deviceorientation", handleDeviceTilt);
function handleDeviceTilt(event){
// Here we can use event.beta, event.gamma
// Note that event.alpha is just the pass
// alpha values are w.r.t. Earth's magnetic poles in the real world
// In theory, we don't need to know which way is north to build a simple bubble-level app
// or a simple driving simulator.
}

...where beta and gamma values are nice and usable as long as the phone or tablet is held parallel to the ground (like resting on a table). But when the phone or tablet is held UPRIGHT (like parallel to a wall) we can't get realistic values. gamma gets more and more crazy as the device approaches the perfect vertical position (aligned with Earth's gravity) which is the most mon way of holding a mobile device (i.e. portrait mode).

The question is,

What would be the simplest way to calculate the correct angles like realBeta and realGamma (perhaps by using Math.cos() and Math.sin() or by some other method) in order to unlock the so called gimbal lock? Given that the "distortedness" or the "inaccuracy" in gamma is proportional in some way to beta, there must exist a solution but how exactly do we get realGamma or actualGamma or fixedCorrectGamma?

Note 1: There are a few spirit level apps on PlayStore which suggest that it is doable. However as of 2021 there seems to be no open source code available for such an app written in javascript.

Note 2: This issue has been mentioned here.

Note 3: Precision is necessary but acceptable approximations are also wele. However, the closer we get to exact angles the better. If necessary alpha may be omitted for simplicity.

WHAT IS THIS USEFUL FOR?

By solving this problem we would be able to use deviceorientation to make something similar to this,


...or for example we could use gamma (or some kind of similar processed rotation data) to steer a car in a vertical racing game.


If the link HERE does not work please search and find any page that features some kind of live demo where it will let you see what numbers are reported by your mobile device. Watch γ (gamma) values carefully as beta approaches 90deg.

Final thought,

We may need some arcane math-magics.

To observe the problem in action with an Android device you may visit HERE and scroll down to "Show orientation angles". Once you turn it on and start getting live numbers, try holding your device as « upright » as you can, then slightly tilt it left or right. To make it more tangible scroll further down and find "Turn the Gyro-Cube ON". Finally view the behavior of the cube while doing the same upright positioning and the same leaning.

NOTE: You must view the page with https and NOT http

DISCLAIMER: We are not affiliated with any of the parties found on the Gyro-Cube demo page.

The [now-solved] problem starts with...

window.addEventListener("deviceorientation", handleDeviceTilt);
function handleDeviceTilt(event){
// Here we can use event.beta, event.gamma
// Note that event.alpha is just the pass
// alpha values are w.r.t. Earth's magnetic poles in the real world
// In theory, we don't need to know which way is north to build a simple bubble-level app
// or a simple driving simulator.
}

...where beta and gamma values are nice and usable as long as the phone or tablet is held parallel to the ground (like resting on a table). But when the phone or tablet is held UPRIGHT (like parallel to a wall) we can't get realistic values. gamma gets more and more crazy as the device approaches the perfect vertical position (aligned with Earth's gravity) which is the most mon way of holding a mobile device (i.e. portrait mode).

The question is,

What would be the simplest way to calculate the correct angles like realBeta and realGamma (perhaps by using Math.cos() and Math.sin() or by some other method) in order to unlock the so called gimbal lock? Given that the "distortedness" or the "inaccuracy" in gamma is proportional in some way to beta, there must exist a solution but how exactly do we get realGamma or actualGamma or fixedCorrectGamma?

Note 1: There are a few spirit level apps on PlayStore which suggest that it is doable. However as of 2021 there seems to be no open source code available for such an app written in javascript.

Note 2: This issue has been mentioned here.

Note 3: Precision is necessary but acceptable approximations are also wele. However, the closer we get to exact angles the better. If necessary alpha may be omitted for simplicity.

WHAT IS THIS USEFUL FOR?

By solving this problem we would be able to use deviceorientation to make something similar to this,


...or for example we could use gamma (or some kind of similar processed rotation data) to steer a car in a vertical racing game.


If the link HERE does not work please search and find any page that features some kind of live demo where it will let you see what numbers are reported by your mobile device. Watch γ (gamma) values carefully as beta approaches 90deg.

Final thought,

We may need some arcane math-magics.

Share Improve this question edited May 5, 2024 at 20:36 TheNightwalker asked Sep 17, 2021 at 0:17 TheNightwalkerTheNightwalker 6711 gold badge11 silver badges30 bronze badges 12
  • 8 Converting the angles to Quaternion could be a solution. Take a look at this similar question, stackoverflow./questions/56769428/… – HardcoreGamer Commented Sep 17, 2021 at 1:18
  • 7 You may want to remove the PS statement in your question, as it is unnecessarily confrontational. You're asking the internet to solve your problem for you without demonstrating any of your own work or elaborating on why your restrictions are in place. You may want to elaborate why some of your restrictions are included (alpha is likely required, quaternions are an obvious solution, and modernizing "outdated" libraries is always an option). The internet is not responsible for testing your solution for you, and you should always verify any answer yourself before using it in production. – rfestag Commented Oct 16, 2021 at 17:00
  • 1 Sure, why not. Those statements were temporary anyways and were just targeting the smart people who are willing to earn some reward. Done edits and now, I think it's more patible with the pure-research-mode. – TheNightwalker Commented Oct 16, 2021 at 20:46
  • 3 Looking back at this, it's still not quite clear what you are trying to do. You mention caring about only beta and gamma, but what is your goal? To implement a web application like the Spirit Level app you reference? Your diagram implies that you are particularly interested in holding the phone portrait mode, and detecting rotation about the axis that points at the user (assuming it is flat against a wall). In that case, only beta should matter, and the jitter of gamma would be irrelevant. Or do you also care about tilt out from the wall? – rfestag Commented Oct 16, 2021 at 21:45
  • 3 Check this - codepen.io/nitnelav/pen/KxYdqB – S H A S H A N K Commented Oct 22, 2021 at 18:41
 |  Show 7 more ments

2 Answers 2

Reset to default 3

The three angles you are given is an orientation expressed as a chain of 3 rotations, in a specific order, from a specific starting point.

What you are asking for is this same orientation expressed as a different chain of rotations from a different starting point.

Converting between these representations requires linear algebra. That means using matrices.

The first step is to convert the angles to a rotation matrix (code from here):

function getRotationMatrix( alpha, beta, gamma ) {
    const degtorad = Math.PI / 180; // Degree-to-Radian conversion
    var cX = Math.cos( beta  * degtorad );
    var cY = Math.cos( gamma * degtorad );
    var cZ = Math.cos( alpha * degtorad );
    var sX = Math.sin( beta  * degtorad );
    var sY = Math.sin( gamma * degtorad );
    var sZ = Math.sin( alpha * degtorad );

    var m11 = cZ * cY - sZ * sX * sY;
    var m12 = - cX * sZ;
    var m13 = cY * sZ * sX + cZ * sY;

    var m21 = cY * sZ + cZ * sX * sY;
    var m22 = cZ * cX;
    var m23 = sZ * sY - cZ * cY * sX;

    var m31 = - cX * sY;
    var m32 = sX;
    var m33 = cX * cY;

A way to read the matrix is that it contains 3 column vectors:

  • [m11, m21, m31] is the vector pointing to the right relative to the screen
  • [m12, m22, m32] is the vector pointing up relative to the screen
  • [m13, m23, m33] is the vector pointing directly out from the screen

These vectors are given in a coordinate space where the Z axis points directly up, e.g. opposite to earth's gravity.

What you asked for is to express this orientation as rotations from the upright position. For convenience later we will rearrange the matrix so that the order of the column vectors bees out, right, up:

    return [
        m13, m11, m12,
        m23, m21, m22,
        m33, m31, m32
    ];
};

And since you are assigning the greatest importance to the rotation about the out axis (X) and the least importance to the rotation about the up axis (Z), we will depose this matrix to rotations in the order X-Y-Z (code from here):

function getEulerAngles( matrix ) {
    var radtodeg = 180 / Math.PI; // Radian-to-Degree conversion
    var sy = Math.sqrt(matrix[0] * matrix[0] +  matrix[3] * matrix[3] );
 
    var singular = sy < 1e-6; // If
 
    if (!singular) {
        var x = Math.atan2(matrix[7] , matrix[8]);
        var y = Math.atan2(-matrix[6], sy);
        var z = Math.atan2(matrix[3], matrix[0]);
    } else {
        var x = Math.atan2(-matrix[5], matrix[4]);
        var y = Math.atan2(-matrix[6], sy);
        var z = 0;
    }
    return [radtodeg * x, radtodeg * y, radtodeg * z];
}

Now, to get the representation you want, simply write:

var rotation = getEulerAngles(getRotationMatrix(alpha, beta, gamma));
  • rotation[0] is the steering wheel angle that you wanted for the upright position.
  • rotation[1] is the tilt angle.
  • rotation[2] is the "pass" angle that you were not interested in.

I have uploaded a working demo that you can access from your phone browser here.

I'm afraid that obtaining the info you are looking for only through the deviceorientation event is not possible precisely for the reason you say yourself: gimbal lock.

There are hordes of mathematicians studying in this regard, the result we can learn from their studies is that the only solution to the gimbal lock problem is to avoid it: when we start to get too close, we have to change our approach.

An alternative way to obtain that angle (which works only when the phone is almost vertical, so exactly when we need an alternative) could be to use devicemotion data rather than deviceorientation data.

window.addEventListener("devicemotion", event => {
  const { x } = event.accelerationIncludingGravity;

  const radiantsRes = Math.asin(x / 9.80665);
  const degreesRes = (radiantsRes * 180) / Math.PI;

  console.log(degreesRes.toFixed(2), radiantsRes.toFixed(10))
});

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论