似乎仍然有使用四元数的万向节锁

发布于 2024-11-10 18:28:25 字数 5435 浏览 1 评论 0原文

我使用参考四元数维护对象的位置...

我使用以下代码从 2D xy 触摸滑动旋转参考四元数...

-(void)rotateViewOnX:(CGFloat)x andOnY:(CGFloat)y
{
    // x and y should be a simple Cartesian pixel movements ...
    // the screen swipe moved m pixels in the x direction and n pixels in the y direction ...

    // ignore micro movements ...
    if(abs(x) < 0.1 && abs(y) < 0.1)
        return;

    // ignore excessive movements ...
    if(abs(x) > VIEW_PORTAL_SIZE/3.0 || abs(y) > VIEW_PORTAL_SIZE/3.0)
        return;

    // simulate a very, very large trackball ... 
    double radius = VIEW_PORTAL_SIZE/2.0;
    x = x/radius;
    y = y/radius;
    double z = sqrt(1 - x*x - y*y);
    Vector *trackball = [[Vector alloc] initWithX:x andY:y andZ:z];

    // use dot product to get the angle of rotation on the trackball
    double theta = acos([self.zReferenceVector dotProduct:trackball]);

    // use cross product to get the axis of rotation - convert to a unit vector
    // this is an autoreleased object ...
    Vector *rotationAxis = [[self.zReferenceVector crossProduct:trackball] normalise];

    // create a quaternion to represent the latest movement from the simulated trackball ...
    Quaternion *change = [[Quaternion alloc] initWithAngle:theta aroundVector:rotationAxis];
    [change unitise];

    // rotate the reference quaternion by this quaternion ...
    self.zReferenceQuaternion = [[change times:self.zReferenceQuaternion] times:[change inverse]];
    [self.zReferenceQuaternion unitise];

    // clean-up ...
    [trackball release]; trackball = nil;
    [change release]; change = nil;

    // and paint ...
    [self setNeedsDisplay];
}

要旋转对象,我将以下代码应用于对象中的每个向量点...

// align the cube to the rotated reference quaternion ...
v = [self.zReferenceQuaternion rotateVector:v];

它使用以下方法...

-(Vector*)rotateVector:(Vector*)v
{
    Quaternion *myInverse = [self inverse];
    Quaternion *pureQuat = [[Quaternion alloc] initWithValues:0.0 :v.x :v.y :v.z];
    Quaternion *step1 = [self times: pureQuat];
    Quaternion *step2 = [step1 times: myInverse];
    [pureQuat release];
    return [[[Vector alloc] initWithX:step2.x andY:step2.y andZ:step2.z] autorelease];
}

我的问题是,当对象处于 180 度左右时,如果我沿 x 方向旋转它,我无法沿 y 方向移动它。相反,如果我在 y 方向上旋转它,旋转大约 180 度时,我无法在 x 方向上移动它。不知道我做错了什么!


后脚本...

为了回答评论员,整个四元数类目前如下:

#import "Quaternion.h"

@implementation Quaternion

@synthesize w=_w;
@synthesize x=_x;
@synthesize y=_y;
@synthesize z=_z;

// ----- initialisers

// this is the designated initaliser for w, x, y, z ...
-(id) initWithValues:(double)a :(double)b :(double)c :(double)d
{
    self = [super init];
    if(self)
    {
        _w = a; //1
        _x = b; //i
        _y = c; //j
        _z = d; //k
    }
    return self;
}

-(id) initWithAngle:(double)angle aroundVector:(Vector*)vector
{
    // some preliminaries ...
    double halfAngle = angle / 2.0;
    double sinAngleOnTwo = sin(halfAngle); // do once
    Vector *v = [vector normalise];

    // calculate the quaternion ...
    double w = cos(halfAngle);
    double x = v.x * sinAngleOnTwo;
    double y = v.y * sinAngleOnTwo;
    double z = v.z * sinAngleOnTwo;

    // return result ...
    return [self initWithValues:w :x :y :z];
}

-(Quaternion*)copy
{
    Quaternion *a = self;
    return [[Quaternion alloc] initWithValues:a.w :a.x :a.y :a.z];
}

// ----- algebra

-(Quaternion*)conjugate
{
    Quaternion *a = self;
    return [[[Quaternion alloc] initWithValues:a.w :-a.x :-a.y :-a.z ] autorelease];
}

-(Quaternion*)plus:(Quaternion*)b
{
    Quaternion *a = self;
    return [[[Quaternion alloc] initWithValues:a.w+b.w :a.x+b.x :a.y+b.y :a.z+b.z] autorelease];
}

-(Quaternion*)minus:(Quaternion*)b
{
    Quaternion *a = self;
    return [[[Quaternion alloc] initWithValues:a.w-b.w :a.x-b.x :a.y-b.y :a.z-b.z] autorelease];
}

