【问题标题】:Maths for calculating panorama angle from gyroscope in AS3在 AS3 中从陀螺仪计算全景角度的数学
【发布时间】:2014-02-12 00:36:07
【问题描述】:

我正在尝试添加数学以控制带有陀螺仪的全景图并且正在苦苦挣扎。这是一个内置在 AS3 中的移动应用程序。

我得到了来自陀螺仪的数据(x 和 y),并且得到了当前角度(平移和倾斜)。我想要做的是根据来自陀螺仪的数据用新的角度更新 cameraController。

我一直在尝试将我在 https://github.com/fieldOfView/krpano_fovplugins/blob/master/gyro/source/gyro.source.js 上找到的 Javascript 转换为 Actionscript 3,它有点工作 - 但不是真的。

编辑

感谢我尝试了这些更改并添加了相机回滚,因为欧拉数学需要它,它运行但数学有问题。

全景图似乎只是向上和向左移动,在将手机向另一方向移动一段时间后它会向下移动,然后向右移动。

您能看到我在 Javascript 中遗漏的任何重要内容吗?

import com.adobe.nativeExtensions.GyroscopeEvent;

import flash.events.Event;
import flash.geom.Orientation3D;

public class GyroscopeMaths
{
    public function GyroscopeMaths()
    {
        super();
    }

    private var isTopAccessible:Boolean = false;
    private var isDeviceAvailable:Boolean;
    private var isEnabled:Boolean = false;
    private var vElasticity:Number = 0;
    private var isVRelative:Boolean = false;
    private var isCamRoll:Boolean = false;
    private var friction:Number = 0.5;
    private var isTouching:Boolean = false;
    private var validSample:Boolean = false;
    private var firstSample:* = null;
    private var hOffset:Number = 0;
    private var vOffset:Number = 0;
    private var hLookAt:Number = 0;
    private var vLookAt:Number = 0;
    private var camRoll:Number = 0;
    private var vLookAtNow:Number = 0;
    private var hLookAtNow:Number = 0;
    private var hSpeed:Number = 0;
    private var vSpeed:Number = 0;
    private var vElasticSpeed:Number = 0;
    private var camRollNow:Number;
    private var pitch:Number;
    private var yaw:Number;
    private var altYaw:Number;
    private var factor:Number;
    private var degRad:Number = Math.PI / 180;

    public function handleDeviceOrientation(x:Number, y:Number, z:Number):void {
        // Process event.alpha, event.beta and event.gamma
        var orientation:* = rotateEuler({
            "yaw":y * degRad,
            "pitch":x * degRad,
            "roll": z * degRad
        });
        yaw = wrapAngle(orientation.yaw / degRad);
        pitch = orientation.pitch / degRad;
        altYaw = yaw, factor;
        hLookAtNow = Pano.instance.pan;
        vLookAtNow = Pano.instance.tilt;
        hSpeed = hLookAtNow - hLookAt,
            vSpeed = vLookAtNow - vLookAt;

        // Ignore all sample until we get a sample that is different from the first sample
        if (!validSample) {
            if (firstSample == null) {
                firstSample = orientation;
            } else {
                if (orientation.yaw != firstSample.yaw || orientation.pitch != firstSample.pitch || orientation.roll != firstSample.roll) {
                    firstSample = null;
                    validSample = true;
                    if (isVRelative) {
                        vOffset = -pitch;
                    }
                }
            }
            return;
        }

        // Fix gimbal lock
        if (Math.abs(pitch) > 70) {
            altYaw = y;


            var altYaw:Number = wrapAngle(altYaw);
            if (Math.abs(altYaw - yaw) > 180) {
                altYaw += (altYaw < yaw) ? 360 :-360;
            }

            var factor:Number = Math.min(1, (Math.abs(pitch) - 70) / 10);
            yaw = yaw * (1 - factor) + altYaw * factor;

            //camRoll *= (1 - factor);
        }

        // Track view change since last orientation event
        // ie:user has manually panned, or krpano has altered lookat
        hOffset += hSpeed;
        vOffset += vSpeed;

        // Clamp vOffset
        if (Math.abs(pitch + vOffset) > 90) {
            vOffset = (pitch + vOffset > 0) ? (90 - pitch) :(-90 - pitch)
        }

        hLookAt = wrapAngle(-yaw - 180 + hOffset);
        vLookAt = Math.max(Math.min((pitch + vOffset), 90), -90);

        // Dampen lookat
        if (Math.abs(hLookAt - hLookAtNow) > 180) {
            hLookAtNow += (hLookAt > hLookAtNow) ? 360 :-360;
        }

        hLookAt = (1 - friction) * hLookAt + friction * hLookAtNow;
        vLookAt = (1 - friction) * vLookAt + friction * vLookAtNow;

        if (Math.abs(camRoll - camRollNow) > 180) {
            camRollNow += (camRoll > camRollNow) ? 360 :-360;
        }

        camRoll = (1 - friction) * camRoll + friction * camRollNow;

        var wAh:Number = wrapAngle(hLookAt);
        Pano.instance.panoGyroChange(wAh, vLookAt);
        //krpano.view.camroll = wrapAngle(camRoll);

        if (vOffset != 0 && vElasticity > 0) {
            if (vSpeed == 0) {
                if (vElasticity == 1) {
                    vOffset = 0;
                    vElasticSpeed = 0;
                } else {
                    //  vElasticSpeed = 1 - ((1 - vElasticSpeed) * krpano.control.touchfriction);
                    vOffset *= 1 - (Math.pow(vElasticity, 2) * vElasticSpeed); // use Math.pow to be able to use saner values

                    if (Math.abs(vOffset) < 0.1) {
                        vOffset = 0;
                        vElasticSpeed = 0;
                    }
                }
            } else {
                vElasticSpeed = 0;
            }
        }
    }

