Jump to content
  • Advertisement
Sign in to follow this  
Icebone1000

quaternion as orientation problem

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

If you intended to correct an error in the post then please contact us.

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 this post


Link to post
Share on other sites
Advertisement

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 this post


Link to post
Share on other sites
initial orientation:
initial.png
after apply a force, the ship starts rotate:
rotatingfine.png
until get like this(or in the other direction(to the right)):
stucked.png


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 this post


Link to post
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 this post


Link to post
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 this post


Link to post
Share on other sites
orientation.AddScaledVector( rotation, duration_p );[/quote]
What does that line do? And what are the types involved?

Share this post


Link to post
Share on other sites

orientation.AddScaledVector( rotation, duration_p );

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 this post


Link to post
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 this post


Link to post
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

Share this post


Link to post
Share on other sites
¦D~~

You was right(Why did you give up!?) Theres a hidden stupidness on my addscaledvector function..this tiny little thing "*="...I dont know exactly the error, but I just copy and pasted one code over mine on that operator and it seems to be work just fine!
Thanks for the help

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!