본문 바로가기

소셜독

산책기록 gps 필터링 개선

오늘은 산책정보를 좀 더 보기좋게 바꿔보려고 시도를 했다. 고등학교때 센서데이터를 보정하기 위해 칼만필터를 사용했던 기억이 나서, 칼만필터를 써보기로 결정했다.

kalman-filter

 

kalman-filter

Kalman filter (and Extended Kalman Filter) Multi-dimensional implementation in Javascript. Latest version: 1.9.4, last published: 10 days ago. Start using kalman-filter in your project by running `npm i kalman-filter`. There are no other projects in the np

www.npmjs.com

간단하게 원리를 찾아보니 이전값을 통해서 예측값을 만들어내고, 측정된 값을 바로 반영하는게 아니라, 예측된 값과 함께 계산해서 반영하는 원리이다. 구체적인 부분은 어려워서 이해를 포기했다.

const {KalmanFilter} = require('kalman-filter');

const observations = [[0, 1], [0.1, 0.5], [0.2, 3], [4, 2], [1, 2]];
const kFilter = new KalmanFilter({observation: 2});
const res = kFilter.filterAll(observations)

이 라이브러리를 이용하면 2D좌표의 데이터들을 필터링하여 가공할 수 있다. 라이브러리를 사용해서 유의미한 필터링을 확인했다. 하지만 라이브러리가 내가 원하는 방향으로 연산을 수정하기도 좀 어렵고, 기존에 사용중인 로직에 그대로 사용하게 되면 연산해야할 양이 지나치게 많아질것 같아서 새로 만들기로 했다.

export class GpsFilter {
  protected round: number;
  protected prevDataArr: Array<Array<number>>;
  protected predictVal: Array<number>;

  constructor(round: number) {
    this.round = round;
    this.prevDataArr = [];
    this.predictVal = [];
  }

  clearFilter() {
    this.prevDataArr = [];
    this.predictVal = [];
  }

  filterNewData(data: [number, number]) {
      //0번은 latitude, 1번은 longitude

    if (this.prevDataArr.length < 1) {
      this.prevDataArr.push(data);
      return data;
    } else if (this.prevDataArr.length < 2) {
      let gapSum = [0, 0];
      gapSum[0] += data[0] - this.prevDataArr[0][0];
      gapSum[1] += data[1] - this.prevDataArr[0][1];

      const gapAvg = [gapSum[0], gapSum[1]];
      this.predictVal = [data[0] + gapAvg[0], data[1] + gapAvg[1]];
      this.prevDataArr.push(data);
      return data;
    } else {
      let gapSum = [0, 0];
      for (let i = 0; i < this.prevDataArr.length - 1; i++) {
        gapSum[0] += this.prevDataArr[i + 1][0] - this.prevDataArr[i][0];
        gapSum[1] += this.prevDataArr[i + 1][1] - this.prevDataArr[i][1];
      }

      const gapAvg = [
        gapSum[0] / (this.prevDataArr.length - 1),
        gapSum[1] / (this.prevDataArr.length - 1),
      ];

      const filteredData = [
        (data[0] + this.predictVal[0]) / 2,
        (data[1] + this.predictVal[1]) / 2,
      ];
      if (this.prevDataArr.length > this.round) {
        this.prevDataArr.shift();
      }
      this.prevDataArr.push(filteredData);
      this.predictVal = [data[0] + gapAvg[0], data[1] + gapAvg[1]];

      return filteredData;
    }
  }
}

크게 어려운 부분은 없고, 이전 좌표들에서의 변화량을 측정해서 다음 좌표를 예측하는 코드이다. 내가 기록했던 데이터 상에서는 칼만필터랑 크게 차이가 나지않았고, 필요하다면 내가 이전 좌표에 대한 내용을 얼마나 반영할지 조정도 할 수있어서, 이렇게 코드를 작성해봤다. 그리고 바로 실제 측정에 나섰다.

기기는 총 2대로 테스트 했다. 1번과 2번사진은 한기기로 동시에 측정을 한 것이다. 3번은 다른기기로, 동일한 경로를 움직였지만, GPS측정값이 온전하게 일치하지 않는다는점을 고려하고 봐야한다. https://github.com/mauron85/react-native-background-geolocation 해당 라이브러리가 기본적으로 뽑아내는 위치정보도 나름 쓸만한 수준으로 나온다. 라이브러리 내부에서 기본적으로 보정을 하는 느낌이다. 타사와 비교해서도 크게 차이가 나는 느낌은 아니다. 맨 마지막 사진이 내가 만든 필터를 적용하여 기록을 한 부분인데, 전체적으로 좀 둥글해지는 느낌이다. 필터가 적용되기때문에, 과보정된 부분이 중간중간에 보이는데, 이부분은 계수를 조정해서 좀 더 보완을 하면 될것같다. 라이브러리가 뽑아주는 위치정보에 내가 만든 필터 함수를 적용해서 하는 방식으로 진행하기로 결정했다.