Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice
  3. Dismiss Notice

Question Maintaining rigidbody balance aka anti-overturning

Discussion in 'Physics' started by Qriva, Mar 2, 2024.

  1. Qriva

    Qriva

    Joined:
    Jun 30, 2019
    Posts:
    1,352
    I need to create helper script that keeps the rigidbody balance in given threshold.
    Ultimately I just want to prevent overturn. However I need to note that I don't want the rigidbody to face upward all the time - I want to apply force (or do something similar) after certain angle, for instance 70 deg on X or Z axis.
    upload_2024-3-2_21-24-24.png
    I tried to test PID controller, but I am unable to make it work. So far I managed to run it properly on Y axis, but I don't need this axis, so I moved to next stage. I added PID for X and Z, but it's unstable, it goes crazy after rotating it a bit.
    Code (CSharp):
    1. public class FlipBalancerTest : MonoBehaviour
    2.     {
    3.         public Rigidbody body;
    4.         [Range(0f, 1f)]
    5.         public float force = 1;
    6.         //public PID pid;
    7.         public PID pidX;
    8.         public PID pidZ;
    9.  
    10.         [Range(-180, 180)]
    11.         public float targetAngle = 0;
    12.  
    13.         private void FixedUpdate()
    14.         {
    15.             float angleX = AngleDifference(body.rotation.eulerAngles.x, targetAngle);
    16.             float angleZ = AngleDifference(body.rotation.eulerAngles.z, targetAngle);
    17.             float x = pidX.Update(0, angleX, Time.fixedDeltaTime);
    18.             float z = pidZ.Update(0, angleZ, Time.fixedDeltaTime);
    19.             body.AddTorque(x * force, 0, z * force);
    20.         }
    21.  
    22.         private float AngleDifference(float a, float b)
    23.         {
    24.             return (a - b + 540) % 360 - 180;
    25.         }
    26.  
    27.         [System.Serializable]
    28.         public class PID
    29.         {
    30.             public float pFactor, iFactor, dFactor;
    31.  
    32.             float integral;
    33.             float lastError;
    34.  
    35.  
    36.             public PID(float pFactor, float iFactor, float dFactor)
    37.             {
    38.                 this.pFactor = pFactor;
    39.                 this.iFactor = iFactor;
    40.                 this.dFactor = dFactor;
    41.             }
    42.  
    43.  
    44.             public float Update(float setpoint, float actual, float timeFrame)
    45.             {
    46.                 float present = setpoint - actual;
    47.                 integral += present * timeFrame;
    48.                 float deriv = (present - lastError) / timeFrame;
    49.                 lastError = present;
    50.                 return present * pFactor + integral * iFactor + deriv * dFactor;
    51.             }
    52.         }
    53.  
    54.     }

    The problem is that
    AddTorque
    behaves in completely unexpected way, for example if I create simple cube without gravity, rotate it towards down (90,0,0), and apply torque every frame on Z axis like this:
    Code (CSharp):
    1. private void FixedUpdate()
    2. {
    3.     body.AddTorque(0, 0, 1f);
    4. }
    It does not spin at all, then after moment i starts to spin like crazy in random way.

    Any help appreciated.
     
  2. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    1,085
    Code (CSharp):
    1. rb.AddForceAtPosition((Vector3.up-transform.up)*10f,transform.position+transform.up);
     
  3. Qriva

    Qriva

    Joined:
    Jun 30, 2019
    Posts:
    1,352
    Sorry, but I am not sure how this can help.

    Also putting center of mass on the bottom does not mean it cannot be upside down, plus it affects everything else related to that object.
     
  4. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    1,085
    Here's another option:
    Code (CSharp):
    1.     public float strength=30;
    2.     public float damper=0.1f;
    3.  
    4.     void FixedUpdate()
    5.     {
    6.         Quaternion rot = Quaternion.FromToRotation(transform.up, Vector3.up);
    7.         rb.AddTorque((new Vector3(rot.x, 0, rot.z)-rb.angularVelocity*damper)*strength);
    8.     }
     
  5. ippdev

    ippdev

    Joined:
    Feb 7, 2010
    Posts:
    3,853
    Turn angularDrag to 2-3 and use a counterforce of -velocity per axis that gets stronger the closer you get to maxAngle to push it back upright.
     
  6. Qriva

    Qriva

    Joined:
    Jun 30, 2019
    Posts:
    1,352
    I am not sure I get what you mean by that part.

    Applying negative rotation as it is not gonna work as it will probably cause oscilation or even overturning to other side in the worst case, this is why I tried to go towards PID direction. However right now I am facing other problem, the one I described in the last part of first post - I am unable to understand and properly apply torque to rigidbody.
     
  7. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    1,085
    If you're just wanting to clamp the rigidbody using AddTorque then try this:
    Code (CSharp):
    1.     public float strength=2;
    2.     public float damper=1;
    3.     public float maxAngle=70;
    4.  
    5.     void FixedUpdate()
    6.     {
    7.         float angle=Vector3.Angle(transform.up,Vector3.up);
    8.         if (angle>maxAngle)
    9.         {
    10.             Quaternion rot = Quaternion.FromToRotation(transform.up, Vector3.up);
    11.             rb.AddTorque((new Vector3(rot.x, 0, rot.z)-rb.angularVelocity*damper)*(angle-maxAngle)*strength);
    12.         }
    13.     }


    Or you can do a hard clamp on the transform like this:
    Code (CSharp):
    1.     transform.localEulerAngles=new Vector3(Mathf.Clamp(-Mathf.DeltaAngle(transform.localEulerAngles.x,0),-70,70),transform.localEulerAngles.y,transform.localEulerAngles.z); // clamp x axis
    2.     transform.localEulerAngles=new Vector3(transform.localEulerAngles.x,transform.localEulerAngles.y,Mathf.Clamp(-Mathf.DeltaAngle(transform.localEulerAngles.z,0),-70,70)); // clamp z axis
    3.  
     
  8. Qriva

    Qriva

    Joined:
    Jun 30, 2019
    Posts:
    1,352
    Are you really just copy-pasting what chatGPT told you?
     
  9. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    1,085
    I've never tried ChatGPT. Although I'd like to try it, if it was free!. :)
     
  10. Qriva

    Qriva

    Joined:
    Jun 30, 2019
    Posts:
    1,352
    Sorry, your answers sounded like generated, however it is free below version 4 I belive.
     
  11. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    1,085
    No worries. I can see why you'd think that. But I generally just post code so then there's less confusion. I find if I try to describe a solution to a problem then it just results in more questions and I eventually end up still having to write the code anyway.

    BTW - when ippdev suggested you turn angular drag to 2-3 he means set the rigidbody's angular drag setting in the inspector. Doing this can help to dampen any forces that you add and prevent oversteer. Although this won't be necessary with my solution as it does its own damping.
     
  12. Qriva

    Qriva

    Joined:
    Jun 30, 2019
    Posts:
    1,352
    Well, it depends, usually short explanation is good, because even if your code works I want to be able to understand how it works, in my case I am able to do that, but someone new to coding or without knowledge in topic might have problem with that.
    Plus I am still unable to understand why addTorque behaves in such a weird way.

    Ah, I dunno why I couldn't get that, I think I misread angularDrag as angularVelocity. Anyway I am not sure it would work when colliding with other bodies, but still the second problem is it will probably affect how the object behaves (there are other scripts changing things) and I probably don't want that.
     
  13. Qriva

    Qriva

    Joined:
    Jun 30, 2019
    Posts:
    1,352
    I really don't know what I is wrong. Am I using PID controller in wrong way or torque should be used differently?
    I tried to add additional damping to torque what made whole thing more stable, but it simply reduces how crazy the rotation becomes.
     
  14. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    1,085
    I had look at your script to see if it would help me to understand what you was trying to do. It seems you really are just trying to keep a rigidbody balanced upright.

    Your script has an issue in that it's only controlling the X and Z axis. Because of this it's possible that in certain orientations the rigidbody can start rapidly rotating on the Y axis causing the object to lose control.

    You don't need to set any angular drag as the PID controller does the damping with the D factor parameter.

    So add a Y axis and use the inspector to set the P factor and D factor on all axis to 0.1

    The whole PID controller is unnecessarily complicated really. If you just want to gently reorientate a rigidbody so that it's always upright then you can do something like this:
    Code (CSharp):
    1.         Quaternion rot = Quaternion.FromToRotation(transform.up, Vector3.up);
    2.         body.AddTorque((new Vector3(rot.x, 0 , rot.z)-body.angularVelocity*0.3f)*10); //0.3 damper and 10 strength
    Or if you need it to reorientate to Quaternion.identity then do this:
    Code (CSharp):
    1.         Quaternion rot = Quaternion.Inverse(transform.rotation);
    2.         body.AddTorque((new Vector3(rot.x, rot.y , rot.z)-body.angularVelocity*0.3f)*10); //0.3 damper and 10 strength
     
  15. Qriva

    Qriva

    Joined:
    Jun 30, 2019
    Posts:
    1,352
    You are probably right, I noticed that if you don't control Y rotation, the other two can create weird angles causing uncontrolable torque, something like spaceship with broken thrusters on one axis.
    However I don't want to change Y, and in this approach I would like to add torque only above certain angle.
    BTW I try different ways to solve overturn problem, so don't take all things from my other threads as one.
     
  16. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    1,085
    Well if you still want to use the PID controller and don't want to add Y then you may be able to switch to using AddRelativeTorque instead of AddTorque. It all depends on what your final goal is.