绕轴点旋转刚体

3

我正在尝试围绕一个枢轴点(在这种情况下是原点)旋转刚体,而不是其质心。

有人建议应用三个变换:

  1. 将刚体变换到原点

  2. 以其质心为中心旋转刚体

  3. 将刚体从原点移开。

以下是我的代码:

btMatrix3x3 orn = btPhys->getWorldTransform().getBasis();   
btQuaternion quat;
orn.getRotation(quat);
btVector3 axis = quat.getAxis();

//Move rigidbody 2 units along its axis to the origin
btPhys->translate(btVector3(-2.0 * axis.getX(), 0.0, -2.0 * axis.getZ()));

//Rotate the rigidbody by 1 degree on its center of mass
orn *= btMatrix3x3(btQuaternion( btVector3(1, 0, 0), btScalar(degreesToRads(-1))));
btPhys->getWorldTransform().setBasis(orn);  

//Update axis variable to apply transform on
orn.getRotation(quat);
axis = quat.getAxis();

//Move the rigidbody 2 units along new axis
btPhys->translate(btVector3(2.0 * axis.getX(), 0.0, 2.0 * axis.getZ())); 

然而,枢轴点似乎在移动,而不是停留在原地(原点)。有没有更好的方法(实际上有效)可以围绕一个枢轴点旋转刚体?
编辑: 我添加了一些用于旋转函数的合理性检查代码:
//Code that doesn't work
btVector3 invTrans = btPhys->offsetToPivot.rotate(btVector3(1.0, 0.0, 0.0), btScalar(degreesToRads(-1)));
//Values printed out are identical to offsetToPivot
printf("invTrans: %f %f %f\n", invTrans.getX(), invTrans.getY(), invTrans.getZ());

//Sanity code that DOES work
//Arbitrary vector
btVector3 temp = btVector3(0.0, 2.0, 0.0);
temp = temp.rotate(btVector3(1.0, 0.0, 0.0), btScalar(degreesToRads(-1)));
printf("temp %f %f %f\n", temp.getX(), temp.getY(), temp.getZ());

1
你应该先将枢轴点转换为原点,然后旋转,最后应用反向变换。 - paddy
我已经编辑了我的答案。 - Estiny
对我来说,假设 offsetToPivot 最初设置为 btVector3(0.0, 2.0, 0.0),那么这两个代码片段的作用完全相同。如果它被设置为 btVector3(-2.0, 0.0, 0.0),显然不会起作用,因为您尝试围绕它所在的轴旋转点,这当然会使其停留在原地。 - Estiny
2个回答

1
在下面的函数中,这些转换执行了您描述的三个步骤:
int x = cos(angRads) * (initial.x - axisOfRotation.x) - sin(angRads) * (initial.y - axisOfRotation.y) + axisOfRotation.x;      
int y = sin(angRads) * (initial.x - axisOfRotation.x) + cos(angRads) * (initial.y - axisOfRotation.y) + axisOfRotation.y;

即:
步骤1:将刚体转换到原点。
    initial.x - axisOfRotation.x
    initial.y - axisOfRotation.y

步骤2:以刚体的质心为中心旋转。
    cos(angRads) * initial.x - sin(angRads) * initial.y
    sin(angRads) * initial.x + cos(angRads) * initial.y 

步骤三:将刚体从原点移开。
    +axisOfRotation.x;
    +axisOfRotation.y;

这里有一个递归函数,可以完美地执行你所需的操作,并返回向量中所有连续旋转的点:(将其用作基准)
rotateCoordinate(vector<Point>& rotated, Point& axisOfRotation, Point initial, 
                            float angRads, int numberOfRotations){
    // base case: when all rotations performed return vector holding the rotated points
    if(numberOfRotations <= 0) return;
    else{
        // apply transformation on the initial point
        int x = cos(angRads) * (initial.x - axisOfRotation.x) - sin(angRads) * (initial.y - axisOfRotation.y) + axisOfRotation.x;
        int y = sin(angRads) * (initial.x - axisOfRotation.x) + cos(angRads) * (initial.y - axisOfRotation.y) + axisOfRotation.y;
        // save the result
        rotated.push_back(Point(x, y));
        // call the same function this time on the rotated point and decremented number of rotations
        rotateCoordinate(rotated, axisOfRotation, Point(x,y), angRads, numberOfRotations -1);
    }
}

其中Point是:


struct Point {
    int x, y;
    Point(int xx, int yy) : x(xx), y(yy) { }
    Point() :x(0), y(0) { }
};

如果想要了解更多有关数学原理的内容,请点击这里


那么使用Bullet的意义是什么呢?顺便说一下,您的变换只能用于旋转2D平面中的“点”,而问题是要围绕3D轴旋转“刚体”。这比那复杂一些。即使将其缩放到3D空间(这很痛苦),您仍然只计算身体的位置,但方向在哪里呢? - Estiny
@Estiny 你说得对。我会更新答案以符合问题要求,抱歉! - Ziezi

1

