Determining the torque needed to rotate an object to a given rotation

I use the following code to determine the new rotation for an object and to instantly rotate it to that orientation.

Quaternion rotation = Quaternion.FromToRotation(oldPoint, newPoint);
transform.localRotation *= rotation;

I want to change this code to use AddTorque to start the object rotating in the direction of the new rotation. I plan to use the distance between startingPoint and targetPoint to control the magnitude of the torque that is applied. Is there a way to use the beginning and ending rotations to determine the correct vector to pass to AddTorque() so that the object will rotate in the proper direction?

BTW, I don't want to use Quaternion.Slerp() since I want to object to behave physically correctly.

more ▼

asked Feb 22, 2011 at 03:56 AM

avatar image

114 10 9 15

(comments are locked)
10|3000 characters needed characters left

4 answers: sort voted first

Fun question. Just like F = m a for linear forces, T = I alpha for angular forces. T is the torque, I is the inertia 3x3 tensor, and alpha is the angular acceleration. So basically your question amounts to finding an angular acceleration from a given change in rotation, and then multiplying that by I to get T.

Angular acceleration is a Vector3 whose direction is the axis of rotation and magnitude is rotational acc. in radians/sec^2. Since you already have two direction vectors (which need to be normalized), you can simply compute x = Vector3.Cross(oldPoint, newPoint) to get the required axis of rotation. This is the direction of alpha, but you still need the correct magnitude. We want radians/sec^2, so we need the angle between the two vectors. The magnitude of cross product is sin(theta) |v| |u|, since length of v and u are both 1, we just need Asin(x.magnitude).

Since you want to fully reach your newPoint in one frame, you can instead apply an impulse which is sort of like an instantaneous acceleration or change in velocity. So to summarize.

Vector3 x = Vector3.Cross(oldPoint.normalized, newPoint.normalized);
float theta = Mathf.Asin(x.magnitude);
Vector3 w = x.normalized * theta / Time.fixedDeltaTime;

This gives us the desired change in angular velocity (w). Now we just multiply by the inertia tensor. Unfortunately this is in some weird diagonal space. It is easiest to transform w into this space, compute T, then transform T back to global coords.

Quaternion q = transform.rotation  rigidbody.inertiaTensorRotation;
T = q  Vector3.Scale(rigidbody.inertiaTensor, (Quaternion.Inverse(q) * w));

Then just apply T to the rigidbody:

rigidbody.AddTorque(T, ForceMode.Impulse)

NOTE: PhysX seems to limit the speed of rotation, so this actually only works if the amount you are rotating by isn't too large.

more ▼

answered Feb 25, 2011 at 05:09 AM

avatar image

771 26 22 37

UPDATE: turns out you can change the max. angular velocity in Edit->Project Settings->Physics

Feb 25, 2011 at 05:13 AM hellcats

o.O nice answer!

Apr 12, 2011 at 05:57 PM Joshua

Wow.... What is the meaning of life!?!

All jokes aside, very interesting answer. I have been trying to implement it for a while but I am not getting anything out of T. I might be a little confused as to what "old point" and "new point" represent. I replaced them with the position vectors of the object that I wish to rotate and the object I wish to rotate towards. Em I doing this wrong? While I love your answer and this all sounds like exactly what I am looking for but this is beyond me so if you could explain what oldpoint and new point is. That would be great.

I am trying to rotate an object towards another object by torque. Picture a arrow at a bottom of the screen and a ball at the top, the arrow is pointing down and I wish to have it point at the ball but using torque to do so. I think that this is what your explanation goes over but I have not been able to get it to work so far.

Oct 08, 2012 at 03:35 AM Anxo

This simple and clear solution worked right away in my scenario. How can it be expanded to include rotation around the Z axis as well? This is not currently taken into account. Would simply multiplying the 'q' quaternion by Quaternion.FromToRotation using the local current and destination rotations do the trick?

Nov 12, 2012 at 02:26 PM daterre

daterre - I've been trying to figure that out myself (expanding to full rotation), but am missing something.

I find if I do not factor in the inertialTensor, and simply assign rigidbody.angularVelocity = w; then I get the exact desired rotation in a single frame. Factoring in the inertialTensor makes it lag.

ForceMode.Impulse assumes you have included mass in your value, however, switching to ForceMode.VelocityChange didn't have any effect in my case since my object's mass is 1.

Combining angular velocities is the sticking point for me.

Here's a straightforward expansion upon hellcats' presentation above, which provides full alignment:

