Problem with object rotation

Post Reply
yang
Posts: 11
Joined: Thu Oct 30, 2014 2:40 pm

Problem with object rotation

Post by yang »

Hi,everyone.
I want to rotate a rigid object around x or y or z axis each time so that I can adjust its orientation to what I want it to be.
I am using the code bellow to set the euler angle of the object, and it seems that the value of angx, angy,angz must be [-PI/2, PI/2], if exceeded,the rotation becomes strange. I want to rotate it from[0,2PI],which means [0,360]degrees.
Can anyone tell me how to fix this?

Code: Select all

btTransform tranf =  cube->getCenterOfMassTransform();
btQuaternion newQrot;
S3float lastAng;
tranf.getBasis().getEulerZYX(lastAng.z,lastAng.y,lastAng.x,1);
 if(xrotation)
    newQrot.setEulerZYX(lastAng.z,lastAng.y,angx);
 else if(yrotation)
    newQrot.setEulerZYX(lastAng.z,angy,lastAng.x);
 else if(zrotation)
    newQrot.setEulerZYX(angz,lastAng.y,lastAng.x);
tranf.setRotation(newQrot);
cube->setCenterOfMassTransform(tranf);
Basroil
Posts: 463
Joined: Fri Nov 30, 2012 4:50 am

Re: Problem with object rotation

Post by Basroil »

yang wrote:I am using the code bellow to set the euler angle of the object, and it seems that the value of angx, angy,angz must be [-PI/2, PI/2], if exceeded,the rotation becomes strange. I want to rotate it from[0,2PI],which means [0,360]degrees.
Can anyone tell me how to fix this?
You can fix it by fixing your definitions! 0-2PI is equivalent to 0-PI for angles less than PI and -PI-0 for >PI. You should also be able to define rotations in straight quaternions (rather than euler definitions shifted into quaternions), which should prevent issues.
yang
Posts: 11
Joined: Thu Oct 30, 2014 2:40 pm

Re: Problem with object rotation

Post by yang »

Basroil wrote:
yang wrote:I am using the code bellow to set the euler angle of the object, and it seems that the value of angx, angy,angz must be [-PI/2, PI/2], if exceeded,the rotation becomes strange. I want to rotate it from[0,2PI],which means [0,360]degrees.
Can anyone tell me how to fix this?
You can fix it by fixing your definitions! 0-2PI is equivalent to 0-PI for angles less than PI and -PI-0 for >PI. You should also be able to define rotations in straight quaternions (rather than euler definitions shifted into quaternions), which should prevent issues.
Hi,thank you for your replying.
I have changed the definition of quaternion like this.

Code: Select all

if(xrotation)
        newQrot = btQuaternion(lastAng.y,angx,lastAng.z);//y,x,z
    //newQrot.setEulerZYX(lastAng.z,lastAng.y,ax);
    else if(yrotation)
        newQrot = btQuaternion(angy,lastAng.x,lastAng.z);//y,x,z
    //newQrot.setEulerZYX(lastAng.z,ay,lastAng.x);
    else if(zrotation)
        newQrot = btQuaternion(lastAng.y,lastAng.x,az);//y,x,z
    //newQrot.setEulerZYX(angz,lastAng.y,lastAng.x);
But it doesn't work well either.
What else should I do?
User avatar
drleviathan
Posts: 849
Joined: Tue Sep 30, 2014 6:03 pm
Location: San Francisco

Re: Problem with object rotation

Post by drleviathan »

My advice would be to NOT use Euler angles for incremental rotation adjustments. Euler angles have several problems that make it hard get the rotation you want by adjusting the input angles:

(1) Rotations don't commute so the consequences of adjustments are seldom obvious.

(2) Euler angles suffer from gimbal lock where there is a singularity of Eulerian solutions. At the singularity the decomposition of the rotation into Euler angles is non-linear and small rotation adjustments can have very large consequences on the resulting angles.

(3) There is more than one way to apply the Euler angles. The way you're using them is probably not the way Euler himself was using them.

What I think you should do is learn how to use incremental quaternions.

Given some rotation Q1 you can rotate the body about any of its local axes by some dQ and combine the two rotations to get the new final one:

Q2 = dQ * Q1