这个方法实际上是有效的,只是你没有正确地应用它。你的第二次翻译是沿着世界轴执行的,但你已经旋转了对象,所以你必须沿着旋转后的向量将其移回。

正确的代码应该看起来差不多像这样:

btMatrix3x3 orn = btPhys->getWorldTransform().getBasis();   
btQuaternion quat;
orn.getRotation(quat);
btVector3 axis = quat.getAxis();

//Move rigidbody 2 units along its axis to the origin
btPhys->translate(btVector3(-2.0 * axis.getX(), 0.0, -2.0 * axis.getZ()));

//Rotate the rigidbody by 1 degree on its center of mass
orn *= btMatrix3x3(btQuaternion( btVector3(1, 0, 0), btScalar(degreesToRads(-1))));
btPhys->getWorldTransform().setBasis(orn);  

//Get rotation matrix
btTransform invRot(btQuaternion(btVector3(1, 0, 0), btScalar(degreesToRads(-1))),btVector3(0,0,0));
//Rotate your first translation vector with the matrix
btVector3 invTrans(-2.0 * axis.getX(), 0.0, -2.0 * axis.getZ());
invTrans = invRot * invTrans;

//Update axis variable to apply transform on
orn.getRotation(quat);
axis = quat.getAxis();

//Translate back by rotated vector
btPhys->translate(-invTrans); 

我不确定旋转是否应该是负数(我现在无法检查),但你可以轻松尝试两种方法。

编辑。

好的,所以你忘记提到你执行连续旋转而不是单个旋转。这个过程对于围绕中心点进行单次旋转(例如30度旋转)是正确的。我再次查看了你的代码,并理解你试图沿本地x和z轴执行第一次平移。然而,这不是发生的事情。在这行代码中:

btVector3 axis = quat.getAxis();

变量axis是一个单位向量,表示围绕其旋转的轴线。它不是坐标系。我之前没有注意到这一点。四元数很棘手,你应该多了解一些关于它们的知识,因为许多人误用它们。

在连续情况下可行的解决方案是将最后一次平移(从质心到枢轴 - 在我的示例中由invTrans表示)存储在对象中,并使用它执行第一次平移,然后以相同的方式旋转它,并使用它移动到正确的位置。

更正后的代码如下:

btMatrix3x3 orn = btPhys->getWorldTransform().getBasis();   
btQuaternion quat;
orn.getRotation(quat);

//Move rigidbody 2 units along its axis to the origin
btPhys->translate(btPhys->offsetToPivot);

//Rotate the rigidbody by 1 degree on its center of mass
orn *= btMatrix3x3(btQuaternion( btVector3(1, 0, 0), btScalar(degreesToRads(-1))));
btPhys->getWorldTransform().setBasis(orn);  

//Get rotation matrix
btTransform invRot(btQuaternion(btVector3(1, 0, 0), btScalar(degreesToRads(-1))),btVector3(0,0,0));
//Rotate your first translation vector with the matrix
btVector3 invTrans = invRot * btPhys->offsetToPivot;

//Update axis variable to apply transform on
orn.getRotation(quat);
axis = quat.getAxis();

//Translate back by rotated vector
btPhys->translate(-invTrans); 
btPhys->offsetToPivot = invTrans;

在开始整个过程之前,您必须将offsetToPivot设置为相对于质心的位置。

我有一个印象,你问题的主要来源是缺乏线性代数和基本空间变换的理解。如果您计划继续从事这个领域,我强烈建议您深入了解这个主题。此外,在纸上画出您的问题真的很有帮助。

编辑2。

好的,我已经尝试了您的代码:

btVector3 temp = vec3(0,2,0);
btTransform invRot(btQuaternion(btVector3(1, 0, 0), btScalar(-0.017453f)),btVector3(0,0,0));
temp = invRot * temp;

在此之后,temp 等于 {0.000000000,1.99969542,-0.0349042267}


谢谢您的回复,但我不太确定您的意思。invRot不应该是应用于刚体的相同旋转(现在有一个btVector3)吗?我尝试使用这段代码,但它会导致刚体在上下移动时绕其质心旋转。这个刚体必须高度为0才能工作吗? - Jaitnium
谢谢你的更新。我现在明白你在做什么,但我仍然认为代码不太对。我打印了invTrans,它似乎与bt->offsetToPivot相同;所以由于某种原因invRot没有被正确应用。 - Jaitnium
bt->offsetToPivot在这行代码之后与invTrans相同: "btVector3 invTrans = invRot * btPhys->offsetToPivot;" 行为是刚体仍然在其质心上旋转。 - Jaitnium
@Jaitnium 那么 btPhys->offsetToPivot 的初始值是多少呢?你有在任何地方设置它吗?还有位置呢?如果你不提供关键信息,你怎么想象别人能帮助你呢?实际上,我已经检查了这行代码,它肯定会将 invTrans 设置为与 offsetToPivot 不同的值。还有一个问题...你的函数 degreesToRads 返回什么? - Estiny
@Jaitnium,你能把这段代码放到你的问题里吗?这样我就完全看不到它了... - Estiny
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接