与指南针和陀螺仪的传感器融合:0 到 360 度之间

发布于 2024-12-29 20:25:39 字数 2519 浏览 0 评论 0原文

我正在开发一个小型室内导航应用程序,其中使用陀螺仪和指南针来确定设备方向。我使用陀螺仪来平滑指南针数据。我的传感器融合如下所示。这是我的motionHandler,一切都发生在其中。

// Listen to events from the motionManager
motionHandler = ^ (CMDeviceMotion *motion, NSError *error) {

        __block float heading;
        heading = mHeading;

        CMAttitude *currentAttitude = motion.attitude;

        //Initial heading setting
        if (lastHeading == 0 && heading != 0) {
            updatedHeading = heading;
        }
        lastHeading = heading;

        if (oldQuaternion.w != 0 || oldQuaternion.x != 0 || oldQuaternion.y != 0 || oldQuaternion.z != 0){
            diffQuaternion = [self multiplyQuaternions:[self inverseQuaternion:oldQuaternion] :currentAttitude.quaternion];
            diffQuaternion = [self normalizeQuaternion:diffQuaternion];
        }            
        oldQuaternion = currentAttitude.quaternion;

        diffYaw = RADIANS_TO_DEGREES([self yawFromQuaternion:diffQuaternion]);         

        quaternion = currentAttitude.quaternion;

        //Get Pitch
        rpy.pitch = -[self pitchFromQuaternion:quaternion];
        rpy.pitch += M_PI/2;                        

        //Use Yaw-Difference for Heading
        updatedHeading = updatedHeading - diffYaw;

        //Heading has to be between 0 and 360 degrees
        if (updatedHeading < 0) {
            updatedHeading = 360 + updatedHeading;
        }
        else if (updatedHeading > 360) {
            updatedHeading -= 360;
        }

        //fusionate gyro estimated heading with new magneticHeading
        updatedHeading = (19.0*updatedHeading + 1.0*heading)/20.0;

        //generate queternion
        rotation = [self createFromAxisAngle:0 :rpy.pitch :DEGREES_TO_RADIANS(updatedHeading)];
    };

实际的传感器融合公式如下:updatedHeading = (19.0*updatedHeading + 1.0*heading)/20.0;。 这是我的 didUpdateHeading 函数,它接收最新的航向信息:

- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
    // Get new heading
    mHeading = newHeading.magneticHeading;    

    mHeading += 90;

    if (mHeading > 360) {
        mHeading -= 360;
    }
}

diffYaw 是陀螺仪计算的航向的变化。 rotation 是最终的四元数。 除了一种特殊情况:在 0 度和 360 度之间的过渡处,这一切都很完美。

如果 updatedHeading 接近但小于 360,并且 mHeading 略高于 0,则结​​果会沿圆圈移动。例如,如果 updatedHeading = 355 且 mHeading = 5,正确的结果应该在 360 到 5 之间。但是我的公式计算出 337.5 度,这显然是完全错误的!

我认为这个问题必须有任何通用的解决方法......

I'm developing a small indoor navigation app in which I use the gyro and the compass for the device orientation. I use the gyro to smooth the compass data. My sensor fusion looks like the following. This is my motionHandler where everything happens.

// Listen to events from the motionManager
motionHandler = ^ (CMDeviceMotion *motion, NSError *error) {

        __block float heading;
        heading = mHeading;

        CMAttitude *currentAttitude = motion.attitude;

        //Initial heading setting
        if (lastHeading == 0 && heading != 0) {
            updatedHeading = heading;
        }
        lastHeading = heading;

        if (oldQuaternion.w != 0 || oldQuaternion.x != 0 || oldQuaternion.y != 0 || oldQuaternion.z != 0){
            diffQuaternion = [self multiplyQuaternions:[self inverseQuaternion:oldQuaternion] :currentAttitude.quaternion];
            diffQuaternion = [self normalizeQuaternion:diffQuaternion];
        }            
        oldQuaternion = currentAttitude.quaternion;

        diffYaw = RADIANS_TO_DEGREES([self yawFromQuaternion:diffQuaternion]);         

        quaternion = currentAttitude.quaternion;

        //Get Pitch
        rpy.pitch = -[self pitchFromQuaternion:quaternion];
        rpy.pitch += M_PI/2;                        

        //Use Yaw-Difference for Heading
        updatedHeading = updatedHeading - diffYaw;

        //Heading has to be between 0 and 360 degrees
        if (updatedHeading < 0) {
            updatedHeading = 360 + updatedHeading;
        }
        else if (updatedHeading > 360) {
            updatedHeading -= 360;
        }

        //fusionate gyro estimated heading with new magneticHeading
        updatedHeading = (19.0*updatedHeading + 1.0*heading)/20.0;

        //generate queternion
        rotation = [self createFromAxisAngle:0 :rpy.pitch :DEGREES_TO_RADIANS(updatedHeading)];
    };

The actual sensor fusion formula is this line: updatedHeading = (19.0*updatedHeading + 1.0*heading)/20.0;.
And this is my didUpdateHeading-function which receives the newest heading-information:

- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
    // Get new heading
    mHeading = newHeading.magneticHeading;    

    mHeading += 90;

    if (mHeading > 360) {
        mHeading -= 360;
    }
}

diffYaw is the change of the heading computed by the gyroscope. rotation ist the final quaternion.
This works perfect, except one particular case: at the transition between 0 und 360 degrees.

If updatedHeading is near to but smaller than 360 and mHeading is just above 0 the result moves in a circle. For example if updatedHeading = 355 and mHeading = 5, the correct result should be between 360 and 5. But my formula computes 337,5 degrees, which is clearly completly wrong!

There have to be any common workarounds for this problem, I think…

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

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

发布评论

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

评论(1

浅黛梨妆こ 2025-01-05 20:25:39

我通常在这些类型的角度计算中执行类似以下操作:

updatedHeading -= angleDiff(updatedHeading, mHeading) * 0.05;

其中angleDiff() 是:

double angleDiff( double angle1, double angle2 )
{
    double angle = angle1 - angle2;
    if( angle > 180 ) {
        angle -= 360;
    } else if( angle <= -180 ) {
        angle += 360;
    }
    return angle;
}

您可能希望在此之后将updatedHeading 返回到0-360 范围内:

updatedHeading = fmod( updatedHeading + 360, 360 );

您的示例通过此计算给出了355.5。

I usually do something like the following in these types of angle computations:

updatedHeading -= angleDiff(updatedHeading, mHeading) * 0.05;

where angleDiff() is:

double angleDiff( double angle1, double angle2 )
{
    double angle = angle1 - angle2;
    if( angle > 180 ) {
        angle -= 360;
    } else if( angle <= -180 ) {
        angle += 360;
    }
    return angle;
}

You may want to get updatedHeading back in the 0-360 range after this by:

updatedHeading = fmod( updatedHeading + 360, 360 );

Your example gives 355.5 with this computation.

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