For example, suppose pitch, yaw, and roll of your object correspond to right-handed rotations about X, Y, and Z respectively you could add a bit of any of these rotations to your current rotation. If you wanted to roll a bit:

Code: Select all

    float deltaRoll = PI/10.0f;
    body->setRotation(btQuaternion(zAxis, deltaRoll) * body->getRotation());
yang
Posts: 11
Joined: Thu Oct 30, 2014 2:40 pm

Re: Problem with object rotation

Post by yang »

drleviathan wrote:My advice would be to NOT use Euler angles for incremental rotation adjustments. Euler angles have several problems that make it hard get the rotation you want by adjusting the input angles:

(1) Rotations don't commute so the consequences of adjustments are seldom obvious.

(2) Euler angles suffer from gimbal lock where there is a singularity of Eulerian solutions. At the singularity the decomposition of the rotation into Euler angles is non-linear and small rotation adjustments can have very large consequences on the resulting angles.

(3) There is more than one way to apply the Euler angles. The way you're using them is probably not the way Euler himself was using them.

What I think you should do is learn how to use incremental quaternions.

Given some rotation Q1 you can rotate the body about any of its local axes by some dQ and combine the two rotations to get the new final one:

Q2 = dQ * Q1

For example, suppose pitch, yaw, and roll of your object correspond to right-handed rotations about X, Y, and Z respectively you could add a bit of any of these rotations to your current rotation. If you wanted to roll a bit:

Code: Select all

    float deltaRoll = PI/10.0f;
    body->setRotation(btQuaternion(zAxis, deltaRoll) * body->getRotation());
It works well by using your method.
Thanks
yang
Posts: 11
Joined: Thu Oct 30, 2014 2:40 pm

Re: Problem with object rotation

Post by yang »

drleviathan wrote:My advice would be to NOT use Euler angles for incremental rotation adjustments. Euler angles have several problems that make it hard get the rotation you want by adjusting the input angles:

(1) Rotations don't commute so the consequences of adjustments are seldom obvious.

(2) Euler angles suffer from gimbal lock where there is a singularity of Eulerian solutions. At the singularity the decomposition of the rotation into Euler angles is non-linear and small rotation adjustments can have very large consequences on the resulting angles.

(3) There is more than one way to apply the Euler angles. The way you're using them is probably not the way Euler himself was using them.

What I think you should do is learn how to use incremental quaternions.

Given some rotation Q1 you can rotate the body about any of its local axes by some dQ and combine the two rotations to get the new final one:

Q2 = dQ * Q1

For example, suppose pitch, yaw, and roll of your object correspond to right-handed rotations about X, Y, and Z respectively you could add a bit of any of these rotations to your current rotation. If you wanted to roll a bit:

Code: Select all

    float deltaRoll = PI/10.0f;
    body->setRotation(btQuaternion(zAxis, deltaRoll) * body->getRotation());
I am sorry but I have another question.
What am I supposed to do if I want to rotate the object around x,y,z at the same time.
the code would be:

Code: Select all

btQuaternion qx(btVector3(1,0,0),angX);
btQuaternion qy(btVector3(0,1,0),angY);
btQuaternion qz(btVector3(0,0,1),angZ);
tranf.setRotation(qz * qy * qx * body->getRotation());
but this time it does not work well. Can you tell me how to fix this?
User avatar
drleviathan
Posts: 849
Joined: Tue Sep 30, 2014 6:03 pm
Location: San Francisco

Re: Problem with object rotation

Post by drleviathan »

As I mentioned: in general rotations don't commute, which means rotating first by Q1 and then by Q2 does not necessarily produce the same result as first rotating by Q2 and then by Q1. This means the order of qz * qy * qx matters very much.

The order that the rotations are applied, as you wrote them, is as follows: first an angle around the object's local xAxis, then an angle about the object's local yAxis, and finally a angle about its local zAxis. The rotations operate from the left on the vector, and the nearest rotation to the vector operates first.

If you know the three angles and you know that they should be applied in the order you've written them then it should Just Work. Since it isn't working we must conclude that the angles are not actually known, or the order is wrong. Since I don't know where you got your angles or ordering I can't know how to fix them. You'll have to supply more info. Specifically, what are you really trying to do? What object are you trying to rotate? How do you know the final rotation that you want to achieve?
yang
Posts: 11
Joined: Thu Oct 30, 2014 2:40 pm

