为什么在安卓系统中执行getSpeed()方法总是返回0?

23

我需要从GPS获取速度和航向。然而,从location.getSpeed()获得的唯一数字是0,有时还无法获取。我的代码:

        String provider = initLocManager();
    if (provider == null)
        return false;
    LocationListener locListener = new LocationListener() {
        public void onLocationChanged(Location location) {
            updateWithNewLocation(location, interval, startId);
            Log.i(getString(R.string.logging_tag), "speed =" + location.getSpeed());
        }

        public void onProviderDisabled(String provider){
            updateWithNewLocation(null, interval, startId);
        }

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

    _locManager.requestLocationUpdates(provider, interval,  DEFAULT_GPS_MIN_DISTANCE, locListener);


    private String initLocManager() {
    String context = Context.LOCATION_SERVICE;
    _locManager = (LocationManager) getSystemService(context);

    Criteria criteria = new Criteria();
    criteria.setAccuracy(Criteria.ACCURACY_FINE);
    criteria.setAltitudeRequired(false);
    criteria.setBearingRequired(true);
    criteria.setSpeedRequired(true);
    criteria.setCostAllowed(true);
    //criteria.setPowerRequirement(Criteria.POWER_LOW);
    String provider = _locManager.getBestProvider(criteria, true);

    if (provider == null || provider.equals("")) {
        displayGPSNotEnabledWarning(this);
        return null;
    }

    return provider;
}

我尝试使用Criteria,但没有成功。有人知道问题出在哪里吗?

11个回答

26

location.getSpeed()只返回使用location.setSpeed()设定的值,这是可以为位置对象设置的值。

要使用GPS计算速度,您需要进行一些数学计算:

Speed = distance / time

所以你需要做:

(currentGPSPoint - lastGPSPoint) / (time between GPS points)

所有的速度都转换为英尺/秒,或者以您想要显示的方式展示。当我制作跑步应用程序时,我就是这样做的。

更具体地说,您需要计算绝对距离:

(sqrt((currentGPSPointX - lastGPSPointX)^2) + (currentGPSPointY - lastGPSPointY)^2)) / (time between GPS points)

可能有助于创建一个新的TrackPoint类或类似的东西,该类将保留GPS位置以及拍摄时间。


1
它会返回米或千米的距离? - Shashwat Gupta
6
我发现getSpeed()返回零的问题,即使'hasSpeed()'为真并且有运动。在某些6.0.1设备上,它总是返回零!这是来自融合位置服务的问题。普通的LocationManager提供速度,但在更新中似乎不太准确和不够频繁。所以我按照这里建议的使用自己的速度计算方式。 - Dave Hubbard
9年过去了,这个方法对我起作用了。在Android Studio模拟器的位置路线移动中,location.speed(kotlin)返回了正确的结果(假定),但是在现实世界驾驶中始终返回0公里/小时。唉唉。 - BENN1TH
1
这个答案是错误的,它并不总是返回0,并且不需要使用setSpeed()方法使其不为零。Android可以在没有你的情况下获取速度,不需要手动计算。 - user924

13

Imbru答案看起来非常好,但如果你正在使用单位,则并不是很有帮助。

这是我计算每秒米速(m/s)的方法。

object : LocationListener() {
    var previousLocation: Location? = null

    override fun onLocationChanged(location: Location) {
        val speed = if (location.hasSpeed()) {
            location.speed
        } else {
            previousLocation?.let { lastLocation ->
                // Convert milliseconds to seconds
                val elapsedTimeInSeconds = (location.time - lastLocation.time) / 1_000.
                val distanceInMeters = lastLocation.distanceTo(location)
                // Speed in m/s
                distanceInMeters / elapsedTimeInSeconds
            } ?: 0.0
        }
        previousLocation = location

        /* There you have it, a speed value in m/s */
        functionThatUsesSpeedInMeterPerSecond(speed)

        . . .

    }

    . . .

}

5
仅在location.hasSpeed()==false的情况下进行速度计算。有些人报告说即使location.hasSpeed()==truelocation.getSpeed()始终返回0。因此,我会使用location.hasSpeed() && location.getSpeed()>0作为条件。 - Julien Kronegg
1
我赞同Julien Kronegg的建议,如果.hasSpeed()== true,则更喜欢直接使用getSpeed用法,因为GPS不需要两个位置及其距离来计算速度,而是可以使用多普勒效应更准确地计算速度。这意味着在某一时刻,getSpeed将更准确,而距离/时间计算将随着两个位置之间经过的时间增加而变得更精确,但仅提供两个测量之间的平均速度,而不是新位置进入时的速度。 - FrankKrumnow
@JulienKronegg 是谁报告说即使有速度也可能为0的? - user924
@user924,你可以在SO上找到一些帖子:https://www.google.com/search?q=Android+location.getSpeed()+always+return+0+site:stackoverflow.com - Julien Kronegg
@JulienKronegg 可能他们没有正确的检查(使用 hasSpeed)。 - user924

