Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

How to calculate a rigidbody's mass-normalized energy for sleepThreshold?

Discussion in 'Physics' started by LazloBonin, Mar 18, 2015.

  1. LazloBonin

    LazloBonin

    Joined:
    Mar 6, 2015
    Posts:
    809
    I'm using different gravities per area for my game and therefore I need to apply a gravitational force at each fixedUpdate. However, this won't let the rigidbodies sleep. I could manually make them sleep when they are below the sleep threshold (Physics.sleepThreshold), but I don't know how to calculate it for each object.

    The documentation vaguely says "The mass-normalized energy threshold, below which objects start going to sleep." How is the mass-normalized energy calculated for a given rigidbody?
     
  2. Partel-Lang

    Partel-Lang

    Joined:
    Jan 2, 2013
    Posts:
    2,548
    Kinetic energy is calculated by
    Code (CSharp):
    1. float E = rigidbody.mass * Mathf.Pow(rigidbody.velocity.magnitude, 2) * 0.5f;
    mass-normalized probably means mass is just ignored, so that leaves us with:

    Code (CSharp):
    1. float E = Mathf.Pow(rigidbody.velocity.magnitude, 2) * 0.5f;
    You should probably add to the result the rotational kinetic energy, which can be calculated the same way:

    Code (CSharp):
    1. E += Mathf.Pow(rigidbody.angularVelocity.magnitude, 2) * 0.5f;
     
  3. LazloBonin

    LazloBonin

    Joined:
    Mar 6, 2015
    Posts:
    809
    I used these calculations, then compared it to the treshold (E < Physics.SleepTreshhold), but it seems that this triggers much more easily than Unity's default sleep. I also tried checking if that condition was maintained over 2 frames, and it's still triggering more easily. Any idea why?
     
  4. Razieln64

    Razieln64

    Joined:
    May 3, 2008
    Posts:
    129
    Thanks for the info.

    To speed up computations don't compute the power of 2.

    You can write it as :

    Code (csharp):
    1. float E = rigidbody.velocity.sqrMagnitude * 0.5f
    2. E += rigidbody.angularVelocity.sqrMagnitude * 0.5f;
    Same thing with the angular velocity.
     
    FlightOfOne and SparrowGS like this.
  5. LazloBonin

    LazloBonin

    Joined:
    Mar 6, 2015
    Posts:
    809
    Any update on this? Maybe from someone in the Unity physics team?
     
  6. Abhinav91

    Abhinav91

    Joined:
    Oct 21, 2013
    Posts:
    67
    Bumping this thread. I'm trying to understand this as well.
    The Physics Manager documentation says "The mass-normalized kinetic energy threshold below which a non-kinematic Rigidbody may go to sleep. Rigidbodies whose kinetic energy divided by their mass is below this threshold will be candidates for sleeping."

    I tried searching 'Mass-normalized Kinetic Energy' on Google because I don't know what 'Mass-normalized' means. Couldn't find anything. Then, according to the second sentence, I calculated the Kinetic Energy and divided it by the mass of the Rigidbody, and then compared that with the sleepThreshold, but the Rigidbody didn't go to sleep when the result went below the sleepThreshold value.
     
    Last edited: Jan 24, 2016
  7. LazloBonin

    LazloBonin

    Joined:
    Mar 6, 2015
    Posts:
    809
    You do have to call Sleep() in that if condition.
     
  8. Abhinav91

    Abhinav91

    Joined:
    Oct 21, 2013
    Posts:
    67
    I'm not setting the Rigidbody to sleep manually based on an IF condition. I'm letting the Physics engine handle that.
    All I'm doing is calculating the Kinetic Energy and the (Kinetic Energy / Rigidbody Mass), and then seeing if either of these values match the sleepThreshold value when the Rigidbody is set to sleep.
    I'm trying to understand exactly how the sleepThreshold is calculated.
     
    LazloBonin likes this.
  9. Abhinav91

    Abhinav91

    Joined:
    Oct 21, 2013
    Posts:
    67
  10. IronLionZion

    IronLionZion

    Joined:
    Dec 15, 2015
    Posts:
    78
    I also would like to know the answer to this.
     
  11. Abhinav91

    Abhinav91

    Joined:
    Oct 21, 2013
    Posts:
    67
    Haha wow man! This thread is more than 1 year old. Has no one been able to find an answer to this yet? On my side, no luck so far.
    Can any of the Unity Devs help us with this?
     
  12. Partel-Lang

    Partel-Lang

    Joined:
    Jan 2, 2013
    Posts:
    2,548
    OK, decided to give it another try..

    So according to the docs, sleep threshold is "The mass-normalized kinetic energy threshold below which a non-kinematic Rigidbody may go to sleep. Rigidbodies whose kinetic energy divided by their mass is below this threshold will be candidates for sleeping."

    I found this paper for calculating the kinetic energy of a 3D rigid-body and arrived to this solution:

    Code (CSharp):
    1. /// <summary>
    2.     /// Gets the mass normalized kinetic energy of a Rigidbody.
    3.     /// </summary>
    4.     public static float GetMassNormalizedKineticEnergy(Rigidbody r) {
    5.         // Linear energy
    6.         float E = 0.5f * r.mass * Mathf.Pow(r.velocity.magnitude, 2f);
    7.  
    8.         // Angular energy
    9.         E += 0.5f * r.inertiaTensor.x * Mathf.Pow(r.angularVelocity.x, 2f);
    10.         E += 0.5f * r.inertiaTensor.y * Mathf.Pow(r.angularVelocity.y, 2f);
    11.         E += 0.5f * r.inertiaTensor.z * Mathf.Pow(r.angularVelocity.z, 2f);
    12.  
    13.         // Mass-normalized
    14.         return E /= r.mass;
    15.     }
    Running it in a test got me thinking that the kinetic energy is not the only thing used for determining whether to sleep or not. Also the sleep threshold description seems to hint that ("may go to sleep"). So I figured there might be a timer involved so objects wouldn't go to sleep immediately after settling down or before being accelerated by gravitation. There are probably also collision events used to wake up the Rigidbody.
    Anyway, I arrived to the script below. That is not a perfect match, but its usable at least I guess. Cant really debug it further without looking at the engine.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class KineticEnergyTest : MonoBehaviour {
    5.  
    6.     private Rigidbody r;
    7.     private float sleepTimer;
    8.  
    9.     void Start() {
    10.         r = GetComponent<Rigidbody>();
    11.     }
    12.  
    13.     void FixedUpdate() {
    14.         // Calculate the mass-normalized kinetic energy
    15.         float E = GetMassNormalizedKineticEnergy(r);
    16.  
    17.         // Should the Rigidbody sleep according to our calculations?
    18.         bool isSleeping = E < Physics.sleepThreshold;
    19.  
    20.         // ...if so, run a timer...
    21.         if (isSleeping) sleepTimer += Time.deltaTime;
    22.         else sleepTimer = 0f;
    23.  
    24.         // If timer has expired, consider it sleeping
    25.         bool isSleepingFinally = sleepTimer >= 0.25f;
    26.  
    27.         // Debugging
    28.         bool isTrue = r.IsSleeping() == isSleepingFinally;
    29.         if (!isTrue) Debug.Log("ERROR! Calculated sleeping " + isSleepingFinally + "; Rigidbody.IsSleeping(): " + r.IsSleeping() + "; Timer: " + sleepTimer);
    30.     }
    31.  
    32.     /// <summary>
    33.     /// Gets the mass normalized kinetic energy of a Rigidbody.
    34.     /// </summary>
    35.     public static float GetMassNormalizedKineticEnergy(Rigidbody r) {
    36.         // Linear energy
    37.         float E = 0.5f * r.mass * Mathf.Pow(r.velocity.magnitude, 2f);
    38.  
    39.         // Angular energy
    40.         E += 0.5f * r.inertiaTensor.x * Mathf.Pow(r.angularVelocity.x, 2f);
    41.         E += 0.5f * r.inertiaTensor.y * Mathf.Pow(r.angularVelocity.y, 2f);
    42.         E += 0.5f * r.inertiaTensor.z * Mathf.Pow(r.angularVelocity.z, 2f);
    43.  
    44.         // Mass-normalized
    45.         return E /= r.mass;
    46.     }
    47.  
    48.     // Wake up if something collided with us.
    49.     void OnCollisionEnter() {
    50.         sleepTimer = 0f;
    51.     }
    52.  
    53.     // Wake up for example when ground has been pulled away from beneath us.
    54.     void OnCollisionExit() {
    55.         sleepTimer = 0f;
    56.     }
    57. }
    Just make a scene with a Rigidbody and something for it to fall on and add the script to the Rigidbody.
    The "ERROR" messages you'll get are the frames in which my calculations don't match with Rigidbody.IsSleeping();

    Cheers,
    Pärtel
     
    shough and ilmario like this.
  13. shough

    shough

    Joined:
    Sep 13, 2017
    Posts:
    5
    @Partel-Lang Wow that is fantastic! Especially the GetMassNormalizedKineticEnergy. I think there is a scale of 1000x on the sleep threshold, i.e. a PhysX sleep threshold of 3 will stop when your kinetic energy value is 0.003.
     
  14. shough

    shough

    Joined:
    Sep 13, 2017
    Posts:
    5
    Here's a script that seems to work well for rolling ball sleep thresholding, it has a couple of modifications: 1) Taking the 1000x scale thing into account. 2) Checking if E is non-zero, in which case it won't force sleeping. This is needed because otherwise you can't start a sleeping object with ApplyForce.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class PlayerMotorST : MonoBehaviour {
    6.  
    7.     Rigidbody rb;
    8.  
    9.     const float CUSTOM_SLEEP_THRESHOLD = 5f / 1000f;
    10.     float sleepTimer = 0f;
    11.  
    12.     void Start () {
    13.         rb = GetComponent<Rigidbody> ();
    14.     }
    15.  
    16.     void FixedUpdate () {
    17.         // Calculate the mass-normalized kinetic energy
    18.         float E = GetMassNormalizedKineticEnergy(rb);
    19.  
    20.         // Should the Rigidbody sleep according to our calculations?
    21.         bool shouldSleep = E < CUSTOM_SLEEP_THRESHOLD;
    22.  
    23.         // Check if it is zeroed out
    24.         bool isSleeping = shouldSleep && (E > 0.00001f);
    25.  
    26.         Debug.Log ("E"+E);
    27.         Debug.Log ("Should Sleep?" + shouldSleep);
    28.         Debug.Log ("Is sleeping?" + isSleeping);
    29.  
    30.         //Debug.Log ("Adjusted threshold"+CUSTOM_SLEEP_THRESHOLD);
    31.         // ...if so, run a timer...
    32.         if (isSleeping) sleepTimer += Time.deltaTime;
    33.         else sleepTimer = 0f;
    34.  
    35.         // If timer has expired, consider it sleeping
    36.         bool isSleepingFinally = sleepTimer >= 0.25f;
    37.  
    38.         //Execute
    39.         if (isSleepingFinally)
    40.             rb.Sleep ();
    41.     }
    42.  
    43.     float GetMassNormalizedKineticEnergy(Rigidbody r) {
    44.         // Linear energy
    45.         float E = 0.5f * r.mass * Mathf.Pow(r.velocity.magnitude, 2f);
    46.  
    47.         // Angular energy
    48.         E += 0.5f * r.inertiaTensor.x * Mathf.Pow(r.angularVelocity.x, 2f);
    49.         E += 0.5f * r.inertiaTensor.y * Mathf.Pow(r.angularVelocity.y, 2f);
    50.         E += 0.5f * r.inertiaTensor.z * Mathf.Pow(r.angularVelocity.z, 2f);
    51.  
    52.         // Mass-normalized
    53.         return E /= r.mass;
    54.     }
    55.  
    56.     // Wake up if something collided with us.
    57.     void OnCollisionEnter() {
    58.         sleepTimer = 0f;
    59.     }
    60.  
    61.     // Wake up for example when ground has been pulled away from beneath us.
    62.     void OnCollisionExit() {
    63.         sleepTimer = 0f;
    64.     }
    65.  
    66.  
    67. }
    68.  
    Edit: Btw, the reason I looked into this is a problem where rolling balls freeze on inclines with the default Unity sleep threshold. They freeze at the peak of their movement when gravity is still acting. But the timer is a very nice fix for this, you could also use some kind of incline sensing if you really need to fine tune it.

    Edit 2: Revisiting this post realizing I'm a moron. You can simply send a WakeUp call.
     
    Last edited: Jan 1, 2019