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.

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.

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.

Gentlemen, what is the sense of multiplying by ‘inertiaTensorRotation’ when it is always W=1 and XYZ=0, at least this is what Debug.Log shows me?

I stumbled on this thread looking for the answer - so here it is for anybody else who’s looking
this example allows an object to rotate to face another object using torque only

change speed etc with a float rotationTorque and change the ridged body angular drag to suit

    Vector3 targetPosition = transform.InverseTransformPoint(new Vector3(target.transform.position.x,target.transform.position.y, target.transform.position.z));
        
    requiredTorqueX = (targetPosition.x / targetPosition.magnitude);    
    requiredTorqueY = (targetPosition.y / targetPosition.magnitude);
        
    rbody.AddRelativeTorque(((rotationTorque) *requiredTorqueY),((rotationTorque) * requiredTorqueX) * -1 , 0f, ForceMode.Force);

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

Hi Sol. To be clear you will actually need two torques, one to start the object rotating, and one to stop it at the desired rotation. Then you need to decide how you want it to accelerate / decelerate.

It’s a bit like asking what force you need to get to the moon: Not much if you don’t mind waiting a long time then smashing into the surface. However if you want to get there in an hour, and to accelerate smoothly you could accelerate at around 13g for 30 minutes, turn-around, then decelerate at the same rate for another half an hour. Same with your torques and rotation. Does that make sense?

So the magnitude of the torque(s) you need depends not only the angle change, but on the time you want it to take (as well as the acceleration / deceleration strategy). The rotation won’t be very “physically correct” if it instantaneously moves from one rotation to another, unless the object has no rotational inertia.

So we must involve time in this, and you need to decide how long you want your rotation to appear to take. On that basis you’re not really working out a torque, but a torque at a given time into the “animation”. What’s the method signature you’re after, would this pattern do?..

/// <summary>
/// Animates the rotation.
/// Performs a realistic torque-driven rotation from one stationary position to another.
/// Half the time is spent accelerating, the other half decelerating.
/// </summary>
/// <returns>The rotation at the elapsed time</returns>

Quaternion AnimateRotation (Vector3 from, Vector3 to, float timeElapsed, float totalTime) {
    Quaternion currentRotation;
    // I'll write the code if you're stuck
    return currentRotation;
}

If your object is 3D, and rotating around more than one axis you’ll need an inertia tensor rather than the simplified “moment of inertia” about one axis (which is just one element of a 3x3 matrix). In which case read-on. If you do go down the inertia tensor route, bear in mind…

  1. Unity diagonalises them, so “intermediate axis” instability doesn’t work properly (see solution below).
  2. Adding a 3D collider(s) to a rigidbody gives you access to rigidbody.inertiaTensor, which is only approximate.
  3. You’ll have to do your multiplications carefully, again see below.

====

On a related note in case it helps, I got a cool simulation of a phone flipping according to the [40735-flipping-phones.zip|40735]**

I’ll be going through this step-by-step when I add a section to my new game physics course in March 2015. I’ll be removing the cumbersome single and double underscore notation don’t worry!

How to the answer provided to take as input the amount of degrees that we want to rotate instead of a oldPoint and newPoint inputs.

I have a gameObject: objectHeld, which I need to rotate using torque by rotationY degrees. Below code works, but it is not the proper solution for rigidbody, when I use this, if the object is near the walls it can go thru the walls.

objectHeld.transform.localRotation = Quaternion.Euler(0.0f, rotationY, 0.0f);

Thanks!

I’m using a vastly different algorithm to achieve a target rotation via torque over multiple frames.

I found the effect to be quite pleasing to the eye when using a damp scale of around 0.1f.

Note that there can be occasional jitter around the target rotation, and some epsilon and snapping should be added.

	if (doDamp)
	{
		// Find Axis that will take us from current rotation to Identity
		Vector3 currentRotation 			= m_rigidBody.rotation.eulerAngles;

		Vector3 deltaAngle 					= currentRotation - targetRotation;
		Vector3 deltaAngleWrapped 			= new Vector3(Mathf.DeltaAngle(deltaAngle.x, 0.0f), Mathf.DeltaAngle(deltaAngle.y, 0.0f), Mathf.DeltaAngle(deltaAngle.z, 0.0f));
		Vector3 velocityProjected 			= Vector3.Project(m_rigidBody.angularVelocity, deltaAngleWrapped);
		Vector3 torque 						= deltaAngleWrapped - velocityProjected;

		torque = torque * dampScale;

		if (doDampLog) {
			Vector3 currentRotationWrapAngle 	= new Vector3(Mathf.DeltaAngle(currentRotation.x, 0.0f), Mathf.DeltaAngle(currentRotation.y, 0.0f), Mathf.DeltaAngle(currentRotation.z, 0.0f));
			Debug.Log("Torque: " + torque + " rotation: " + currentRotationWrapAngle + " torqueAngle: " + deltaAngleWrapped + " velocity on angle: " + velocityProjected);
		}

		m_rigidBody.AddTorque(torque);
	}