9
这是我自定义的LocationListener,用于手动获取速度和通过位置对象获取速度(如果有速度信息)。
 new LocationListener() {
        private Location mLastLocation;

        @Override
        public void onLocationChanged(Location pCurrentLocation) {
            //calcul manually speed
            double speed = 0;
            if (this.mLastLocation != null)
                speed = Math.sqrt(
                        Math.pow(pCurrentLocation.getLongitude() - mLastLocation.getLongitude(), 2)
                                + Math.pow(pCurrentLocation.getLatitude() - mLastLocation.getLatitude(), 2)
                ) / (pCurrentLocation.getTime() - this.mLastLocation.getTime());
            //if there is speed from location
            if (pCurrentLocation.hasSpeed())
                //get location speed
                speed = pCurrentLocation.getSpeed();
            this.mLastLocation = pCurrentLocation;
            ////////////
            //DO WHAT YOU WANT WITH speed VARIABLE
            ////////////
        }

        @Override
        public void onStatusChanged(String s, int i, Bundle bundle) {

        }

        @Override
        public void onProviderEnabled(String s) {

        }

        @Override
        public void onProviderDisabled(String s) {

        }
    };

1
我使用了你的代码,它返回速度为2.25,但我的车速大约是60公里/小时。如何精确计算运动速度? - prasanthMurugan
@Kevin ABRIOUX,计算速度的单位是什么?是公里/小时吗? - AKASH WANGALWAR
建议使用位置.distanceTo(其他位置)的内置方法来计算位置之间的距离,而不是自行计算。时间差的除数可以保持不变。输出为每秒米数,因此乘以3.6即可得到公里/小时。 - FrankKrumnow
我错误地认为除数是正确的。但它计算的时间差是以毫秒为单位的,而速度是以米每秒为单位的。 - FrankKrumnow

6
在一个球形星球上,应该使用以下公式计算距离:
private static Double distance(Location one, Location two) {
       int R = 6371000;        
       Double dLat = toRad(two.getLatitude() - one.getLatitude());
       Double dLon = toRad(two.getLongitude() - one.getLongitude());
       Double lat1 = toRad(one.getLatitude());
       Double lat2 = toRad(two.getLatitude());         
       Double a = Math.sin(dLat / 2) * Math.sin(dLat / 2)
               + Math.sin(dLon / 2) * Math.sin(dLon / 2) * Math.cos(lat1) * Math.cos(lat2);        
       Double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));        
       Double d = R * c;
       return d;
   }
private static double toRad(Double d) {
       return d * Math.PI / 180;
   }

3
如果用户在坡上怎么办? - Anil P Babu
即使是谷歌似乎也忘记了高度差。也许是因为糟糕的卫星定位和仅提供2D位置的基站/ WiFi?查看Location.distanceTo代码-我不完全理解-我可以看到两个位置的纬度和经度以及方位角扮演着一个角色,但高度却无处可见。 - FrankKrumnow

1

getSpeed()方法实际上很好用,但您需要使用高请求间隔,例如1秒,并且需要高精度。首先,我使用了3秒的间隔和PRIORITY_BALANCED_POWER_ACCURACY,但一直收到0值,直到我按照我的说法进行更改。我正在使用融合位置提供程序API。

public class Main3Activity extends AppCompatActivity {

private FusedLocationProviderClient mFusedLocationClient;
private int speed;
private double lat;
private double lng;

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

    mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
}

@Override
protected void onResume() {
    super.onResume();
    if(!runtime_permissions()) {
        requestLocations();
    }
}

@Override
protected void onPause() {
    super.onPause();
    //stop location updates when Activity is no longer active
    if (mFusedLocationClient != null) {
        mFusedLocationClient.removeLocationUpdates(mLocationCallback);
    }
}

@SuppressLint("MissingPermission")
private void requestLocations(){
    LocationRequest mLocationRequest = new LocationRequest();
    mLocationRequest.setInterval(1000);;
    mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

    mFusedLocationClient.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.myLooper());
}

