Уменьшить дрожание GPS

мое приложение основано на данных GPS, и для этого я использую Fused-location-provider. Время от времени я видел, что есть дрожание GPS, и некоторые координаты GPS не соответствуют дороге. Это неприемлемо. Что я пытался сделать, так это реализовать Kalman filter, и я сделал:

fun process(
    newSpeed: Float,
    newLatitude: Double,
    newLongitude: Double,
    newTimeStamp: Long,
    newAccuracy: Float
) {

    if (variance < 0) { // if variance < 0, object is unitialised, so initialise with current values
        setState(newLatitude, newLongitude, newTimeStamp, newAccuracy)
    } else { // else apply Kalman filter
        val duration = newTimeStamp - timeStamp
        if (duration > 0) { // time has moved on, so the uncertainty in the current position increases
            variance += duration * newSpeed * newSpeed / 1000
            timeStamp = newTimeStamp
        }

        val k = variance / (variance + newAccuracy * newAccuracy)

        latitude += k * (newLatitude - latitude)
        longitude += k * (newLongitude - longitude)
        variance *= (1 - k)

        retrieveLocation(newSpeed, longitude, latitude, duration, newAccuracy)
    }
}

И я использую его всякий раз, когда получаю новое местоположение, как

KalmanFilter.process(
    it.newSpeed,
    it.newLatitude,
    it.newLongitude,
    it.newTimeStamp,
    it.newAccuracy
)

Это помогло получить чуть более точные результаты, но все же - не устранило дрожание GPS вне дороги (см. изображение):

введите здесь описание изображения

Вещи, которые я хочу знать:

  1. Правильно ли реализован мой фильтр Калмана? Есть ли способ улучшить текущее решение?
  2. Как избежать сценариев, где координаты находятся за пределами дороги? (Что-то похожее на функцию привязки к дорогам, но как алгоритм внутри приложения)

person MaartinAndroid    schedule 21.11.2019    source источник
comment
Приемник должен реализовать свой собственный фильтр Калмана, так что ваш лишний. Попробуйте использовать значения точности - проверьте точность, когда ваши показания правильные и неправильные. Я ожидаю увидеть более низкую точность для неправильных значений. В этом случае используйте эти значения для фильтрации показаний. Если они выше некоторого порога, игнорируйте эти показания.   -  person TDG    schedule 22.11.2019
comment
@TDG Спасибо за ответ. Я также фильтрую точность объекта местоположения, но это только точность 68%. Есть ли что-нибудь еще, что я могу сделать?   -  person MaartinAndroid    schedule 22.11.2019
comment
Больше ничего не могу придумать, извините.   -  person TDG    schedule 22.11.2019


Ответы (1)


Похоже на вашей картинке не джиттер (или не только джиттер), а пропуск в данных GPS:

Разрыв между точками 1 и 2

между точками 1 и 2 на картинке нет точек на дороге, и нет возможности добавить их с помощью какой-либо реализации фильтра Калмана (если они отсутствуют в исходных необработанных данных GPS), потому что нет информации о местоположении дороги. Если вы не можете изменить прошивку трекера, вы можете использовать привязку к дорогам из API Google Maps Roads, как в этот ответ:

public class MainActivity extends AppCompatActivity implements OnMapReadyCallback {

    private GoogleMap mGoogleMap;
    private MapFragment mapFragment;

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

        mapFragment = (MapFragment) getFragmentManager()
                .findFragmentById(R.id.map_fragment);
        mapFragment.getMapAsync(this);
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {
        mGoogleMap = googleMap;

        List<LatLng> sourcePoints = new ArrayList<>();
        sourcePoints.add(new LatLng(-35.27801,149.12958));
        sourcePoints.add(new LatLng(-35.28032,149.12907));
        sourcePoints.add(new LatLng(-35.28099,149.12929));
        sourcePoints.add(new LatLng(-35.28144,149.12984));
        sourcePoints.add(new LatLng(-35.28194,149.13003));
        sourcePoints.add(new LatLng(-35.28282,149.12956));
        sourcePoints.add(new LatLng(-35.28302,149.12881));
        sourcePoints.add(new LatLng(-35.28473,149.12836));

        PolylineOptions polyLineOptions = new PolylineOptions();
        polyLineOptions.addAll(sourcePoints);
        polyLineOptions.width(5);
        polyLineOptions.color(Color.BLUE);
        mGoogleMap.addPolyline(polyLineOptions);

        mGoogleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(sourcePoints.get(0), 15));


