4

Because this is a complex question that usually results in much confusion (I've asked variations on this question previously, but never asked the question the right way and never got an answer), I'm going to try to make this as clear as possible.

Facts:

  • I'm using Unity.
  • I'm can get the Forward, Up and Right vectors easily from any Quaternion rotation.
  • I can't simply record my own Euler angles, modify them and apply the rotation through a new Quaternion because the object is controlled by physics.
  • I don't understand maths very well at all unless it's written in code (or pseudo-code), so this would be most beneficial to me.
  • A C++ style answer would be easiest for me to understand, but I can work out pretty much any kind of code.
  • I'm NOT trying to get anyone to write the code for me! I'm only asking for the answer in code or pseudo-code because I never learned to read normal maths squiggles; I'm a programmer, not a mathematician.
  • Unity uses a left-handed coordinate system. X = right, Y = up, Z = forward.

What I'm trying to do:

I'm trying to play an animation on a humanoid bone structure and, using torque (rotational force), push the physics ragdoll into approximately the same pose as the bone structure.

The problem:

I can work fully in Quaternions right up to the point where I need to apply the torque to the rigidbodies. The AddTorque function effectively works in Euler angles, which means I can't use the Quaternions. I can easily extract Euler angles from the Quaternions, but they are unreliable and cause the ragdoll to spaz out severely.

What I need:

I need to calculate reliable 3D Euler angles (as in, ones that don't flip from + to - "randomly") from Forward, Up and Right vectors. I realise this is a bit complicated, but that's why I ask here: I lack the knowledge and experience to work out this problem myself.

Given that the vectors themselves are reliable, I see no reason why it would not be possible to work out reliable Euler angles from them. Also, I don't know what order of rotation I would want or need for the Euler angles, but I believe that would be fairly easy to modify later.

Any help would be much appreciated!

Clonkex
  • 3,373
  • 7
  • 38
  • 55
  • 1
    I've responded to similar questions before; I don't think you'll be able to implement a solution without understanding the maths. Sorry, but Physics is like that. – Beta Jun 14 '14 at 05:01
  • Maybe you could try me. I understand a fair amount of maths, but only when written in code-speak. – Clonkex Jun 14 '14 at 06:13
  • 1
    The [youtube video](http://www.youtube.com/watch?v=De0PoxaKlww) says that you effectively give the torque vector in the __local frame of the rigid body__. This has nothing to do with Euler angles! – Tobias Jun 14 '14 at 06:47
  • All right, do you have tidy acceptance test for the conversion function? – Beta Jun 14 '14 at 06:54
  • @Tobias Not directly, no. However, you try using that function to push a rigidbody toward a _Quaternion_ rotation and tell me how you get on. Umm... if you succeed with that could you _actually_ tell me how you got on? I'd love it if there were a way to push a rigidbody towards a Quaternion rotation with that function! :) – Clonkex Jun 14 '14 at 06:56
  • @Beta Do you mean a simple test that serves to prove whether the code works or not? Well yes, but it's Unity, so I don't think I could give it to you easily. – Clonkex Jun 14 '14 at 06:58
  • That's an overcomplicated test, but maybe we can work with it. Do you have some of the input-output pair it uses? I ask because there are different conventions for Euler angles, and I don't know what `Forward, Up and Right` are, so we have to reverse-engineer the test. – Beta Jun 14 '14 at 07:05
  • Umm....hmm, I may be able to understand the maths, but I don't understand the analyses terms you're using. All I have is a blank level with a cube. I assign the test script to it, start the game and then rotate the cube in the editor to see if the debug output from the script matches what I expect. Unity uses left-handed rotations. X = right, Y = up, Z = forward. Hope that helps. – Clonkex Jun 14 '14 at 07:13
  • That's your idea of a simple test? I'm sorry, but this is hopeless. – Beta Jun 14 '14 at 07:27
  • @Beta Hey!! :( That's very unkind! Why is that a bad test? It's extremely simple, easy to set up and does the job really well. What's wrong with it? What is your primary field of expertise, that you think this is hopeless when to me it seems like a really good start? – Clonkex Jun 14 '14 at 07:31
  • I'm a physicist and programmer, and an acceptance test that requires 1) user interaction, 2) firing up the whole executable, 3) irreproducible test data and 4) heaven knows how many calls to the function under test, is *not* extremely simple. If you want to pursue this, fine, try several small rotations around different axes (*not* all perpendicular), and for each one record your description of the rotation, the resultant [Forward, Up, Right] vectors, the Euler angles and your description of the resultant motion of the model, then add all of that to your question and maybe we can use it. – Beta Jun 14 '14 at 07:49
  • Ah, a physicist. Look, I don't see any reason that those complicated and fiddly scientific tests are necessary. My way of doing it is considerably faster than yours. No calculating stuff by hand, no writing stuff down, just hit F5 and see if it works. It really _is_ simple if you don't strip it down to each action required by the computer: It's simple in that I only have to click Go and see what happens. That's all I need and that's all I want. However, looking at what I've just said from your point of view, I can see why you said this is hopeless (my tests are not reproducible... _continued_ – Clonkex Jun 14 '14 at 08:07
  • ...in any way, shape or form, for a start). I'm not stupid and I'm well aware of the reasons for a scientific approach to experimentation. But in this case, I wasn't asking for someone to conduct experiments for me, I was asking if there was a known way to achieve a specific goal. I'm not looking for a theoretical solution, I was looking for a practical one (pseudo-code counts as practical). For the time being, I'm just going to go over to Unity Answers and see if they have a more Unity-specific answer for me. If no one over there can help, _then_ I'll consider coming back here. Thanks anyway. – Clonkex Jun 14 '14 at 08:07
  • 2
    I never did it. But here is how it looks for me: You can connect two given quaternions with a Slerp. Search http://courses.cms.caltech.edu/cs171/quatut.pdf for `Slerp`. You can parameterize `t` therein as `t(tSmooth)` to get a smooth curve in dependence of your smoothened time `tSmooth` for which you can calculate the angular acceleration (the formula for the angluar velocity is given in the paper). The angular acceleration expressed in local coordinates is what you need for `AddTorque`. – Tobias Jun 14 '14 at 09:38
  • @Tobias That sounds very helpful indeed. From the sounds of it you haven't used Unity before and are giving a more general answer. Just so you know, Unity has a built-in `Slerp` function for its quats. Your comment gives me great hope, because that would let me work entirely with quats and never have to try to get Euler angles. I'm not great a maths, particularly when written in the standard form (as in that paper), although, weirdly, I actually know what a Mobius strip is, since I went to a uni open day recently. I wonder if you could provide a full answer that elaborates... _continued_ – Clonkex Jun 14 '14 at 09:48
  • ...more on what I would need to do to find that angular acceleration you speak of. That is, in pseudo-code, how would I achieve that? My biggest problem is that I don't understand the maths squiggles in the paper enough. If it were written in code, I'd have a far greater chance of understanding it. Anyway, so far you seem to understand my problem the best, and I really hope you can help me! :D – Clonkex Jun 14 '14 at 09:50
  • @Tobias Just re-read my last two comments - confusing! Summary of what I was trying to say: Unity has a built-in Slerp function, so that bit's easy, and I would need a code-based (or pseudo-code-based) answer for me to understand it. Right now I'm doing everything I can to try and understand your comment, but I'm struggling because I don't know what parameterize means. Going to Google it and see I can get a better understanding. – Clonkex Jun 14 '14 at 10:21
  • It looks actually quite easy to me. For the angular velocity in global coordinates you need to calculate $q_{10}:=q_1 q_0^{-1}$ ([Hamilton-multiplication](http://en.wikipedia.org/wiki/Quaternion#Hamilton_product)). This should be a unit quaternion (maybe you need to normalize). From this you can read off the rotation axis $v$ and the needed unit-time rotation velocity $\Omega$. You can use $v$ for your rotation but you need to transform it into the local frame. For a starter do that and apply an impulse torque into that direction to see what happens. – Tobias Jun 14 '14 at 10:27
  • Maybe, in unity there already exists some function for quaternion inversion and quaternion multiplication. – Tobias Jun 14 '14 at 10:29
  • @Tobias No, I lack the mathematical skills to interpret how that _should_ look ;) However, Unity has built-in functions for quat inverse and quat mult. I can't understand what you're doing there, but if I were to guess... are you getting the difference between two quats? Because I already have code for that. My confusion is now what I would need to _do_ with the quats. – Clonkex Jun 14 '14 at 10:35
  • q10 is not the difference. It is the product of q1 with the inverse of q0. Thereafter, normalize q10. (Unity should have normalization.) The vector part is direction of the rotation vector. The norm of the vector part is cos(Omega). Just start with the vector part transform it into local coordinates and apply a torque to it. Look whether it rotates at least in the right direction. – Tobias Jun 14 '14 at 10:42
  • @Tobias Oh I see! Ok, time to go test. I'll report back with the results :) – Clonkex Jun 14 '14 at 12:38
  • @Tobias It works!! Thank you so much!! :D If you want, post a full answer and I'll select it as the answer to the question to give you the rep points, since you did actually solve my problem :) – Clonkex Jun 15 '14 at 01:06

2 Answers2

8

First, I'd like to say that I solved my problem entirely due to @Tobias's efforts. Many, many thanks! All this time I've been approaching the problem from the wrong end. I assumed that I needed to use AddTorque in a particular way (with Euler angles) and worked from there, but @Tobias (and @JarkkoL a little later) pointed out that I needed to use AddTorque differently.

So this is what I did, in UnityScript (effectively JavaScript):

var quat0:Quaternion;
var quat1:Quaternion;
var quat10:Quaternion;
quat0=transform.rotation;
quat1=target.transform.rotation;
quat10=quat1*Quaternion.Inverse(quat0);
rigidbody.AddTorque(quat10.x,quat10.y,quat10.z,ForceMode.Force);

And, against all expectations, this WORKS!! It just... works! Sure, it takes a long time for the rigidbody cube to settle down, but that's because I need a PID controller. Or maybe quat10 needs normalising, not sure. I'll work it out :)

I had no idea you could actually use that part of a quat by itself.

Clonkex
  • 3,373
  • 7
  • 38
  • 55
  • 1
    Yes, if you look at some literature about unit quaternions for representing 3D rotations, you'll find out that q=[v*sin(angle/2), cos(angle/2)], where v=3D rotation axis (unit vector) and angle=rotation angle about the axis. – JarkkoL Jun 15 '14 at 01:14
  • @JarkkoL So...the thing I never really _got_ about quaternions, is why do are they better than just a plain old axis-angle representation (which seems like it has no problems to me)? Are they just better for calculations or something? – Clonkex Jun 15 '14 at 01:16
  • 2
    Yes, there's a whole class of mathematical operations you can apply to quaternions. For example if you need to concatenate rotations (e.g. to perform hierarchical transformations), you just multiply two quaternions like you do with matrices. – JarkkoL Jun 15 '14 at 01:25
  • In the [documentation](http://docs.unity3d.com/ScriptReference/Rigidbody.AddTorque.html) they write that the body will start spinning around the global axis while [on youtube](http://www.youtube.com/watch?v=De0PoxaKlww) they say that the torque is applied w.r.t. a local axis. The youtube version would mean that you need to transform the torque vector into local coordinates. More about that in the next comment. Both versions agree for a rot about one rot axis. They differ for concatenated rots about several axes. – Tobias Jun 15 '14 at 05:55
  • q0 is the orientation of the frame at the time point when you want to apply the torque. To transform the torque vector q10 into coordinates w.r.t. q0 you can multiply Inverse(q0)*q10*q0. Background: Imagine representation of q10 w.r.t. q0 as rotating q10 back with Inverse(q0) which just gives Inverse(q0)*q1*Inverse(q0)*q0 = __Inverse(q0)*q1__. ([Quaternions as rotations](http://courses.cms.caltech.edu/cs171/quatut.pdf) and [homogeneous coords](http://en.wikipedia.org/wiki/Homogeneous_coordinates)) – Tobias Jun 15 '14 at 06:13
  • Please, try the modified version `quat10=Quaternion.Inverse(quat0)*quat1;` with rotations about several axes. If it works you can mark your answer as solution. The question then appears as answered in the list of questions. – Tobias Jun 15 '14 at 06:19
  • @Tobias with that modified version, the test cube initially spins towards the correct angle, but just after it appears to settle, it quickly becomes unstable and begins spinning wildly, not stopping. `quat10=quat1*Quaternion.Inverse(quat0);` actually works better. Also, be aware that there are two versions of the `AddTorque` function - `AddTorque` and `AddRelativeTorque`. You could be looking at the wrong one; I'm using `AddTorque` without transforming `quat10` to any other frame of reference. – Clonkex Jun 15 '14 at 11:59
  • Interesting, because they also use `AddTorque` in the [youtube video](http://www.youtube.com/watch?v=De0PoxaKlww). But, I do not have any experience with Unity. So it was just a guess. Thanks for testing anyway. `arcsin` of the scalar component of `quat10` should give you the angle by which you have to turn. If you give this as a angular velocity step and apply the opposite angular velocity step after unit time (without drag) you should directly land at `quat1`. Good luck. – Tobias Jun 15 '14 at 12:08
  • @Tobias Thanks! I have a PID controller system set up and I'm currently tuning the values to get it working optimally :) – Clonkex Jun 15 '14 at 12:42
1

First off, I think you would have better luck in Unity forums for Unity specific questions (: That said, I think you are misinterpreting the AddTorque() interface if this one is what you are using: http://docs.unity3d.com/ScriptReference/Rigidbody.AddTorque.html

Instead of passing Euler angles you pass a vector to the function that's the axis of rotation. I'm not familiar with Unity, but I believe the length of the vector specifies how much torque to add. Euler angles are inherently bad representation for rotations so you should always assume (unless otherwise documented) that well established API's work with quaternions, axis/angle pairs or matrices when it comes to rotations. Euler angles are more of a convenience representation for end users.

JarkkoL
  • 1,898
  • 11
  • 17
  • Actually I don't believe I would have more luck on Unity Answers - the culture over there doesn't even come close to SO, and there seems to be very few experts. On the `AddTorque()` function: you are correct. The function does indeed work with a vector. The problem was how to work out what that vector was. I'm about to answer my own question, so you'll get to see what I did in the end :) – Clonkex Jun 15 '14 at 00:49