GPS位置波动。

12

我正在Google地图上绘制心形折线,根据半径(1-100米)确定大小。一旦绘制完成,用户需要沿着心形边界行走,并从起点到终点结束(从底部开始行走,然后向左,向右,最后再次到达底部)。

我已经能够绘制心形并获取360个点(latlng)。以下是我的代码,它将绘制心形和图像。

private void initPath() {
    path = new PolylineOptions();
    path.color(ContextCompat.getColor(mActivity,R.color.heart_green));
    path.width(25);

    // offset to bottom
    double offsetY = getY(Math.toRadians(180));

    for (int angle = 0; angle <= 360; angle++) {
        double t = Math.toRadians(angle);
        double x = getX(t);
        double y = getY(t) - offsetY;
        //Log.d(TAG, "angle = " + angle + "(x = " + x + ",y= " + y + ")");

        //convert x,y to lat lng
        LatLng latLng = getLatLng(x, y, center);
        path.add(latLng);

        heart360Points.add(latLng);

    }

}

private double getX(double t) {
    return radius * 16 * Math.sin(t) * Math.sin(t) * Math.sin(t) / HEART_RATIO;
}

private double getY(double t) {
    return radius * (13 * Math.cos(t) - 5 * Math.cos(2 * t) - 2 * Math.cos(3 * t) - Math.cos(4 * t)) / HEART_RATIO;
}

private LatLng getLatLng(double dx, double dy, LatLng centerHeart) {
    return new LatLng(centerHeart.latitude + Math.toDegrees(dy / EARTH_RADIUS),
            centerHeart.longitude + Math.toDegrees(dx / EARTH_RADIUS) / Math.cos(Math.toRadians(centerHeart.latitude)));

}   

这里是一张心形图片。

但是每当我试图走在心形边缘时,GPS位置会出现大量波动,所以我从未能够完成在心形上的行走。我目前每秒请求一次位置。

以下是我的位置代码。

 private static final long FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS = 2000 ;
 @Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.love_lead_perform);
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
    mApiClient = new GoogleApiClient.Builder(this)
            .addApi(ActivityRecognition.API)
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .build();

    mApiClient.connect();


    mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
    mSettingsClient = LocationServices.getSettingsClient(this);
    createLocationCallback();
    createLocationRequest();
    createLocationSettingsRequest();


} 

private void createLocationSettingsRequest() {
    LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
    builder.addLocationRequest(mLocationRequest);
    mLocationSettingsRequest = builder.build();
}

private void createLocationRequest() {
    mLocationRequest = new LocationRequest();
    mLocationRequest.setInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS);
    mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS);
    mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
}   

我不明白为什么GPS定位会波动太大。即使我站在原地,也会得到不同的GPS定位。

如何获取准确的GPS定位?


你在 manifest.xml 文件中调用了哪些权限? - statosdotcom
1
也许你需要参军 - 我听说他们得到比普通人更好的GSP数据 :) 但我认为你在这个问题上并不孤单,即使是PokemonGo(我不玩)也一直在地图上摇晃。因此,您可能会遭受设备GPS信号的“默认”随机性,这可能会受到您所在位置(隧道、建筑物、树木==遮挡GPS所需的卫星)和/或天气的影响 - 这似乎会影响地理缓存。 - Patrick Artner
2
是的,这是真的,但同样也是真的,“Waz3”,“Ub3r”和正确的“G00gle Directions”运作得非常好,能够在几米范围内平稳移动和精确定位...我认为他们对我们这些小开发者隐藏了一些秘密。 - statosdotcom
2
@statosdotcom - 他们可能会将手机位置数据与GPS结合起来,或者平均滑动5到20秒的数据,以消除根据你移动的速度而产生的折线。甚至可以基于从你最后n个位置获取的向量丢弃突然的“侧向移动”-即有很多东西可以填充/消除错误数据并预测“好”的样本。 - Patrick Artner
1
@PatrickArtner 很好的分析和评论,谢谢。你完全正确,这不仅仅是使用正确的工具的问题,而且还要结合人类理性的专业知识,即不仅仅盲目地依赖技术数据,而是利用准确的数据来预测正在发生的事情。人+机器...哇...我们将去往何方? :D - statosdotcom
显示剩余2条评论
4个回答

