如何使用陀螺仪和加速度计来测量行驶距离?

52

我想制作一个应用程序,使用陀螺仪和加速度计计算iPhone旅行的精确距离(不是长距离),无需使用GPS。

我该如何解决这个问题?

7个回答

86

这个问题的基本微积分表达式如下所示:

enter image description here

(对于y和z方向上的位移也有类似的表达式),基本几何知识是勾股定理:

enter image description here

因此,一旦你将加速度计信号通过低通滤波器并在采样间隔dt内进行时间分组,你就可以找到x方向的位移(请见谅我的C语言...)

float dx=0.0f;
float vx=0.0f;
for (int i=1; i<n; i++)
 {
   vx+=(acceleration_x[i-1] + acceleration_x[i])/2.0f*dt;
   dx+=vx*dt;
 }

相应地,dy和dz也是如此。这里

float acceleration_x[n];

这个包含了从测量开始到结束的x加速度值,每隔dt时间间隔采集一次,时间点分别为0, dt, 2*dt, 3*dt, ... (n-1)*dt。

要计算总位移,只需要执行以下操作:

dl=sqrt(dx*dx + dy*dy + dz*dz);

对于这个问题,陀螺仪并非必需品,但如果你要测量线性距离,你可以使用陀螺仪数据来控制设备的旋转幅度是否过大。如果旋转幅度太大,则让用户重新进行测量。


1
不错。我刚看到提问者从未投票或接受答案,所以我给他点了个赞 :-) 实际上,即使使用辛普森积分法,在几秒钟后也会因误差传播而遇到麻烦。 - Kay
谢谢Kay,我怀疑问题在细节上,但我相信这不是不可能解决的。就我个人而言,加速度计的响应在高频时可能是非线性的,或者他们可能没有准确地减去重力。在这两种情况下,过滤掉问题频率(可能需要抑制30 Hz以上的所有频率)和运行时校准(静止1秒钟并测量漂移以进行补偿)应该有所帮助。我想现在必须在我的Android上尝试它。 - drlemon
获取准确的结果仍然是一个未解决的问题,即你真正可以用于游戏或其他方面。就像Ali所说,David Sachs在Android上进行了一些非常有价值的研究(请参阅Ali提供的其Google Tech Talk链接)。你可能会在我下面回答中提供的链接中找到有用的想法。准备做一些复杂的数学运算(卡尔曼滤波和导数)。 - Kay
@drlemon:为什么你要这样做 - (acc_x[i-1]+acc_x[i])/2? - Ashwin
@drlemon:这个不应该更准确吗?https://dev59.com/Qeo6XIcBkEYKwwoYPR_f - Ashwin

48

你可以通过两次线性加速度积分来获取位置,但是误差非常大,在实践中没有用处。

这里是一个解释 (Google Tech Talk),在23:20的时候。我强烈推荐这个视频。

类似的问题:


更新(2013年2月24日):@Simon 是的,如果您对运动有更多了解,例如一个人走路,传感器在他的脚上,那么您可以做更多的事情。这些被称为

     领域特定假设

如果这些假设不成立,则它们会失败并且实现起来可能非常麻烦。尽管如此,如果它们有效,则可以做一些有趣的事情。请参阅我的答案中的链接Android加速度计准确性(惯性导航)用于室内定位。


2
我认为他们没有解释任何东西。他说:“有一些方法可以改善线性运动估计...但任何类型的方向误差都非常重要,包括加速度计和陀螺仪之间的交叉轴误差等各种误差。” 对我来说,看起来他们根本不知道发生了什么,因为如果他们知道,他们就可以建议一些方法来改善结果。感谢这个视频,非常有启发性!我有时间时会玩一下那个加速度计。 - drlemon
Ali,你似乎在SO上针对十几个相同的问题发布了负面答案。虽然我发现你的链接很有用,感谢你,但我也觉得你推广一个负面答案来回答一个开放性问题很奇怪。看看周围,在SO上有一些有趣的答案。你真正的动机是什么? - mojuba
@Ali:你有什么建议可以代替这个吗?我也需要克服这种技术的局限性。 - Aki
@Aki 我应该更了解你的应用程序。但归根结底,答案总是这样的:使用方向和/或进行领域特定的假设。 - Ali
我的意思是,这不像开车一样,我正在使用9自由度,并将寻找传感器融合算法,但正如在GoogleTech演讲中所说,双重积分很麻烦。我的唯一希望是使用卡尔曼滤波器的DSAs,但我找不到任何信息。如果有时这些假设被打破了怎么办? - Aki
显示剩余22条评论

5

