- Home /
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.
This is what I'm doing too, but as you mention, it's not useful for rigidbody rotations (even rigidBody.$$anonymous$$oveRotation just sets the new rotation...). -- Is there no way to just convert a quaternion to the desired torque?
Answer by hellcats · Feb 25, 2011 at 05:09 AM
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.
UPDATE: turns out you can change the max. angular velocity in Edit->Project Settings->Physics
Ah! that max ang velocity, you saved my life in 2021 ten years ago.
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.
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?
I don't understand why this answer got so many votes. Looks like nobody was able to make it actually work. Yes it may work at certain conditions (very high acceleration/rotation speed, small angles). 1. If your target angle is exact opposite of the current angle, there will be no rotation at all due to how cross product works. 2. The answer completely ignores the fact that to correctly arrive at given target rotation you need to start slowing down at some point. This will just continue going until the rotations meet and then inertia swings rigidbody to opposite direction. Hence the "wobbliness" people mention here.
Answer by Alex_K98 · Feb 14, 2015 at 07:12 PM
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?
The way the Unity engine reports inertia tensor is a bit "squiffy". It always deals with diagonalised tensors, and seems not to transform them into the frame of the rotating object.
I found that by translating to the frame of the rotating object, we get the required behaviour. I'll be adding another section to my game physics course in $$anonymous$$arch 2015 to go through this step-by-step.
I've also updated my answer above to remove the link to my course.
BenTristem, by saying 'transform them into the frame of the rotating object' you mean 'transform to local object coordinate system'?
That's right $$anonymous$$, however I only looked at this briefly. I've attached my prototype above, and will be going through it and thinking about it much more carefully soon.
Answer by remigillig · Nov 27, 2014 at 08:23 PM
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
.
How to change the answer provided to take as input the amount of degrees that we want to rotate the object ins$$anonymous$$d 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!
Answer by _OK_ · Sep 21, 2016 at 07:12 PM
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);
Answer by BinaryAgent · Nov 21, 2011 at 12:27 PM
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
Your answer
Follow this Question
Related Questions
Torque towards Quaternion. 0 Answers
New to Unity: Trying to (loosely) simulate animal flight with separate buttons for each wing... 0 Answers
Calculating Torque but cant add Quaternion 0 Answers
Lerp 180 degrees while holding B and lerp back 180 degrees when let go of B. 2 Answers
player rotation to follow the mouse 2 Answers