围绕枢轴点旋转刚体
Posted
技术标签:
【中文标题】围绕枢轴点旋转刚体【英文标题】:Rotating a RigidBody around a pivot point 【发布时间】:2015-09-09 22:26:00 【问题描述】:我正在尝试围绕枢轴点(在本例中为原点)而不是其质心旋转刚体。
我有一个建议应用三个转换:
将刚体转换为原点
围绕质心旋转刚体
从原点变换刚体。
这是我的代码:
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());
【问题讨论】:
您应该将枢轴点转换为原点,然后旋转,然后应用反向转换。 我已经编辑了我的答案。 对我来说,假设 offsetToPivot 最初设置为btVector3(0.0, 2.0, 0.0)
,这两个代码片段的工作方式完全相同。如果它被设置为btVector3(-2.0, 0.0, 0.0);
它显然不会工作,因为你试图围绕它所在的轴旋转点,这当然会把它留在同一个地方。
【参考方案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();
变量轴是一个单位向量,表示您的对象围绕其旋转的轴。它不是它的坐标系。我以前没有注意到这部分。四元数很棘手,您应该阅读更多关于它们的信息,因为很多人都误用了它们。
在连续情况下可行的解决方案是将最后一次平移(从质心到枢轴 - 在我的示例中由 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 设置为相对于质心的位置。
我的印象是,您的问题的主要根源是缺乏对线性代数和基本空间变换的理解。如果您打算继续从事该领域的工作,我强烈建议您阅读该主题。把你的问题写在纸上也很有帮助。
EDIT2.
好的,我已经试过你的代码了:
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 才能正常工作? 感谢您的更新。我了解您现在在做什么,但我仍然认为代码不太正确。我打印出 invTrans ,它似乎和 bt->offsetToPivot; 一样所以由于某种原因 invRot 没有得到正确应用。 @Jaitnium 你知道......我无法在任何地方运行此代码,所以它是从我的脑海中写出来的,我无法检查它。如果您了解该方法,您至少可以尝试自己修复它。你在哪一行打印这些值?你看到btPhys->offsetToPivot = invTrans;
这行了吗?在这条线之后,它们应该是相等的。 RigidBody 的行为是什么?您为offsetToPivot
分配了什么初始值?当您开始旋转时,最初的 WorldTransform
是什么?您是否按照我的建议尝试过使用调试器?
bt->offsetToPivot 与 invTrans 后的行相同:“btVector3 invTrans = invRot * btPhys->offsetToPivot;”行为是刚体仍在其质心上旋转。
@Jaitnium 那么btPhys->offsetToPivot
的初始值是多少?你甚至把它放在任何地方吗?和职位?如果您不提供关键信息,您如何想象有人可以帮助您?实际上我已经检查了这一行,它肯定将invTrans
设置为offsetToPivot
以外的其他值。还有一个问题......你的函数degreeToRads返回什么?【参考方案2】:
在以下函数中,这些转换执行您描述的三个步骤:
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;
即:
第一步:将刚体转换为原点。
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
第 3 步:将刚体从原点转换。
+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)
;
进一步阅读解释其背后的数学原理here。
【讨论】:
那么使用 Bullet 有什么意义呢?顺便提一句。您的转换仅适用于在 2D 平面中旋转Point
,而问题是围绕 3D 轴旋转 Rigid Body
。它比这复杂一点。即使你将它缩放到 3D 空间(这很痛苦),你仍然只计算身体的位置,但是方向在哪里?
@Estiny 你是对的。我会更新答案以符合问题要求,抱歉!以上是关于围绕枢轴点旋转刚体的主要内容,如果未能解决你的问题,请参考以下文章