    private function rotateEuler(euler:Object):Object {
        // This function is based on http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToMatrix/index.htm
        // and http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToEuler/index.htm
        trace(euler);
        var heading:Number;
        var bank:Number;
        var attitude:Number;
        var ch:Number = Math.cos(euler.yaw);
        var sh:Number = Math.sin(euler.yaw);
        var ca:Number = Math.cos(euler.pitch);
        var sa:Number = Math.sin(euler.pitch);
        var cb:Number = Math.cos(euler.roll);
        var sb:Number = Math.sin(euler.roll);

        var matrix:Array = [
            sh * sb - ch * sa * cb, -ch * ca, ch * sa * sb + sh * cb,
            ca * cb, -sa, -ca * sb,
            sh * sa * cb + ch * sb, sh * ca, -sh * sa * sb + ch * cb
        ]; // Note:Includes 90 degree rotation around z axis

        /* [m00 m01 m02] 0 1 2
        * [m10 m11 m12] 3 4 5
        * [m20 m21 m22] 6 7 8 */

        if (matrix[3] > 0.9999) {
            // Deal with singularity at north pole
            heading = Math.atan2(matrix[2], matrix[8]);
            attitude = Math.PI / 2;
            bank = 0;
        } else if (matrix[3] < -0.9999) {
            // Deal with singularity at south pole
            heading = Math.atan2(matrix[2], matrix[8]);
            attitude = -Math.PI / 2;
            bank = 0;
        } else {
            heading = Math.atan2(-matrix[6], matrix[0]);
            bank = Math.atan2(-matrix[5], matrix[4]);
            attitude = Math.asin(matrix[3]);
        }

        return {
            yaw:heading,
            pitch:attitude,
            roll:bank
        };
    }

    private function wrapAngle(value:Number):Number {
        value = value % 360;
        return (value <= 180) ? value :value - 360;
    } // wrap a value between -180 and 180
    //function stringToBoolean(value:Number):String 
    //{ return (String("yesontrue1").indexOf( String(value:Number) ) >= 0) };
}

【问题讨论】:

标签: actionscript-3 math mobile air gyroscope


【解决方案1】:

如果没有您的程序的其余部分,我将无法编译它,但我已经更正了我能找到的语法错误。也就是说,如果您可以使用 JS 或 As3 进行编程,您应该能够按照逻辑编写自己的类(它们都是 EMCAScript)。继续追求,直到你遇到一个比“它不起作用”(这通常是一个没人愿意回答的问题)更可靠的问题。

private var isTopAccessible:Boolean = false;
private var isDeviceAvailable:Boolean;
private var isEnabled:Boolean = false;
private var vElasticity:Number = 0;
private var isVRelative:Boolean = false;
private var isCamRoll:Boolean = false;
private var friction:Number = 0.5;
private var isTouching:Boolean = false;
private var validSample:Boolean = false;
private var firstSample:* = null;
private var hOffset:Number = 0;
private var vOffset:Number = 0;
private var hLookAt:Number = 0;
private var vLookAt:Number = 0;
private var camRoll:Number = 0;
private var vLookAtNow:Number = 0;
private var hLookAtNow:Number = 0;
private var hSpeed:Number = 0;
private var vSpeed:Number = 0;
private var vElasticSpeed:Number = 0;
private var camRollNow:Number;
private var pitch:Number;
private var yaw:Number;
private var altYaw:Number;
private var factor:Number;
private var degRad:Number = Math.PI / 180;

