【问题标题】:Android: How to get accurate altitude?Android:如何获得准确的高度?
【发布时间】:2012-03-10 20:05:45
【问题描述】:

我需要仅使用 GPS 获得准确的高度测量。

我试过Location.getAltitude(),但这非常不准确。 有什么建议吗?

【问题讨论】:

标签: android gps location altitude


【解决方案1】:

使用智能手机/平板电脑 GPS 的高度存在两个问题:

  1. 高度是 WGS84 参考椭球体上方的高度。它不是高于地面或海平面的高度。这里有更多细节:http://www.gpspassion.com/forumsen/topic.asp?TOPIC_ID=10915。这个错误可以纠正;以下是如何手动操作的描述:http://www.unavco.org/edu_outreach/tutorial/geoidcorr.html。网络文章链接到计算器以获取大地水准面高度以进行校正;我不知道是否还有可用于此计算的 Web 服务。
  2. 对于相对便宜的 GPS 接收器,GPS 高度非常不准确。这是一篇关于此的文章:http://gpsinformation.net/main/altitude.htm。处理这种不准确性的一种方法是过滤海拔数据。我使用圆形数组数据结构来记住最后几个(我使用了 4 个)高度读数并计算平均值。这足以让我的应用程序获得相对准确的垂直速度读数。

【讨论】:

  • 谢谢。所以我想我无法获得准确的高度,除非我的应用程序可以访问互联网以校正大地水准面高度。
  • 您可以离线进行。您只需要计算椭球和海平面之间的差异。这是不平凡的。看到这个答案:stackoverflow.com/a/12811478/1561404
  • 您说过,根据最近 4 个海拔读数,您计算了平均值,但这是针对单个位置的?我有一项服务,当用户走路时,我会不断更新用户的位置。我的问题是:如果我对特定区域的所有位置(没有椭球体和海平面之间差异的原始数据)取平均值,该值是准确的还是只是平均值不准确?
【解决方案2】:

另一种方法是解析 NMEA 字符串。 $GPGGA sentence 已经包含经过校正的海拔高度数据。

因此,只需为您的 LocationManager 创建一个 NMEA 字符串侦听器并解析消息:

private GpsStatus.NmeaListener mNmeaListener = new GpsStatus.NmeaListener() {
    @Override
    public void onNmeaReceived(long timestamp, String nmea) {
        parseNmeaString(nmea);
    }
};

public void registerLocationManager(Context context) {
        mLocationManager = (LocationManager) mContext.getSystemService(LOCATION_SERVICE);
        mLocationManager.addNmeaListener(mNmeaListener);
}

private void parseNmeaString(String line) {
        if (line.startsWith("$")) {
            String[] tokens = line.split(",");
            String type = tokens[0];

            // Parse altitude above sea level, Detailed description of NMEA string here http://aprs.gids.nl/nmea/#gga
            if (type.startsWith("$GPGGA")) {
                if (!tokens[9].isEmpty()) {
                    mLastMslAltitude = Double.parseDouble(tokens[9]);
                }
            }
        }
    }

您可以替换通过位置侦听器接收到的最后一个 Location 对象的高度,或者通过 NMEA 解析整个新位置。

【讨论】:

  • 这种方法效果很好,但是您应该寻找任何“GGA”($--GGA)句子,而不仅仅是$GPGGA。我在my answer 中使用正则表达式。
  • Call requires API level 24 ... android.location.LocationManager#addNmeaListener
  • NmeaListener 已弃用。请改用OnNmeaMessageListener
  • @Berťák 你可以检查它是否包含或以 GGA type.endsWith("GGA") 结尾,不需要像你那样使用长模式
【解决方案3】:

另一种方法是通过气压计测量高度。

通过使用压力,您可以计算出用户的高度。我不确定这个的精度水平以及它是否比其他答案的方法更准确。

使用hypsometric formula可以计算海拔高度:

变量定义:

P0: Sea-level pressure 
P: Atmospheric pressure 
T: Temperature 

你可以通过environment sensors获取android中的压力

SensorManager.getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE,pressure)

【讨论】:

  • 这不会减少它,因为天气会影响压力,因此会影响气压计上的读数。
  • 是的,天气会影响气压计。但根据具体情况,您可能不会受到天气影响,或者更少。例如,您可以使用气压计测量您乘坐的飞机的高度。 GPS不可能。但是,当我只是提供替代方案时,可以否决这个答案@ben。这完全取决于用例,将 1 个传感器当作无用的传感器扔出门外是错误的做法。
  • 这在理论上是可行的,但如果您进行了足够多的测试,您会发现结果变化很大且不准确。我知道很多应用程序都是这样做的,开发人员偶然发现了这种类型的线程,这让他们认为计算高度就这么简单,它有助于应用程序的大型生态系统假装你在某个高度,而你不。抱歉,我并不是要免费为您投反对票,但这并不是在大多数情况下获得有效高度值的正确答案。大多数应用都需要查看 WGS84 偏移量。
  • 顺便说一句,我们说的是大的不准确,而不是小的不准确。如果单独使用气压计,预计会经常出现 100 英尺的差异。
  • @ben 当你说得对时,把压力当作帮手,当你有一个非常好的修复时,每隔几分钟用 GPS 测量校准一次,这是你最好的事情可以做。我刚刚添加了一个答案,指出了这一点。在接下来的几个月里,我将尝试确认“非常频繁地相差 100 英尺”。
