• 12
• 12
• 9
• 10
• 13

# quaternion as orientation problem

This topic is 2557 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hi, Im current doing a rigid body physics engine, euler integration, and using quaternions as orientation..

So its the first time Im testing it, I created a very simple app for testing it: a sprite at the middle of the screen, I press the arrows to apply forces to it, left and right apply forces at ( 0.0f, -100.0f, 0.0f), in the sprite X direction, to obvioulsy generate a great torque onto it...

Things works really fine( better than I expected for a first testing), but I realize a issue I didnt expected(and I dont remember seeing it on the book game physics engine dev)..The orientation quaternion is normalized at my integration method, probbaly to avoid instability, I dunno, but it makes my orientation stop when some angle is achieved( I dont know exactly, but is a bit more than 90 degrees ).

So I tried not normalizing it, it sure solves the orientation limit, but it is destroying my transformation matrix, computed with the orientation and position..(it scales the sprite , until it disappears(culling) )..
So I tried just using the normalized version to make the transformation matrix, and keep the quaternion unnormalized, but them, when it gets at the critical angle(the one that stop/scale..) it suddenly rotates damn fast for a few frames and disapear...(checking my logs, the quaternion is like this: w:-1.#IND00 i:-1.#IND00 j:-1.#IND00 k:-1.#IND00, with makes sense...)

Im lost, Im donk know what I have to do with it..

##### Share on other sites

Things works really fine( better than I expected for a first testing), but I realize a issue I didnt expected(and I dont remember seeing it on the book game physics engine dev)..The orientation quaternion is normalized at my integration method, probbaly to avoid instability, I dunno, but it makes my orientation stop when some angle is achieved( I dont know exactly, but is a bit more than 90 degrees ).

Can you elaborate on that behavior? Renormalizing the quaternion after each integration step is fine and shouldn't result in any limits to any angles.

##### Share on other sites
initial orientation:

after apply a force, the ship starts rotate:

until get like this(or in the other direction(to the right)):

I logged the quaternion on a text file, and I realize that it get stucked when the quaternion gets on those values:
w: 0.554958 i: 0.000000 j: 0.000000 k: 0.831878
when rotating left, and:
w: 0.554958 i: 0.000000 j: 0.000000 k:-0.831878

when rotating right..
So I though that is due normalization...Since the quaternion keep changing, but normalization change it back..

Now Im thinking the problem can be a lot more problematic..When it get stucked on those values, if I start rotate to the other side, it will not follow the path it did till get to the other stucked orientation, it will just keep rotating, and then get stucked again( exemple: press left, ship rotates CCW till get stucked, then press right, and ship will rotate CCW again till get stucked on the oher side)
That happens only if it gets rotated that much to get stucked, if I keep short rotations it works fine...

##### Share on other sites
You are definitely doing something wrong.

Can you break down the computation of applying a force to the object when it's in one of those "extreme" orientations? Give us enough detail so we can reproduce your numbers as we follow.

Actually, I don't understand why you need a force at all... Once your object has started to rotate, it should continue to rotate freely if you don't apply any external forces. So perhaps we just need to know how you are applying the angular velocity to your orientation to update it forward one frame.

##### Share on other sites

You are definitely doing something wrong.

Can you break down the computation of applying a force to the object when it's in one of those "extreme" orientations? Give us enough detail so we can reproduce your numbers as we follow.

Actually, I don't understand why you need a force at all... Once your object has started to rotate, it should continue to rotate freely if you don't apply any external forces. So perhaps we just need to know how you are applying the angular velocity to your orientation to update it forward one frame.

True...what happens is that it starts to rotate less when it starts to get close to the stuck point..it shouldnt
My intention is just make a ship controled by forces, kinda like Asteroids games(up and down accelerates, left and right only rotates), but also using forces to rotate it, faking propulsion u know..
I will show my integration method, the ship setup, and the main loop where I apply forces.

My integrate method first:
 void Phys::RigidBody::Integrate(real duration_p){ if( duration_p == (real)0.0 ) return; //work out the acceleration from the force--------------- //LINEAR: //a = f/m = f*1/m //ra = a + f*1/m(NOTE: acceleration is not accumulated) /*Vector3*/resultingAccel = acceleration;//add the constant acceleration(like gravity) resultingAccel.AddScaledVector( forceAcum, inverseMass ); //ANGULAR: //a = torqe/moi Vector3 angularAccel = inverseInertiaTensorWorld*torqueAcum; //------------------------------------------------------- //update velocity from acceleration----------- //LINEAR: //v= v + ra*t velocity.AddScaledVector( resultingAccel, duration_p ); //ANGULAR: //v= v + aa*t rotation.AddScaledVector( angularAccel, duration_p ); //--------------------------------------------------- //update position------------------------------------ //LINEAR: //p= p + v*t position.AddScaledVector( velocity, duration_p ); //ANGULAR: orientation.AddScaledVector( rotation, duration_p ); //--------------------------------------------------- //calculate trafo matrix(from position and orientation) and //update inertia tensor(from local to world coords)--------- CalculateDerivedData(); //---------------------------------------------------------- //clear accumulated force- ClearAccumulators(); //------------------------ } 
The function the integrate method uses, where the orientation is normalized:
 void Phys::RigidBody::CalculateDerivedData(){ orientation.Normalize(); //calculate transformation matrix from orientation quaternion and vector position: trafoMatrix.SetOrientation( orientation, position ); //calculate inertia tensor(inverted) in world coordinates( orientation accordingly with the object ): Matrix3x3 tmpMOrient; tmpMOrient.SetOrientation( orientation ); inverseInertiaTensorWorld = inverseInertiaTensor * tmpMOrient; } 

Heres how I setup my rigid body and apply force to it:
 ... Ship myNave; Phys::Matrix3x3 SphereInertia(12, 0.0f, 0.0f, 0.0f, 12, 0.0f, 0.0f, 0.0f, 12 ); if( myNave.Create( 1.0f, SphereInertia, Phys::Vector3(0.0f, 0.0f, 0.0f), g_dx.pDevice, TEXT("SpaceCrax.dds") ) == false ) return -1; ... if( GetAsyncKeyState( VK_LEFT) & 0x8000 ){ Phys::Vector3 direction( myNave.trafoMatrix.m11, myNave.trafoMatrix.m12, myNave.trafoMatrix.m13 ); myNave.AddForceAtBodyPoint( direction.Inverted(), Phys::Vector3(0.0f, -100.0f, 0.0f) ); } if( GetAsyncKeyState( VK_RIGHT) & 0x8000 ){ Phys::Vector3 direction( myNave.trafoMatrix.m11, myNave.trafoMatrix.m12, myNave.trafoMatrix.m13 ); myNave.AddForceAtBodyPoint( direction, Phys::Vector3(0.0f, -100.0f, 0.0f) ); } myNave.Integrate( (Phys::real)gTimr.GetSeconds() ); myNave.position = Phys::Vector3();//keep ship at center for debugging 
And the method used to apply force:
 void Phys::RigidBody::AddForceAtBodyPoint( const Phys::Vector3 &force, const Phys::Vector3 &point ){ //convert point to world coordinates: Vector3 Wpoint( trafoMatrix*point ); //split force to accumulators: AddForceAtPoint( force, Wpoint ); } //----------------------- void Phys::RigidBody::AddForceAtPoint( const Vector3 &force, const Vector3 &point ){ //get point where force is being applied, relative to the center of mass: Vector3 pf = point - position; //split force to accumulators: //linear force: forceAcum += force; //angular force( T = pf X f ): torqueAcum += pf.CrossProduct( force ); } 

--edit--
The Ship class is derived from RigidBody.

##### Share on other sites
What does that line do? And what are the types involved?

##### Share on other sites

What does that line do? And what are the types involved?
[/quote]

Ah! Sorry..
orientation is a quaternion
rotation is an axis-angle representation of angular velocity( its a Vector3)

This function is used to rotate a quaternion from an axis-angle vector, it works by transforming the axis-angle vector into a quaternion with w = 0
 //------------------------------------------------------------------------------ //Add Scaled Vector Function //------------------------------------------------------------------------------ //Usefull for rotating quaternions from an axis-angle rotation/velocity. //The scale param can represent the delta time(use 1 for an absolute rotation). // q1 = q0 + ( qaa * q0 )* t/2 //qaa = q( 0, aax, aay, aaz), aa stands for axis-angle //------------------------------------------------------------------------------ void AddScaledVector( const Vector3 &v, const real scale ){ //pass the axis-angle vector to quaternion, alredy scaling it: Quaternion qw( 0, v.x * scale, v.y * scale, v.z * scale ); //multiply qw by this quaternion: qw *= (*this); //add this quaternion with qw/2: w += qw.w * (real)0.5; i += qw.i * (real)0.5; j += qw.j * (real)0.5; k += qw.k * (real)0.5; } //------------------------------------------------------------------------------ 

the vector3::AddScaledVector is offcourse simpler(the one used for velocity, rotation and position ):
 void AddScaledVector( const Vector3 &v, const real scale ){ x += v.x * scale; y += v.y * scale; z += v.z * scale; } 
Do you think this problem is a math problem rather than logical..some stupid calculation mistake? The only comcplicated math going on here are setting a matrix from quaternion I think..heres how I do it:
 //--------------------------------------------------------------------- //Set Orientation Function //--------------------------------------------------------------------- //Transform this matrix on the matrix version of the given quaternion. //--------------------------------------------------------------------- void SetOrientation( const Quaternion &q ){ //powers: real r_one_minus_2ii = (real)1.0 - (real)2.0*q.i*q.i; real r_2jj = (real)2.0 * q.j*q.j; real r_2kk = (real)2.0 * q.k*q.k; //multiplications: real r_2ij = (real)2.0 * q.i*q.j; real r_2wk = (real)2.0 * q.w*q.k; real r_2jk = (real)2.0 * q.j*q.k; real r_2wi = (real)2.0 * q.w*q.i; real r_2ik = (real)2.0 * q.i*q.k; real r_2wj = (real)2.0 * q.w*q.j; //update: m11 = (real)1.0 - r_2jj - r_2kk; m12 = r_2ij + r_2wk; m13 = r_2ik - r_2wj; m21 = r_2ij - r_2wk; m22 = r_one_minus_2ii - r_2kk; m23 = r_2jk + r_2wi; m31 = r_2ik + r_2wj; m32 = r_2jk - r_2wi; m33 = r_one_minus_2ii - r_2jj; } //--------------------------------------------------------------------- 
This is based on the book 3d math primer for graphics and game dev, i just pre-calculate the commom expressions..

##### Share on other sites
Your Quaternion::AddScaledVector function seems correct, so the problem is not where I thought it would be. Personally, I would normalize the quaternion at the end of that function, instead of doing it at the beginning of Phys::RigidBody::CalculateDerivedData, but that's not that important.

It still would be helpful to trace what happens when you arrive to the limit position and your angular velocity is not zero. Why doesn't it continue rotating?

For instance, starting with q = 0.554958 + 0.831878*k, this code doesn't have any problems rotating it further using (0,0,0.01) a few times:
#include <boost/math/quaternion.hpp> #include <iostream> typedef boost::math::quaternion<double> Q; int main() { Q orientation(0.554958,0.0,0.0,0.831878); Q angular_velocity(0.0, 0.0, 0.0, 0.01); for (int i=0; i<20; ++i) { std::cout << orientation << '\n'; orientation += 0.5*angular_velocity*orientation; normalize(orientation); } } 

What is your code doing differently? You can use a debugger to find out.

##### Share on other sites
What annoying...I cant figure out..
The thing is, everything updates, the rotation(angular velocity), ang acceleration(when applying force), even the orientation quaternion does change, but then normalizing it changes it back to the [color="#1C2837"]w: 0.554958 i: 0.000000 j: 0.000000 k: 0.831878 value...
[color="#1C2837"]o__o maybe my normalizing is incorrect?:
[color="#1C2837"] void Normalize(){ //calcule magnitude: real length = real_sqrt( w*w + i*i + j*j + k*k ); assert( length );//prevent zero lenght quaternion. length = (real)1.0/length; w *= length; i *= length; j *= length; k *= length; } 

[color="#1C2837"]Here are the quaternion values just before being normalized, at the limit situation:
[color="#1C2837"]w = 0.645522 i = 0 j = 0 k = 0.967632
[color="#1C2837"]w = 0.647816 i = 0 j = 0 k = 0.97107..
[color="#1C2837"]..
[color="#1C2837"]This is the values it get before changing back to the stucked one.

[color="#1C2837"]Im logging the quaternion and the rotation(angular velocity) every time I appply force on it(I can log anything btw):
[color="#1C2837"]-applying left force-
[color="#1C2837"] [color="#1C2837"] LEFT w: 0.554958 i: 0.000000 j: 0.000000 k: 0.831878 rot: 0.000000 0.000000 -40.570995 626: 0.016708 LEFT w: 0.554958 i: 0.000000 j: 0.000000 k: 0.831878 rot: 0.000000 0.000000 -40.709618 627: 0.016706 LEFT w: 0.554958 i: 0.000000 j: 0.000000 k: 0.831878 rot: 0.000000 0.000000 -40.848843 628: 0.016938 LEFT w: 0.554958 i: 0.000000 j: 0.000000 k: 0.831878 rot: 0.000000 0.000000 -40.988049 629: 0.016607 LEFT w: 0.554958 i: 0.000000 j: 0.000000 k: 0.831878 rot: 0.000000 0.000000 -41.129189 630: 0.016708 LEFT w: 0.554958 i: 0.000000 j: 0.000000 k: 0.831878 rot: 0.000000 0.000000 -41.267570 631: 0.016856 LEFT w: 0.554958 i: 0.000000 j: 0.000000 k: 0.831878 rot: 0.000000 0.000000 -41.406796 632: 0.016787 LEFT w: 0.554958 i: 0.000000 j: 0.000000 k: 0.831878 rot: 0.000000 0.000000 -41.547253 633: 0.016527 LEFT w: 0.554958 i: 0.000000 j: 0.000000 k: 0.831878 rot: 0.000000 0.000000 -41.687138 [color="#1C2837"]

[color="#1C2837"]Dont know if it helps..remember also that the orientation stops slowly, not all of sudden..it starts change less and less, untill get at the stucked value:
[color="#1C2837"]-some values before it stuck-
[color="#1C2837"] [color="#1C2837"] LEFT w: 0.554963 i: 0.000000 j: 0.000000 k: 0.831875 rot: 0.000000 0.000000 -14.221514 296: 0.016848 LEFT w: 0.554962 i: 0.000000 j: 0.000000 k: 0.831876 rot: 0.000000 0.000000 -14.360069 WM 297: 0.016727 LEFT w: 0.554961 i: 0.000000 j: 0.000000 k: 0.831876 rot: 0.000000 0.000000 -14.500462 WM 298: 0.016695 LEFT w: 0.554960 i: 0.000000 j: 0.000000 k: 0.831877 rot: 0.000000 0.000000 -14.639845 299: 0.016464 LEFT w: 0.554960 i: 0.000000 j: 0.000000 k: 0.831877 rot: 0.000000 0.000000 -14.778961 WM 300: 0.017114 LEFT w: 0.554960 i: 0.000000 j: 0.000000 k: 0.831877 rot: 0.000000 0.000000 -14.916156 301: 0.016425 LEFT w: 0.554959 i: 0.000000 j: 0.000000 k: 0.831878 rot: 0.000000 0.000000 -15.058761 WM 302: 0.016994 LEFT w: 0.554959 i: 0.000000 j: 0.000000 k: 0.831878 rot: 0.000000 0.000000 -15.195628 303: 0.016452 LEFT w: 0.554959 i: 0.000000 j: 0.000000 k: 0.831878 rot: 0.000000 0.000000 -15.337236 [color="#1C2837"]

[color="#1C2837"][color="#000000"][color="#1C2837"][s]I cant accurate give the values without never normalizing it, cause it doesnt stuck, and the scaling starts sooner than when it gets at the limit situation..[/s]
[color="#1C2837"]-edit-
[color="#1C2837"]My bad..I will just give the values arround the stucked one:
[color="#1C2837"] [color="#1C2837"][size=2] LEFT w: 1.386143 i: 0.000000 j: 0.000000 k: 1.169424 rot: 0.000000 0.000000 -6.261209 143: 0.016483 LEFT w: 1.464514 i: 0.000000 j: 0.000000 k: 1.353969 rot: 0.000000 0.000000 -8.120884 144: 0.016717 LEFT w: 1.590134 i: 0.000000 j: 0.000000 k: 1.659929 rot: 0.000000 0.000000 -11.257738 145: 0.016767 LEFT w: 1.839525 i: 0.000000 j: 0.000000 k: 2.312805 rot: 0.000000 0.000000 -17.974499 146: 0.016622 LEFT w: 2.639032 i: 0.000000 j: 0.000000 k: 4.797811 rot: 0.000000 0.000000 -41.233078 147: 0.016990 LEFT w:19.029388 i: 0.000000 j: 0.000000 k:92.451141 rot: 0.000000 0.000000 -411.037170 148: 0.016562 LEFT w:33866516.000000 i: 0.000000 j: 0.000000 k:3137967104.000000 rot: 0.000000 0.000000 -43121704.000000 149: 0.016763 LEFT w:-1.#IND00 i:-1.#IND00 j:-1.#IND00 k:-1.#IND00 rot: -1.#IND00 -1.#IND00 -1.#INF00 150: 0.030748 LEFT w:-1.#IND00 i:-1.#IND00 j:-1.#IND00 k:-1.#IND00 rot: -1.#IND00 -1.#IND00 -1.#IND00 [color="#1C2837"][size=2]`

Thats was unexpect...it just increases very high all of sudden.. I will see if I can figure out why this happens, but Im showing this now since I alredy posted, my bad..
-edit-
[font=arial, verdana, tahoma, sans-serif][size=2] Ok, its a combination of issues with the transformation matrix, starting with the fact I use its X vector to apply force, but since it is not more an accurate matrix, the force starts getting huge, the calculation of the torque also is innacurate because of it, and then everything exponentially goes bad..
I dont think this helps because when normalizing the quaternion those issues doesnt help..[/font]

[color="#1C2837"]-edit-
[color="#1C2837"]Dx Im getting color and size tags bugging on my post