Re: Problem with object rotation

Post by yang »

drleviathan wrote:As I mentioned: in general rotations don't commute, which means rotating first by Q1 and then by Q2 does not necessarily produce the same result as first rotating by Q2 and then by Q1. This means the order of qz * qy * qx matters very much.

The order that the rotations are applied, as you wrote them, is as follows: first an angle around the object's local xAxis, then an angle about the object's local yAxis, and finally a angle about its local zAxis. The rotations operate from the left on the vector, and the nearest rotation to the vector operates first.

If you know the three angles and you know that they should be applied in the order you've written them then it should Just Work. Since it isn't working we must conclude that the angles are not actually known, or the order is wrong. Since I don't know where you got your angles or ordering I can't know how to fix them. You'll have to supply more info. Specifically, what are you really trying to do? What object are you trying to rotate? How do you know the final rotation that you want to achieve?
Thank you again.
What I want to do is rotate a object(say,a cube)using free-hand.The data of two hands are from Kinect depth data. Here is the full function of object rotation which works well when rotates object around one of x,y,z axis each time. The axis will be selected by calculate the movement of two hands.

Code: Select all

void CGLPainter::BulletObjRotation(CVirtualObj **_obj)
{
	if(_obj[0] == NULL) return;

	S3float hand0(m_handInfo[0].x,m_handInfo[0].y,m_handInfo[0].z);
	S3float hand1(m_handInfo[1].x,m_handInfo[1].y,m_handInfo[1].z);

	int objNum = GetObjNum();
	for (int idx = 0; idx < objNum; idx++)
	{
		S3float mov;
		btVector3 pos = _obj[idx]->mp_btRidObj->getCenterOfMassPosition();
		btTransform tranf =  _obj[idx]->mp_btRidObj->getCenterOfMassTransform();
		S3float center(pos[0],pos[1],pos[2]);

		if (!_obj[idx]->m_xrot && !_obj[idx]->m_yrot && !_obj[idx]->m_zrot)
		{
			tranf.getBasis().getRotation(_obj[idx]->m_lastRot);
		}
		if (m_handInfo[0].status !=LOST && m_handInfo[1].status !=LOST 
			&& GetDisOf2Pts(hand0,center) < 180 
			&& GetDisOf2Pts(hand1,center) < 180)
		{
			btVector3 newpos = btVector3(
				(m_handInfo[0].x + m_handInfo[1].x)/2.f,
				(m_handInfo[0].y + m_handInfo[1].y)/2.f,
				(m_handInfo[0].z + m_handInfo[1].z)/2.f
				);
			_obj[idx]->mp_btRidObj->setGravity(btVector3(0,0,0));
			_obj[idx]->mp_btRidObj->setLinearVelocity(btVector3(0,0,0));
			_obj[idx]->mp_btRidObj->setAngularVelocity(btVector3(0,0,0));
			mov.x = abs(m_handDisplacement[0].x)+ abs(m_handDisplacement[1].x);
			mov.y = abs(m_handDisplacement[0].y)+ abs(m_handDisplacement[1].y);
			mov.z = abs(m_handDisplacement[0].z)+ abs(m_handDisplacement[1].z);
			float thr = 25;

			if ( !_obj[idx]->m_xrot && !_obj[idx]->m_yrot && !_obj[idx]->m_zrot)
			{
				DrawCircle(center,110,'n');

				if (mov.x < mov.y && mov.x < mov.z)
				{
					if(mov.y + mov.z>thr+10)
					{
						_obj[idx]->m_lastAng.x = -1*atan2( (m_handInfo[0].y - m_handInfo[1].y),
							(m_handInfo[0].z - m_handInfo[1].z));
						_obj[idx]->m_xrot = true;
						_obj[idx]->m_yrot = _obj[idx]->m_zrot = false;
					}
				}
				else if (mov.y < mov.x && mov.y < mov.z)
				{
					if(mov.x + mov.z>thr)
					{
						_obj[idx]->m_lastAng.y = -1*atan2( (m_handInfo[0].z - m_handInfo[1].z),
							(m_handInfo[0].x - m_handInfo[1].x));
						_obj[idx]->m_yrot = true;
						_obj[idx]->m_xrot = _obj[idx]->m_zrot = false;
					}
				}

				else if (mov.z < mov.x && mov.z < mov.y)
				{
					if(mov.x + mov.y>thr)
					{
						_obj[idx]->m_lastAng.z = -1*atan2( (m_handInfo[0].x - m_handInfo[1].x),
							(m_handInfo[0].y - m_handInfo[1].y));
						_obj[idx]->m_zrot = true;
						_obj[idx]->m_xrot = _obj[idx]->m_yrot = false;
					}
				}
			}

			else //in rotation
			{
				float angX = -1*atan2( (m_handInfo[0].y - m_handInfo[1].y),
					(m_handInfo[0].z - m_handInfo[1].z));
				float angY = -1*atan2( (m_handInfo[0].z - m_handInfo[1].z),
					(m_handInfo[0].x - m_handInfo[1].x));
				float angZ = -1*atan2( (m_handInfo[0].x - m_handInfo[1].x),
					(m_handInfo[0].y - m_handInfo[1].y));

				btQuaternion qx(btVector3(1,0,0),angX - _obj[idx]->m_lastAng.x);
				btQuaternion qy(btVector3(0,1,0),angY - _obj[idx]->m_lastAng.y);
				btQuaternion qz(btVector3(0,0,1),angZ - _obj[idx]->m_lastAng.z);

				if (_obj[idx]->m_xrot)
				{
					DrawCircle(center,110,'x');
					tranf.setRotation(qx * _obj[idx]->m_lastRot);
					if(mov.y + mov.z <1.3 && mov.y + mov.z !=0) 
						_obj[idx]->m_xrot = false;
				}
				else if (_obj[idx]->m_yrot)
				{
					DrawCircle(center,110,'y');
					tranf.setRotation(qy * _obj[idx]->m_lastRot);
					if(mov.x + mov.z<1.3 && mov.x + mov.z !=0) 
						_obj[idx]->m_yrot = false;
				}
				else if (_obj[idx]->m_zrot)
				{
					DrawCircle(center,110,'z');
					tranf.setRotation(qz * _obj[idx]->m_lastRot);
					if(mov.x + mov.y<1.3 && mov.x + mov.y != 0) 
						_obj[idx]->m_zrot = false;
				}
			}
			tranf.setOrigin(newpos);
			_obj[idx]->mp_btRidObj->setCenterOfMassTransform(tranf);
		}

		//release
		if ( (m_handInfo[0].status ==LOST && m_handInfo[1].status ==LOST) 
			|| GetDisOf2Pts(hand0,center) > 180 
			|| GetDisOf2Pts(hand1,center) > 180)
		{
			_obj[idx]->mp_btRidObj->activate();
			_obj[idx]->mp_btRidObj->setGravity(btVector3(0,m_gravity,0));
			_obj[idx]->m_xrot = _obj[idx]->m_yrot = _obj[idx]->m_zrot = false;
		}
	}
}
Also the code below is what I am trying to do and it does not work as I expected.