【解决方案4】:

如果设备有气压计,那么可以使用它来提高相对准确度。我并不是要使用气压计来计算与海平面相关的高度,这可以在几个公式中找到,因为这高度依赖于天气条件。

您想要做的是在您知道设备具有良好定位且具有高精度值时获取 GPS 高度。此时,您获取大气压力并将该压力设置为参考。

当压力增加约 12 hPa 时,您会知道您的海拔高度下降约 100 m (https://en.wikipedia.org/wiki/Atmospheric_pressure "在海拔较低的高度,压力每下降约 1.2 kPa (12 hPa) 100 米。")。

不要将该值视为精确值,但由于树木覆盖视线和其他因素,GPS 确定的高度变化很大,而在这些条件下,气压计将保持非常精确。

下图是大约 1 小时 20 分钟的自行车骑行。起点和终点在海拔约 477 m 处相同,通过 GPS 确定。实测压力为 1015.223 hPA。

最低点为 377 m,实测压力为 1025.119 hPa。所以在这种情况下,100 m 会产生 10 hPa 的差异。

最高点550 m,实测压力1007.765 hPa。

终点高度相同,压力与起始条件完全相同(压力可能会因天气条件而变化,但事实并非如此)。温度下降了 1°C 左右,所以一切都很稳定。

包含许多变化的黑线是通过 GPS 测量的高度,镜像但干净的线是气压。它几乎没有变化,仅仅是因为压力不像 GPS 质量那样变化很大。这是一条非常平滑、非常精确的曲线。这不是由于滞后。这是用博世BME280传感器测量的,该传感器能够检测关门、楼层检测变化、电梯方向、无人机,噪声为0.2 Pa,等于1.7 cm,高度变化400m时误差为1m .这种传感器集成在一些智能手机中。例如,Pixel 3 包含一个博世 BMP380。

如果您镜像压力图,如黑色虚线所示,您会看到它与 GPS 高度非常匹配。它比 GPS 精确得多,但唯一的问题是不能将其作为绝对值。

GPS 和压力的样本都是以 1 秒的间隔采集的,因此 d3 库中没有曲线平滑导致一些错误印象。

因此,在 GPS 定位良好的情况下,也许每 10 到 30 分钟左右重新调整一次压力,这将为您提供一个良好的基础,以便通过两者之间的压力进行高度测量。

【讨论】:

  • 嘿,你的方法看起来很有趣!在 GPS 方面,您如何定义“好的解决方案”?
  • @forstackoverflowizi 我会基于水平精度并对其进行几次采样。在开发过程中,我会对所有数据进行采样,并查看何时出现最佳匹配,以及它与水平精度的关系,因为这是我们获得的唯一精度数据。
【解决方案5】:

除了 GPS 之外,还有其他方法可以获取高度。您可以使用气压计,但由于配备气压传感器的设备并不多(只有新设备)。我会建议使用网络服务来获取所需的数据。

这是一个可以帮助您解决的问题:Get altitude by longitude and latitude in Android

【讨论】:

    【解决方案6】:

    对于新手,我制作了一个库,将 LocationManager 包装到 rxjava 可观察对象中,并添加一些可观察帮助器以从 Nmea/GPGGA mre info here 获取海平面高度

    【讨论】:

      【解决方案7】:

      有一些库,例如开源 CS-Map,它提供了在大型表中执行这些查找的 API。您指定坐标,它会告诉您需要应用到该位置的椭球高度以获得“真实世界”正高的高度偏移。

      注意:之前使用过 CS-Map,插入它并不是完全直接的 5 分钟工作。提前警告您,它比使用一组 lat-long 调用单个 API 更复杂坐标并返回一个高度。 我不再在我们从事此类工作的公司工作,因此很遗憾无法查看代码以准确说明要调用哪个 API。

      现在(2019 年)正在谷歌搜索它,似乎 CS-Map 已合并到 OSGeo 中的 MetaCRS 中,即“Open Source Geospatial Foundation”项目。同样的搜索让我找到了this old CS-Map wikithe PROJ GitHub page,其中PROJ 似乎与CS-Map 类似。

      【讨论】:

        【解决方案8】:

        我建议使用 NmeaListener 作为 sladstaetter 在his answer 中建议。然而,根据NMEA documentation 的说法,“$GPGGA”并不是你能得到的唯一句子。您应该查找任何“GGA”语句 ($--GGA)。

        正则表达式非常适合,例如:

        @Override
        public void onNmeaReceived(final long timestamp, final String nmea) {
            final Pattern pattern = Pattern.compile("\\$..GGA,[^,]*,[^,]*,[^,]*,[^,]*,[^,]*,[^,]*,[^,]*,[^,]*,([+-]?\\d+(.\\d+)?),[^,]*,[^,]*,[^,]*,[^,]*,[^,]*$");
            final Matcher matcher = pattern.matcher(nmea);
            if (matcher.find()) {
                final float altitude = Float.parseFloat(matcher.group(1));
                // ...enjoy the altitude!
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-08-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-10-14
          相关资源
          最近更新 更多