Фильтрация показаний компаса - PullRequest
5 голосов
/ 18 марта 2011

Я использую направление компаса для поворота MKMapView.Поворот был немного прерывистым, поэтому я пытаюсь отфильтровать его, как это делает Google Maps на 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)];}

Спасибо за любую помощь

Ответы [ 4 ]

4 голосов
/ 20 марта 2011
- (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)];

}
2 голосов
/ 18 марта 2011

Если предыдущее скользящее среднее и новый курс отличаются более чем на 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)

Я надеюсь, что это работает и более эффективно, чем тригонометрический метод, описанный в Усреднение углов (благодарность Марку Рэнсому за это.

0 голосов
/ 27 октября 2017

В конце концов, это то, что сработало для меня.

  • Код в C #.
  • Измерения в радианах, хотя в примерах кода в ссылке будут использоваться градусы.-
  • Адаптировано с https://rosettacode.org/wiki/Averages/Mean_angle.
  • Опущено управление очередью, аналогичное @ d0n13.

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);
}
0 голосов
/ 26 февраля 2013

Я просто хотел добавить свою версию кода, опубликованную d0n, с несколькими изменениями.

Мой код - C-код для Arduino, поэтому я не использовал библиотек (например, я получаю абсолютное значение без функции 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; 
}
...