Code: Select all

void CGLPainter::BulletObjRotation3(CVirtualObj **_obj)
{
	if(_obj[0] == NULL) return;

	S3float hand0(m_handInfo[0].x,m_handInfo[0].y,m_handInfo[0].z);
	S3float hand1(m_handInfo[1].x,m_handInfo[1].y,m_handInfo[1].z);

	int objNum = GetObjNum();
	for (int idx = 0; idx < objNum; idx++)
	{
		S3float mov;
		btVector3 pos = _obj[idx]->mp_btRidObj->getCenterOfMassPosition();
		btTransform tranf =  _obj[idx]->mp_btRidObj->getCenterOfMassTransform();
		S3float center(pos[0],pos[1],pos[2]);

		if (m_handInfo[0].status !=LOST && m_handInfo[1].status !=LOST 
			&& GetDisOf2Pts(hand0,center) < 180 
			&& GetDisOf2Pts(hand1,center) < 180)
		{
			btVector3 newpos = btVector3(
				(m_handInfo[0].x + m_handInfo[1].x)/2.f,
				(m_handInfo[0].y + m_handInfo[1].y)/2.f,
				(m_handInfo[0].z + m_handInfo[1].z)/2.f
				);

			_obj[idx]->mp_btRidObj->setGravity(btVector3(0,0,0));
			_obj[idx]->mp_btRidObj->setLinearVelocity(btVector3(0,0,0));
			_obj[idx]->mp_btRidObj->setAngularVelocity(btVector3(0,0,0));
			mov.x = abs(m_handDisplacement[0].x)+ abs(m_handDisplacement[1].x);
			mov.y = abs(m_handDisplacement[0].y)+ abs(m_handDisplacement[1].y);
			mov.z = abs(m_handDisplacement[0].z)+ abs(m_handDisplacement[1].z);
			float thr = 20;

			if ( !_obj[idx]->m_xrot && !_obj[idx]->m_yrot && !_obj[idx]->m_zrot)
			{
				DrawCircle(center,110,'n');
				tranf.getBasis().getRotation(_obj[idx]->m_lastRot);

				if (mov.x < mov.y && mov.x < mov.z)
				{
					if(mov.y + mov.z>thr+10)
					{
						_obj[idx]->m_lastAng.x = -1*atan2( (m_handInfo[0].y - m_handInfo[1].y),
							(m_handInfo[0].z - m_handInfo[1].z));
						_obj[idx]->m_lastAng.y = -1*atan2( (m_handInfo[0].z - m_handInfo[1].z),
							(m_handInfo[0].x - m_handInfo[1].x));
						_obj[idx]->m_lastAng.z = -1*atan2( (m_handInfo[0].x - m_handInfo[1].x),
							(m_handInfo[0].y - m_handInfo[1].y));
						_obj[idx]->m_xrot = true;
						_obj[idx]->m_yrot = _obj[idx]->m_zrot = false;
					}
				}
				else if (mov.y < mov.x && mov.y < mov.z)
				{
					if(mov.x + mov.z>thr)
					{
						_obj[idx]->m_lastAng.x = -1*atan2( (m_handInfo[0].y - m_handInfo[1].y),
							(m_handInfo[0].z - m_handInfo[1].z));
						_obj[idx]->m_lastAng.y = -1*atan2( (m_handInfo[0].z - m_handInfo[1].z),
							(m_handInfo[0].x - m_handInfo[1].x));
						_obj[idx]->m_lastAng.z = -1*atan2( (m_handInfo[0].x - m_handInfo[1].x),
							(m_handInfo[0].y - m_handInfo[1].y));
						_obj[idx]->m_yrot = true;
						_obj[idx]->m_xrot = _obj[idx]->m_zrot = false;
					}
				}

				else if (mov.z < mov.x && mov.z < mov.y)
				{
					if(mov.x + mov.y>thr)
					{
						_obj[idx]->m_lastAng.x = -1*atan2( (m_handInfo[0].y - m_handInfo[1].y),
							(m_handInfo[0].z - m_handInfo[1].z));
						_obj[idx]->m_lastAng.y = -1*atan2( (m_handInfo[0].z - m_handInfo[1].z),
							(m_handInfo[0].x - m_handInfo[1].x));
						_obj[idx]->m_lastAng.z = -1*atan2( (m_handInfo[0].x - m_handInfo[1].x),
							(m_handInfo[0].y - m_handInfo[1].y));
						_obj[idx]->m_zrot = true;
						_obj[idx]->m_xrot = _obj[idx]->m_yrot = false;
					}
				}
			}

			else //in rotation
			{
				float angX = -1*atan2( (m_handInfo[0].y - m_handInfo[1].y),
					(m_handInfo[0].z - m_handInfo[1].z));
				float angY = -1*atan2( (m_handInfo[0].z - m_handInfo[1].z),
					(m_handInfo[0].x - m_handInfo[1].x));
				float angZ = -1*atan2( (m_handInfo[0].x - m_handInfo[1].x),
					(m_handInfo[0].y - m_handInfo[1].y));

				btQuaternion qx(btVector3(1,0,0),angX - _obj[idx]->m_lastAng.x);
				btQuaternion qy(btVector3(0,1,0),angY - _obj[idx]->m_lastAng.y);
				btQuaternion qz(btVector3(0,0,1),angZ - _obj[idx]->m_lastAng.z);
				DrawCircle(center,110,'x');
				DrawCircle(center,110,'y');
				DrawCircle(center,110,'z');

				tranf.setRotation(qz * qy * qx *_obj[idx]->m_lastRot);
				if(mov.y + mov.z < 2 && mov.y + mov.z !=0) 
					_obj[idx]->m_xrot = false;
				if(mov.x + mov.z<2 && mov.x + mov.z !=0) 
					_obj[idx]->m_yrot = false;
				if (mov.x + mov.y<2 && mov.x + mov.y != 0) 
					_obj[idx]->m_zrot = false;
			}

			tranf.setOrigin(newpos);
			_obj[idx]->mp_btRidObj->setCenterOfMassTransform(tranf);
		}


		//release
		if ( (m_handInfo[0].status ==LOST && m_handInfo[1].status ==LOST) 
			|| GetDisOf2Pts(hand0,center) > 180 
			|| GetDisOf2Pts(hand1,center) > 180)
		{
			_obj[idx]->mp_btRidObj->activate();
			_obj[idx]->mp_btRidObj->setGravity(btVector3(0,m_gravity,0));
			_obj[idx]->m_xrot = _obj[idx]->m_yrot = _obj[idx]->m_zrot = false;
		}
	}
}
I would really appreciate it if you could help me with this problem.
User avatar
drleviathan
Posts: 849
Joined: Tue Sep 30, 2014 6:03 pm
Location: San Francisco

