1
\$\begingroup\$

How to calculate the torque required to rotate rigid body about constant axis and rotating axis?

Convention:

Identity Rotation Matrix:

100 = Row1 = Right (X positive right)  
010 = Row2 = Up (Y positive up)  
001 = Row3 = Forward (Z positive into the screen)  

I have a rigid body sitting with its global transform equal to the identity and I want to calculate the torque required to yaw/rotate it a variable number of degrees about the global Y axis to end up facing in some direction flat along the XZ plane with its up axis aligned with +Y. While its doing so I also want the rigid body to roll about its forward axis proportional to the yaw distance. There should be no change in pitch.

I originally began by implementing the solution by ja72 here: https://physics.stackexchange.com/a/379854/239673

With this solution, I calculate R, extract the axis/angle from R, and then use it to determine the distance I need to yaw (yaw_distance = axis.y * angle). With this yaw distance and the current global angular velocity about Y I can work out how much torque to apply (torque += y_acceleration * vec3(0,1,0)). This correctly rotates the rigid body to the desired yaw target without any change in pitch or roll.

The problem occurs when I attempt to roll during the yaw. To add roll I begin by working out the distance to my target roll (with max roll decreasing as yaw gets closer to its target). I then convert the global angular velocity to local space to get the forward/roll angular speed (z component) and use both the roll distance and roll speed to work about the required torque about the global forward axis (torque += roll_acceleration * get_forward(global_transform)).

While this approach does eventually arrive at the correct orientation, the roll introduces a variety of pitch changes that make the overall motion ugly (for lack of a better word). My expectation/goal is to have it roll while yawing without any pitch changes.

I've verified that the yaw code works correctly on its own and the roll code works correctly on its own. Combined is a fail.

How can I calculate the torque required to yaw about a constant axis and roll about a rotating axis such that there is no change in pitch?

Thanks :)

Edit 1: I moved this over from physics as recommended.

Edit 2: After searching in game development instead of physics I discovered this question/answer (Is there any way to keep the applied torques in the old planes, even if the ship's local plane has rotated?). I believe this would also explain my situation - I'm seeing the effects of precession and while accurate, it isn't want I want. I need to manipulate the roll outside of the physics (e.g. direct manipulation of the transform's rotation). Is this correct?

Update 1:

In case this is of any use to anyone else, here is what I've done so far:

I've changed the rigid body to operate on 3 new axis in a new pseudo-space (don't know what to call it):

  1. Yaw: uses the global Y axis. Completely independent of the rigid_body's up axis.
  2. Pitch: uses the rigid_body's right axis projected onto the XZ plane.
  3. Roll: uses the rigid_body's forward axis.
class rigidy_body
{
    float3 torque; //x = torque on right's xz projection, y = torque on global y axis, z = torque on forward axis.
    float3 angular_velocity; //x is vel on right's xz projection, y is vel on global y axis, z is vel on forward axis.
    matrix33 rotation;
};

void rigid_body::update(float time)
{
    const float3 angular_acceleration = torque * mass_inverse; //mass_inverse is now a float, not a tensor.
    angular_velocity += angular_acceleration * time;
    update_rotation(time);
    zero(torque);
}

void rigid_body::update_rotation(float time)
{
    if(get_length_squared(angular_velocity) > 0.f)
    {
        const matrix33 yaw = build_matrix33_from_axis_angle(float3(0,1,0), angular_velocity.y * time);      
        const matrix33 roll = build_matrix33_from_axis_angle(get_forward(rotation), angular_velocity.z * time);

        const float3 flat_right = normalize(get_xz(get_right(rotation)));
        if(get_length_squared(flat_right) > 0.f)
        {
            const matrix33 pitch = build_matrix33_from_axis_angle(flat_right.value(), angular_velocity.x * time);
            rotation = rotation * pitch * roll * yaw;
        }
        else
        {
            rotation = rotation * roll * yaw;
        }                       

        //account for numerical drift
        rotation = orthonormalize(rotation);
    }
}

I can now yaw and have the rigidy_body roll into the yaw turn while maintaining pitch. However, it does have some downsides:

  1. The controller code is far more complicated and costly than ja72's method as it needs to handle a number of special cases when determining how much torque needs to be applied to reach the desired rotation. e.g. Facing straight up or down, etc.
  2. The semantics around torque, angular velocity etc are confusing.
  3. The distribution of mass on the rigid_body is assumed to be uniform despite the actual shape - no inertial tensor.
  4. The yaw/roll relationship is inconsistent if the rigid_body is not oriented roughly along the xz plane. (i.e. roll is tied to yaw and the amount of yaw about the global y is not the same as the amount of yaw required if it was yawing about its true up axis).
  5. If there are any other systems that want to apply torque in the future it will be non-trivial as the whole implementation is specific to this controller. I'm still not sure how I'll handle this if I continue down this path but I've considered implementing a parallel traditional angular rigid_body physics system and somehow the user controller compensates for it. This would require taking the true angular_velocity and converting into whatever this new pseudo-space is.

Does any of this make sense? Is there a better way?

\$\endgroup\$
  • \$\begingroup\$ I was just about to link you to the Q&A you added in your edit. You're correct: the behaviour you're describing here is not how rotational physics work, so you can't just do it with a torque on a free-spinning object. Instead, you can think of your object as fixed to a pair of gimbals: the outer gimbal yaws at a controlled rate, and the inner gimbal rolls at an independently controlled rate. \$\endgroup\$ – DMGregory Aug 23 at 1:43
  • \$\begingroup\$ Ok thanks. I will try refactoring it so I can run each "gimble" through the same physics code to maintain the right feel (acceleration, inertia, etc). \$\endgroup\$ – reaper Aug 23 at 1:58
  • \$\begingroup\$ Also, how do I mark your comment and/or your original post as the answer? \$\endgroup\$ – reaper Aug 23 at 1:59
  • \$\begingroup\$ We could close this question as a duplicate of the one you linked. Or you could edit it to ask for help creating the style of rotation you have in mind / setting up the gimbals. A bit more information about your context / application could be useful here too. \$\endgroup\$ – DMGregory Aug 23 at 2:02
  • \$\begingroup\$ I've updated my original post to document my progress. \$\endgroup\$ – reaper Aug 29 at 17:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.