过滤电位计数据时出现问题(嘈杂、高尖峰)

Trouble filtering potentiometer data (noisy, high spikes)

我使用 Arduino 微控制器从电位计收集了一些数据。这是以 500 Hz 采样的数据(这是很多数据):

http://cl.ly/3D3s1U3m1R1T?_ga=1.178935463.2093327149.1426657579

如果你放大,你会发现基本上我有一个只是来回旋转的罐子,也就是说,我应该看到一个线性增加然后一个线性减少。虽然数据的一般形状证实了这一点,但几乎每一次都会有一些非常烦人(有时非常宽)的尖峰阻碍了一个非常好的形状。有什么办法可以制作某种类型的算法或过滤器来解决这个问题吗?我尝试了一个中值过滤器,并使用了百分位数,但都没有用。我的意思是我觉得这不应该是最难的事情,因为我可以清楚地看到它应该是什么样子——基本上是尖峰发生位置的最小值——但出于某种原因,我尝试的一切都惨遭失败,或者至少失去了原始数据.

如果能为此提供任何帮助,我将不胜感激。

您的锅的最大和最小点附近似乎有大尖峰。例如,您可以将有效数据的范围限制在 200 到 300 之间。

另一种选择是像这样的一阶低通滤波器

alpha = 0.01 %parameter to tune!

p_filtered(1) = p(1);
for i=2:length(p)
    p_filtered(i) = alpha*p(i) + (1-alpha)* p_filtered(i-1);
end

噪声尖峰是由旋转旋钮时 POT 的抽头沿电阻轨道弹跳引起的。这是他们的通病。将来您应该考虑在 POT 输出端添加一个 0.1uF 电容器,这应该可以解决问题。

对于您当前的数据,最简单的选择是只做一个简单的移动平均并直观地调整平均样本数,直到尖峰被充分抑制而不影响基础数据。请注意,移动平均线只是具有 sinc 频率响应的低通滤波器。

post 处理此类数据的正常方法是执行 FFT(使用适当的窗口函数),将感兴趣信号上方的噪声值归零,然后进行逆 FFT。这也只是低通滤波(使用 sinc * 窗口函数加权移动平均值),但是您使用 FFT 提供的洞察力 select 您的截止频率。如果您对执行此操作所涉及的数学不满意,那么只需使用简单的移动平均过滤器即可。应该可以满足你的需求。

有很多方法可以解决您的问题。然而,其中 none 将永远是完美的。我会在这里给你2种方法。

移动平均线(低通滤波器

在 Matlab 中,一种无需显式使用 FFT 即可 "low pass" 过滤数据的简单方法是使用 filter 函数(在基础包中可用,您不需要任何特定工具箱)。

您为过滤器创建一个内核,并应用它两次(每个方向一次),以取消引入的相移。这实际上是一个具有零相移的 "Moving Average" 滤波器。 内核的大小(长度)将控制平均过程的繁重程度。

例如,2 个不同的过滤器长度:

n = 100 ; %// length of the filter
kernel = ones(1,n)./n ;
q1 = filter( kernel , 1 , fliplr(p) ) ;  %// apply the filter in one direction
q1 = filter( kernel , 1 , fliplr(q1) ) ; %// re-apply in the opposite direction to cancel phase shift

n = 500 ; %// length of the filter
kernel = ones(1,n)./n ;
q2 = filter( kernel , 1 , fliplr(filter( kernel , 1 , fliplr(p) )) ) ; %// same than above in one line

将根据您的数据产生:

如您所见,每种过滤器尺寸都有其优缺点。过滤得越多,消除的尖峰就越多,但原始信号的变形就越大。您可以自行选择最佳设置。


2) 搜索衍生异常

这是一种不同的方法。您可以在信号上观察到尖峰大多是突然的,这意味着信号值的变化很快,幸运的是比所需信号的 "normal" 变化率更快。这意味着您可以计算信号的导数,并识别所有尖峰(导数将比曲线的其余部分高得多)。
由于这仅识别尖峰的 "beginning" 和 "end"(不是中间偶尔出现的高原),我们需要稍微扩展通过此方法识别为故障的区域。
完成错误数据的识别后,您只需丢弃这些数据点并在原始间隔内重新插入曲线(支持您留下的点)。

%% // Method 2 - Reinterpolation of cancelled data

%// OPTIONAL slightly smooth the initial data to get a cleaner derivative
n = 10 ; kernel = ones(1,n)./n ;
ps = filter( kernel , 1 , fliplr(filter( kernel , 1 , fliplr(p) )) ) ;

%// Identify the derivative anomalies (too high or too low)
dp = [0 diff(ps)] ;             %// calculate the simplest form of derivative (just the difference between consecutive points)
dpos = dp >= (std(dp)/2) ;      %// identify positive derivative above a certain threshold (I choose the STD but you could choose something else)
dneg = dp <= -(std(dp)/2) ;     %// identify negative derivative above the threshold
ixbad = dpos | dneg ;           %// prepare a global vector of indices to cancel

%// This will cancel "nPtsOut" on the RIGHT of each POSITIVE derivative
%// point identified, and "nPtsOut" on the LEFT of each NEGATIVE derivative
nPtsOut = 100 ;                 %// decide how many points after/before spikes we are going to cancel
for ii=1:nPtsOut
    ixbad = ixbad | circshift( dpos , [0 ii]) | circshift( dneg , [0 -ii]) ;
end

%// now we just reinterpolate the missing gaps
xp = 1:length(p) ;                              %// prepare a base for reinterpolation
pi = interp1( xp(~ixbad) , p(~ixbad) , xp )  ;  %// do the reinterpolation

这将产生:

红色信号是上述移动平均的结果,绿色信号是微分方法的结果。
您还可以更改设置以调整此结果(导数的阈值,'nPtsOut' 甚至数据的初始平滑)。

如您所见,与移动平均法相比,对于相同数量的尖峰取消,它更尊重初始数据的完整性。但是它也不完美,有些区间还是会变形。但正如我一开始所说,没有任何方法是完美的。