Search Unity

Input.acceleration Calibration

Discussion in 'Scripting' started by WolfBeardedLion, Apr 7, 2015.

  1. WolfBeardedLion

    WolfBeardedLion

    Joined:
    Apr 5, 2013
    Posts:
    27
    I have searched internet and read all the documentation, forums, Unity answers, and blogs I could find in an effort to answer this question myself. 99% of the time that works, but not this time so here we go:

    Code (CSharp):
    1. Vector3 theAcceleration = Input.acceleration;
    2.  
    3. if(theAcceleration.y <= -0.007f + startTiltY)
    4. {
    5.     // Do something #1
    6. }
    7. else if(theAcceleration.y >= 0.007f + startTiltY)
    8. {
    9.     // Do something #2
    10. }
    11. else
    12. {
    13.     // Do something #3
    14. }
    startTiltY is set in Start() to ensure the user can play holding the device at any Y angle. Everything works great if the user holds the device between parallel to the ground to tilted towards them near perpendicular to the ground. If the user is holding the device perpendicular or any other way, then "Do something #1" and "Do something #2" won't ever be fired depending on which way you are over-tilting.

    I am assuming this return value is being capped at a certain point, or wrapping over to the other side of its range.

    How can I ensure no matter how the user is holding his/her device my code will still know if the user is tilting to go up or down? Thanks in advance.
     
    Last edited: Apr 8, 2015
    kiddinn95 likes this.
  2. Polymorphik

    Polymorphik

    Joined:
    Jul 25, 2014
    Posts:
    599
    What you need to do is calibrate.
     
  3. WolfBeardedLion

    WolfBeardedLion

    Joined:
    Apr 5, 2013
    Posts:
    27
    Thank you for responding, Polymorphik. I do recall reading a couple of post where this was mentioned but no one has gone into more detail than that from what I've found online.

    Could you elaborate a bit on how this process works? or if you can supply a link to this answer, I would be very happy with that as well. Thanks again.
     
  4. Polymorphik

    Polymorphik

    Joined:
    Jul 25, 2014
    Posts:
    599
    Well what you need is to first get the initial orientation of the phone. Based off this orientation we are essentially setting our origin (axis) based on this initial orientation. Rotations are better represented by a matrix.

    So step one is to create a baseMatrix to which you will send accelerometer information to compensate for the initial orientation of the device (calibration).

    Code (CSharp):
    1.     Matrix4x4 baseMatrix = Matrix4x4.identity;
    2.  
    3.     public void Calibrate() {
    4.         Quaternion rotate = Quaternion.FromToRotation(new Vector3(0.0f, 0.0f, -1.0f), Input.acceleration);
    5.      
    6.         Matrix4x4 matrix = Matrix4x4.TRS(Vector3.zero, rotate, new Vector3(1.0f, 1.0f, 1.0f));
    7.      
    8.         this.baseMatrix = matrix.inverse;
    9.     }
    Now call this when the controller is first created like in Start for instance. Now instead of using the Input.acceleration values directly we will multiply it by the baseMatrix (calibration) to get the adjusted input.

    Code (CSharp):
    1.     public Vector3 AdjustedAccelerometer {
    2.         get {
    3.             return this.baseMatrix.MultiplyVector(Input.acceleration);
    4.         }
    5.     }
    Now you can use the adjusted value to tilt your device in any orientation such as laying on your side, laying on your back etc. Hope this helps.
     
    tanapp and kiddinn95 like this.
  5. WolfBeardedLion

    WolfBeardedLion

    Joined:
    Apr 5, 2013
    Posts:
    27
    Thank you for taking the time to break this down for me.

    Yesterday, I came across this old blog post from 2010: http://theantranch.com/blog/simple-iphone-calibration/

    I had to convert it to C# and change the old iPhoneInput.acceleration call to Input.acceleration, but it was working very well when I tested the new build.

    Code (CSharp):
    1.  
    2. private Quaternion calibrationQuaternion;
    3.  
    4. // Used to calibrate the Input.acceleration
    5. void CalibrateAccelerometer()
    6. {
    7.     Vector3 accelerationSnapshot = Input.acceleration;
    8.  
    9.     Quaternion rotateQuaternion = Quaternion.FromToRotation(
    10.         new Vector3(0.0f, 0.0f, -1.0f), accelerationSnapshot);
    11.  
    12.     calibrationQuaternion = Quaternion.Inverse(rotateQuaternion);
    13. }
    14.    
    15. void Start()
    16. {
    17.     CalibrateAccelerometer();
    18. }
    19.  
    20. void Update()
    21. {    
    22.     Vector3 theAcceleration = Input.acceleration;
    23.     Vector3 fixedAcceleration = calibrationQuaternion * theAcceleration;
    24.  
    25.     // Use fixedAcceleration for any logic that follows
    26. }
    27.  
    Thank you again, Polymorphik, for helping me get this figured out.
     
    novicecode likes this.
  6. Polymorphik

    Polymorphik

    Joined:
    Jul 25, 2014
    Posts:
    599
    No problem glad I could help
     
    WolfBeardedLion likes this.
  7. Starkeslicht

    Starkeslicht

    Joined:
    Apr 13, 2015
    Posts:
    1
    Hi,

    Thank you both for the post. I've been searching for ages for a method of calibrating a mobile device so that perpendicular is not zero; rather the player’s preferred position is zero. The movement controls that I have work as desired to move the object other than being calibrated.

    If I understand this correctly, you are checking the initial values of the device’s orientation and storing them. And if the accelerometer changes from those values, you apply code for movement. Pardon my ignorance as I am new to programming.

    How do you end up applying fixedAcceleration to your game object? Do you continue to use if / else statements to control based on degree of tilt? For example, if fixedAcceleration > .007 you apply movement forward.

    Below is my code:

    Code (CSharp):
    1.    
    2. void FixedUpdate ()
    3.     {
    4.         float moveHorizontal = Input.acceleration.normalized.x; // left / right movement
    5.         float moveVertical = -Input.acceleration.normalized.z; //forward / backwards
    6.  
    7.         // main three directional movement control
    8.         Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
    9.  
    10.         rigidbody.AddForce(movement * speed * Time.deltaTime);
    11.  
    12.     }
    13.  
    I’m going to try to experiment this evening with the code you’ve listed. If you have any suggestions to offer, it would be most appreciated.
     
  8. Polymorphik

    Polymorphik

    Joined:
    Jul 25, 2014
    Posts:
    599
    Hello, and welcome to the community! Well this will depend on how you want the player to interact with the device. Do you want them to tilt it side to side? or do you want them to tilt towards them and away from the them? Or a mixture of both?

    First off yes, you are correct. What needs to happen when you load the controller is it needs to get calibrated via the code I posted. The initial orientation of the device will get stored in a 4x4 matrix since rotations are easily represented by a matrix. We then multiply that baseMatrix by the Input.acceleration in order to get a newly adjusted value where the initial orientation of the device is taken into account.

    What I do is I create a 'dead zone' basically any value below this I ignore as input.

    Here is a method where I query the accelerometer based on settings defined by the user. You can ignore those but the basic thing to take away from this is the deadZone. Once you get the adjusted accelerometer Vector3 you can now begin to further clean up any garbage input the device might send back. The values generally on all axis are from -1.0 to 1.0 but if you look at the Input.accelerometer that is not always the case. So I adjust the values based on the deadZone (user defined) and I make sure to clamp the values between -1.0 to 1.0 to make sure I don't get any weird input.

    The other part I adjust is the angle. Now by default the angle at which you would need to tilt the device to read -1.0 or 1.0 or the maximum tilt on either direction is 90º. This can be kinda damaging based on how the game works, if its a racing game then I totally understand but not all games should require you to tilt all the way in order to reach max velocity, again each game is different. So what I do is take the angle and divide it by the maximum tilt angle which is 90º, this is all done with variables no hardcoded values) I then take that value and divide it into a variable called 'value' which will get sent back. The switch statement is generic so I can adjust what axis does what for which input (kinda confusing but my accelerometer works for any game not just mine its generic not specific). Lastly I multiply it by some sensitive value and return it. There is invert that will flip the negative sign kinda how joysticks have the invert Y for shooters.
    Code (CSharp):
    1.     float AccelerometerInput(AccelerometerOrientationSettings oSettings) {
    2.         this.input = this.AdjustedAccelerometer;
    3.         float value = 0.0f;
    4.      
    5.         switch(oSettings.axis) {
    6.         case Axis.XAxis:
    7.             value = input.x;
    8.             break;
    9.         case Axis.YAxis:
    10.             value = input.y;
    11.             break;
    12.         case Axis.ZAxis:
    13.             value = input.z;
    14.             break;
    15.         default:
    16.             UnityEngine.Debug.LogWarning(typeof(Accelerometer).ToString() + " - AccelerometerInput(): Axis is not defined! Return 0.0f");
    17.             return 0.0f;
    18.         }
    19.      
    20.         if(Mathf.Abs(value) < this.settings.DeadZone) {
    21.             value = 0.0f;
    22.         } else {
    23.             if(value > 0.0f) {
    24.                 value -= this.settings.DeadZone;
    25.             } else {
    26.                 value += this.settings.DeadZone;
    27.             }
    28.  
    29.             float trueValue = oSettings.angle / Accelerometer.maxAngle;
    30.             value /= trueValue;
    31.  
    32.             value /= (1.0f - this.settings.DeadZone);
    33.         }
    34.  
    35.         value = Mathf.Clamp(value, -1.0f, 1.0f);
    36.      
    37.         return value * this.settings.Sensitivity * ((oSettings.invert) ? -1.0f : 1.0f);
    38.     }
    How I get the input
    Code (CSharp):
    1.     /// <summary>
    2.     /// Returns the horizontal movement.
    3.     /// </summary>
    4.     /// <returns>The movement.</returns>
    5.     public override float HorizontalMovement() {
    6.         return this.AccelerometerInput(this.settings.horizontal);
    7.     }
    8.  
    9.     /// <summary>
    10.     /// Returns the vertical movement.
    11.     /// </summary>
    12.     /// <returns>The movement.</returns>
    13.     public override float VerticalMovement () {
    14.         return this.AccelerometerInput(this.settings.vertical);
    15.     }
    Anyways based off of these values I recommend you change the velocity of the gameObject instead of adding a push. Changing the velocity can be more responsive than adding forces. Also by adjusting the velocity you can cap the speed with simple Vector math. If you need help implementing an accelerometer let me know and I would be graded to help! I had to learn all of this on my own so I know how hard it can be.

    Hope this helps.
     
    novicecode likes this.