public function handleDeviceOrientation(x:Number, y:Number):void {
    // Process event.alpha, event.beta and event.gamma
    var orientation:* = rotateEuler({
        "yaw":y * degRad,
        "pitch":x * degRad,
        "roll":0
    });
    yaw = wrapAngle(orientation.yaw / degRad);
    pitch = orientation.pitch / degRad;
    altYaw = yaw, factor;
    hLookAtNow = Pano.instance.pan;
    vLookAtNow = Pano.instance.tilt;
    hSpeed = hLookAtNow - hLookAt,
    vSpeed = vLookAtNow - vLookAt;

    // Ignore all sample until we get a sample that is different from the first sample
    if (!validSample) {
        if (firstSample == null) {
            firstSample = orientation;
        } else {
            if (orientation.yaw != firstSample.yaw || orientation.pitch != firstSample.pitch || orientation.roll != firstSample.roll) {
                firstSample = null;
                validSample = true;
                if (isVRelative) {
                    vOffset = -pitch;
                }
            }
        }
        return;
    }

    // Fix gimbal lock
    if (Math.abs(pitch) > 70) {
        altYaw = y;

        /*switch(deviceOrientation) {
            case 0:
                if ( pitch>0 )
                altYaw += 180;
                break;
            case 90:
                altYaw += 90;
                break;
            case -90:
                altYaw += -90;
                break;
            case 180:
                if ( pitch<0 )
                altYaw += 180;
                break;
        }*/

        var altYaw:Number = wrapAngle(altYaw);
        if (Math.abs(altYaw - yaw) > 180) {
            altYaw += (altYaw < yaw) ? 360 :-360;
        }

        var factor:Number = Math.min(1, (Math.abs(pitch) - 70) / 10);
        yaw = yaw * (1 - factor) + altYaw * factor;

        //camRoll *= (1 - factor);
    }

    // Track view change since last orientation event
    // ie:user has manually panned, or krpano has altered lookat
    hOffset += hSpeed;
    vOffset += vSpeed;

    // Clamp vOffset
    if (Math.abs(pitch + vOffset) > 90) {
        vOffset = (pitch + vOffset > 0) ? (90 - pitch) :(-90 - pitch)
    }

    hLookAt = wrapAngle(-yaw - 180 + hOffset);
    vLookAt = Math.max(Math.min((pitch + vOffset), 90), -90);

    // Dampen lookat
    if (Math.abs(hLookAt - hLookAtNow) > 180) {
        hLookAtNow += (hLookAt > hLookAtNow) ? 360 :-360;
    }

    hLookAt = (1 - friction) * hLookAt + friction * hLookAtNow;
    vLookAt = (1 - friction) * vLookAt + friction * vLookAtNow;

    if (Math.abs(camRoll - camRollNow) > 180) {
        camRollNow += (camRoll > camRollNow) ? 360 :-360;
    }

    camRoll = (1 - friction) * camRoll + friction * camRollNow;

    var wAh:Number = wrapAngle(hLookAt);
    Pano.instance.panoGyroChange(wAh, vLookAt);
    //krpano.view.camroll = wrapAngle(camRoll);

    if (vOffset != 0 && vElasticity > 0) {
        if (vSpeed == 0) {
            if (vElasticity == 1) {
                vOffset = 0;
                vElasticSpeed = 0;
            } else {
                //  vElasticSpeed = 1 - ((1 - vElasticSpeed) * krpano.control.touchfriction);
                vOffset *= 1 - (Math.pow(vElasticity, 2) * vElasticSpeed); // use Math.pow to be able to use saner values

                if (Math.abs(vOffset) < 0.1) {
                    vOffset = 0;
                    vElasticSpeed = 0;
                }
            }
        } else {
            vElasticSpeed = 0;
        }
    }
}

private function rotateEuler(euler:Object):Object {
    // This function is based on http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToMatrix/index.htm
    // and http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToEuler/index.htm
    trace(euler);
    var heading:Number;
    var bank:Number;
    var attitude:Number;
    var ch:Number = Math.cos(euler.yaw);
    var sh:Number = Math.sin(euler.yaw);
    var ca:Number = Math.cos(euler.pitch);
    var sa:Number = Math.sin(euler.pitch);
    var cb:Number = Math.cos(euler.roll);
    var sb:Number = Math.sin(euler.roll);

    var matrix:Array = [
        sh * sb - ch * sa * cb, -ch * ca, ch * sa * sb + sh * cb,
        ca * cb, -sa, -ca * sb,
        sh * sa * cb + ch * sb, sh * ca, -sh * sa * sb + ch * cb
    ]; // Note:Includes 90 degree rotation around z axis

    /* [m00 m01 m02] 0 1 2
     * [m10 m11 m12] 3 4 5
     * [m20 m21 m22] 6 7 8 */

    if (matrix[3] > 0.9999) {
        // Deal with singularity at north pole
        heading = Math.atan2(matrix[2], matrix[8]);
        attitude = Math.PI / 2;
        bank = 0;
    } else if (matrix[3] < -0.9999) {
        // Deal with singularity at south pole
        heading = Math.atan2(matrix[2], matrix[8]);
        attitude = -Math.PI / 2;
        bank = 0;
    } else {
        heading = Math.atan2(-matrix[6], matrix[0]);
        bank = Math.atan2(-matrix[5], matrix[4]);
        attitude = Math.asin(matrix[3]);
    }

    return {
        yaw:heading,
        pitch:attitude,
        roll:bank
    };
}

private function wrapAngle(value:Number):Number {
    value = value % 360;
    return (value <= 180) ? value :value - 360;
} // wrap a value between -180 and 180
//function stringToBoolean(value:Number):String 
//{ return (String("yesontrue1").indexOf( String(value:Number) ) >= 0) };

【讨论】:

  • 感谢您解决这部分问题。实际上我把它复杂化了,我只需要公共静态函数 toDegrees(radians:Number):Number { return (radians * 180 / Math.PI); }
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-09-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多