Search Unity

Setting the center of mass of FixedJoints

Discussion in 'Physics' started by Iron-Warrior, Dec 1, 2019.

  1. Iron-Warrior

    Iron-Warrior

    Joined:
    Nov 3, 2009
    Posts:
    688
    Hello, I am currently working on a VR project where the player's hands are rigidbodies that exist in the world. Each physics tick, these rigidbodies have their velocity/angular velocity set to be a value that interpolates towards the position/orientation of the VR controllers. This trailer we released can give you an idea of the motivation behind this.

    To allow the VR player to grab objects in the world, I used a FixedJoint attached to the grabbing hand with the grabbed object set as the connected body. This overall works pretty well, but issues arise when the grabbed object is large/far away from the hand's center of mass. Below I have a diagram showing my issue:



    The blue object represents the hand and has a constant angular force applied on the X axis. The center of mass is shown with the green dot. It rotates around its center of mass, as expected. We can see what happens when a FixedJoint is added below.



    The new center of mass is calculated to be between the two objects, which is correct, and for most uses would make sense. However, since the hand rigidbodies have their angular velocity set assuming their center of mass is at their pivot, it results in really wonky behaviour.

    So my question is: is it possible to manually set the center of mass between an object and a connected joint (or several connected joints, as the VR player can grab several objects at once). My end goal is to ensure that I have full control over the pivot that my hand object is rotating around with angular velocity. I am aware this is not physically accurate, so I am wondering if there is some way to make this happen.

    Thanks for any help,
    Erik
     
  2. Olmi

    Olmi

    Joined:
    Nov 29, 2012
    Posts:
    589
    I'm not sure I understood you correctly, but you can move the center of mass by just setting RigidBody.CenterOfMass manually? Calculate a position between the object(s), then move your objects RigidBody's CoM to that position? When custom CoM is set, you have to manually update the value to be correct, it won't automatically react after that change.
     
  3. tjmaul

    tjmaul

    Joined:
    Aug 29, 2018
    Posts:
    50
    I guess it should work if you set the CoM of all your connected objects to the CoM of your hand and recalculate the CoM when you break the joint to let go of the object.

    Another possibility is to remove the rigidbody component from the object you grabbed, parent it to the hand object and set the center of mass of the new compound object to the center of the hand. When letting go you need to re-add the rigidbody component to the thrown objects and set its velocity to the velocity at the position of the object (see rb.GetPointVelocity) and copy the angular velocity of the hand.
     
  4. Iron-Warrior

    Iron-Warrior

    Joined:
    Nov 3, 2009
    Posts:
    688
    Although you can move the CoM, when you are connected via a FixedJoint the center of mass of the connected objects seems to be calculated automatically.

    Setting the CoM of all the connected objects does correctly set the pivot, but seems to change the axis about which it rotates. Below shows two objects connected by a FixedJoint, with both objects having their CoM set to the red object's pivot (can verify that this is correct with Rigidbody.worldCenterOfMass).



    The angular velocity is set to be (1, 0, 0), so it should be rotating about the X axis, rather that where it currently is. I'm guessing some other value needs to be set to ensure the rotation axis doesn't get modified by the FixedJoint.

    I've thought about using compound objects, and while it could be viable with some fiddling, this seems to be a lot cleaner if I could get it to work.

    Thanks both for the help so far.
     
  5. tjmaul

    tjmaul

    Joined:
    Aug 29, 2018
    Posts:
    50
    I’m not 100% sure without further researching your setup but this looks like a problem with the moment of inertia.

    a bit of physical background here:
    One of the describing properties of a rigidbody in 3D space is its inertia tensor which is accessible in PhysX but usually computed automatically using all the child colliders.

    In a simple case, eg a box of matches, this tensor is a 3x3 matrix where only the diagonal values are non-zero and correspond with “how hard it is to accelerate rotation around a given axis”, meaning one of the main axes (right, up, forward). It turns out, that bodies only “want” to rotate around their main axes and even worse, only the two axes with the highest and lowest moment of inertia are stable. If you spin it around the axis with the “intermediate” moment of inertia, it’ll become unstable after the slightest disturbance and transfer rotational energy to the other axis and do weird stuff. There are nice videos about this on YouTube.


    long story short:
    With the colliders you set up in your video, you have created a complicated situation with a moment of inertia tensor that also has non-zeros outside the diagonal.
    Even worse, since you can grab other objects in an arbitrary pose, you’d have to transform the inertia matrices into different spaces and set them manually so they match up when connected by the joint.
    Do yourself a favor and remove the second rigidBody component upon grabbing and parent the object to the hand.
    Set the inertia tensor to something like Matrix3x3.identity and change the CoM to the center of your hand.

    when releasing, copy the angular velocity of the hand and get the velocity of the grabbed body with hand.rigidbody.GetPointVelocity(object.tf.position) and set that to the now unparented object with the newly created rigidbody component.

    let me know if it worked!
     
    Olmi and Iron-Warrior like this.
  6. tjmaul

    tjmaul

    Joined:
    Aug 29, 2018
    Posts:
    50
  7. Iron-Warrior

    Iron-Warrior

    Joined:
    Nov 3, 2009
    Posts:
    688
    Thanks for the responses @tjmaul. Did a lot of fiddling around, and ended up with a fairly simple solution that seems (?) to work.

    Code (csharp):
    1. joint.connectedBody.centerOfMass = LocalCoM(joint.connectedBody, rb.position);
    2. joint.connectedBody.inertiaTensor = Vector3.one;
    Where LocalCoM is:

    Code (csharp):
    1. private Vector3 LocalCoM(Rigidbody rb, Vector3 worldCoM)
    2. {
    3.     return rb.transform.InverseTransformDirection(worldCoM - rb.position);
    4. }
    (I promise I normally use proper function names). Since the intertia tensor on rigidbodies is defined as being "...relative to the center of mass.", I'm assuming setting it (its diagonal, anyways) to (1, 1, 1) performs your suggestion above, without needing to do parenting (this works with a FixedJoint).

    Interestingly, other kinds of joints (hinges and character joints are what I tested) do not modify the center of mass, while makes sense intuitively, so it's nice to see it reflected in PhysX.

    https://imgur.com/DBDjkW4

    The forums seem to be having an issue with Gyfcat, so put this one on imgur.
     
  8. tjmaul

    tjmaul

    Joined:
    Aug 29, 2018
    Posts:
    50
    Im happy I could help :)