Re: Problem with object rotation

Post by drleviathan »

If I understand the code correctly, you take the two hand positions (m_handInfo[0] and m_handInfo[1]), compute the difference between them, and use the components of the difference to scale the Eulerian angles.

So this would mean:

hands together = object not rotated
hands separate along z-axis (forward-back?) = object rotates about its local zAxis
hands separate along y-axis (vertical?) = object rotates about its local yAxis
hands separate along x-axis (left-right?) = object rotates about its local xAxis

That kind of mapping might make sense for rotations about one axis (other two axes being ignored), but its just not going to work for arbitrary rotations. To get something intuitive you're going to have to discard it.

My advice would be to use an imaginary line between the two hands and measure rotations of that line to apply incremental rotations on the object. The pseudo-code would look something like this:

Code: Select all

    // this is pesudo-code
    newLine = (m_handInfo[0] - m_handInfo[1]).normalize();
    btQuaternion dQ = rotationBetween(oldLine, newLine);
    object.setRotation(dQ * object.getRotation());
    oldLine = newLine;
This would make it possible to "roll" the object around as if you were holding it. The initial imaginary line would be locked into the object when it is first created and as you roll the line you would similarly roll the object.
yang
Posts: 11
Joined: Thu Oct 30, 2014 2:40 pm

