安卓定位不准确

3

我在尝试获取当前用户的位置。我尝试重构代码以获得更好的结果,但是无论如何,我都只能得到离真实位置相差900-600米的荒谬位置。

我该如何才能获得更好的结果,以将精度强制控制在50米范围内?

以下是我的代码:

package com.agam.mapslocation;

import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.app.Activity;
import android.content.Context;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;

public class MapsActivity extends Activity {

    private static final int ONE_MINUTE = 1000 * 60 * 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_maps);

        final LocationManager locationManager = (LocationManager) this
                .getSystemService(Context.LOCATION_SERVICE);

        final EditText et = (EditText) findViewById(R.id.editText1);

        // Define a listener that responds to location updates
        LocationListener locationListener = new LocationListener() {
            public void onLocationChanged(Location l) {
                // Called when a new location is found by the network location
                // provider.
                Location gps = locationManager
                        .getLastKnownLocation(LocationManager.GPS_PROVIDER);
                Location net = locationManager
                        .getLastKnownLocation(LocationManager.NETWORK_PROVIDER);

                Location bestLocation = null;
                bestLocation = isBetterLocation(gps, net);
                bestLocation = isBetterLocation(bestLocation, l);
                if(bestLocation!=null)
                    displayLocation(et, bestLocation);
            }

            public void onStatusChanged(String provider, int status,
                    Bundle extras) {

            }

            public void onProviderEnabled(String provider) {
                if (provider.equals(LocationManager.GPS_PROVIDER)) {
                    et.setText("GPS ON!");
                }
            }

            public void onProviderDisabled(String provider) {
            }
        };

        // Register the listener with the Location Manager to receive location
        // updates
        locationManager.requestLocationUpdates(
                LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);
        locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0,
                0, locationListener);
    }

    public static void displayLocation(View v, Location l) {
        ((EditText) v).setText(String.format(
                "Long:%s,\nLat:%s,\nAccu:%s,\nTime ago:%s,\nProvider:%s",
                l.getLongitude(), l.getLatitude(), l.getAccuracy(),
                new java.util.Date().getTime() - l.getTime(), l.getProvider()));
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.activity_maps, menu);
        return true;
    }

    /**
     * Determines whether one Location reading is better than the current
     * Location fix
     * 
     * @param location
     *            The new Location that you want to evaluate
     * @param currentBestLocation
     *            The current Location fix, to which you want to compare the new
     *            one
     */
    protected Location isBetterLocation(Location location,
            Location currentBestLocation) {
        if (currentBestLocation == null) {
            // A new location is always better than no location
            return location;
        }

        // Check whether the new location fix is newer or older
        long timeDelta = location.getTime() - currentBestLocation.getTime();
        boolean isSignificantlyNewer = timeDelta > ONE_MINUTE;
        boolean isSignificantlyOlder = timeDelta < -ONE_MINUTE;
        boolean isNewer = timeDelta > 0;

        // If it's been more than two minutes since the current location, use
        // the new location
        // because the user has likely moved
        if (isSignificantlyNewer) {
            return location;
            // If the new location is more than two minutes older, it must be
            // worse
        } else if (isSignificantlyOlder) {
            return currentBestLocation;
        }

        // Check whether the new location fix is more or less accurate
        int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation
                .getAccuracy());
        boolean isLessAccurate = accuracyDelta > 0;
        boolean isMoreAccurate = accuracyDelta < 0;
        boolean isSignificantlyLessAccurate = accuracyDelta > 200;

        // Check if the old and new location are from the same provider
        boolean isFromSameProvider = isSameProvider(location.getProvider(),
                currentBestLocation.getProvider());

        // Determine location quality using a combination of timeliness and
        // accuracy
        if (isMoreAccurate) {
            return location;
        } else if (isNewer && !isLessAccurate) {
            return location;
        } else if (isNewer && !isSignificantlyLessAccurate
                && isFromSameProvider) {
            return location;
        }
        return currentBestLocation;
    }

    /** Checks whether two providers are the same */
    private boolean isSameProvider(String provider1, String provider2) {
        if (provider1 == null) {
            return provider2 == null;
        }
        return provider1.equals(provider2);
    }
}

注意:我强调静态位置,例如不移动,因为这可能有助于回答问题。

1
NETWORK_PROVIDER 是一个粗略的位置提供者,而 GPS_PROVIDER 则是一个精确的位置提供者。如果您移除了网络提供者,应用程序将被强制使用 GPS,并返回更接近的位置。请参阅此链接:http://developer.android.com/training/basics/location/locationmanager.html#TaskPickLocationProvider。 - Bill Gary
@BillGary,我如何在没有网络提供商的情况下仍能够(相对快速地)获取位置(GPS需要大约10-20秒钟...)? - funerr
2
你必须决定你想要速度(粗略)还是准确性(精细)。这在以下链接中有讨论。http://developer.android.com/guide/topics/location/strategies.html#BestPerformance - Bill Gary
但是我该如何在一个方法中请求特定提供者(GPS)的新位置? - funerr
2个回答

3

要获取准确的位置,您需要考虑两个因素:

  1. 代码,例如:

    • 使用正确的位置提供程序(GPS vs. 网络)
    • 在您的应用程序中拥有正确的权限
  2. 设备

    • 拥有适当的传感器(并非所有Android设备都具有GPS、Wifi或电话功能)
    • 启用适当的传感器(例如用户没有关闭GPS)
    • 安装了正确的Google服务(制造商决定)

以下将对这些进行讨论。

代码