5
位置的波动是它的正常行为。问题在于,您应该何时/如何接受或丢弃意外的位置变化。正如用户(Patrick Artner)所评论的那样,您应该丢弃突然的侧移。

您可以通过两种方式丢弃这些运动:

  1. 让我们决定最小标准(1米)。

    假设最小距离为1米,因为如果人类“步行(而不是站立)”,那么这可能是可能的,人类可以在“X”秒内至少走过1米的距离。因此,如果覆盖的距离小于1米,则可以简单地丢弃。

  2. 让我们决定最大标准(5米)。

    假设最大距离为5米,因为如果人类“步行(而不是奔跑)”,那么这是不可能的,人类可以在“X”秒内覆盖超过5米的距离。因此,如果覆盖的距离超过5米,则可以简单地丢弃。

其中,

距离=新位置-旧位置(如何获得距离?

X =一定时间(以秒为单位)

只接受小于5米和大于1米的位置(距离范围可以根据您的理解进行选择)。


3
基本的GPS系统误差已经描述,例如,在这里。使用扩展卡尔曼滤波器将来自GPS(IMHO原始、未过滤的GPS数据来自GpsStatus.NmeaListener/OnNmeaMessageListener)和其他传感器(加速度计、陀螺仪、磁力计等)的数据融合可以提高精度,但是今天还没有可能创建具有所描述功能(沿着100m“复杂”图形的路径跟踪)的好应用程序。如果简化路径(如图所示)。

"Simplified" heart path

跟踪越过检查点的“地理围栏”及其顺序-也许您可以实现该功能,但在许多情况下精度将不可接受。


2
“没有得到精确的GPS坐标是很常见的,因为它取决于许多因素,比如:

  • 开放的天空或室内条件
  • 障碍物,如云、建筑物
  • 设备上的GPS芯片配置/ CPU功率
  • 蜂窝网络信号强度

您可以采用一些解决方法。

  • 使用Kalman Filter算法,如@andrii-omelchenko所建议的。
  • 或者您可以使用地理围栏机制并设置地理围栏,Android系统将在进入/退出地理围栏时向您回调

    • A) 设置几个地理围栏圆(可能是接下来一组纬度和经度的3~5个)
    • B) 进入该地理围栏后,标记步骤为完成
    • C) 重复A直到用户完全走过全程。
  • 结合上述地理围栏逻辑,使用Android活动识别来检查用户是否步行

以下是示例地理围栏图像,以帮助您了解

enter image description here


2

是的,GPS位置波动是正常的,但如果您想要更高的准确性,请执行以下操作。这里我向您展示我的技巧。

我相信您也在做同样的事情。只是向您展示我的方法。

步骤1.创建此类 GoogleLocationService.java

public class GoogleLocationService {
private GoogleServicesCallbacks callbacks = new GoogleServicesCallbacks();
LocationUpdateListener locationUpdateListener;
Context activity;
protected GoogleApiClient mGoogleApiClient;
protected LocationRequest mLocationRequest;

public static final long UPDATE_INTERVAL_IN_MILLISECONDS = 2000;


public GoogleLocationService(Context activity, LocationUpdateListener locationUpdateListener) {
    this.locationUpdateListener = locationUpdateListener;
    this.activity = activity;
    buildGoogleApiClient();
}

protected synchronized void buildGoogleApiClient() {
    //Log.i(TAG, "Building GoogleApiClient");
    mGoogleApiClient = new GoogleApiClient.Builder(activity)
            .addConnectionCallbacks(callbacks)
            .addOnConnectionFailedListener(callbacks)
            .addApi(LocationServices.API)
            .build();
    createLocationRequest();
    mGoogleApiClient.connect();
}

protected void createLocationRequest() {
    mLocationRequest = new LocationRequest();
    mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
    mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

}

private class GoogleServicesCallbacks implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {

    @Override
    public void onConnected(Bundle bundle) {
        startLocationUpdates();
    }