Re: Problem with object rotation

Post by yang »

drleviathan wrote:If I understand the code correctly, you take the two hand positions (m_handInfo[0] and m_handInfo[1]), compute the difference between them, and use the components of the difference to scale the Eulerian angles.

So this would mean:

hands together = object not rotated
hands separate along z-axis (forward-back?) = object rotates about its local zAxis
hands separate along y-axis (vertical?) = object rotates about its local yAxis
hands separate along x-axis (left-right?) = object rotates about its local xAxis

That kind of mapping might make sense for rotations about one axis (other two axes being ignored), but its just not going to work for arbitrary rotations. To get something intuitive you're going to have to discard it.

My advice would be to use an imaginary line between the two hands and measure rotations of that line to apply incremental rotations on the object. The pseudo-code would look something like this:

Code: Select all

    // this is pesudo-code
    newLine = (m_handInfo[0] - m_handInfo[1]).normalize();
    btQuaternion dQ = rotationBetween(oldLine, newLine);
    object.setRotation(dQ * object.getRotation());
    oldLine = newLine;
This would make it possible to "roll" the object around as if you were holding it. The initial imaginary line would be locked into the object when it is first created and as you roll the line you would similarly roll the object.
In fact,you totally understood what I was trying to do. I tried it based on your pesudo-code and it finally works as what I want.
Thank you very much. I hope you can help me again if I have further questions. :D
Post Reply