dorukeker / gyronorm.js

JavaScript project for accessing and normalizing the accelerometer and gyroscope data on mobile devices
MIT License
641 stars 65 forks source link

acceleration normalized to direction of motion #45

Open arjunroychowdhury opened 7 years ago

arjunroychowdhury commented 7 years ago

Hi, thanks for this excellent library. My use case is to detect acceleration values relative to the direction of motion in a car. In other words, the device could be kept in any orientation but I'd like the x/y/z to be normalized in the direction of motion. What might be the best settings to use ? So far I am using the following options (which is not producing intended results - depending on how my phone is oriented acc x/y/z report different values when I move)

Also note that in the retuned value, I am seeing absolute:false - my understanding reading the documentation is this should be true since I am using WORLD base

I am testing on iPhone 7 (latest OS update)

let gyroNormArgs = {
    logger: function (data) {console.log ("Gyr-log:"+JSON.stringify(data))},
    gravityNormalized:true, 
    orientationBase:GyroNorm.WORLD,
    frequency:1000,
    screenAdjusted:true,
  }
dorukeker commented 6 years ago

Hello there, I am glad to see the library being used in different use cases. Couple of remarks to clarify the use of parameters and the return values:

The parameters you pass are correct.

orientationBase only efects the deviceOrientation values; alpha, beta and gamma values. The acceleration values stay with respect to the axis' of the device. These values are returned like this from the JS API and GyroNorm passes them to your application.

What you are trying can be achieved by some calculation. Something like... getting the orientation world based and accelerationIncludingGravity and using it to calculate the acceleration in world axis. Gyronorm does not contain such calculation at the moment. So you need to implement on your side. You can also implement within Gyronorm and send a pull-request ;) I'll be more than glad to merge.

One last thing: you need to use the accelerationIncludingGravity values.

Let me know if anything is not clear. Cheers, Doruk

arjunroychowdhury commented 6 years ago

(Edited post - added matrix inversion)

Thanks @dorukeker

I did a bit of reading. It looks like if I normalize the values of gx gy and gz (including gravity) with the inverse of the rotation matrix, I'll get earth co-ordinates.

However I am not getting orientation independent values

import {mat3} from 'gl-matrix';

    let gyroNormArgs = {
        gravityNormalized:false, // tried true too
        orientationBase:GyroNorm.WORLD, // tried .GAME too
        frequency:1000,
        screenAdjusted:false,
      }

Inside the callback with data (gdata):

       let alpha = gdata.dm.alpha;
       let beta = gdata.dm.beta;
       let gamma = gdata.dm.gamma;
       let rotatematrix = this.getRotationMatrix(alpha, beta, gamma);
       let relativeacc = new Array(3);
       let earthacc = new Array(3);
       let inv = new Array(9)
       relativeacc[0] = gdata.dm.gx;
       relativeacc[1] = gdata.dm.gy;
       relativeacc[2] = gdata.dm.gy;
       mat3.invert(inv,rotatematrix);
       mat3.multiply(earthacc, inv, relativeacc);
       let accEarthX = earthacc[0];
       let accEarthY = earthacc[1];
       let accEarthZ = earthacc[2];
       console.log (`---RAW DATA --- ` + JSON.stringify(gdata));
       console.log (`*** EARTH DATA X=${accEarthX}, Y=${accEarthY} Z=${accEarthZ}`)

The rotation matrix:

    // credit:https://stackoverflow.com/a/36662093/1361529
    getRotationMatrix(alpha, beta, gamma) {

        let out = [];
        let  _z = alpha;
        let _x = beta;
        let _y = gamma;

        let cX = Math.cos( _x );
        let cY = Math.cos( _y );
        let cZ = Math.cos( _z );
        let sX = Math.sin( _x );
        let sY = Math.sin( _y );
        let sZ = Math.sin( _z );

        out[0] = cZ * cY + sZ * sX * sY,    // row 1, col 1
        out[1] = cX * sZ,                   // row 2, col 1
        out[2] = - cZ * sY + sZ * sX * cY , // row 3, col 1

        out[3] = - cY * sZ + cZ * sX * sY,  // row 1, col 2
        out[4] = cZ * cX,                   // row 2, col 2
        out[5] = sZ * sY + cZ * cY * sX,    // row 3, col 2

        out[6] = cX * sY,                   // row 1, col 3
        out[7] = - sX,                      // row 2, col 3
        out[8] = cX * cY                    // row 3, col 3

      return out
      };

