基于手机加速度计如何计算距离

16
我想用一部安卓手机来建造类似于这个视频中展示的东西:http://www.youtube.com/watch?v=WOt9mb5QqRs 我已经开发了一款应用程序,可以通过 socket 发送传感器信息(仍在寻找适合安卓的 WebSocket 实现)。我打算使用这些信息与 Web 应用程序进行交互,比如基于手机运动来移动图像。但问题是,我尝试使用加速度计数据计算距离,但结果非常糟糕。我想知道是否有人能帮我找到正确的方程式,但首先,这种做法是否可行?
目前为止,我正在使用以下方程式: 速度 = 加速度 * 时间; 距离 = 速度 * 时间 + (加速度 * 时间^2) / 2; 然后,我会根据显示器屏幕分辨率将距离从每秒米转换为像素。
这些计算是在每次接收到传感器数据时通过浏览器中的 JavaScript 计算得出的,大约每 80 毫秒计算一次。
2个回答

27
基本原理很简单。在模拟世界中,你使用连续数学,即:
velocity = integrate(acceleration)
distance = integrate(velocity)

在数字世界中,使用离散数学可将积分转化为求和,这一过程更加简便。
velocity = sum(acceleration)
distance = sum(velocity)

只需将读取到的加速度值相加,最终就能得到距离。但是,在地球上,由于重力作用,存在一个大约为10m/s²的向下恒定加速度。找出向量中哪部分是重力是难点。顺便提一句,重力是加速计检测倾斜的原理。因此,无论如何,除非你能独立计算出倾斜角度(例如通过陀螺仪),否则你的代码将主要测量倾斜而不是距离。
额外回答:
根据OP发布的“评论”(作为此答案下面或上面的答案),看起来我需要提供进一步的解释。实现非常简单,对数学不熟悉的人会认为它必须比那更复杂。伪代码如下:
// Set distance to zero at start-up:
var distance_X = 0
var velocity_X = 0

function update_acceleration_X (acceleration_X) {
    velocity_X = velocity_X + acceleration_X
    distance_X = distance_X + velocity_X
}

// To use the distance value just read the distance_X variable:
function get_distance_X_and_reset () {
    x = distance_X
    distance_X = 0
    return x
}

除非您将距离变量重置为零,否则距离始终是从软件第一次启动的位置开始测量的。必须不断读取加速度计(最好以加速度计本身测量力的速率),并相应地更新速度和距离的值。当您想要知道从起点到当前位置的距离时,只需读取距离变量即可。

几个注意点:任何程度的倾斜,无论多么轻微,都会增加漂移。这意味着除非不断跟踪倾斜角度本身,否则总会有一定量的恒定加速度朝一个方向或另一个方向。即使核潜艇配备了高精度的加速度计和陀螺仪,因为GPS在水下无法工作,也需要定期浮出水面并与GPS同步以纠正此漂移。

其次,加速度计测量的是力而不是运动。它可以测量任何类型的力。我提到了重力,但它还可以测量由于与桌子摩擦引起的颠簸、由于心跳和呼吸引起的手部微震等等。好消息是,长期来看,所有这些力都会平均分布,公式仍然是正确的。但在短期内,这意味着您的读数将会有噪声。人们想出了许多技巧来最小化此噪声,例如使用Weiner和Kalman滤波器。

第三,正如您可能已经注意到的那样,加速度计读数不是恒定的。我不仅指每次读取时值都不同,这是显而易见的,而且它还会在读数之间改变值。我们错过的每个值都会影响我们的准确性,因此尽可能频繁地读取值非常重要。现在,好消息是,在长期来看,由于大多数是由于急动或振动引起的错误,所有这些由于错过值而导致的错误应该会平均分布,公式仍然是正确的。但这意味着在短期内,这会为我们的系统添加噪声。如果使用良好的预测性滤波器(例如Kalman滤波器),则应该能够解决这个问题,但较弱的滤波器可能需要一些帮助。一种方法是将每个加速度读数与先前的读数平均值进行平均。请注意,它必须是先前的“真实”读数,而不是先前的平均读数。

比这更精确的内容涉及惯性测量单元(IMU)和惯性导航以及许多相当复杂的向量和矩阵数学。但是,现在有一些开源项目正在进行这项工作(不到10年前,这些东西严格属于军事领域,因为您知道,潜艇和巡航导弹使用它们)。

这些Sparkfun文章底部有一些不错的链接和参考代码:

http://www.sparkfun.com/products/9268

http://www.sparkfun.com/products/8454

希望这些都有

当然,如果你想要真正的单位,需要按采样率进行缩放。例如,在80毫秒内以9m/s^2加速意味着你的速度是(9m/s^2 * 0.08s) = 0.72m/s。以上伪代码简化了单位转换的过程。最终的值仍将表示距离,只是这个数字与任何实际测量单位的关系很小。你可以在最后应用一个校准到像素值的缩放函数。下面是一个带有真实世界单位的示例,以澄清正在发生的事情:
given the following acceleration readings:
9m/s/s
3m/s/s
0m/s/s
0m/s/s
0m/s/s
-5m/s/s
-7m/s/s

assuming an 80ms sample rate
we can derive the following velocities:
0.72m/s (what you get from accelerating 9m/s for 80ms)
0.96m/s
0.96m/s
0.96m/s
0.96m/s
0.56m/s
0m/s

from that we can derive the following distances:
57.6mm (what you get from moving at 0.72m/s for 80ms)
134.4mm
211.2mm
288mm
364.8mm
409.6mm

现在,如果您使用派生距离并按照惯例进行反向计算(v = (s2-s1)/ta = (v2-v1)/t),则应该可以获得加速度读数。请注意保留HTML标签。

1
这就是物理原理的工作方式。也许你忘记了速度只是加速度的积分。当时间离散化时,积分变成求和。 - slebetman
1
将时间和值离散化的值相加并不等同于积分。您必须考虑加速度值之间的时间/间隔。 - brutella
1
@Ashwin:我强烈建议你尝试我的方法,以测试计算结果是否与实际长度相符。然后再将其与你的代码进行比较。也就是说,从你的代码中去掉0.5并再次尝试。 - slebetman
我怀疑这个简单的公式是否有效,至少在移动应用上不行。我有一个基于加速度计的应用程序,并集成了加速度,但从未得到正确的答案。我没有看到任何能够完成此功能的App Store应用程序,我认为它不可能如此简单。 - Amir
1
@Amir 这个公式确实太简单和天真了,无法起作用。它基本上做出了太多的假设,使其不切实际。 - Derek 朕會功夫
显示剩余6条评论

2

最大速度 = 时间 * 加速度。
平均速度 = 最大速度 / 2。
行驶距离 = 平均速度 * 时间。


行驶距离 = 时间 * 时间 * 加速度 / 2。


1
这是正确答案。上面的答案是错误的。他说你在0-80毫秒内从0加速到9m/s2。但是为了计算距离,他是这样做的。v=90.80,d=v0.80。v是第80毫秒的最终速度。他通过将最终速度乘以80毫秒来计算距离。 - Ashwin
1
@Ashwin 我知道这已经很老了,但他实际上是正确的!查一下黎曼和。上面的答案是错误的,因为它处理的是连续加速度的情况,而不是像手机那样离散测量间隔的情况。干杯。 - Simon O'Hanlon

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