        List<LatLng> snappedPoints = new ArrayList<>();
        new GetSnappedPointsAsyncTask().execute(sourcePoints, null, snappedPoints);
    }


    private String buildRequestUrl(List<LatLng> trackPoints) {
        StringBuilder url = new StringBuilder();
        url.append("https://roads.googleapis.com/v1/snapToRoads?path=");

        for (LatLng trackPoint : trackPoints) {
            url.append(String.format("%8.5f", trackPoint.latitude));
            url.append(",");
            url.append(String.format("%8.5f", trackPoint.longitude));
            url.append("|");
        }
        url.delete(url.length() - 1, url.length());
        url.append("&interpolate=true");
        url.append(String.format("&key=%s", <your_Google_Maps_API_key>);

        return url.toString();
    }


    private class GetSnappedPointsAsyncTask extends AsyncTask<List<LatLng>, Void, List<LatLng>> {

        protected void onPreExecute() {
            super.onPreExecute();
        }

        protected List<LatLng> doInBackground(List<LatLng>... params) {

            List<LatLng> snappedPoints = new ArrayList<>();

            HttpURLConnection connection = null;
            BufferedReader reader = null;

            try {
                URL url = new URL(buildRequestUrl(params[0]));
                connection = (HttpURLConnection) url.openConnection();
                connection.setRequestMethod("GET");
                connection.connect();

                InputStream stream = connection.getInputStream();

                reader = new BufferedReader(new InputStreamReader(stream));
                StringBuilder jsonStringBuilder = new StringBuilder();

                StringBuffer buffer = new StringBuffer();
                String line = "";

                while ((line = reader.readLine()) != null) {
                    buffer.append(line+"\n");
                    jsonStringBuilder.append(line);
                    jsonStringBuilder.append("\n");
                }

                JSONObject jsonObject = new JSONObject(jsonStringBuilder.toString());
                JSONArray snappedPointsArr = jsonObject.getJSONArray("snappedPoints");

                for (int i = 0; i < snappedPointsArr.length(); i++) {
                    JSONObject snappedPointLocation = ((JSONObject) (snappedPointsArr.get(i))).getJSONObject("location");
                    double lattitude = snappedPointLocation.getDouble("latitude");
                    double longitude = snappedPointLocation.getDouble("longitude");
                    snappedPoints.add(new LatLng(lattitude, longitude));
                }

            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (JSONException e) {
                e.printStackTrace();
            } finally {
                if (connection != null) {
                    connection.disconnect();
                }
                try {
                    if (reader != null) {
                        reader.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            return snappedPoints;
        }

        @Override
        protected void onPostExecute(List<LatLng> result) {
            super.onPostExecute(result);

            PolylineOptions polyLineOptions = new PolylineOptions();
            polyLineOptions.addAll(result);
            polyLineOptions.width(5);
            polyLineOptions.color(Color.RED);
            mGoogleMap.addPolyline(polyLineOptions);

            LatLngBounds.Builder builder = new LatLngBounds.Builder();
            builder.include(result.get(0));
            builder.include(result.get(result.size()-1));
            LatLngBounds bounds = builder.build();
            mGoogleMap.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 10));


        }
    }

}

не для всех, но хотя бы для подробного просмотра маршрута (из-за ограничений: 100 точек GPS и 2500 запросов в день на пользователя (IP) и 10 запросов в секунду) "онлайн" или "предварительно обработать" полный маршрут один раз, хранить и показывать не сырой, а уже обработанный маршрут - это своего рода "Что-то похожее на функциональность привязки к дорогам, но как алгоритм внутри приложения".

person Andrii Omelchenko    schedule 26.11.2019