根据步行速度插入2个GPS位置

tra*_*m_3 2 java algorithm gps android interpolation

问题:


鉴于两个位置:

大号1 =(纬度1,经度1,时间戳1),大号2 =(纬度2,经度2,时间戳2),

以及可配置但恒定的移动速度:

v =每秒1.39米(例如).

当他从L 1行进到L 2时,我们如何在这两个位置之间进行插值以估计用户位置?


我一直在寻找这个问题的解决方案,到目前为止我发现,对于小距离(远离极点),可以使用线性插值.所以,我在维基百科上查找了线性插值,发现了这个:

// 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);
}
Run Code Online (Sandbox Code Playgroud)

所以我正在考虑使用这个lerp函数来插入L 1L 2之间的纬度和经度.这很容易.我如何计算t?我猜我必须计算一些时间增量,但我如何计算运动速度?


编辑:

我正在测试各种收集GPS位置的方法.为此,我正在整个步行记录航点.我需要在这些航路点之间进行插值,使用移动速度来估计我沿着步行的位置.然后我可以将我的结果与估算值进行比较,看看他们的表现如何.

例:

地图

dav*_*ave 10

仔细查看纬度/经度点之间的计算距离,方位等

它包含几个可能对您有帮助的公式和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 - a));

    return (radius * c);
}
Run Code Online (Sandbox Code Playgroud)

这使用的平均地球半径为6371 km.有关此数字及其准确性的说明,请参阅Wikipedia.

在给定行进距离(以km为单位)的情况下,现在可以计算两点之间的新中间位置:

double bearing = CalculateBearing(startLocation, endLocation);

Location intermediaryLocation = CalculateDestinationLocation(startLocation, bearing, distanceTravelled);
Run Code Online (Sandbox Code Playgroud)

假设速度为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;
}
Run Code Online (Sandbox Code Playgroud)

作为额外的奖励,您甚至可以确定在任意两点之间旅行所需的时间:

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

double timeRequired = distanceBetweenPoints / v;
Run Code Online (Sandbox Code Playgroud)

与仅使用坐标的增量的简单线性插值相比,这将导致在任何距离上的更高精度.虽然这种方法并不完美,但误差通常为0.3%或更低,这是完全可以接受的.如果您需要更好的解决方案,您可能需要查看Vincenty公式.

  • 这是一个非常有用的答案!我唯一要注意的是您的方法`CalculateDistanceBetweenLocations`中有`double c = 2 * Math.atan2(Math.sqrt(a),Math.sqrt(1-1));`而是:`double c = 2 * Math.atan2(Math.sqrt(a),Math.sqrt(1-a));` (2认同)

小智 6

如果您首先将纬度/经度转换为 n 向量(https://en.wikipedia.org/wiki/N-vector),这样的计算实际上非常简单。转换后,您可以使用标准插值,并且还可以避免长距离、极点或日期变更线的任何问题。

如果你检查维基百科页面上的“外部链接”,有一个页面(http://www.navlab.net/nvector/)解决了十个问题,并且该页面上的问题6(插值位置)应该是相同的作为你的问题。正如您所看到的,该解决方案对于任何距离都是精确的,并且也适用于任何地球位置,例如两极。


Ale*_*ien 3

我猜我必须计算一些时间增量,但是如何考虑移动速度呢?

在线性插值中,在您的情况下,您使用迭代变量 t 在两个时间点之间进行迭代,该变量从开始时间 t1 运行到结束时间 t2,并具有预定义的步长。假设步长 = 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.
}
Run Code Online (Sandbox Code Playgroud)

  • 此代码假设纬度和经度位于平坦的笛卡尔平面上。地球并不完全是这样,地球是一个球体。 (2认同)