Интерполяция между 2 местоположениями GPS на основе скорости ходьбы



Задача:





Дано два места:



L1 = (широта1, долгота1, метка времени1), L2 = (широта2, долгота2, метка времени2),



И настраиваемая, но постоянная скорость движения:



V = 1,39 метра в секунду (например).



Как мы можем интерполировать между этими двумя местоположениями, чтобы оценить местоположение пользователя, когда он путешествует от L1 to L2?





Я искал решения этой задачи и до сих пор обнаружил, что для малых расстояний (вдали от полюсов) можно использовать линейную интерполяцию. Итак, я посмотрел линейную интерполяцию в Википедии и нашел следующее:

// Imprecise method which does not guarantee v = v1 when t = 1,
// due to floating-point arithmetic error.
float lerp(float v0, float v1, float t) {
return v0 + t*(v1-v0);
}


Поэтому я думаю использовать эту функцию lerp для интерполяции широты и долготы между L1 и L2. Это была самая легкая часть. Как Я вычисляю t ? Я предполагаю, что мне нужно вычислить некоторые временные дельты, но как я могу учитывать скорость движения?





Правка:



Я тестирую различные методы сбора GPS-локаций. Для этой цели я записываю путевые точки-места на протяжении всей прогулки. Мне нужно интерполировать между этими путевыми точками, используя скорость движения,чтобы оценить свое положение вдоль дорожки. Затем я могу сравнить свои результаты с оценками, чтобы увидеть, насколько они хороши живущий.



Пример:



карта

604   5  

5 ответов:

Я предполагаю, что мне нужно вычислить некоторые временные дельты, но как это сделать фактор скорости передвижения?

При линейной интерполяции в вашем случае вы выполняете итерацию между двумя временными точками, используя переменную итерации t, которая выполняется от начального времени t1 до конечного времени t2, с заранее определенным шагом. Шаг Asume = 1 секунда, что вполне пригодно для вашего приложения.

long t1 = location1.getTimeStamp(); // in milliseconds;
long t2 = location2.getTimeStamp();
double deltaLat = location2.latitude - location1.latitude;
doule deltaLon =  location2.longitude- location1.longtude;
// remove this line if you don't have measured speed:
double deltaSpeed =  location2.speed - location1.speed;

long step = 1 * 1000; // 1 second in millis 
for (long t = t1; t1 < t2; t+= step) {

   // t0_1 shall run from 0.0 to (nearly) 1.0 in that loop
  double t0_1 = (t - t1) / (t2 - t1);
  double latInter = lat1 + deltaLat  * t0_1;
  double lonInter = lon1 + deltaLon  * t0_1;
  // remove the line below if you dont have speed
  double speedInter = speed1 + deltaSpeed  * t0_1;
  Location interPolLocation = new Location(latInter, lonInter, speedInter);
  // add interPolLocation to list or plot.
}

Внимательно посмотрите на вычислите расстояние, Азимут и многое другое между точками широты / долготы

Он содержит несколько формул и примеров JavaScript, которые могут вам помочь. Я знаю, что это не Java, но это должно быть достаточно просто, чтобы перенести код. Особенно учитывая подробное описание формулы.

Редактировать:

Хотя кажется нормальным использовать линейную интерполяцию для более коротких расстояний, на самом деле это может быть совсем не так, особенно когда вы получаете ближе к полюсам. Из примера видно, что вы находитесь в Гамбурге, это уже будет иметь эффект, который заметен на протяжении нескольких сотен метров. Смотрите этот ответ для хорошего объяснения.

Проблема: расстояние между 1 градусом долготы сильно варьируется в зависимости от вашей широты.

Это потому, что Земля не плоская, а сфера - фактически эллипсоид. Поэтому прямая линия на двумерной карте-это , а не прямая на глобус-и наоборот.

Чтобы обойти эту проблему, можно использовать следующий подход:

  1. получить пеленг от начальной координаты (L1) до конечной координаты (L2)
  2. вычислить новую координату от начальной координаты (L1) вдоль траектории большого круга, учитывая вычисленный Пеленг и заданное расстояние
  3. повторите этот процесс, но используя только что вычисленную координату в качестве начальной координаты

Мы можем создать несколько простых функции, которые сделают трюк для нас:

double radius = 6371; // earth's mean radius in km

// Helper function to convert degrees to radians
double DegToRad(double deg) {
    return (deg * Math.PI / 180);
}

// Helper function to convert radians to degrees
double RadToDeg(double rad) {
    return (rad * 180 / Math.PI);
}

// Calculate the (initial) bearing between two points, in degrees
double CalculateBearing(Location startPoint, Location endPoint) {
    double lat1 = DegToRad(startPoint.latitude);
    double lat2 = DegToRad(endPoint.latitude);
    double deltaLon = DegToRad(endPoint.longitude - startPoint.longitude);

    double y = Math.sin(deltaLon) * Math.cos(lat2);
    double x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(deltaLon);
    double bearing = Math.atan2(y, x);

    // since atan2 returns a value between -180 and +180, we need to convert it to 0 - 360 degrees
    return (RadToDeg(bearing) + 360) % 360;
}