    @Override
    public void onConnectionSuspended(int i) {
        mGoogleApiClient.connect();
    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {

        if (connectionResult.getErrorCode() == ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED) {
            Toast.makeText(activity, "Google play service not updated", Toast.LENGTH_LONG).show();

        }
        locationUpdateListener.cannotReceiveLocationUpdates();
    }

    @Override
    public void onLocationChanged(Location location) {
        if (location.hasAccuracy()) {
            if (location.getAccuracy() < 30) {
                locationUpdateListener.updateLocation(location);
            }
        }
    }
}

private static boolean locationEnabled(Context context) {
    boolean gps_enabled = false;
    LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
    try {
        gps_enabled = lm.isProviderEnabled(LocationManager.GPS_PROVIDER);
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return gps_enabled;
}

private boolean servicesConnected(Context context) {
    return isPackageInstalled(GooglePlayServicesUtil.GOOGLE_PLAY_STORE_PACKAGE, context);
}

private boolean isPackageInstalled(String packagename, Context context) {
    PackageManager pm = context.getPackageManager();
    try {
        pm.getPackageInfo(packagename, PackageManager.GET_ACTIVITIES);
        return true;
    } catch (PackageManager.NameNotFoundException e) {
        e.printStackTrace();
        return false;
    }
}


public void startUpdates() {
    /*
     * Connect the client. Don't re-start any requests here; instead, wait
     * for onResume()
     */
    if (servicesConnected(activity)) {
        if (locationEnabled(activity)) {
            locationUpdateListener.canReceiveLocationUpdates();
            startLocationUpdates();
        } else {
            locationUpdateListener.cannotReceiveLocationUpdates();
            Toast.makeText(activity, "Unable to get your location.Please turn on your device Gps", Toast.LENGTH_LONG).show();
        }
    } else {
        locationUpdateListener.cannotReceiveLocationUpdates();
        Toast.makeText(activity, "Google play service not available", Toast.LENGTH_LONG).show();
    }
}

//stop location updates
public void stopUpdates() {
    stopLocationUpdates();
}

//start location updates
private void startLocationUpdates() {

    if (checkSelfPermission(activity, ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && checkSelfPermission(activity, ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
        return;
    }
    if (mGoogleApiClient.isConnected()) {
        LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, callbacks);
    }
}

public void stopLocationUpdates() {
    if (mGoogleApiClient.isConnected()) {
        LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, callbacks);
    }
}

public void startGoogleApi() {
    mGoogleApiClient.connect();
}

public void closeGoogleApi() {
    mGoogleApiClient.disconnect();
}

 }

步骤二:将此接口命名为LocationUpdateListener.java

 public interface LocationUpdateListener {

/**
 * Called immediately the service starts if the service can obtain location
 */
void canReceiveLocationUpdates();

/**
 * Called immediately the service tries to start if it cannot obtain location - eg the user has disabled wireless and
 */
void cannotReceiveLocationUpdates();

/**
 * Called whenever the location has changed (at least non-trivially)
 * @param location
 */
void updateLocation(Location location);

/**
 * Called when GoogleLocationServices detects that the device has moved to a new location.
 * @param localityName The name of the locality (somewhere below street but above area).
 */
void updateLocationName(String localityName, Location location);
}

您可以直接在需要更新位置并删除位置服务的类中调用以下代码。
 private GoogleLocationService googleLocationService;

 googleLocationService = new GoogleLocationService(context, new LocationUpdateListener() {
    @Override
    public void canReceiveLocationUpdates() {
    }

    @Override
    public void cannotReceiveLocationUpdates() {
    }

    //update location to our servers for tracking purpose
    @Override
    public void updateLocation(Location location) {
        if (location != null ) {
            Timber.e("updated location %1$s %2$s", location.getLatitude(), location.getLongitude());

        }
    }

    @Override
    public void updateLocationName(String localityName, Location location) {

        googleLocationService.stopLocationUpdates();
    }
});
googleLocationService.startUpdates();


and call this onDestroy 
if (googleLocationService != null) {
    googleLocationService.stopLocationUpdates();
}

如果你看下代码,我做了一件事。 getAccuracy() 描述的是以米为单位的偏差。因此,该数值越小,精度就会越好。

public void onLocationChanged(Location location) {
    if (location.hasAccuracy()) {
        if (location.getAccuracy() < 30) {
            locationUpdateListener.updateLocation(location);
        }
    }
}

感谢,希望这可以帮助您。

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