确保您已按照Android Developer的指导进行操作(感谢Bill Gary)。

但请记住,只有设备本身具备必要的硬件和配置,代码才能正常工作 - 请参见下文。

设备

要了解您的位置定位出现问题的原因,真正有帮助的是了解您的设备如何定位自身。位置是通过几种技术获得的:
1. GPS芯片组 - 最准确 - 较慢 - 需要在室外或靠近窗户附近 - 需要GPS芯片组开启
2. WiFi上可见的MAC地址(通过查询像Google这样的中央数据库) - 通常非常准确(取决于有多少WiFi存在以及它们是否曾经被看到,误差在10-100米之间) - 相当快速 - 需要打开WiFi - 需要连接到互联网的数据连接(不一定在WiFi上) - 需要在设备上安装Google服务(一些廉价设备可能没有此功能)
3. 您正在使用的手机基站的位置(网络提供商) - 精度为数百米至数千米 - 常常立即 - 需要开启电话功能
当你请求定位(除了最后一个缓存的位置)时,一个好的GPS位置提供者实际上会查看比GPS更多的内容:
  1. 如果启用了GPS芯片组,则开始GPS跟踪

    • 几十秒后,仅在获取到固定点之后,LocationProvider才会返回GPS位置
    • 稍后,设备将与可见的WiFi MAC地址/名称一起发送设备的GPS位置到Google,并将其合并到他们的WiFi AP数据库中
  2. 如果启用了WiFi,则开始扫描WiFi环境

    • 几秒钟后,当WiFi扫描完成时,它将结果发送到Google的服务器
    • 一秒钟左右后,服务器返回一个位置固定值
通常你可以在Google地图上看到这些效果,例如:当你启动它时,你会看到一个大致的位置(基站)从网络提供商处,几秒钟后它会放大到一个更精确的区域(WiFi),最后你可能会得到一个GPS固定点。
这总是值得的:
  • 使用正确/通用的方法选择LocationProvider(而不是硬编码)
  • 检查相关的硬件传感器是否存在且已启用
  • 检查Google Maps是否按照您的预期定位您的设备(如果您的设备没有Google Maps,则没有Google服务,可能只有基本的仅GPS提供商)。
  • 如果怀疑GPS芯片组,请使用GPS应用程序获取详细视图以了解其正在做什么和能力,例如https://play.google.com/store/apps/details?id=com.eclipsim.gpsstatus2&hl=en

编辑

从您获得的基本结果来看,您似乎只有来自网络提供商的结果。 所以,做:

  • 检查GPS是否打开,
  • 检查您的GPS接收器是否可以获取信号(例如使用GPS应用进行调试),并且
  • 通过logcat将一些调试信息放入您的代码中,以便您可以看到isBetterLocation()方法为什么决定选择一个位置而不是另一个位置。

有时候我确实能得到准确的结果(例如8米~),但有时候我甚至等待很长时间(2分钟 - 在室外),而且我得到的网络精度仍然很差。你确定我的代码适用于我的目的吗? - funerr
你的手机型号是什么?你下载了GPS应用吗?如果有的话,在应用中开始GPS跟踪并监控随时间获取到多少颗卫星。完成后,你能否反馈一下? - Andrew Alcock
是的,我下载了一个应用程序,它告诉我它可以看到大约1-4颗卫星(在室内约2-3分钟),但没有使用固定,当我查看谷歌地图时,位置相当准确,并且我已经阅读过GPS在室内工作不好(但我的应用程序确实需要室内位置)。 - funerr
@agam360:很抱歉问了这么多问题 - 我是想确定这是否根本是硬件问题而不是软件问题(这既是好事又是坏事。 好处是不同型号的手机用户会有更好的体验。 坏处是也许我们无法改善您的体验)。在软件层面上,您需要了解所有LocationProviders及其功能,例如Google Maps可能正在使用由WiFi确定的位置,该位置中间准确度,但非常快速。 - Andrew Alcock
似乎可能是软件问题,因为我下载了一个应用程序,可以在200-100米范围内(部分室内)找到解决方法。我去了阳台等了大约3分钟,除了我的网络提供商(850米左右)之外没有任何结果。 我有一部三星Galaxy S3手机 - 它是辅助GPS吗? (我会检查你的答案,因为接下来的20个小时我都不会在线,但如果我们继续讨论直到找到解决方案,我会非常感激 :) ) - funerr
显示剩余5条评论

0

尽快将locationListeners设置为所有可用的位置提供程序,并等待。设置监听器要求提供程序立即提供它们拥有的所有数据(minTime = 0,minDistance = 0)。使用getLastKnownLocation作为第一个位置近似值。 随着时间的推移,您将开始从不同的提供程序获取一些位置数据。您应该手动选择最适合您情况的位置(最精确,我想)。尽可能长时间地等待(您等待的时间越长,您可能会获得更好的位置值)。 当等待时间结束时,取出您收到的最佳位置并开始使用它。您可以继续监听位置更新并改善位置估计,如果系统能够给您更好的值。


我的代码不是做了类似的事情吗?我能在它上面有什么改进吗? - funerr
你的代码基本上没问题。我只不喜欢一个地方:你调用getLastKnownLocation函数的地方。你应该在onCreate中调用它们并保留最佳返回值。不要在每次位置提供者在onLocationChanged方法中给你新数据时都调用它们。 - Leonidos
同样如我所提到的,如果可能的话,您可以在MapsActivity启动之前开始监听位置更新。 - Leonidos
无法实现,因为应用程序在启动时应该运行并显示位置。 - funerr

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