And here is my output (Phone face up, on table, testing in house):

    2017-09-10 19:48:34.458837-0400 TripData[6642:2247482] ---RAW DATA --- {"do":{"alpha":242.09,"beta":0.4,"gamma":-0.8,"absolute":false},"dm":{"x":0,"y":0,"z":0.13,"gx":-0.13,"gy":-0.07,"gz":-9.67,"alpha":0.02,"beta":0.05,"gamma":0}}
    2017-09-10 19:48:34.458906-0400 TripData[6642:2247482] *** EARTH DATA X=-0.13144212418873122, Y=-0.07079655168384706 Z=-0.06641397637870017
    2017-09-10 19:48:35.461572-0400 TripData[6642:2247482] ---RAW DATA --- {"do":{"alpha":242.1,"beta":0.4,"gamma":-0.8,"absolute":false},"dm":{"x":0,"y":0,"z":0.12,"gx":-0.13,"gy":-0.07,"gz":-9.69,"alpha":0.02,"beta":-0.07,"gamma":-0.06}}
    2017-09-10 19:48:35.461715-0400 TripData[6642:2247482] *** EARTH DATA X=-0.13524640367025567, Y=-0.06279441851157147 Z=-0.06682268550466884

And here is my output (Phone face down, on table, testing in house):

    2017-09-10 19:49:32.592124-0400 TripData[6642:2247482] ---RAW DATA --- {"do":{"alpha":65.79,"beta":179.79,"gamma":-1,"absolute":false},"dm":{"x":0,"y":0.01,"z":0.1,"gx":-0.04,"gy":-0.16,"gz":9.9,"alpha":0.06,"beta":0.06,"gamma":-0.01}}
    2017-09-10 19:49:32.592262-0400 TripData[6642:2247482] *** EARTH DATA X=-0.05167393815904252, Y=-0.16648286542066582 Z=-0.14971058625381142
    2017-09-10 19:49:33.598502-0400 TripData[6642:2247482] ---RAW DATA --- {"do":{"alpha":65.8,"beta":179.79,"gamma":-1,"absolute":false},"dm":{"x":-0.01,"y":0,"z":0.09,"gx":-0.05,"gy":-0.17,"gz":9.9,"alpha":-0.01,"beta":-0.06,"gamma":0.11}}
    2017-09-10 19:49:33.598629-0400 TripData[6642:2247482] *** EARTH DATA X=-0.02944164169071515, Y=-0.1595350723662996 Z=-0.18434139638083227
    2017-09-10 19:49:34.594939-0400 TripData[6642:2247482] ---RAW DATA --- {"do":{"alpha":65.8,"beta":179.79,"gamma":-1,"absolute":false},"dm":{"x":-0.01,"y":0,"z":0.11,"gx":-0.04,"gy":-0.17,"gz":9.91,"alpha":-0.07,"beta":-0.06,"gamma":0.05}}
    2017-09-10 19:49:34.595080-0400 TripData[6642:2247482] *** EARTH DATA X=-0.020228116574781345, Y=-0.16120266970200792 Z=-0.18167146881330154

