我有一个传感器,该传感器报告带有时间戳和值的读数。但是,它不会以固定的速率生成读数。
我发现可变利率数据难以处理。大多数过滤器期望固定的采样率。固定采样率也可以更轻松地绘制图形。
是否有一种算法可以将可变采样率重新采样为固定采样率?
我有一个传感器,该传感器报告带有时间戳和值的读数。但是,它不会以固定的速率生成读数。
我发现可变利率数据难以处理。大多数过滤器期望固定的采样率。固定采样率也可以更轻松地绘制图形。
是否有一种算法可以将可变采样率重新采样为固定采样率?
Answers:
最简单的方法是像Jim Clay建议的那样进行某种样条插值(线性或其他方式)。但是,如果您有大量的批处理功能,尤其是如果您有一组不确定的非均匀样本,那么有一种“完美的重构”算法非常优雅。由于数量上的原因,它可能并非在所有情况下都可行,但至少值得在概念上有所了解。我首先在本文中读过它。
技巧是将您的一组非均匀样本视为已经通过sinc插值从均匀样本中重构出来。遵循本文中的注释:
请注意,这提供了一组线性方程,每个非均匀样本,其中未知数是等距样本,如下所示:y (k T )
在上式中,是未知均匀采样的数量,是均匀采样率的倒数,是非均匀采样的数量(可能大于)。通过计算该系统的最小二乘解,可以重建均匀样本。从技术上讲,仅需要非均匀样本,但是根据它们在时间上的“分散”程度,插值矩阵可能会病得很厉害。在这种情况下,使用更多不均匀的样本通常会有所帮助。Ť 米Ñ Ñ
作为一个玩具示例,这是上述方法与轻微抖动的网格上的三次样条插值之间的比较(使用numpy):
(此答案的末尾包含用于重现以上情节的代码)
综上所述,对于高质量,鲁棒的方法,从以下一篇论文中开始的内容开始可能会更合适:
A. Aldroubi和Karlheinz Grochenig,不变移空间中的非均匀采样和重构,SIAM修订版,2001年,第1期。4,585-620。(pdf链接)。
K. Grochenig和H.Schwab,《不变移位空间中非均匀采样的快速局部重建方法》,SIAM J.矩阵分析。Appl。,24(2003),899-913。
-
import numpy as np
import pylab as py
import scipy.interpolate as spi
import numpy.random as npr
import numpy.linalg as npl
npr.seed(0)
class Signal(object):
def __init__(self, x, y):
self.x = x
self.y = y
def plot(self, title):
self._plot(title)
py.plot(self.x, self.y ,'bo-')
py.ylim([-1.8,1.8])
py.plot(hires.x,hires.y, 'k-', alpha=.5)
def _plot(self, title):
py.grid()
py.title(title)
py.xlim([0.0,1.0])
def sinc_resample(self, xnew):
m,n = (len(self.x), len(xnew))
T = 1./n
A = np.zeros((m,n))
for i in range(0,m):
A[i,:] = np.sinc((self.x[i] - xnew)/T)
return Signal(xnew, npl.lstsq(A,self.y)[0])
def spline_resample(self, xnew):
s = spi.splrep(self.x, self.y)
return Signal(xnew, spi.splev(xnew, s))
class Error(Signal):
def __init__(self, a, b):
self.x = a.x
self.y = np.abs(a.y - b.y)
def plot(self, title):
self._plot(title)
py.plot(self.x, self.y, 'bo-')
py.ylim([0.0,.5])
def grid(n): return np.linspace(0.0,1.0,n)
def sample(f, x): return Signal(x, f(x))
def random_offsets(n, amt=.5):
return (amt/n) * (npr.random(n) - .5)
def jittered_grid(n, amt=.5):
return np.sort(grid(n) + random_offsets(n,amt))
def f(x):
t = np.pi * 2.0 * x
return np.sin(t) + .5 * np.sin(14.0*t)
n = 30
m = n + 1
# Signals
even = sample(f, np.r_[1:n+1] / float(n))
uneven = sample(f, jittered_grid(m))
hires = sample(f, grid(10*n))
sinc = uneven.sinc_resample(even.x)
spline = uneven.spline_resample(even.x)
sinc_err = Error(sinc, even)
spline_err = Error(spline, even)
# Plot Labels
sn = lambda x,n: "%sly Sampled (%s points)" % (x,n)
r = lambda x: "%s Reconstruction" % x
re = lambda x: "%s Error" % r(x)
plots = [
[even, sn("Even", n)],
[uneven, sn("Uneven", m)],
[sinc, r("Sinc")],
[sinc_err, re("Sinc")],
[spline, r("Cubic Spline")],
[spline_err, re("Cubic Spline")]
]
for i in range(0,len(plots)):
py.subplot(3, 2, i+1)
p = plots[i]
p[0].plot(p[1])
py.show()
这听起来像是异步采样率转换的问题。为了从一种采样率转换为另一种采样率,我们可以通过执行sinc插值来计算信号的连续时间表示,然后以新的采样率进行重新采样。你在做什么并没有太大的不同。您需要重新采样信号,以使采样时间固定。
可以通过将每个样本与Sinc函数卷积来计算连续时间信号。由于sinc函数不确定地继续,因此我们使用更实用的方法,例如具有有限长度的窗口sinc。棘手的部分是,由于您的样本会随时间移动,因此在重采样时,可能需要对每个样本使用具有不同相位偏移的正弦。
来自采样信号的连续时间信号:
其中是你的采样时间。但是,就您而言,采样时间不是固定的。因此,我认为您需要用该样本的采样时间替换它。
由此您可以重新采样信号:
)
其中是所需的采样时间。
放在一起,您将得到:
由于这不是因果关系或难于处理的,因此可以用有限支持函数代替Sinc函数,并相应地调整求和的极限。
令kernel(t)为窗口Sinc或长度为2k的其他类似函数,则:
我希望这会有所帮助...,但我可能在此过程中犯了一个错误,并且可能需要大量的数学运算。我建议研究采样率转换以获取更多信息。也许其他人也可以给出更好的解释或解决方案。
我认为雅各布的答案非常可行。
就引入失真而言,一种可能较差的简便方法是进行多项式插值。我将使用线性插值(简单,信号性能不佳)或三次样条曲线(仍然不太硬,信号性能更好)在任意时间从任意时间样本中生成样本。
(一个月后),任何插值方法都有两个主要选择:
1)最要使用的缺失点的数据点数量, 2 4 6 ...
2)要使用的基础函数类别:线性,多项式,正弦余弦(傅立叶),分段三次(B样条或插值样条),类似Sinc的...
(选择0是使用别人的方法和代码,还是自己动手做。)
将一条直线拟合到很容易:
2点[-1, ],[1, ]:估计
点平均值:平均
通用:参见例如
数字食谱 p。781:拟合直线
并估计。可以用相同的方法拟合二次方,三次方,正弦余弦...。
[ X 我,ÿ 我 ] X 我 = 0
ÿ 我 [ X 我,ÿ 我 ]
ý 0〜一
我知道您的数据均匀分布,缺少一些点,对吗?
线性插值在这种情况下的效果如何?
好吧,让我们尝试COS与 = 0.25:1个0 -1 0 1 0 -1 0 ...
任何点平均的2邻居0,太可怕了。
4个邻居:[1 0(缺失-1)0 1]的平均值= 1/2,太糟糕了。
(对此尝试使用4邻居过滤器[-1 3 3 -1] /4。)˚F
与4个或6个或8个邻居进行线性插补可能足以处理您的数据。
我建议您从一种深入了解的方法开始,然后再进入类似Sinc的样条曲线,尽管这些方法也可能很有趣。
(显然,NO单的内插方法都不可能适合的组合的不计其数
[信号,噪声,误差度量,测试功能]中出现的现实。
有在世界上多个方法,以多个旋钮,比测试功能。
尽管如此画廊方法和测试功能可能有用。)
如果您使用matlab,则可以通过时间序列来实现。
time % is your starting vector of time
data % vector of data you want to resample
data_TS = timeseries(data,time); % define the data as a timeseries
new_time = time(0):dt:time(end); % new vector of time with fixed dt=1/fs
data_res = resample(data_TS,new_time); % data resampled at constant fs
在进行一些特殊处理之前,您可以尝试类似以下的简单操作(伪代码-无插值,但是可以添加)
TimeStamp[] //Array of Sensor TimeStamps -NULL terminated – TimeStamp[i] corresponds to Reading[i]
Reading[] //Array of Sensor Readings -NULL terminated
AlgorithmOut //Delimited file of of readings in fixed sample time (5ms)
CurrentSavedReading = Reading[0]
SampleTime=TimeStamp[0] //ms virtual sample time, 5ms fixed samples
i = 0 // loop index
While(TimeStamp[i] != NULL)
{
FileWrite (CurrentSavedReading, AlgorithmOut)//write value to file
SampleTime = SampleTime + 5//ms
if(SampleTime > TimeStamp[i])
{
i++
CurrentSavedReading = Reading[i]
}
}