【问题标题】:Vector rotation problem矢量旋转问题
【发布时间】:2011-09-26 05:49:49
【问题描述】:

我正在使用 IK 开发一个程序,并遇到了我起初认为是一个微不足道的问题,但后来却遇到了麻烦。

背景:

一切都在 3d 空间中。 我正在使用 3d 矢量和四元数来表示变换。

我有一个肢体,我们称之为 V1。 我想将它旋转到 V2 上。

我得到了 V1 和 V2 之间的角度。 然后V1旋转的轴穿过V2。

然后从轴和角度制作一个四元数。

然后我将四肢的当前方向乘以轴角四元数。

我相信这是我想要的肢体局部空间。

此肢体连接到一系列其他链接。为了获得世界空间,我将父本地空间与子本地空间相结合,直到到达根。

如果我要旋转到的向量包含在 X 和 Y 平面内,或者如果肢体连接到的身体没有被修改,这似乎很有效。如果修改了任何内容,例如旋转根节点,则在第一次迭代时,向量将旋转得非常接近所需向量。在那之后,虽然它会开始在整个地方旋转并且永远不会达到目标。

我已经逐行检查了所有的数学运算,并且似乎都是正确的。我不确定是否有一些我不知道或只是在看的东西。我的逻辑合理吗?还是我不知道什么?非常感谢任何帮助!

Quaternion::Quaternion(const Vector& axis, const float angle)
{
float sin_half_angle = sinf( angle / 2 );

v.set_x( axis.get_x() * sin_half_angle );
v.set_y( axis.get_y() * sin_half_angle );
v.set_z( axis.get_z() * sin_half_angle );

w = cosf( angle / 2 );
}

Quaternion Quaternion::operator* (const Quaternion& quat) const
{

Quaternion result;

Vector v1( this->v );
Vector v2( quat.v  );

float s1 = this->w;
float s2 = quat.w;

result.w  = s1 * s2 - v1.Dot(v2);
result.v  = v2 * s1 + v1 * s2 + v1.Cross(v2); 

result.Normalize();

return result;
}

Vector Quaternion::operator* (const Vector& vec) const
{

Quaternion quat_vec(vec.get_x(), vec.get_y(), vec.get_z(), 0.0f);
Quaternion rotation( *this );

Quaternion rotated_vec = rotation * ( quat_vec * rotation.Conjugate() ); 

return rotated_vec.v;
}

Quaternion Quaternion::Conjugate()
{
    Quaternion result( *this );
    result.v = result.v * -1.0f;
    return result; 
}

Transform Transform::operator*(const Transform tran)
{
return Transform( mOrient * transform.getOrient(), mTrans + ( mOrient *   tran.getTrans());
}

Transform Joint::GetWorldSpace()
{       
Transform world = local_space;

Joint* par = GetParent();

while ( par ) 
{
    world = par->GetLocalSpace() * world;
    par = par->GetParent();
}

return world;
}

void RotLimb()
{
Vector end_effector_worldspace_pos = end_effector->GetWorldSpace().get_Translation();
Vector parent_worldspace_pos       = parent->GetWorldSpace().get_Translation();

Vector parent_To_end_effector      = ( end_effector_worldspace_pos - parent_worldspace_pos ).Normalize(); 
Vector parent_To_goal              = ( goal_pos                    - parent_worldspace_pos ).Normalize(); 

float dot = parent_To_end_effector.Dot( parent_To_goal );

Vector rot_axis(0.0f,0.0f,1.0f);
float  angle = 0.0f;

if (1.0f - fabs(dot) > EPSILON) 
{
    //angle    = parent_To_end_effector.Angle( parent_To_goal );            
    rot_axis = parent_To_end_effector.Cross( parent_To_goal ).Normalize();

    parent->RotateJoint( rot_axis, acos(dot) );
}
}

void Joint::Rotate( const Vector& axis, const float rotation )
{
    mLocalSpace = mlocalSpace * Quaternion( axis, rotation );
}

【问题讨论】:

  • 您确定这不是数值精度问题吗?我不知道您使用的是哪种迭代算法,但在存在数值错误的情况下它可能不会收敛。也许您需要将可接受的最小误差设置得更高,或者考虑使用另一种数值数据类型
  • 我同意 sinelaw。在某些时候,您的算法必须说“足够接近”并且只需将值分配为等于目标值。
  • 您能提供公式或(伪)代码吗?如果没有更多细节,很难猜测出了什么问题。我唯一能给你的是二维空间中的旋转是可交换的,也许你在某个地方混淆了乘法的顺序?
  • 这里的大多数人都严重缺乏千里眼。你必须展示你的代码。我怀疑你在旋转后没有正确更新肢体的局部空间,但我在这里在黑暗中拍摄。
  • @sinelaw - 我曾认为它可能也是如此。不过,它似乎在第一次旋转后并没有收敛。我不得不将容差设置得太高。这让我相信肯定有其他问题。

标签: c++ linear-algebra inverse-kinematics


【解决方案1】:

当您在注释中写道轴应在关节的局部坐标系中计算时,您是正确的:

我想知道是否会出现此问题,因为我正在计算以获取关节在世界空间中的轴和角度,然后将其应用于本地空间。

轴从世界坐标系到关节坐标系的旋转看起来像这样:

rot_axis = parent->GetWorldSpace().Inverse().get_Rotation() * rot_axis

可能还有其他问题需要调试,但这是我在您发布的代码中看到的唯一逻辑错误。

【讨论】:

  • 写完评论后,我回家测试了这个问题。果然这就是问题所在,我只是添加了将轴转换为关节的局部空间的代码……那时一切都很好。有趣的是我们有时会忽略的事情......我直到现在才忘记这篇文章并看到了这个答案。这是我所做的。感谢您抽出宝贵时间回复。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-16
  • 1970-01-01
相关资源
最近更新 更多