-(Quaternion*)times:(Quaternion*) b
{
    Quaternion *a = self;
    double real = a.w*b.w - a.x*b.x - a.y*b.y - a.z*b.z;
    double i = a.w*b.x + a.x*b.w + a.y*b.z - a.z*b.y;
    double j = a.w*b.y - a.x*b.z + a.y*b.w + a.z*b.x;
    double k = a.w*b.z + a.x*b.y - a.y*b.x + a.z*b.w;
    return [[[Quaternion alloc] initWithValues:real :i :j :k] autorelease];
}

-(double)norm
{
    Quaternion *a = self;
    return a.w*a.w + a.x*a.x + a.y*a.y + a.z*a.z;
}

-(Quaternion*)inverse
{
    Quaternion *a = self;
    double n = [a norm];
    return [[[Quaternion alloc] initWithValues:a.w/n :-a.x/n :-a.y/n :-a.z/n] autorelease];
}

-(Quaternion*)divides:(Quaternion*) b
{
    Quaternion *a = self;
    return [[a inverse] times: b];
}

-(double)magnitude
{
    Quaternion *a = self;
    return sqrt([a norm]);
}

-(void)unitise
{
    double m = [self magnitude];
    self.w /= m;
    self.x /= m;
    self.y /= m;
    self.z /= m;
}

-(Quaternion*)unitQuaternion
{
    Quaternion *u = [[self copy] autorelease];
    [u unitise];
    return u;
}

-(Vector*)rotateVector:(Vector*)v
{
    Quaternion *vAsPureQuat = [[Quaternion alloc] initWithValues:0.0 :v.x :v.y :v.z];
    Quaternion *r = [[self times: vAsPureQuat] times:[self inverse]];
    [vAsPureQuat release];
    return [[[Vector alloc] initWithX:r.x andY:r.y andZ:r.z] autorelease];
}

// ----- misc

-(NSString*)toString
{
    Quaternion *a = self;
    return [NSString stringWithFormat:@"%f + %fi + %fj +%fk", a.w, a.x, a.y, a.z];
}

@end

I maintain the position of an object using a reference quaternion ...

I rotate a reference quaternion from 2D x-y touch swipes using the following code ...

-(void)rotateViewOnX:(CGFloat)x andOnY:(CGFloat)y
{
    // x and y should be a simple Cartesian pixel movements ...
    // the screen swipe moved m pixels in the x direction and n pixels in the y direction ...

    // ignore micro movements ...
    if(abs(x) < 0.1 && abs(y) < 0.1)
        return;

    // ignore excessive movements ...
    if(abs(x) > VIEW_PORTAL_SIZE/3.0 || abs(y) > VIEW_PORTAL_SIZE/3.0)
        return;

    // simulate a very, very large trackball ... 
    double radius = VIEW_PORTAL_SIZE/2.0;
    x = x/radius;
    y = y/radius;
    double z = sqrt(1 - x*x - y*y);
    Vector *trackball = [[Vector alloc] initWithX:x andY:y andZ:z];

    // use dot product to get the angle of rotation on the trackball
    double theta = acos([self.zReferenceVector dotProduct:trackball]);

    // use cross product to get the axis of rotation - convert to a unit vector
    // this is an autoreleased object ...
    Vector *rotationAxis = [[self.zReferenceVector crossProduct:trackball] normalise];

    // create a quaternion to represent the latest movement from the simulated trackball ...
    Quaternion *change = [[Quaternion alloc] initWithAngle:theta aroundVector:rotationAxis];
    [change unitise];

    // rotate the reference quaternion by this quaternion ...
    self.zReferenceQuaternion = [[change times:self.zReferenceQuaternion] times:[change inverse]];
    [self.zReferenceQuaternion unitise];

    // clean-up ...
    [trackball release]; trackball = nil;
    [change release]; change = nil;

    // and paint ...
    [self setNeedsDisplay];
}

To rotate the object I apply the following code to each Vector point in the object ...

// align the cube to the rotated reference quaternion ...
v = [self.zReferenceQuaternion rotateVector:v];

Which uses the following method ...

-(Vector*)rotateVector:(Vector*)v
{
    Quaternion *myInverse = [self inverse];
    Quaternion *pureQuat = [[Quaternion alloc] initWithValues:0.0 :v.x :v.y :v.z];
    Quaternion *step1 = [self times: pureQuat];
    Quaternion *step2 = [step1 times: myInverse];
    [pureQuat release];
    return [[[Vector alloc] initWithX:step2.x andY:step2.y andZ:step2.z] autorelease];
}

My problem is that when the object is at around 180 degrees if I rotated it there in an x-direction I cannot move it in the y-direction. Conversely, if I rotated it there in the y-direction, at around 180 degrees rotated I cannot move it in the x-direction. Not sure what I am doing wrong!


Post script ...

In answer to the commentator, the entire Quaternion class, as it currently stands follows:

#import "Quaternion.h"

@implementation Quaternion

@synthesize w=_w;
@synthesize x=_x;
@synthesize y=_y;
@synthesize z=_z;

// ----- initialisers

