Search Unity

Featherstone's solver for articulations

Discussion in 'Physics Previews' started by yant, Dec 11, 2019.

  1. jselstad

    jselstad

    Joined:
    Apr 12, 2016
    Posts:
    6
    Thank you, kstorey.

    I am using drives on the fingers (yields the best friction); I’m not sure the current Unity API allows for Articulation roots to be parented/constrained to other Rigidbodies or have spatial linear drives... yet... but luckily the Force/Torque solution I’m using works relatively well.

    The new properties in 2020.1a23 should be very useful for suppressing solver instability. The physics timestep is currently running at 90hz, but increasing that may be another path towards improvement (since I’m able to sample interpolated hand tracking frames for each physics timestep as well).

    Is there a plan for implementing “Rigidbody interpolation” for articulations, Yant? (It re-interpolates the motion of the objects from the physics timestep to the render timestep). If not, I’ll need to keep the simulation timestep as a multiple of the framerate to maintain smooth motion...
     
  2. Kobix

    Kobix

    Joined:
    Jan 23, 2014
    Posts:
    82
    How to enable..? Are PhysX Articulations calculated on GPU?
     
  3. yant

    yant

    Unity Technologies

    Joined:
    Jul 24, 2013
    Posts:
    381
    This is not exposed yet in Unity, I'm afraid.
     
  4. yant

    yant

    Unity Technologies

    Joined:
    Jul 24, 2013
    Posts:
    381
    We don't have this ready yet -- as there are some architectural difficulties in how Joints were designed over a decade ago, but this is on the short-list to be addressed.
     
    hippocoder likes this.
  5. yant

    yant

    Unity Technologies

    Joined:
    Jul 24, 2013
    Posts:
    381
    No, we don't have a plan for interpolation yet.

    Just wanted to voice this idea that we have this call Physics.Simulate(), which is useful to run the simulation once. The way you use it is disable the automatic simulation and then write a script that calls into Physics.Simulate() as required. That setup should allow easy integration for custom update schemes, including twice per frame. (as opposed to trying to match the required frequency per update by tweaking the global simulation frequency).

    Hope that helps.
     
    mvaandrager and jselstad like this.
  6. flawless_code

    flawless_code

    Joined:
    Feb 5, 2020
    Posts:
    9
    It's ok, new AMD processors have very high core count :)
     
  7. flawless_code

    flawless_code

    Joined:
    Feb 5, 2020
    Posts:
    9
    Cool! That would make Unity much more universal.

    Upgrading to new alpha version as we speak, might report soon about improvements.
     
    yant likes this.
  8. zalo10

    zalo10

    Joined:
    Dec 5, 2012
    Posts:
    19
    So I made a simple test scene for spherical joints. ArticulationBug.gif

    It looks like there's a bug in the way that the X, Y, and Z drives work together... I suspect there's a wrongly-ordered quaternion multiplication somewhere.

    Here's the code and scene I'm using:
    Code (CSharp):
    1. using UnityEngine;
    2. public class ArticulationTest : MonoBehaviour {
    3.     public ArticulationBody body;
    4.     void FixedUpdate() {
    5.         // Normalize Quaternion to suppress greater than 360 degree rotations
    6.         Quaternion normalizedRotation = Quaternion.Normalize(transform.rotation);
    7.  
    8.         // Calculate drive targets
    9.         Vector3 driveTarget = new Vector3(
    10.           Mathf.DeltaAngle(0, normalizedRotation.eulerAngles.x),
    11.           Mathf.DeltaAngle(0, normalizedRotation.eulerAngles.y),
    12.           Mathf.DeltaAngle(0, normalizedRotation.eulerAngles.z));
    13.  
    14.         // Copy the X, Y, and Z target values into the drives...
    15.         ArticulationDrive xDrive = body.xDrive; xDrive.target = driveTarget.x; body.xDrive = xDrive;
    16.         ArticulationDrive yDrive = body.yDrive; yDrive.target = driveTarget.y; body.yDrive = yDrive;
    17.         ArticulationDrive zDrive = body.zDrive; zDrive.target = driveTarget.z; body.zDrive = zDrive;
    18.     }
    19. }
    20.  
     

    Attached Files:

  9. zalo10

    zalo10

    Joined:
    Dec 5, 2012
    Posts:
    19
    After some trial-and-error, drive code without any singularities looks like this:
    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. public class ArticulationTest : MonoBehaviour {
    4.     public ArticulationBody body;
    5.     public float Strength = 1f;
    6.     void FixedUpdate() {
    7.         // Calculate the delta between the current rotation and the desired rotation
    8.         Quaternion deltaRotation = Quaternion.Normalize(Quaternion.Inverse(body.transform.localRotation) * transform.rotation);
    9.  
    10.         // Calculate drive velocity necessary to undo this delta in one fixed timestep
    11.         ArticulationReducedSpace driveTargetForce = new ArticulationReducedSpace(
    12.           ((Mathf.DeltaAngle(0, deltaRotation.eulerAngles.x) * Mathf.Deg2Rad) / Time.fixedDeltaTime) * Strength,
    13.           ((Mathf.DeltaAngle(0, deltaRotation.eulerAngles.y) * Mathf.Deg2Rad) / Time.fixedDeltaTime) * Strength,
    14.           ((Mathf.DeltaAngle(0, deltaRotation.eulerAngles.z) * Mathf.Deg2Rad) / Time.fixedDeltaTime) * Strength);
    15.  
    16.         // Apply the force in local space (unlike AddTorque which is global space)
    17.         // Ideally we'd use inverse dynamics or jointVelocity, but jointVelocity is bugged in 2020.1a22
    18.         body.jointForce = driveTargetForce;
    19.     }
    20. }
    21.  
    I assume this can be substituted in for the existing xDrive, yDrive, and zDrive code in the engine. The bug probably comes from one of two places...

    1. You can't just reconstruct the body's orientation from the reducedCoordinate joint positions (it's unclear why not, probably a different ordering in which x, y, and z are applied):

    Code (CSharp):
    1.         // This will give you the incorrect orientation
    2.         Quaternion jointRotation = Quaternion.Euler(
    3.             body.jointPosition[0] * Mathf.Rad2Deg,
    4.             body.jointPosition[1] * Mathf.Rad2Deg,
    5.             body.jointPosition[2] * Mathf.Rad2Deg);
    2. It's possible the ordering of the quaternion multiply in line 8 above is reversed; I see similar unstable behavior in my code when I intentionally reverse the order...

    EDIT: It looks like setting the jointVelocity directly is bugged for any of the joints further down the kinematic chain... Looks like another bug...

    EDIT 2: Setting jointForce has a similar effect, but it propagates down the chain properly. Please fix jointVelocity!

    EDIT 3: Colliders that are children of the articulation do not appear to be detected by the articulation's joints... Do they need to be fixed joint articulations?

    EDIT 4: Fixed Joint articulation children do not appear to be incorporated properly either... Looks like I'm not going to be able to weasel out of some deep quaternion management if I want oriented colliders in my articulations...

    EDIT 5: Setting jointForce on a branching articulation (eg a humanoid) hard-crashes the editor :(
     
    Last edited: Feb 9, 2020
    frosted and CodeKiwi like this.
  10. flawless_code

    flawless_code

    Joined:
    Feb 5, 2020
    Posts:
    9
    Hey, try to have at least one collider on every Articulation, with non-zero weight. It can be very small sphere, I think that helped me in the past.
     
    mvaandrager likes this.
  11. zalo10

    zalo10

    Joined:
    Dec 5, 2012
    Posts:
    19
    Thank you, I did try that, but only the rotary joint's small, centered collider affected the inertia of the system appropriately. :(
     
  12. flawless_code

    flawless_code

    Joined:
    Feb 5, 2020
    Posts:
    9
    Btw, I noticed new alpha is out:

     
    yant likes this.
  13. zalo10

    zalo10

    Joined:
    Dec 5, 2012
    Posts:
    19
    Unfortunately I wasn't able to use the maxAngularVelocity or maxDepenetrationVelocities to achieve any additional stability with respect to spherical joints... (and setting jointVelocity still crashes :( ).
     
  14. yant

    yant

    Unity Technologies

    Joined:
    Jul 24, 2013
    Posts:
    381
    The fix for crash when accessing jointPosition/jointVelocity is en route trunk as we speak. I'll post here when I have dates for a 20.1 build. Thanks.
     
    sohojoe and flawless_code like this.
  15. Kobix

    Kobix

    Joined:
    Jan 23, 2014
    Posts:
    82
    Hey yant, how hard it would it be to get other friction types working? It's not urgent right now, but I believe it will come handy with gripper physics.
     
  16. yant

    yant

    Unity Technologies

    Joined:
    Jul 24, 2013
    Posts:
    381
    For those following the latest alphas - the fix has just been merged into 20.2a1. I'm working on back-port as we speak.
     
  17. yant

    yant

    Unity Technologies

    Joined:
    Jul 24, 2013
    Posts:
    381
    Merged into 2020.1b1.
     
    hippocoder likes this.
  18. tri-quad

    tri-quad

    Joined:
    Feb 17, 2020
    Posts:
    1
    Just started messing with Articulations in 2020.1.0a25 today, super fun! I setup a simple object with a few revolute joints and I'm trying to create motor-ish controls for the joints for some RL experiments I want to run -- I've got it working, but some of the behavior isn't quite what I'd expect. Here's my test controller:

    Code (CSharp):
    1. public class RevoluteController : MonoBehaviour {
    2.     public Vector3 torque = Vector3.forward * 5.0f;
    3.     void FixedUpdate() {
    4.         // Set torque to be applied based on state of F and G keys
    5.         if (!Input.GetKey(KeyCode.F) && !Input.GetKey(KeyCode.G)) { return; }
    6.         Vector3 tt = torque * Time.deltaTime * 100.0f;
    7.         if (Input.GetKey(KeyCode.G)) { tt *= -1.0f; }
    8.  
    9.         // Apply torque to children
    10.         Vector3 antiTorque = Vector3.zero;
    11.         foreach (Transform child in transform) {
    12.             child.gameObject.GetComponent<ArticulationBody>().AddRelativeTorque(tt);
    13.             antiTorque -= child.localToWorldMatrix.MultiplyVector(tt);
    14.         }
    15.  
    16.         // Apply inverse torque to this object
    17.         gameObject.GetComponent<ArticulationBody>().AddTorque(antiTorque);
    18.     }
    19. }
    Without applying the inverse torque to the parent, pressing F/G keys will spin the whole object even when the children have reached limits; with the inverse torque applied, angular moment seems to be conserved. The issue I'm running into is that non-angular momentum is *not* conserved -- the object can jump into the air (expected) and then start flying around in different directions as I toggle the F and G keys (not expected!).

    I spent a little bit of time trying to figure out how to use jointForce for the antiTorque instead of naively using the applied torque as it will cause weird behavior if the applied torque isn't perfectly aligned with the unconstrained axis, but couldn't figure out how to convert jointForce to a local or world vector.

    I then tried to switch to a drive-based approach, but couldn't figure out how to get my controller to set the xDrive target. This...

    Code (CSharp):
    1.         foreach (Transform child in transform) {
    2.             ArticulationBody ab = child.gameObject.GetComponent<ArticulationBody>();
    3.             ArticulationDrive ad = ab.xDrive;
    4.             ad.target = targetRotation;
    5.         }
    ...does not seem to be updating the drive target. Meanwhile, this...

    Code (CSharp):
    1.         foreach (Transform child in transform) {
    2.             ArticulationBody ab = child.gameObject.GetComponent<ArticulationBody>();
    3.             ab.xDrive.target = targetRotation;
    4.         }
    ...gives a compile error: "error CS1612: Cannot modify the return value of 'ArticulationBody.xDrive' because it is not a variable"

    So a few questions:
    1. How can I simulate a motor and apply torque in a way that does not affect the momentum of the Articulation object?
    2. How can I convert jointForce to a vector in local or world space?
    3. What's the best way to update an ArticulationBody drive target from a script?
    4. Am I going about this all wrong?
    Thanks!
     
  19. CodeKiwi

    CodeKiwi

    Joined:
    Oct 27, 2016
    Posts:
    71
    ArticulationDrive is a struct, try:
    Code (CSharp):
    1. foreach (Transform child in transform) {
    2.     ArticulationBody ab = child.gameObject.GetComponent<ArticulationBody>();
    3.     ArticulationDrive ad = ab.xDrive;
    4.     ad.target = targetRotation;
    5.     ab.xDrive = ad;
    6. }
     
    tri-quad and Kobix like this.
  20. flawless_code

    flawless_code

    Joined:
    Feb 5, 2020
    Posts:
    9
    @tri-quad
    Yep, what CodeKiwi said.
    Structs are copied every time, classes are not. Modifying struct in one place does not reflect changes in other places. :)
     
  21. tavovallejo

    tavovallejo

    Joined:
    Nov 22, 2014
    Posts:
    24
    hello, is there a way to lock the tip of an articulation tree (position and rotation). For example the tip of a rope that's gets stuck in a branch; Or a robotic arm trying to lift something that is fixed in world space, so it gets stuck.
     
  22. Kobix

    Kobix

    Joined:
    Jan 23, 2014
    Posts:
    82
    Anyone has problems with Articulated bodies drifting over time?

    No, not yet. Supposedly Articulations with Joints will be coming soon.
     
    tavovallejo likes this.
  23. Kobix

    Kobix

    Joined:
    Jan 23, 2014
    Posts:
    82
    Ok, I have my robot on wheels; and I found out if the root articulation has bigger mass, the more it drifts. Otherwise,if I set bigger mass onto my wheels, the drifting stops.
     
    Last edited: Mar 17, 2020
  24. Edy

    Edy

    Joined:
    Jun 3, 2010
    Posts:
    1,688
    How have you put the articulation on wheels?
     
unityunity