Upgrade to Pro — share decks privately, control downloads, hide ads and more …

位置情報を正確にトラッキングする技術

 位置情報を正確にトラッキングする技術

DroidKaigi 2017で発表した資料です。

F3b8da330ac8f07f3aa4d8e240506b6b?s=128

Takamitsu Mizutori

March 15, 2017
Tweet

Transcript

  1. ਫௗܟຬ lҐஔ৘ใΛਖ਼֬ʹτϥοΩϯά͢Δٕज़z @mizutory /

  2. ࣗݾ঺հ 4POZ&SJDTTPOͰܞଳి࿩ͷ։ൃΛ9QFSJB9
 ·Ͱ΍͍ͬͯ·ͨ͠ɻ
 %FWJDF%SJWFS #PPU$PEF .FNPSZ.BOBHFS (PMESVTI$PNQVUJOHגࣜձࣾΛ࢝ΊΔ Ґஔ৘ใΛ࢖ͬͨ"OESPJEJ04ΞϓϦΛ։ൃͯ͠དྷ·ͨ͠ɻ

  3. None
  4. Ґஔ৘ใͷऔಘ Ϛοϓ΁ͷඳը Ґஔ৘ใͷϑΟϧλϦϯά όοςϦʔফඅʹ͍ͭͯ

  5. Ґஔ৘ใͷऔಘ

  6. Ґஔ৘ใτϥοΩϯάͷͨΊͷΞʔΩςΫνϟ .BJO"DUJWJUZ -PDBUJPO4FSWJDF TUBSU CJOE PO4FSWJDF$POOFDUFE

  7. -PDBUJPO.BOBHFS LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE); Criteria criteria = new

    Criteria(); criteria.setAccuracy(Criteria.ACCURACY_FINE); criteria.setPowerRequirement(Criteria.POWER_HIGH); criteria.setAltitudeRequired(false); criteria.setSpeedRequired(true); criteria.setCostAllowed(true); criteria.setBearingRequired(false); //API level 9 and up criteria.setHorizontalAccuracy(Criteria.ACCURACY_HIGH); criteria.setVerticalAccuracy(Criteria.ACCURACY_HIGH); Integer gpsFreqInMillis = 1000; Integer gpsFreqInDistance = 5; // in meters locationManager.addGpsStatusListener(this); locationManager.requestLocationUpdates(gpsFreqInMillis, gpsFreqInDistance, criteria, this, null); LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE); Criteria criteria = new Criteria(); criteria.setAccuracy(Criteria.ACCURACY_FINE); criteria.setPowerRequirement(Criteria.POWER_HIGH); criteria.setAltitudeRequired(false); criteria.setSpeedRequired(true); criteria.setCostAllowed(true); criteria.setBearingRequired(false); //API level 9 and up criteria.setHorizontalAccuracy(Criteria.ACCURACY_HIGH); criteria.setVerticalAccuracy(Criteria.ACCURACY_HIGH); Integer gpsFreqInMillis = 1000; Integer gpsFreqInDistance = 5; // in meters locationManager.addGpsStatusListener(this); locationManager.requestLocationUpdates(gpsFreqInMillis, gpsFreqInDistance, criteria, this, null); LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE); Criteria criteria = new Criteria(); criteria.setAccuracy(Criteria.ACCURACY_FINE); criteria.setPowerRequirement(Criteria.POWER_HIGH); criteria.setAltitudeRequired(false); criteria.setSpeedRequired(true); criteria.setCostAllowed(true); criteria.setBearingRequired(false); //API level 9 and up criteria.setHorizontalAccuracy(Criteria.ACCURACY_HIGH); criteria.setVerticalAccuracy(Criteria.ACCURACY_HIGH); Integer gpsFreqInMillis = 1000; Integer gpsFreqInDistance = 5; // in meters locationManager.addGpsStatusListener(this); locationManager.requestLocationUpdates(gpsFreqInMillis, gpsFreqInDistance, criteria, this, null); LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE); Criteria criteria = new Criteria(); criteria.setAccuracy(Criteria.ACCURACY_FINE); criteria.setPowerRequirement(Criteria.POWER_HIGH); criteria.setAltitudeRequired(false); criteria.setSpeedRequired(true); criteria.setCostAllowed(true); criteria.setBearingRequired(false); //API level 9 and up criteria.setHorizontalAccuracy(Criteria.ACCURACY_HIGH); criteria.setVerticalAccuracy(Criteria.ACCURACY_HIGH); Integer gpsFreqInMillis = 1000; Integer gpsFreqInDistance = 5; // in meters locationManager.addGpsStatusListener(this); locationManager.requestLocationUpdates(gpsFreqInMillis, gpsFreqInDistance, criteria, this, null);
  8. $SJUFSJBͷ࡞੒ Criteria criteria = new Criteria(); criteria.setAccuracy(Criteria.ACCURACY_FINE); criteria.setPowerRequirement(Criteria.POWER_HIGH); criteria.setAltitudeRequired(false); criteria.setSpeedRequired(true);

    criteria.setCostAllowed(true); criteria.setBearingRequired(false); //API level 9 and up criteria.setHorizontalAccuracy(Criteria.ACCURACY_HIGH); criteria.setVerticalAccuracy(Criteria.ACCURACY_HIGH); Criteria criteria = new Criteria(); criteria.setAccuracy(Criteria.ACCURACY_FINE); criteria.setPowerRequirement(Criteria.POWER_HIGH); criteria.setAltitudeRequired(false); criteria.setSpeedRequired(true); criteria.setCostAllowed(true); criteria.setBearingRequired(false); //API level 9 and up criteria.setHorizontalAccuracy(Criteria.ACCURACY_HIGH); criteria.setVerticalAccuracy(Criteria.ACCURACY_HIGH); Criteria criteria = new Criteria(); criteria.setAccuracy(Criteria.ACCURACY_FINE); criteria.setPowerRequirement(Criteria.POWER_HIGH); criteria.setAltitudeRequired(false); criteria.setSpeedRequired(true); criteria.setCostAllowed(true); criteria.setBearingRequired(false); //API level 9 and up criteria.setHorizontalAccuracy(Criteria.ACCURACY_HIGH); criteria.setVerticalAccuracy(Criteria.ACCURACY_HIGH); Criteria criteria = new Criteria(); criteria.setAccuracy(Criteria.ACCURACY_FINE); criteria.setPowerRequirement(Criteria.POWER_HIGH); criteria.setAltitudeRequired(false); criteria.setSpeedRequired(true); criteria.setCostAllowed(true); criteria.setBearingRequired(false); //API level 9 and up criteria.setHorizontalAccuracy(Criteria.ACCURACY_HIGH); criteria.setVerticalAccuracy(Criteria.ACCURACY_HIGH);
  9. Ґஔ৘ใऔಘ࣌ͷίʔϧόοΫPO-PDBUJPO$IBOHFE ͜͜Ͱ-PDBUJPOΦϒδΣΫτΛऔಘͯ͠ɺ-PDBM#SPBEDBTU.BOBHFSΛ࢖ͬ ͯαʔϏεͷ֎ʹ௨஌͢Δɻ public void onLocationChanged(final Location newLocation) { if(isLogging){

    //locationList.add(newLocation); filterAndAddLocation(newLocation); } Intent intent = new Intent("LocationUpdated"); intent.putExtra("location", newLocation); LocalBroadcastManager.getInstance(this.getApplication()).sendBroadcast(intent); }
  10. Ϛοϓ΁ͷඳը

  11. .BQ"1*,FZͷऔಘ

  12. HSBEMF

  13. .BQ7JFX <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.goldrushcomputing.androidlocationstarterkit.MainActivity">

    <com.google.android.gms.maps.MapView android:id="@+id/map" android:layout_width="match_parent" android:layout_height="match_parent" />
  14. mapView = (MapView) this.findViewById(R.id.map); mapView.onCreate(savedInstanceState); mapView.getMapAsync(new OnMapReadyCallback() { @Override public

    void onMapReady(GoogleMap googleMap) { map = googleMap; map.getUiSettings().setZoomControlsEnabled(false); map.setMyLocationEnabled(false); map.getUiSettings().setCompassEnabled(true); map.getUiSettings().setMyLocationButtonEnabled(true); w ζʔϜίϯτϩʔϧΛඇදࣔʹ͢Δɻ w ࣗ෼ͷҐஔΛࣔ͢ΠϯσΟέʔλʔΛඇදࣔʹ͢Δɻ ޙͰࣗ෼Ͱඳ͘ʣ w ίϯύεͷػೳΛΦϯʹ͢Δ w ݱࡏ஍ʹඈͿϘλϯΛදࣔʹ͢Δ
  15. Ϛοϓ্΁ͷඳը -PDBUJPO4FSWJDF .BJO"DUJWJUZ -PDBUJPO

  16. Ϛοϓ্΁ͷඳը NBQBEE$JSDMF

  17. Ϛοϓ্΁ͷඳը NBQBEE.BSLFS  [PPNͯ͠΋ಉ͡େ͖͞

  18. Ϛοϓ্΁ͷඳը NBQBEE1PMZMJOF

  19. None
  20. ϑΟϧλϦϯά

  21. औಘ࣌ࠁͰϑΟϧλʔ͢Δ

  22. Ґஔ৘ใऔಘλΠϛϯάʹӨڹΛ༩͑Δ΋ͷ • GPSӴ੕ͷҐஔ ʢ಄্ʹͨ͘͞Μ͋Δ͔Ͳ͏͔ʣ • Ӵ੕ͱAndroidػͱͷؒͷো֐෺ʢϏϧɺथ໦ɺӢͳ Ͳʣ • ܞଳి࿩ج஍ہͷҐஔ ʢۙ͘ʹͨ͘͞Μ͋Δ͔ʣ • ܞଳి࿩ج஍ہͱAndroidػͱͷؒͷো֐෺ͷঢ়گ

    ݹ͍Ґஔ৘ใ Ωϟογϡ͞ΕͨҐஔ৘ใ
  23. Ґஔ৘ใͷݹ͞Λଌఆͯ͠ϑΟϧλʔ͢Δ private long getLocationAge(Location newLocation){ long locationAge; if(android.os.Build.VERSION.SDK_INT >= 17)

    { long currentTimeInMilli = (long)(SystemClock.elapsedRealtimeNanos() / 1000000); long locationTimeInMilli = (long)(newLocation.getElapsedRealtimeNanos() / 1000000 locationAge = currentTimeInMilli - locationTimeInMilli; }else{ locationAge = System.currentTimeMillis() - newLocation.getTime(); } return locationAge; } long age = getLocationAge(location); if(age > 5 * 1000){ //more than 5 seconds Log.d(TAG, "Location is old"); oldLocationList.add(location); return false; }
  24. ਫฏํ޲ͷਫ਼౓ͰϑΟϧλʔ͢Δ

  25. ਫฏํ޲ͷਫ਼౓͕औΕͳ͍έʔεΛ஄͘ if(location.getAccuracy() <= 0){ Log.d(TAG, "Latitidue and longitude values are

    invalid."); noAccuracyLocationList.add(location); return false; }
  26. ਫฏํ޲ͷਫ਼౓ͰϑΟϧλʔ͢Δ float horizontalAccuracy = location.getAccuracy(); if(horizontalAccuracy > 10){ //10meter filter

    Log.d(TAG, "Accuracy is too low."); inaccurateLocationList.add(location); return false; }
  27. ϑΟϧλʔͨ͠ྫ

  28. HFU"DDVSBDZ  ͷਫ਼౓͸

  29. ,BMNBO'JMUFSͰϑΟϧλʔ͢Δ /* Kalman Filter */ float Qvalue; long locationTimeInMillis =

    (long)(location.getElapsedRealtimeNanos() / 1000000); long elapsedTimeInMillis = locationTimeInMillis - runStartTimeInMillis; if(currentSpeed == 0.0f){ Qvalue = 3.0f; //3 meters per second }else{ Qvalue = currentSpeed; // meters per second } kalmanFilter.Process(location.getLatitude(), location.getLongitude(), location.getAccuracy(), elapsedTimeInMillis, Qvalue double predictedLat = kalmanFilter.get_lat(); double predictedLng = kalmanFilter.get_lng(); Location predictedLocation = new Location("");//provider name is unecessary predictedLocation.setLatitude(predictedLat);//your coords of course predictedLocation.setLongitude(predictedLng); float predictedDeltaInMeters = predictedLocation.distanceTo(location); if(predictedDeltaInMeters > 60){ Log.d(TAG, "Kalman Filter detects mal GPS, we should probably remove this from track"); kalmanFilter.consecutiveRejectCount += 1; if(kalmanFilter.consecutiveRejectCount > 3){ kalmanFilter = new KalmanLatLong(3); //reset Kalman Filter if it rejects more than 3 times in raw. } kalmanNGLocationList.add(location); return false; }else{ kalmanFilter.consecutiveRejectCount = 0; }
  30. ,BMNBO'JMUFSͰϑΟϧλʔ͢Δ

  31. ,BMNBO'JMUFSͰϑΟϧλʔ͢Δ

  32. RunKeeper

  33. Nike+

  34. Nike+

  35. όοςϦʔ

  36. 時間(秒) 距離(m) GPS個数 バッテリー
 (スタート時) バッテリー
 (ゴール) 消費量 42km走った 場合

    5時間走った 場合 Nexus6 Low Battery (every 5m) 1968 5710.0 790 16% 12% 4% 29.6% 36.6% Nexus6p Mid Battery (every 1m) 1785 5738.8 1349 50% 48% 2% 14.7% 20.1% Nexus6 Low Battery (every 5m) 1497 4583.8 581 22% 20% 2% 18.4% 24% Nexus6p High Battery (every 5m) 1684 5036.0 675 98% 95% 3% 25.1% 32% ෼ɺLNͰͷফඅྔ
  37. όοςϦʔ࢒ྔͷऔಘ private BroadcastReceiver batteryInfoReceiver = new BroadcastReceiver(){ @Override public void

    onReceive(Context ctxt, Intent intent) { int batteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0); int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1); float batteryLevelScaled = batteryLevel / (float)scale; batteryLevelArray.add(Integer.valueOf(batteryLevel)); batteryLevelScaledArray.add(Float.valueOf(batteryLevelScaled)); batteryScale = scale; } };
  38.  όοςϦʔ࢒ྔʹΑͬͯ$SJUFSJBΛม͑Δ  .JOJNVN%JTUBODFͱ.JOJNVN5JNF͸ม͑ͯ΋(14νοϓ͕ফඅ͢ΔΤωϧΪʔ͸ಉ ͡ɻҐஔ৘ใΛड͚औ͔ͬͯΒͷॲཧʹΤωϧΪʔΛ࢖͍ͬͯΔ৔߹ʢαʔόʔ΁ͷૹ৴ͳͲʣ ͸ɺ.JOJNVN%JTUBODFͱ.JOJNVN5JNFͷ஋Λେ͖͘͢Δɻ όοςϦʔ࢒ྔΛҙࣝͨ͠ઃܭ LocationManager locationManager =

    (LocationManager) getSystemService(LOCATION_SERVICE); Criteria criteria = new Criteria(); criteria.setAccuracy(Criteria.ACCURACY_FINE); criteria.setPowerRequirement(Criteria.POWER_HIGH); criteria.setAltitudeRequired(false); criteria.setSpeedRequired(true); criteria.setCostAllowed(true); criteria.setBearingRequired(false); //API level 9 and up criteria.setHorizontalAccuracy(Criteria.ACCURACY_HIGH); criteria.setVerticalAccuracy(Criteria.ACCURACY_HIGH); Integer gpsFreqInMillis = 1000; Integer gpsFreqInDistance = 5; // in meters locationManager.addGpsStatusListener(this); locationManager.requestLocationUpdates(gpsFreqInMillis, gpsFreqInDistance, criteria, this, null);
  39. 時間 (秒) 距離(m) GPS個数 バッテリー
 (スタート時) バッテリー
 (ゴール) 消費量 42km走った

    場合 5時間走った 場合 Nexus6p Mid Battery (every 1m, 1sec) 1785 5738.8 1349 50% 48% 2% 14.7% 20.1% Nexus6p High Battery (every 5m, 1sec) 1684 5036.0 675 98% 95% 3% 25.1% 32% .JOJNVN%JTUBODFͱ.JOJNVN5JNF͸ม͑ͨ৔߹ͷόοςϦʔফඅྔ
  40. ଱ٱੑʹ͍ͭͯ ʔʼଓ͖͸࠙਌ձͰʂ

  41. Blog medium.com/@mizutory/ github.com/mizutori/AndroidLocationStarterKit Code mizutori@goldrushcomputing.com @mizutory Twitter www.goldrushcomputing.com