Search Unity

Manually calculate angular velocity of gameobject

Discussion in 'Scripting' started by Sasmaster, Jan 6, 2015.

  1. Sasmaster

    Sasmaster

    Joined:
    Jan 19, 2010
    Posts:
    44
    I need to find angular velocity of a game object as I can't use rigidbody forces.I have searched every possible reference in the web.Most of that are formulas but I can't figure out how to convert it into code.

    For example here.
     
  2. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,532
    What are you attempting to do? And why can't you use Rigidbodies?
     
  3. Sasmaster

    Sasmaster

    Joined:
    Jan 19, 2010
    Posts:
    44
    I need to get an angular velocity of FPS controller.I can't say why but I can't use the rigid body forces in my scenario.
    The formula seems understandable in general but how do I find quat derivatives and how can I cross product between inverses product and the division of the derivatives...Have no idea how to implement it...
     
  4. BenZed

    BenZed

    Joined:
    May 29, 2014
    Posts:
    524
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class RotationTracker : MonoBehaviour {
    4.  
    5.     //First we'll need a couple of variables to do the calculation.
    6.     Quaternion rotationLast; //The value of the rotation at the previous update
    7.     Quaternion rotationDelta; //The difference in rotation between now and the previous update
    8.  
    9.     //Initialize rotationLast in start, or it will cause an error
    10.     void Start() {
    11.         rotationLast = transform.rotation;
    12.     }
    13.  
    14.     void Update () {
    15.         //Update both variables, so they're accurate every frame.
    16.         rotationDelta = transform.rotation - rotationLast;
    17.         rotationLast = transform.rotation;
    18.    
    19.     }
    20.  
    21.     //Ta daa!
    22.     public Vector3 angularVelocity {
    23.         get {
    24.             return rotationDelta.eulerAngles;
    25.         }
    26.     }
    27.  
    28. }
    29.  
     
    FlightOfOne and Sasmaster like this.
  5. Sasmaster

    Sasmaster

    Joined:
    Jan 19, 2010
    Posts:
    44

    Are you kidding me??!?!?! That's it?? What about inverse of quaternion and cross product and division by 2 .Is this really correct?
     
  6. BenZed

    BenZed

    Joined:
    May 29, 2014
    Posts:
    524
    No, actually it isn't. I pasted before testing it. You can't subtract quaternions. My bad:

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class RotationTracker : MonoBehaviour {
    4.  
    5.     //First we'll need a couple of variables to do the calculation.
    6.     Vector3 rotationLast; //The value of the rotation at the previous update
    7.     Vector3 rotationDelta; //The difference in rotation between now and the previous update
    8.  
    9.     //Initialize rotationLast in start, or it will cause an error
    10.     void Start() {
    11.         rotationLast = transform.rotation.eulerAngles;
    12.     }
    13.  
    14.     void Update () {
    15.         //Update both variables, so they're accurate every frame.
    16.         rotationDelta = transform.rotation.eulerAngles - rotationLast;
    17.         rotationLast = transform.rotation.eulerAngles;
    18.    
    19.     }
    20.  
    21.     //Ta daa!
    22.     public Vector3 angularVelocity {
    23.         get {
    24.             return rotationDelta;
    25.         }
    26.     }
    27.  
    28. }
    29.  
     
    CelsoDev likes this.
  7. Sasmaster

    Sasmaster

    Joined:
    Jan 19, 2010
    Posts:
    44
    Actually you can but it doesn't seem to work fine.There is a jump in avelocity values.

    http://forum.unity3d.com/threads/delta-quaternion.146477/
     
  8. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,532
    Go here:
    https://code.google.com/p/spacepupp.../trunk/SpacepuppyBase/Utils/QuaternionUtil.cs

    I have a function called 'FromToRotation':

    Code (csharp):
    1.  
    2.         /// <summary>
    3.         /// Get the rotation that would be applied to 'start' to end up at 'end'.
    4.         /// </summary>
    5.         /// <param name="start"></param>
    6.         /// <param name="end"></param>
    7.         /// <returns></returns>
    8.         public static Quaternion FromToRotation(Quaternion start,Quaternion end)
    9.         {
    10.             return Quaternion.Inverse(start)* end;
    11.         }
    12.  
    That's basically how you subtract quaternions.

    You'd say:

    Code (csharp):
    1.  
    2. rotationDelta = QuaternionUtil.FromToRotation(rotationLast, transform.rotation);
    3. rotationLast = transform.rotation
    4.  

    Note, rotationDelta isn't the angular velocity though, because the delta is over some arbitrary amount of time. You need to scale those values up to some unit range (per second usually).

    Code (csharp):
    1.  
    2. //still in the Update call
    3. angularVelocity = rotationDelta.euler / Time.deltaTime;
    4.  
    If you want to store the velocity as a quaternion as well you could do:

    Code (csharp):
    1.  
    2. Vector3 axis;
    3. float angle;
    4. QuaternionUtil.GetAngleAxis(rotationDelta, out axis, out angle);
    5. angularVelocity = Quaternion.AxisAngle(axis, angle / Time.deltaTime);
    6.  
    Note though, this can't store a speed greater than 1 full rotation per second, because the quaternion will loop around and store the short angle.
     
    Last edited: Jan 6, 2015
    adamgryu and JoeStrout like this.
  9. Sasmaster

    Sasmaster

    Joined:
    Jan 19, 2010
    Posts:
    44
    Delta time maybe?The values don't looks so well.Something is missing here.
     
  10. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,532
    I'm not sure if I edited before or after you posted that.
     
  11. Sasmaster

    Sasmaster

    Joined:
    Jan 19, 2010
    Posts:
    44
    I am not sure your code is right.The values are too huge and jumpy.You can try it.But thanks for the direction.
     
  12. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,532
    The size of the values are accurate when I test.

    The values don't land on the accurate number though, and instead hover within a short range of it. And I believe this is the result of float error and the fluctuation of framerate.


    This is the script measuring:
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. using com.spacepuppy.Utils;
    6.  
    7. public class QMeasureAngularVelocity : MonoBehaviour {
    8.  
    9.     private Quaternion _lastRot;
    10.     private Quaternion _deltaRot;
    11.     private Vector3 _angularVel;
    12.  
    13.     private void Start()
    14.     {
    15.         _lastRot = this.transform.rotation;
    16.         _deltaRot = Quaternion.identity;
    17.         _angularVel = Vector3.zero;
    18.     }
    19.  
    20.     // Update is called once per frame
    21.     void Update()
    22.     {
    23.         var delta = this.transform.rotation.eulerAngles - _lastRot.eulerAngles;
    24.         var vel = delta / Time.deltaTime;
    25.  
    26.         _deltaRot = QuaternionUtil.FromToRotation(_lastRot, this.transform.rotation);
    27.         _lastRot = this.transform.rotation;
    28.         _angularVel = _deltaRot.eulerAngles / Time.deltaTime;
    29.  
    30.         Debug.Log(vel.ToDetailedString() + " --- " + _angularVel.ToDetailedString());
    31.     }
    32. }
    33.  
    And the script rotating:

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using com.spacepuppy.Utils;
    5.  
    6. public class QRotator : MonoBehaviour {
    7.  
    8.     public float Speed = 100f;
    9.  
    10.     private void Update()
    11.     {
    12.         var v = this.transform.rotation.eulerAngles;
    13.         v.y += this.Speed * Time.deltaTime;
    14.         this.transform.rotation = Quaternion.Euler(v);
    15.     }
    16. }
    17.  
    And the print out:



    I'm not 100% sure why it happens though. And I honestly don't have the time to figure out why.
     
    Last edited: Jan 6, 2015
  13. Polymorphik

    Polymorphik

    Joined:
    Jul 25, 2014
    Posts:
    599
    Hahaha this dude always cracks me up in the forums Doofy is F***ing awesome don't ever change your pic man...anyways lol
     
  14. BenZed

    BenZed

    Joined:
    May 29, 2014
    Posts:
    524
    Haha, thanks. Officer doofy is on the case.

    Well, you shouldnt have to multiply by delta time, because the rotation is already measured in delta time, since youre doing the calculation every update.

    I'm not near my machine right now, i'll take another look at er tomorrow.
     
    Polymorphik likes this.
  15. Sasmaster

    Sasmaster

    Joined:
    Jan 19, 2010
    Posts:
    44
    Well dudes,so far all the proposed code above doesn't seem to be exact simulation of rigidbody's angularVelocity.

    lordofduct's example spits really S***ty numbers,most of which are really huge or even infinity.Also based ,on the math formulas smth is missing here...Anyone has an idea? God damn ,this should be one of the simplest things to calc yet I can't find a single example in the web!
     
  16. A.Killingbeck

    A.Killingbeck

    Joined:
    Feb 21, 2014
    Posts:
    483

    ...what? Delta time is used to make movements framerate independent. Update is called per frame. There could only be 10 Update calls in a second if your game is badly optimized rather than a desired 60.
     
  17. BenZed

    BenZed

    Joined:
    May 29, 2014
    Posts:
    524
    Right, and if your object is spinning one rotation a second, and you're measuring angular velocity between frames, and in your example weve been slowed to 10fps than this solution should evaluate to: 36 degrees spun since last frame.

    Right?

    ...okay, i see what youre saying. Yes, to make it framerate independent, you'd have to multiply by deltatime to get the number of degrees per second.

    Thanks for unconfusing me!
     
  18. BenZed

    BenZed

    Joined:
    May 29, 2014
    Posts:
    524
    Okay, well it's obvious to me now why this code isn't working:

    If the last rotation is the euler equivalent of 359 degrees on any given axis, and the current rotation is the euler equivalent to 1 degrees on the same axis, the magnitude is going to appear to be over 360 degrees since the last frame. So that's not going to work.

    I did some reading, and came up with the following which worked after testing:

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class RotationTracker : MonoBehaviour {
    4.  
    5.     //Holds the previous frames rotation
    6.     Quaternion lastRotation;
    7.  
    8.     //References to the relevent axis angle variables
    9.     float magnitude;
    10.     Vector3 axis;
    11.  
    12.     public Vector3 angularVelocity {
    13.  
    14.         get {
    15.             //DIVDED by Time.deltaTime to give you the degrees of rotation per axis per second
    16.             return (axis * magnitude) / Time.deltaTime;
    17.         }
    18.  
    19.     }
    20.  
    21.     void Start() {
    22.  
    23.         lastRotation = transform.rotation;
    24.  
    25.     }
    26.  
    27.     void Update() {
    28.  
    29.         //The fancy, relevent math
    30.         Quaternion deltaRotation = transform.rotation * Quaternion.Inverse (lastRotation);
    31.         deltaRotation.ToAngleAxis(out magnitude, out axis);
    32.  
    33.         lastRotation = transform.rotation;
    34.  
    35.     }
    36.  
    37.  
    38. }
    39.  
    I placed this code on an object which also had a script that was forcing it to make 1 full rotation per second. This script correctly gave the Vector3 '0,0,360', mind you it does seem to fluctuate somewhat.

    Working scene enclosed:
     

    Attached Files:

  19. BenZed

    BenZed

    Joined:
    May 29, 2014
    Posts:
    524
    I'd also like to add that you'd have to tweak this code to get radians or some other usable value. A vector of 0'0'360 is needlessly large.
     
  20. bobsir

    bobsir

    Joined:
    Dec 13, 2012
    Posts:
    1
    Old thread but im posting for other. I was trying to calculate the angular velocity when I throw something in the HTC vive. I'm using RigidBodies that are kinematic while grabbed, meaning I need to calculate it when released.

    Following is in FixedUpdate:
    Code (CSharp):
    1. var deltaRot = transform.rotation * Quaternion.Inverse( lastRot );
    2. var eulerRot = new Vector3( Mathf.DeltaAngle( 0, deltaRot.eulerAngles.x ), Mathf.DeltaAngle( 0, deltaRot.eulerAngles.y ),Mathf.DeltaAngle( 0, deltaRot.eulerAngles.z ) );
    3.  
    4. angularVelocity = eulerRot / Time.fixedDeltaTime;

    The DeltaAngle is required as it will turn (350, 0,0) into (-10, 0,0), but wont work if you have very high angular velocities.
     
    curbol, ksf000, firest-dead and 4 others like this.
  21. CogumeloSoft

    CogumeloSoft

    Joined:
    Dec 28, 2012
    Posts:
    88
    Bobsir. This just works! Thanks! ;)
     
  22. JoshuaMcKenzie

    JoshuaMcKenzie

    Joined:
    Jun 20, 2015
    Posts:
    916
    I think the mistake here that the fact that most scripts try to derive the angular velocity based on current and last rotation. There's limits to this method for extremely fast rotations. the way I would do it is similar to how Rigidbody would do it.

    have AngularVelocity drive rotations, not use rotations to calculate angular velocity. depending on how accurate you want to apply the physics calculations, to figure out a force applied off-center you'd need to apply a couple steps:

    first off find the Point of Affect (Pa) which will be a position local to the center of mass(cm).
    as far as the rigidbody should be concerned the force at Pa needs to be perpendicular to, or directly along the cm. If the actual force (Fa) was a graze the perpendicular velocity (Vp) needs to be figured out via Vp = Fa * sin(angle of Fa). If Fa was actually perpendicular then the Sin() will equal 1, simplifying the equation.
    Now find the cross product of the T = Vector3.Cross(Pa,Vp) as the Torque (T), representing the euler angle that the rigidbody will rotate around.
    The angular velocity (w) is then found to be w += Tn * (Pa.magnitude * Mp.magnitude).

    Edit
    : whoops this is a typo (Tn * (Pa.magnitude * Vp.magnitude)) is angular momentum (effectively a measurement of leverage)
    Angular velocity w = T / (Pa.magnitude*Pa.magnitude) when Pa is not zero (otherwise w is 0)
    and yes, mass actually doesn't affect angular velocity (hence momentum at point Mp is now perpendicular velocity Vp). Mass is handled in a separate calculation


    Applying force at a point not only affects the spin of the object, but also affects velocity (V) as V += Vp/m. This is where the mass (m) finally comes into play.
    Now in Update (or FixedUpdate, if you intend that this object's transform affects other objects in the scene) you simply apply Velocity and Angular Velocity to your tranform.localPosition and trandform.localEulerAngles respectively

    From there you can add other features like angular dampening... like 0.2f (lose 20% velocity per second) and apply it as
    Code (CSharp):
    1. AngularVelocity = Vector3.MoveTowards(AngularVelocity, Vector3.zero, AngularVelocity * m_angularDampening * m_deltaTime);
    2.  
    3. //where m_deltaTime is  Time.deltaTime in Update
    4. //                  is Time.fixedDeltaTime in FixedUpdate
    yet another feature is velocity Constraints (i.e. RigidBody's freeze position and rotation axis) which can easily be done inside a property setter for velocity and angularvelocity via Vector3.Scale(). Do note that constraining the angles should also affect the Torque result. One cool thing you can do is instead of making it an on/off constraint, you can make it a force multiplier. so positional force multiplier of Vector3 (1,2f.2f,0); would make it take 120% of the x force, 20% of the y force and 0% of the z force
     
    Last edited: Feb 8, 2017
    JBR-games and ggzerosum like this.
  23. BrightBit

    BrightBit

    Joined:
    Jan 22, 2013
    Posts:
    265
    Old thread, I know but I wanted to share my solution. I also needed to calculate the angular velocity for throwing things in VR and I came up with this simple solution in Unity 2018.1.0b13 (should also work in 2017) that should be called every frame:
    Code (CSharp):
    1. Quaternion deltaRotation = itemRotation * Quaternion.Inverse(previousRotation);
    2.  
    3. previousRotation = itemRotation;
    4.  
    5. deltaRotation.ToAngleAxis(out var angle, out var axis);
    6.  
    7. angle *= Mathf.Deg2Rad;
    8.  
    9. angularVelocity = (1.0f / Time.deltaTime) * angle * axis;

    You might need to adjust maxAngularVelocity of the item's Rigidbody since the default value of 7 is quite low.
     
    Last edited: Nov 17, 2019
  24. maya-papaya

    maya-papaya

    Joined:
    Feb 24, 2015
    Posts:
    2


    OMG dude I love you so much (no homo). Once I plugged everything in to my existing code it worked perfectly on the first try, now all I have to do is tweak how strong the rotation is. I've been trying to solve this for days now but I finally found this, youre a lifesaver
     
    FlightOfOne likes this.
  25. FlightOfOne

    FlightOfOne

    Joined:
    Aug 1, 2014
    Posts:
    668

    This almost got me to where I want to go -works great at fast speeds. However, it gives 0 when the speeds are really slow. Any idea how to get this to work at very slow speeds? Thanks!
     
    Last edited: Jun 4, 2018
  26. Leonidas001

    Leonidas001

    Joined:
    Apr 23, 2018
    Posts:
    2
    Here's a small correction to bobsir's very helpful answer to fix some jitters at low velocities.
    Note that I'm using FixedUpdate() to keep track of the position and rotation during the last and forelast FixedUpdate()s in order to alleviate frame rate related issues.

    Code (CSharp):
    1.  
    2.     private void ApplyLastVelocityAndAngularVelocity()
    3.     {
    4.         rigidBody.velocity = (lastFramePosition - foreLastFramePosition) / Time.fixedDeltaTime;
    5.  
    6.         Quaternion deltaRotation = lastFrameRotation * Quaternion.Inverse(foreLastFrameRotation);
    7.         Vector3 eulerRotation = new Vector3(
    8.             Mathf.DeltaAngle(0, Mathf.Round(deltaRotation.eulerAngles.x)),
    9.             Mathf.DeltaAngle(0, Mathf.Round(deltaRotation.eulerAngles.y)),
    10.             Mathf.DeltaAngle(0, Mathf.Round(deltaRotation.eulerAngles.z)));
    11.  
    12.         rigidBody.angularVelocity = eulerRotation / Time.fixedDeltaTime * Mathf.Deg2Rad;
    13.     }
    14.  
     
  27. DavidSWu

    DavidSWu

    Joined:
    Jun 20, 2016
    Posts:
    183
    I am not sure why you are using Euler angles. Angular velocity is not the derivative of the rotation not of the Euler angles.
    The two are dangerously similar in some cases, but this just means that it will work sometimes and fail in situations where say, angles.z != 0

    This should work:

    Code (CSharp):
    1. internal static Vector3 GetAngularVelocity(Quaternion foreLastFrameRotation, Quaternion lastFrameRotation)
    2.         {
    3.             var q = lastFrameRotation * Quaternion.Inverse(foreLastFrameRotation);
    4.           // no rotation?
    5.           // You may want to increase this closer to 1 if you want to handle very small rotations.
    6.          // Beware, if it is too close to one your answer will be Nan
    7.             if(abs(q.w) > 1023.5f / 1024.0f)
    8.                 return new Vector3(0,0,0);
    9.             float gain;
    10.             // handle negatives, we could just flip it but this is faster
    11.             if(q.w < 0.0f)
    12.             {
    13.                 var angle = acos(-q.w);
    14.                 gain = -2.0f * angle / (sin(angle)*Time.deltaTime);
    15.             }
    16.             else
    17.             {
    18.                 var angle = acos(q.w);
    19.                 gain = 2.0f * angle / (sin(angle)*Time.deltaTime);
    20.             }
    21.             return new Vector3(q.x * gain,q.y * gain,q.z * gain);
    22.         }
    Hope that helps!
     
    Lion_C, nxtboyIII, nsalminen and 4 others like this.
  28. GenaSG

    GenaSG

    Joined:
    Apr 19, 2014
    Posts:
    111
    Thank you. Works like a charm.
     
  29. AlexFuture

    AlexFuture

    Joined:
    Mar 24, 2018
    Posts:
    2
    Two years later still helping, been looking for this very thing, Thanks!
     
  30. LordAvarice

    LordAvarice

    Joined:
    Jan 6, 2016
    Posts:
    4
    This did it for my VR game. You just saved me allot of time and headaches, Thank you so much!

    Why use Euler angles? In my case, I lack the knowledge to properly use quaternions.
     
    JBR-games likes this.