【问题标题】:Filtering compass readings过滤罗盘读数
【发布时间】:2011-03-18 03:12:10
【问题描述】:

我正在使用指南针航向来旋转 MKMapView。旋转有点生涩,所以我试图像 iphone 上的谷歌地图一样过滤它(或似乎做了一些诡计)。

我正在尝试使用移动平均公式过滤来自 iphone 指南针的读数,但它在 359 和 0 之间的交叉处失败,因为它开始从 35x 向后平均到 0,并导致地图在接近时向后旋转从西往北。

任何想法最好的方法是过滤此数据,使其从 359 回到零并保持滚动平均值。

代码在这里:

- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading {
static float xd=0;
static float k = 0.22;

// Moving average formula
xd = k * xd + (1.0 - k) * newHeading.magneticHeading;

NSLog(@"%0.2f : %0.2f", newHeading.magneticHeading, xd);    
[map setTransform:CGAffineTransformMakeRotation((-1 * xd * M_PI) /180)];}

感谢您的帮助

【问题讨论】:

标签: iphone filter mkmapview compass-geolocation moving-average


【解决方案1】:
- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)heading
{
    int newHeading;
    int queuecount;

    newHeading = heading.trueHeading;
    [queue addObject:[NSNumber numberWithInt:newHeading]];

    if([queue count] > 10) [queue removeObjectAtIndex:0];
    queuecount = [queue count];

    NSEnumerator *e = [queue objectEnumerator];
    NSNumber *sum;
    int oldd = 0 , newd, average =0;
    BOOL firstLoop = YES;
    while ((sum = [e nextObject])) 
    {
        newd = [sum intValue];
        if(firstLoop) {oldd = newd;firstLoop=NO;}

        if((newd +180) < oldd)
        {
            newd +=360; oldd = newd;
            average = average + newd;
            continue;
        }
        if((newd - 180) > oldd) 
        {
            newd -=360;oldd = newd;
            average = average + newd;
            continue;
        }

        average = average + newd;
        oldd = newd;

    }
    average = (average / queuecount) % 360;

    [map setTransform:CGAffineTransformMakeRotation((-1 * average * M_PI) /180)];

}

【讨论】:

  • 如您所见,这是一个令人讨厌的问题,但我相信有人可以做得比我更好。虽然我对结果感到很满意,但我有点失望,没有我能想出的更巧妙的解决方案。
  • 这看起来是我写答案时的预期。感谢您发布新代码。
  • 我在 github 上添加了一个项目,以便您可以使用 setup。如果您想改进我的代码,尤其是指南针部分,请随意。 github.com/d0n13/MapTest
【解决方案2】:

如果之前的移动平均线和新的航向相差超过 180 度,则将 360 添加到较小的那个。然后在存储新的移动平均线时按 360 取模。所以(没有精确的数学):

HDG   MA
350   350
355   353
  0   356  (because 353 - 0 > 180 so adjusted HDG is 360)
  5   359  (likewise)
 10     2  (likewise, then 362 is new MA, mod 360 to normalize)
350   356  (because 2 - 350 < -180 so adjusted MA is 362)

我希望这种方法比Averaging angles 中描述的三角方法更有效并且更有效(Mark Ransom 引用了该方法)。

【讨论】:

  • 我认为在你计算新的xd之前只是if newHeading - xd &gt; 180 then xd += 360; if newHeading - xd &lt; -180 then newHeading += 360,然后在做xd %= 360之后清理它。
  • 在两个方向的 359 和 0 之间的交叉点仍然会出现故障。我确实解决了它,我会将代码发布到 github 并在此处发布链接。这并不容易,我想让它成为一个更优雅的解决方案。
  • 您认为它为什么会出现故障?当我想到它并在我的答案中编写了相同的输入/输出代码块时,它似乎可以工作。但显然你看到了不同的结果?我真的不怀疑,但想知道为什么。
  • 嗨约翰,当值从 0 到 359 或 359 到 0 时,它仍然表现出我自己遇到的同样问题;平均值低于 180 左右,并在一瞬间将地图颠倒过来。我需要在不达到 180 的情况下越过 0-359 的边界。我似乎无法从数学上弄清楚如何做到这一点,因此不得不在队列接近和离开边界时对队列中的值进行按摩。
【解决方案3】:

我只是想添加我的 d0n 发布的代码版本,并进行了一些调整。

我的代码是 Arduino 的 C 代码,所以我没有使用任何库(例如,我得到了没有 abs 函数的绝对值)...

这个帖子对我很有帮助。谢谢 D0n 和约翰...

int Robot::getHeadingAverage() {

  // setup
  int lastReading = 0;
  int newReading = 0;
  int totalHeadings = 0;
  int avgHeading = 0;

  // loop through all the readings
  for(int i=0; i<totalHeadingReadings; i++) {
    // get the reading
    newReading = headings[i];

    // make sure we have an old reading
    if(i==0) {
      lastReading = newReading;
    }

    if((newReading + 180) < lastReading) {
      newReading = newReading + 360;
    }
    if((newReading - 180) > lastReading) {
      newReading = newReading - 360;
    }

    lastReading = newReading;
    totalHeadings = totalHeadings + newReading;

  }

  // get the average and make sure we do not end up over 360
  avgHeading = (totalHeadings / totalHeadingReadings) % 360;

  // check for negative value
  if(avgHeading < 0) {
    avgHeading = -1 * avgHeading; 
  }

  return avgHeading; 
}

【讨论】:

    【解决方案4】:

    最后,这对我有用。


    static double MeanAngle(List<double> angles)
    {
        double x = 0, y = 0;
        foreach (double angle in angles)
        {
            x += Math.Cos(angle);
            y += Math.Sin(angle);
        }
    
        return Math.Atan2(y / angles.Count, x / angles.Count);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-08-07
      • 2017-09-23
      • 1970-01-01
      • 2011-11-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-13
      相关资源
      最近更新 更多