点赞。令人惊讶的是,这个问题已经出现了很多次,而人们仍然在以正方形的形状重新发明轮子... :( - Ali
@Ali 是的,我计划在接下来的几个月内撰写一篇关于解决此问题并发布我的结果的博客文章(虽然没有找到解决方案,但有一些不错的解决方法),然后将摘要作为常见问题解答发布在这里。离题:我不知道如何通过这个聊天工具与您联系 :( 您也从事iPhone编程吗?我收到了一个(通过SO :) 的合同请求(布拉格),但我很忙。如果您感兴趣,请通过我的网站给我发送电子邮件。顺便说一句:恭喜您达到2k rep :))) - Kay
谢谢 :) 我刚给你发了一封电子邮件,这样你就有我的电子邮件地址以备将来参考。我也在考虑写一篇文章来澄清这个混乱的问题。不幸的是,我没有时间去做。 :( 无论如何,请在你完成时通知我,这样我就可以告诉别人! - Ali

1

我尝试过这个并放弃了(夜里晚了,似乎没有进展)。 这是为Unity3d项目准备的。

如果有人想接手我的工作,我很乐意解释这些东西的作用。

基本上,在一些被证明是误报之后,我想尝试使用低通滤波器进行过滤,然后尝试通过查找趋势来去除跳动,然后使用(acc_x[i-1]+acc_x[i])/2。

看起来误报仍然来自倾斜度,我试图去除它。

如果此代码对您有用或引导您到某个地方,请告诉我!

using UnityEngine;
using System.Collections.Generic;

/// <summary>
/// rbi.noli@gmail.com
/// </summary>
public class AccelerometerInput : MonoBehaviour 
{

    Transform myTransform;
    Gyroscope gyro;
    GyroCam gyroCam;

    void Awake()
    {
        gyroCam= FindObjectOfType<GyroCam> ();
        myTransform = transform;
        if (SystemInfo.supportsGyroscope) {
            gyro = Input.gyro;
            gyro.enabled = true;
        }
    }

    bool shouldBeInitialized = false; 
    void Update () 
    {

        transform.Translate (GetAccelerometer ());// * Time.deltaTime * speed);

        //GetComponent<Rigidbody> ().AddForce (GetAccelerometer ());

    }

    public float speed = 10.0F;

    public Vector3 dir;
    public float f;
    Vector3 GetAccelerometer()
    {

        dir = Input.acceleration;

        dir.x *= gyro.attitude.x;
        dir.z *= gyro.attitude.z;

        if (Mathf.Abs (dir.x) < .001f)
            dir.x = 0;
        dir.y = 0;
        if (Mathf.Abs (dir.z) < .001f)
            dir.z = 0;

        RecordPointsForFilter (dir);

        //print ("Direction : " + dir.ToString("F7"));

        return TestPointsForVelocity();
    }

    Vector3[] points = new Vector3[20];
    int index;
    void RecordPointsForFilter(Vector3 recentPoint)
    {
        if (index >= 20)
            index = 0;
        points [index] = EvaluateTrend (recentPoint);;
        index++;
    }

    //try to remove bounces
    float xTrend = 0;
    float zTrend = 0;
    float lastTrendyX = 0;
    float lastTrendyZ = 0;
    Vector3 EvaluateTrend(Vector3 recentPoint)
    {

        //if the last few points were positive, and this point is negative, don't pass it along
        //accumulate points into a trend
        if (recentPoint.x > 0)
            xTrend += .01f;
        else
            xTrend -= .1f;

        if (recentPoint.z > 0)
            zTrend += .1f;
        else
            zTrend -= .1f;

        //if point matches trend, keep it
        if (xTrend > 0) {
            if (recentPoint.x > 0)
                lastTrendyX = recentPoint.x;
        } else  // xTrend < 0
            if (recentPoint.x < 0)
            lastTrendyX = recentPoint.x;

        if (zTrend > 0) {
            if (recentPoint.z > 0)
                lastTrendyZ = recentPoint.z;
        } else  // xTrend < 0
            if (recentPoint.z < 0)
                lastTrendyZ = recentPoint.z;

        return new Vector3( lastTrendyX, 0, lastTrendyZ);
    }

    Vector3 TestPointsForVelocity()
    {
        float x = 0;
        float z = 0;

        float xAcc = 0;
        float zAcc = 0;

        int successfulHits = 0;
        for(int i = 0; i < points.Length; i++)
        {
            if(points[i]!=null)
            {
                successfulHits ++;
                xAcc += points[i].x;
                zAcc += points[i].z;
            }
        }

        x = xAcc / successfulHits;
        z = zAcc / successfulHits;

        return new Vector3 (x, 0, z);

    }
}

0

0

-1

这里是答案。之前有人问过。

有一个叫做RangeFinder的应用程序也可以做同样的事情(在App Store中可用)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接