// Calculate the destination point from given point having travelled the given distance (in km), on the given initial bearing (bearing may vary before destination is reached)
Location CalculateDestinationLocation(Location point, double bearing, double distance) {

    distance = distance / radius; // convert to angular distance in radians
    bearing = DegToRad(bearing); // convert bearing in degrees to radians

    double lat1 = DegToRad(point.latitude);
    double lon1 = DegToRad(point.logintude);

    double lat2 = Math.asin(Math.sin(lat1) * Math.cos(distance) + Math.cos(lat1) * Math.sin(distance) * Math.cos(bearing));
    double lon2 = lon1 + Math.atan2(Math.sin(bearing) * Math.sin(distance) * Math.cos(lat1), Math.cos(distance) - Math.sin(lat1) * Math.sin(lat2));
    lon2 = (lon2 + 3 * Math.PI) % (2 * Math.PI) - Math.PI; // normalize to -180 - + 180 degrees

    return new Location(RadToDeg(lat2), RadToDeg(lon2));
}

// Calculate the distance between two points in km
double CalculateDistanceBetweenLocations(Location startPoint, Location endPoint) {

    double lat1 = DegToRad(startPoint.latitude);
    double lon1 = DegToRad(startPoint.longitude);

    double lat2 = DegToRad(endPoint.latitude);
    double lon2 = DegToRad(endPoint.longitude);

    double deltaLat = lat2 - lat1;
    double deltaLon = lon2 - lon1;

    double a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) + Math.cos(lat1) * Math.cos(lat2) * Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2);
    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - 1));

    return (radius * c);
}
Для этого используется средний радиус Земли 6371 км. СмотритеWikipedia для объяснения этого числа и его точности. Теперь можно вычислить новое промежуточное положение между двумя точками, учитывая пройденное расстояние (в КМ):
double bearing = CalculateBearing(startLocation, endLocation);

Location intermediaryLocation = CalculateDestinationLocation(startLocation, bearing, distanceTravelled);

Предполагая скорость v (например, 1,39) метров в секунду, теперь можно использовать простую петлю for, чтобы получить точки с интервалом в 1 секунду:

List<Location> locations = new ArrayList<Location>();

// assuming duration in full seconds
for (int i = 0; i < duration; i++){
    double bearing = CalculateBearing(startLocation, endLocation);
    double distanceInKm = v / 1000;
    Location intermediaryLocation = CalculateDestinationLocation(startLocation, bearing, distanceInKm);

    // add intermediary location to list
    locations.add(intermediaryLocation);

    // set intermediary location as new starting location
    startLocation = intermediaryLocation;
}

В качестве дополнительного бонуса вы можете даже определить время, необходимое для перемещения между любыми двумя точками:

double distanceBetweenPoints = CalculateDistanceBetweenLocations(startPoint, endPoint) * 1000; // multiply by 1000 to get meters instead of km

double timeRequired = distanceBetweenPoints / v;
Это приведет к большей точности на любом расстоянии, чем простая линейная интерполяция, использующая только дельту координат. Хотя этот подход не идеален, он будет иметь погрешность в целом 0,3% или меньше, что вполне приемлемо. Если вам нужно лучшее решение, вы можете заглянуть в Формулу Винсенти.

Существуют некоторые другие стратегии интерполяции, которые работают лучше, чем линейная интерполяция, включая кинематическую интерполяцию, которая принимает в качестве входных данных начальную и конечную скорость точек привязки. В качестве примера приведем это сравнение из недавней работы (Long JA (2015) Kinematic interpolation of movement data. Int J Geogr Inf Sci 8816: 1-15. doi: 10.1080/13658816.2015.1081909):

Из Long JA (2015) Кинематическая интерполяция данных о движении. Int J Geogr Inf Sci 8816: 1-15. Дои: 10.1080/13658816.2015.1081909

Существуют R и Python реализации для кинематического Интерполяция. Написать версию Java должно быть легко.

Ага. Линейная интерполяция.

L1 = (1, 2, 3)
L2 = (4, 5, 6)
desired_number_of_interpolation_points = 9
interpolation_points = []

lat_step = (L2[0] - L1[0]) / (desired_number_of_interpolation_points + 1)
lon_step = (L2[1] - L1[1]) / (desired_number_of_interpolation_points + 1)
time_step = (L2[2] - L1[2]) / (desired_number_of_interpolation_points + 1) 

for i in range(1, desired_number_of_interpolation_points + 1)
    interpolation_points.append((lat_step * i, lon_step * i, time_step * i))

Вычисления, подобные этим, на самом деле очень просты, если вы сначала преобразуете lat / longs в n-векторы (https://en.wikipedia.org/wiki/N-vector после преобразования вы можете использовать стандартную интерполяцию, и вы также избежите любых проблем с большими расстояниями, полюсами или линией дат.

Если вы проверяете "внешние ссылки" на странице Википедии, есть страница (http://www.navlab.net/nvector/), где решены десять задач, и задача 6 на этой странице (интерполирована позиция) должна совпадать с вашим вопросом. Как вы можете видеть, это решение точно для любого расстояния и также работает в любом положении Земли, как полюса.

Comments

    Ничего не найдено.