Search Unity

Question (Case 1339471) HingeJoint2D overshooting limits

Discussion in 'Physics' started by CDF, May 24, 2021.

  1. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    I can't seem to get a fast moving hing joint (pinball flipper) to not overshoot its limits.
    It also always seems to come to a rest at +/- 2 degrees from the limits:

    here the limits are 0 - 60
    Whenever I change the motor force from -5000, to 5000 it overshoots.
    Then comes to a rest at 2 degrees off the limit.

    Looks really bad in-game, like the flipper has a wobble to it.

    Any ideas how to fix?
    Can't seem to find a physics settings that can fix this issue. "Max Angular Correction" does nothing.

    flipper.gif
     
  2. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    Solved it by clamping the motor speed based on distance remaining from the limits:

    Code (CSharp):
    1. public class Flipper : MonoBehaviour {
    2.  
    3.     public float speed = 190;
    4.     public float easing = 1;
    5.     public HingeJoint2D joint;
    6.    
    7.     private bool pressed;
    8.  
    9.     private void Update() {
    10.  
    11.         pressed = Input.GetMouseButton(0);
    12.     }
    13.  
    14.     private void FixedUpdate() {
    15.  
    16.         float mass = joint.connectedBody.mass;
    17.         float motorSpeed = mass * speed;
    18.  
    19.         if (pressed) {
    20.  
    21.             ClampMotorSpeed(ref motorSpeed, joint.limits.max - joint.jointAngle);
    22.         }
    23.         else {
    24.  
    25.             ClampMotorSpeed(ref motorSpeed, joint.jointAngle - joint.limits.min);
    26.         }
    27.  
    28.         var motor = joint.motor;
    29.         motor.motorSpeed = pressed ? motorSpeed : -motorSpeed;
    30.  
    31.         joint.motor = motor;
    32.     }
    33.  
    34.     private void ClampMotorSpeed(ref float speed, float diff) {
    35.  
    36.         float amount = diff / easing / Time.fixedDeltaTime;
    37.  
    38.         speed = Mathf.Min(speed, amount);
    39.     }
    40. }
     
  3. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,455
    Glad you found a solution.

    It's just Box2D at this point. One thing to note is that a motor speed of 5000 is pretty fast so does reducing this reduce the overshoot at all? What effect, if any, does increasing the solvers limit on Velocity Iterations in the physics settings have?

    I do know that the latest Box2D has changed how limits work for this joint (b2RevoluteJoint) and while the code looks simpler there, I'd be curious to know if it fixes this as it's not entirely clear what it fixes or improves in all cases. Some things are a tad worse. For this one you can no longer read the limit-state to know if you're at the upper or lower limit for instance.

    I'm not sure if you've ever experimented with Box2D but it's super easy and fun to mess with.

    If you want to send me a simple reproduction project I'd be happy to check.
     
  4. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    Yeah 5000 was a bit of an exaugurated case, but the issue does still occur at lower speeds.
    It looks as if the joint is unstable at the limits, creating a wobbly visual feedback.

    Attached a simple project I setup.
    Can also submit a bug report if you like.

    Here's a comparison (speed = 250):

    Gif doesn't really highlight the issue too well.
    Left is unclamped, default limits
    Right is manually clamped.

    flipper_compare.gif
     

    Attached Files:

  5. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,455
    Yeah, looks like overshoot. The source we're using is in this method here. There's a motor which applies impulse to both bodies then the limit constraint does its work.

    Does increasing solver iterations change it in any way?

    I'l take a look at your project.
     
  6. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    It doesn't appear to. I set them both position and velocity iterations to 100.
    Still same issue.

    However, changing the fixed timestep to a low value, like 0.005 seems to work.
    Although I really don't want to do that
     
  7. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,455
    Yes, verified that myself now.

    Yes, that'll affect all forms of overshoot because the maximum impulses for that joint will be lower.

    I'm just doing a debug build now of Unity. In the meantime, the max impulse applied directly relates to the maximum motor torque so lowering that in theory would have the same effect because it does this:
    Code (CSharp):
    1. float32 maxImpulse = data.step.dt * m_maxMotorTorque;
    2. m_motorImpulse = b2Clamp(m_motorImpulse + impulse, -maxImpulse, maxImpulse);
    By changing the FixedUpdate you're changing the simulation delta-time which is the "data.step.dt" part. You can also control the "m_maxMotorTorque" part.
     
  8. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    It's weird, there are some speed numbers where the issue doesn't occur at all.
    If you change speed from 250 to 200. I can't see any overshoot.
     
  9. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,455
    Probably relates to how close it gets to those specific limits. Maybe at Speed=200 with a wider limit it starts doing it again. Perfect storm values. ;)
     
    CDF likes this.
  10. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,455
    So manually swapping in the newer Box2D b2RevoluteJoint seems to solve the issue because it's a lot more aggressive on dealing with limits. Hmm, this might be a problem because the latest Box2D breaks a bunch of things making backwards compatibilty a real pain which is what we discovered when updating it and testing.

    I'll add this to the list of items pushing for the update. I'll add your project too and a link to this thread. Don't see a point in doing a bug report for it to sit there for months. I'll notify you of progress.

    The changes in this joint alone break the ability to read the limit state but we should be able to calculate that dynamically if it's ever called. In theory we could just upgrade this joint which could happen a lot quicker; maybe in the next round of bug fixes and could be backported.
     
    CDF likes this.
  11. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    Well I guess that's good news haha.
    Thanks for checking it out.

    I remember using Box2D back in my Flash days, about 12yrs ago. And from memory, it was not a pleasant experience with all those definitions, syncing the visuals etc.
    So yeah, I can understand how this is not just a simple update ;)
     
  12. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,455
    So a small update: It looks like we can include this as a bug-fix with backport(s) too. If you're still interested in monitoring this issue then would you mind submitting a bug report with your project above attached (or I can attach it)?
     
    CDF likes this.
  13. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    Case Submitted: Case 1339471
     
  14. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,455
    Just an update to say that I've submitted the HingeJoint2D upgrade to 2021.2, 2021.1 and 2020.3. Whether it'll be allowed into 2020.3 isn't my call but it's likely to get in given how isolated the change is.
     
    CDF likes this.