void UpdateAngularVelocity(Quaternion desired) { var z = Vector3.Cross(transform.forward, desired Vector3.forward); var y = Vector3.Cross(transform.up, desired Vector3.up);

 var thetaZ = Mathf.Asin(z.magnitude);
 var thetaY = Mathf.Asin(y.magnitude);

 var dt = Time.fixedDeltaTime;
 var wZ = z.normalized * (thetaZ / dt);
 var wY = y.normalized * (thetaY / dt);

 var q = transform.rotation * rigidbody.inertiaTensorRotation;
 var T = q * Vector3.Scale(rigidbody.inertiaTensor, Quaternion.Inverse(q) * (wZ + wY));

 // too wobbly
 //rigidbody.AddTorque(T, ForceMode.VelocityChange);

 // stable, but still laggy
 rigidbody.angularVelocity = T;
 rigidbody.maxAngularVelocity = T.magnitude;


I'd like to understand where that lag is coming from. I'm concerned that simply adding the two angular velocities (wZ and wY) only works here because they are getting scaled down small enough by the intertiaTensor.

I would expect rigidbody.angularVelocity to be in object space, but that appears to not be the case?

Dec 11, 2012 at 11:43 PM aleiby
(comments are locked)
10|3000 characters needed characters left

Hi hellcats, thanks for the detailed answer.

I am trying to implement your solution in my project and I am running into difficulty.

Here's my situation: http://imageshack.us/photo/my-images/706/explanationp.png

My object is travelling at a fixed speed on a heading in a 2-d coordinate space (z is always 0). On a mouse click I would like to apply a relative torque to turn it to a new heading. The rotational axis is always (0,0,1) - the z-axis.

The way I see it, there are two ways to rotate an object with a rigidbody: 1) switch on kinematics and do it manually or, 2) apply a rotational force. I'd like to use the second option.

My FixedUpdate function looks like this:

 void FixedUpdate () 
     rigidbody.AddRelativeTorque ( GetTorque(), ForceMode.VelocityChange);                                                                                  
     rigidbody.AddRelativeForce (forwardDirection * Time.deltaTime, ForceMode.VelocityChange); 

The GetTorque function is:

 Vector3 GetTorque()
     m_vHeading = rigidbody.velocity; 
     toTarget = (target.transform.position - gameObject.transform.position);
     x = Vector3.Cross(m_vHeading.normalized, toTarget.normalized);
     angle = Mathf.Asin(x.magnitude);
     Vector3 w = x.normalized * angle / Time.fixedDeltaTime;
     Quaternion q = gameObject.transform.rotation * rigidbody.inertiaTensorRotation;
     Vector3 T = q * Vector3.Scale(rigidbody.inertiaTensor, (Quaternion.Inverse(q) * w));
     return T;

Where totarget is an empty gameobject controlled by mouse move. Can anybody tell me if I am on the right track? Am I over complicating something that should be simple. Any and all help is greatly appreciated.

Thanks, BinaryX

more ▼

answered Nov 21, 2011 at 12:27 PM

avatar image


(comments are locked)
10|3000 characters needed characters left

Check out www.udemy.com/gamephysics when it comes out, I'm just covering exactly this. Password to preview is alpha.

Got a cool simulation of a phone flipping g according to the intermediate axis theorem, so have managed to add significant value to the existing physics engine.

more ▼

answered Dec 26, 2014 at 10:51 PM

avatar image

5 1

(comments are locked)
10|3000 characters needed characters left

Taking hellcats's answer and fixing the "wobbliness" reported by aleiby, I came up with this :

 var x = Vector3.Cross(currentDir.normalized, newDir.normalized);
 float theta = Mathf.Asin(x.magnitude);
 var w = x.normalized * theta / Time.fixedDeltaTime;
 var q = transform.rotation * rigidbody.inertiaTensorRotation;
 var t = q * Vector3.Scale(rigidbody.inertiaTensor, Quaternion.Inverse(q) * w);
 rigidbody.AddTorque(t - rigidbody.angularVelocity, ForceMode.Impulse);

The fix is to substract the current angular velocity. I hope this will be useful for other people googling around for this.

If you modify the inertia tensor yourself, the ForceMode can sometimes be changed to VelocityChange.

more ▼

answered Nov 27, 2014 at 08:23 PM

avatar image

1 1

(comments are locked)
10|3000 characters needed characters left
Your answer
toggle preview:

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this question

By Email:

Once you sign in you will be able to subscribe for any updates here



Answers and Comments



asked: Feb 22, 2011 at 03:56 AM

Seen: 11039 times

Last Updated: Dec 26, 2014 at 10:51 PM