LocationCallback mLocationCallback = new LocationCallback() {
    @Override
    public void onLocationResult(LocationResult locationResult) {
        List<Location> locationList = locationResult.getLocations();
        if (locationList.size() > 0) {
            //The last location in the list is the newest
            Location location = locationList.get(locationList.size() - 1);

            lat = location.getLatitude();
            lng = location.getLongitude();

            //speed in km/h
            speed = (int) ((location.getSpeed() * 3600) / 1000);

        }
    }
};

private boolean runtime_permissions() {
    if(Build.VERSION.SDK_INT >= 23 && ContextCompat.checkSelfPermission(this,
            Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED){

        requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION},100);

        return true;
    }
    return false;
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if(requestCode == 100){
        if( grantResults[0] == PackageManager.PERMISSION_GRANTED){
            onResume();
        }else{
            runtime_permissions();
        }
    }
}

}


我认为使用融合定位提供程序时,由于在信号不好或没有GPS的情况下,您将得到三角形基站或WiFi附近读数,这些读数对于准确性来说相当不准确,对于速度来说可能非常糟糕,因此您不一定会始终获得速度或者速度很差。如果速度很重要,建议仅使用GPS提供程序。虽然我不是专家,但我只在关闭WiFi时使用GPS以节省电力。 - FrankKrumnow

1

(1) 我相信你可以使用 requestLocationUpdates() 方法,然后创建一个 LocationListener 类,并将其 onLocationChange 方法设置为显示 getSpeed()。这就是我最近看到的使用 Location.getLatitude 和 Location.getLongitude 的方法,所以我相信你可以以同样的方式使用 getSpeed(),对吗?

(2) 在刚刚阅读了 Eclipse 描述窗口之后,我发现它确实说了前面那个人说的话:"如果 hasSpeed() 为 false,则返回 0.0f。"但也许这会有所帮助:http://www.ehow.com/how_5708473_convert-latitude-feet.html :)


1

嘿,我也曾经遇到过同样的问题,但现在我已经解决了!只需将值乘以18/5,它几乎可以得到准确的值。

speed=location.getSpeed()*18/5

同时将interval设置为1000*2,将fastest interval设置为1000*1以提高准确度。


2
这个 18/5 的神奇数字从哪里来?另外,这如何解决“getSpeed() 返回 0”的问题? - juzraai
2
getSpeed() 方法返回的值为 m/s,该值始终为零,因此可以使用 18/5 将其转换为 km/h。1 m/s=18/5 Km/hr。 - Aswath shobi
每小时秒数 = 3600; 每公里米数 = 1000。 因此1 m/s = 3600/1000 km/h = 36/10 km/h = 18/5 km/h = 3.6 km/h。 - FrankKrumnow

0

我基本上计算瞬时速度,然后使用setSpeed()方法将其添加到位置中。这相当准确,因为我在车辆内进行了比较,可以检查转速表。

private double calculateInstantaneousSpeed(Location location) {



    double insSpeed = 0;
    if (y1 == null && x1 <= -1) {
        //mark the location y1 at time x1
        y1 = location;
        x1 = duration.getDurationAsSeconds();


    } else {
         //mark the location y2 at time x2
        y2 = location;
        x2 = duration.getDurationAsSeconds();


        //calculate the slope of the curve (instantaneous speed)
        dy = y1.distanceTo(y2);
        dx = x2 - x1;

        insSpeed = dy / dx;

        y1 = y2;
        x1 = x2;

    }

    Singleton.getInstance().instantaneousSpeedSamples.add(insSpeed);
    //System.out.println("Instantaneous Speed m/s: "+insSpeed);
    return insSpeed;
}

0

我之前也遇到过这个问题,希望这可以帮到你。

返回0是因为你的设备无法锁定GPS,或无法连接到GPS。

我曾尝试使用旧款联想设备获取速度,但由于无法锁定GPS,它返回了0。

我尝试使用三星Galaxy Nexus,并返回了我的速度(具有更好的GPS传感器)。

你手机中的GPS传感器可能不太好,或者你所处的区域信号较弱,例如在房屋或建筑物内。


0

getspeed() 运行良好。 您不必使用距离公式进行计算。 getspeed中已经包含了该公式,只要有纬度和经度,就会有一个速度值。


9
我不认为这是正确的,我有经度和纬度,但getSpeed()总是返回0.0。 - Ahmed Hasn.

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