100Hz로 측정 된 신호가 있으며이 신호에 Savitzky-Golay 스무딩 필터를 적용해야합니다. 그러나 면밀히 검사하면 신호가 완벽하게 일정한 속도로 측정되지 않고 측정 간 델타의 범위는 9.7 ~ 10.3ms입니다.
간격이 동일하지 않은 데이터에 대해 Savitzky-Golay 필터를 사용하는 방법이 있습니까? 적용 할 수있는 다른 방법이 있습니까?
100Hz로 측정 된 신호가 있으며이 신호에 Savitzky-Golay 스무딩 필터를 적용해야합니다. 그러나 면밀히 검사하면 신호가 완벽하게 일정한 속도로 측정되지 않고 측정 간 델타의 범위는 9.7 ~ 10.3ms입니다.
간격이 동일하지 않은 데이터에 대해 Savitzky-Golay 필터를 사용하는 방법이 있습니까? 적용 할 수있는 다른 방법이 있습니까?
답변:
한 가지 방법은 동일한 간격으로 데이터를 리샘플링하여 원하는 처리를 수행하는 것입니다. 선형 필터링을 사용한 대역 제한 리샘플링은 데이터의 간격이 균일하지 않기 때문에 좋은 옵션이되지 않습니다. 따라서 일종의 로컬 다항식 보간 (예 : 입방 스플라인)을 사용하여 기본 신호의 값이 "정확한"값을 추정 할 수 있습니다 10 밀리 초 간격
길의 때문에 Savitzky - 골 레이 필터가 유도된다 (즉, 지역의 최소 자승 다항식 맞는로), 비 균일 샘플링을 자연스럽게 일반화있다 - 그것은 단지 더 많은 계산 비용입니다.
일반적인 Savitzky-Golay 필터
표준 필터의 경우, 아이디어는 다항식을 로컬 샘플 집합 (최소 제곱 사용)에 맞추고 중심 샘플을 중심 인덱스 (즉, 0)의 다항식 값으로 대체하는 것입니다. 이는 표준 SG 필터 계수 가 샘플 지수 의 Vandermonde 매트릭스 를 반전시켜 생성 될 수 있음을 의미합니다 . 예를 들어, 5 개의 샘플 (로컬 인덱스 -2, -1,0,1,2)에 대해 로컬 포물선 피팅을 생성 하려면 설계 방정식 시스템 는 다음과 같습니다. A c = y
위의 는 최소 제곱 다항식 의 알려지지 않은 계수입니다 . 에서의 다항식의 값 은 단지 이므로 설계 행렬 의 의사 를 계산하면 (예 : ) SG 필터 계수가 맨 윗줄. 이 경우에는c 0 + c 1 x + c 2 x 2 x = 0 c 0 c = ( A T A ) − 1 A T y
의 미분 은 이므로 행렬의 두 번째 행 ( 평가 )은 스무딩 된 미분 필터입니다. 연속적인 행에 대해서도 같은 주장이 적용됩니다. 첫 번째 행이 위의 Wikipedia에 주어진 평활 계수와 일치하도록 행렬의 크기를 35로 조정했습니다. 미분 필터는 각각 다른 스케일링 계수에 따라 다릅니다.c 1 + 2 c 2 x c 1
비 균일 샘플링
샘플의 간격이 균일하면 필터 계수가 변환 불변 값이므로 결과는 FIR 필터 일뿐입니다. 균일하지 않은 샘플의 경우 계수는 로컬 샘플 간격에 따라 달라 지므로 각 샘플에서 디자인 매트릭스를 구성하고 반전해야합니다. 비 균일 샘플 시간 인 경우 , 우리는 로컬 좌표를 구성 고정 각 센터의 샘플 시간 , 즉,
각 디자인 매트릭스는 다음과 같은 형식입니다.
로컬 샘플 값 으로 점으로 구분 된 의 의사 역행의 첫 번째 행은 해당 샘플에서 평활화 된 값인 을 생성 합니다.
도움이된다면 datageist가 설명하는 방법의 C 구현을 만들었습니다. 자신의 책임하에 자유롭게 사용할 수 있습니다.
/**
* @brief smooth_nonuniform
* Implements the method described in /signals/1676/savitzky-golay-smoothing-filter-for-not-equally-spaced-data
* free to use at the user's risk
* @param n the half size of the smoothing sample, e.g. n=2 for smoothing over 5 points
* @param the degree of the local polynomial fit, e.g. deg=2 for a parabolic fit
*/
bool smooth_nonuniform(uint deg, uint n, std::vector<double>const &x, std::vector<double> const &y, std::vector<double>&ysm)
{
if(x.size()!=y.size()) return false; // don't even try
if(x.size()<=2*n) return false; // not enough data to start the smoothing process
// if(2*n+1<=deg+1) return false; // need at least deg+1 points to make the polynomial
int m = 2*n+1; // the size of the filter window
int o = deg+1; // the smoothing order
std::vector<double> A(m*o); memset(A.data(), 0, m*o*sizeof(double));
std::vector<double> tA(m*o); memset(tA.data(), 0, m*o*sizeof(double));
std::vector<double> tAA(o*o); memset(tAA.data(), 0, o*o*sizeof(double));
std::vector<double> t(m); memset(t.data(), 0, m* sizeof(double));
std::vector<double> c(o); memset(c.data(), 0, o* sizeof(double));
// do not smooth start and end data
int sz = y.size();
ysm.resize(sz); memset(ysm.data(), 0,sz*sizeof(double));
for(uint i=0; i<n; i++)
{
ysm[i]=y[i];
ysm[sz-i-1] = y[sz-i-1];
}
// start smoothing
for(uint i=n; i<x.size()-n; i++)
{
// make A and tA
for(int j=0; j<m; j++)
{
t[j] = x[i+j-n] - x[i];
}
for(int j=0; j<m; j++)
{
double r = 1.0;
for(int k=0; k<o; k++)
{
A[j*o+k] = r;
tA[k*m+j] = r;
r *= t[j];
}
}
// make tA.A
matMult(tA.data(), A.data(), tAA.data(), o, m, o);
// make (tA.A)-¹ in place
if (o==3)
{
if(!invert33(tAA.data())) return false;
}
else if(o==4)
{
if(!invert44(tAA.data())) return false;
}
else
{
if(!inverseMatrixLapack(o, tAA.data())) return false;
}
// make (tA.A)-¹.tA
matMult(tAA.data(), tA.data(), A.data(), o, o, m); // re-uses memory allocated for matrix A
// compute the polynomial's value at the center of the sample
ysm[i] = 0.0;
for(int j=0; j<m; j++)
{
ysm[i] += A[j]*y[i+j-n];
}
}
std::cout << " x y y_smoothed" << std::endl;
for(uint i=0; i<x.size(); i++) std::cout << " " << x[i] << " " << y[i] << " "<< ysm[i] << std::endl;
return true;
}