// this is the designated initaliser for w, x, y, z ...
-(id) initWithValues:(double)a :(double)b :(double)c :(double)d
{
    self = [super init];
    if(self)
    {
        _w = a; //1
        _x = b; //i
        _y = c; //j
        _z = d; //k
    }
    return self;
}

-(id) initWithAngle:(double)angle aroundVector:(Vector*)vector
{
    // some preliminaries ...
    double halfAngle = angle / 2.0;
    double sinAngleOnTwo = sin(halfAngle); // do once
    Vector *v = [vector normalise];

    // calculate the quaternion ...
    double w = cos(halfAngle);
    double x = v.x * sinAngleOnTwo;
    double y = v.y * sinAngleOnTwo;
    double z = v.z * sinAngleOnTwo;

    // return result ...
    return [self initWithValues:w :x :y :z];
}

-(Quaternion*)copy
{
    Quaternion *a = self;
    return [[Quaternion alloc] initWithValues:a.w :a.x :a.y :a.z];
}

// ----- algebra

-(Quaternion*)conjugate
{
    Quaternion *a = self;
    return [[[Quaternion alloc] initWithValues:a.w :-a.x :-a.y :-a.z ] autorelease];
}

-(Quaternion*)plus:(Quaternion*)b
{
    Quaternion *a = self;
    return [[[Quaternion alloc] initWithValues:a.w+b.w :a.x+b.x :a.y+b.y :a.z+b.z] autorelease];
}

-(Quaternion*)minus:(Quaternion*)b
{
    Quaternion *a = self;
    return [[[Quaternion alloc] initWithValues:a.w-b.w :a.x-b.x :a.y-b.y :a.z-b.z] autorelease];
}

-(Quaternion*)times:(Quaternion*) b
{
    Quaternion *a = self;
    double real = a.w*b.w - a.x*b.x - a.y*b.y - a.z*b.z;
    double i = a.w*b.x + a.x*b.w + a.y*b.z - a.z*b.y;
    double j = a.w*b.y - a.x*b.z + a.y*b.w + a.z*b.x;
    double k = a.w*b.z + a.x*b.y - a.y*b.x + a.z*b.w;
    return [[[Quaternion alloc] initWithValues:real :i :j :k] autorelease];
}

-(double)norm
{
    Quaternion *a = self;
    return a.w*a.w + a.x*a.x + a.y*a.y + a.z*a.z;
}

-(Quaternion*)inverse
{
    Quaternion *a = self;
    double n = [a norm];
    return [[[Quaternion alloc] initWithValues:a.w/n :-a.x/n :-a.y/n :-a.z/n] autorelease];
}

-(Quaternion*)divides:(Quaternion*) b
{
    Quaternion *a = self;
    return [[a inverse] times: b];
}

-(double)magnitude
{
    Quaternion *a = self;
    return sqrt([a norm]);
}

-(void)unitise
{
    double m = [self magnitude];
    self.w /= m;
    self.x /= m;
    self.y /= m;
    self.z /= m;
}

-(Quaternion*)unitQuaternion
{
    Quaternion *u = [[self copy] autorelease];
    [u unitise];
    return u;
}

-(Vector*)rotateVector:(Vector*)v
{
    Quaternion *vAsPureQuat = [[Quaternion alloc] initWithValues:0.0 :v.x :v.y :v.z];
    Quaternion *r = [[self times: vAsPureQuat] times:[self inverse]];
    [vAsPureQuat release];
    return [[[Vector alloc] initWithX:r.x andY:r.y andZ:r.z] autorelease];
}

// ----- misc

-(NSString*)toString
{
    Quaternion *a = self;
    return [NSString stringWithFormat:@"%f + %fi + %fj +%fk", a.w, a.x, a.y, a.z];
}

@end

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

温柔少女心 2024-11-17 18:28:25

我发现了我的方法的错误...

它位于维护旋转参考四元数的代码中...我不应该将它乘以逆...在下面的代码片段中,上面的错误代码问题已被注释掉。

// rotate the zReferenceQuaternion quaternion by this change quaternion ...
//self.zReferenceQuaternion = [[change times:self.zReferenceQuaternion] times:[change inverse]];
self.zReferenceQuaternion = [change times:self.zReferenceQuaternion ];
[self.zReferenceQuaternion unitise];

这些运动现在累积在 self.zReferenceQuaternion iVar 中。在屏幕上移动物体时不会出现类似万向节锁的情况。

I have discovered the error of my ways ...

It lay in the code that maintains the rotated reference quaternion ... I should not have been multiplying it by the inverse ... in the following code snippet, the buggy code from the above question has been commented out.

// rotate the zReferenceQuaternion quaternion by this change quaternion ...
//self.zReferenceQuaternion = [[change times:self.zReferenceQuaternion] times:[change inverse]];
self.zReferenceQuaternion = [change times:self.zReferenceQuaternion ];
[self.zReferenceQuaternion unitise];

The movements are now accumulated in the self.zReferenceQuaternion iVar. No gimbal-lock like situation occurs on the screen moving the object around.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文