And here is my output (Phone face up, somewhat perpendicular to table, testing in house):

    2017-09-10 19:50:52.147809-0400 TripData[6642:2247482] ---RAW DATA --- {"do":{"alpha":221.25,"beta":83.19,"gamma":5.78,"absolute":false},"dm":{"x":-0.02,"y":0.02,"z":-0.04,"gx":0.1,"gy":-9.71,"gz":-1.19,"alpha":1.33,"beta":0.21,"gamma":-0.25}}
    2017-09-10 19:50:52.147943-0400 TripData[6642:2247482] *** EARTH DATA X=-11.682112584452096, Y=-0.49477805209007863 Z=-7.201502637877455
    2017-09-10 19:50:53.147058-0400 TripData[6642:2247482] ---RAW DATA --- {"do":{"alpha":221.15,"beta":83.36,"gamma":5.9,"absolute":false},"dm":{"x":-0.03,"y":0.03,"z":0.02,"gx":0.09,"gy":-9.72,"gz":-1.11,"alpha":-0.69,"beta":0.39,"gamma":-0.11}}
    2017-09-10 19:50:53.147192-0400 TripData[6642:2247482] *** EARTH DATA X=7.309035050034833, Y=-10.39162106453064 Z=-5.249487431032232
    2017-09-10 19:50:54.147017-0400 TripData[6642:2247482] ---RAW DATA --- {"do":{"alpha":221.87,"beta":83.48,"gamma":5.16,"absolute":false},"dm":{"x":0,"y":0.04,"z":-0.01,"gx":0.1,"gy":-9.7,"gz":-1.12,"alpha":0.47,"beta":-1.02,"gamma":0.42}}
    2017-09-10 19:50:54.147151-0400 TripData[6642:2247482] *** EARTH DATA X=4.71089937390668, Y=0.3389442491438679 Z=-12.879539738860931
    2017-09-10 19:50:55.147900-0400 TripData[6642:2247482] ---RAW DATA --- {"do":{"alpha":221.78,"beta":83.58,"gamma":5.25,"absolute":false},"dm":{"x":-0.02,"y":0.01,"z":-0.01,"gx":0.08,"gy":-9.73,"gz":-1.1,"alpha":-1.12,"beta":0.39,"gamma":-0.29}}
    2017-09-10 19:50:55.148033-0400 TripData[6642:2247482] *** EARTH DATA X=10.120001205152024, Y=-7.904409539460901 Z=-4.945511645947363
    2017-09-10 19:50:56.177822-0400 TripData[6642:2247482] ---RAW DATA --- {"do":{"alpha":231.77,"beta":83.98,"gamma":-2.92,"absolute":false},"dm":{"x":-0.02,"y":0.18,"z":0.11,"gx":-0.08,"gy":-9.58,"gz":-0.92,"alpha":-9.31,"beta":0.07,"gamma":0.38}}

As you see above, the readings are dependent on orientation and angle. What am I missing? Thank you.

dorukeker commented 6 years ago

Hello there @arjunroychowdhury Wow that is some maths. Frankly speaking I am not very good at the matrix operations. Having said that I see in the code you are using the angles from the deviceMotion

let alpha = gdata.dm.alpha;
let beta = gdata.dm.beta;
let gamma = gdata.dm.gamma;

Which are the change rates in these angles; not actual orientation. The orientation are in the deviceOrientation. Like this:

let alpha = gdata.do.alpha;
let beta = gdata.do.beta;
let gamma = gdata.do.gamma;

// do instead of dm

Can this be what you are looking for? Also try to switch orientation base as you mentioned. (WORLD or GAME) Not sure which one should give you the result you are looking for.

Cheers, Doruk

ziqbal commented 6 years ago

Hi All,

Did you figure this out?

I'll be attempting this as well and would like to save some some time and troubles.

marcusx2 commented 3 years ago

@arjunroychowdhury @ziqbal Anyone figure this out?

ziqbal commented 3 years ago

@marcusx2 I never found the time to check the last feedback from dorukeker. Maybe it works. My advice is that if you're targeting a single device then you should use whatever is provided on that platform, otherwise a generic library to do absolute orientation on all devices and platforms is going to be a serious PITA. Especially when libraries become out of date and "features" become deprecated and obsolete.