我有一个传感器,该传感器报告带有时间戳和值的读数。但是,它不会以固定的速率生成读数。

我发现可变速率数据难以处理。大多数过滤器期望固定的采样率。使用固定采样率也更容易绘制图形。

是否有一种算法可以将可变采样率重新采样为固定采样率?

评论

这是程序员的交叉帖子。有人告诉我这是一个更好的地方。 developers.stackexchange.com/questions/193795 / ...

由什么决定传感器何时报告读数?它仅在读数更改时发送读数吗?一种简单的方法是选择一个“虚拟采样间隔”(T),该间隔仅小于生成读数之间的最短时间。在算法输入处,仅存储最后报告的读数(CurrentReading)。在算法输出处,每T秒将CurrentReading报告为“新样本”,以便过滤器或绘图服务以恒定速率(每T秒)接收读数。不知道这是否适合您的情况。

它尝试每5ms或10ms采样一次。但这是一项低优先级的任务,因此可能会错过或延迟。我的计时精确到1毫秒。处理是在PC上完成的,而不是实时进行的,因此,如果算法更易于实现,则可以采用慢速算法。

您是否看过傅立叶重构?有一个基于不均匀采样数据的傅立叶变换。通常的方法是将傅立叶图像转换回均匀采样的时域。

您知道要采样的基础信号的任何特征吗?如果与被测信号的带宽相比,不规则间隔的数据仍具有相当高的采样率,则诸如对均匀间隔的时间网格进行多项式插值之类的简单操作可能会很好。

#1 楼

最简单的方法是进行某种样条插值,如吉姆·克莱(Jim Clay)建议的(线性或其他方式)。但是,如果您有大量的批处理功能,尤其是如果您有一组不确定的不均匀样本,那么有一种“完美重构”算法非常优雅。由于数量上的原因,它可能并非在所有情况下都可行,但是至少值得在概念上有所了解。我首先在本文中读过它。

诀窍在于将您的一组非均匀样本视为已经通过正弦插值从均匀样本中重构出来。遵循本文中的注释:

$$
y(t)= \ sum_ {k = 1} ^ {N} {y(kT)\ frac {\ sin(\ pi (t-kT)/ T)} {\ pi(t-kT)/ T}} = \ sum_ {k = 1} ^ {N} {y(kT)\ mathrm {sinc}(\ frac {t-kT } {T})}。
$$

请注意,这提供了一组线性方程,每个非均匀样本$ y(t)$都具有一个线性方程,其中未知数等于间隔的样本$ y(kT)$,例如:

$$
\ begin {bmatrix} y(t_0)\\ y(t_1)\\ \ cdots \\ y(t_m )\ end {bmatrix} = \ begin {bmatrix} \ mathrm {sinc}(\ frac {t_0-T} {T})&\ mathrm {sinc}(\ frac {t_0-2T} {T})&\ cdots &\ mathrm {sinc}(\ frac {t_0-nT} {T})\\ \ mathrm {sinc}(\ frac {t_1-T} {T})&\ mathrm {sinc}(\ frac {t_1-2T } {T})&\ cdots&\ mathrm {sinc}(\ frac {t_1-nT} {T})\\ \ cdots&\ cdots&\ cdots&\ cdots \\ \ mathrm {sinc}(\ frac { t_m-T} {T})和\ mathrm {sinc}(\ frac {t_m-2T} {T})&\ cdots和\ mathrm {sinc}(\ frac {t_m-nT} {T})\ end { bmatrix} \ begin {bmatrix} y(T)\\ y(2T)\\ \ cdots \\ y(nT)\ end {bmatrix}。
$$

在上式中,$ n $是未知均匀样本的数量,$ T $是均匀样本速率的倒数,$ m $是非均匀样本的数量(可能大于$ n $)。通过计算该系统的最小二乘解,可以重建均匀样本。从技术上讲,仅需要$ n $个非均匀样本,但是根据它们在时间上的“分散”程度,插值矩阵可能会变得异常不适。在这种情况下,使用更多不均匀的样本通常会有所帮助。

作为一个玩具示例,这是上述方法与轻度抖动网格上三次样条插值的比较​​(使用numpy):


(此答案的末尾包含用于复制上述图的代码)

对于高质量,鲁棒的方法,从以下论文之一开始,可能会更合适:


A. Aldroubi和Karlheinz Grochenig,非不变采样和不变不变空间中的重构,SIAM 。,2001,no。 4,

K. Grochenig和H. Schwab,《不变移位空间中非均匀采样的快速局部重建方法》,SIAM J.矩阵分析。
应用。 ,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()


评论


$ \ begingroup $
好的方法和代码。但是对于$ t_j = jT $且有一些缺失(例如[0 1-3 4 5-7 8] T),我认为这是OP的问题,矩阵中的Sinc s是否都为0?当然有解决方法,但是。
$ \ endgroup $
–丹尼斯
13年5月17日在14:56

$ \ begingroup $
据我了解,OP的问题是关于掉落和/或延迟的样本。此方法基本上只是一个超定方程组,因此删除的样本仅显示为未知数(而不显示为值为0的数据点)。也许这不是您要的?
$ \ endgroup $
–datageist♦
13年5月17日在20:09

$ \ begingroup $
如果$ t_j $都是整数(T = 1)会发生什么?假设我们在J $中有$ j \的数据点[$ j,y_j $],这是一组非零整数,例如{-1 1}或{-2 -1 1 2};不管$ y_j $是不是插值后的$ y_0 = 0 $还是我错过了什么?
$ \ endgroup $
–丹尼斯
13年5月18日在16:20

$ \ begingroup $
如果采样率完全相同(缺少点),则插值矩阵将稀疏(因为每个输出仅取决于一个输入)。通常,非均匀样本的平均样本率需要大于均匀重建率。换句话说,您需要以较低的速率进行重构以“填补空白”(对于您的示例,T> 1)。我明白你的意思了。
$ \ endgroup $
–datageist♦
13年5月19日在7:55

$ \ begingroup $
这样的答案是纯金的。
$ \ endgroup $
–艾哈迈德·法西(Ahmed Fasih)
2015年6月7日,下午3:37

#2 楼

这听起来像是异步采样率转换的问题。为了从一种采样率转换为另一种采样率,我们可以通过执行Sinc插值来计算信号的连续时间表示,然后以新的采样率进行重新采样。你在做什么并没有太大的不同。您需要重新采样信号以使采样时间固定。

连续时间信号可以通过将每个采样与Sinc函数卷积来计算。由于sinc函数可以无限期地继续,因此我们使用更实用的方法,例如具有实际有限长度的窗口sinc。棘手的部分是,因为您的样本会随时间移动,所以在重采样时可能需要对每个样本使用具有不同相位偏移的正弦信号。

来自采样信号的连续时间信号:

$ x(t)= \ sum \ limits_ {n =-\ infty} ^ \ infty x [n] sinc({t-nT_s \ over T_s})$

其中$ T_s $是您的采样时间。但是,根据您的情况,采样时间不是固定的。因此,我认为您需要用该示例的采样时间替换它。

$ x(t)= \ sum \ limits_ {n =-\ infty} ^ \ infty x [n] sinc( {t-nT_s [n] \ over T_s [n]})$

您可以从中重新采样信号:

$ y [n] = x(nT_ { ns} $)

其中$ T_ {ns} $是所需的采样时间。

将它们全部放在一起即可得到:

$ y [m] = \ sum \ limits_ {n =-\ infty} ^ \ infty x [n] sinc({mT_ {ns} -nT_s [n] \ over T_s [n]})$

因为这不是因果关系或难于处理的,所以可以用有限支持函数代替sinc函数,并相应地调整总和的限制。长度为2k的则为:

$ y [m] = \ sum \ limits_ {n = -k} ^ kx [n] kernel({mT_ {ns} -nT_s [n] \ over T_s [ n]})$

我希望这会有所帮助...,但我可能在此过程中犯了一个错误,并且可能需要大量数学运算。我建议研究采样率转换以获取更多信息。也许这里的其他人也可以给出更好的解释或解决方案。

评论


$ \ begingroup $
使用Sinc函数重建信号的连续版本需要将样本等距放置,因此,sinc函数必须适应任意光线样本间隔。可能很难实施。
$ \ endgroup $
–user2718
13-4-5在11:43

$ \ begingroup $
是的,要完全按照此处所示进行操作,效率不是很高。这将需要为每个不同的采样时间计算新的内核系数。但是,可以计算几个内核的集合,并将时间量化为其中一个。相对于量化误差会有性能上的损失。
$ \ endgroup $
–雅各布
13年4月5日在19:32

$ \ begingroup $
您还可以预先计算单个Sinc查找表,并在该查找表的各点之间进行插值。
$ \ endgroup $
– jms
18年6月6日在17:46

#3 楼

我认为Jacob的答案非常可行。我将使用线性插值(简单,信号性能不佳)或三次样条曲线(仍然不太硬,信号性能更好)在任意时间采样任意时间生成采样。

评论


$ \ begingroup $
您的回答似乎比Jacob的实现起来容易得多,所以我首先讲了它。它似乎正在工作,但是我还没有运行全套测试。
$ \ endgroup $
– FigBug
13年4月5日,0:20

$ \ begingroup $
@FigBug-如果您有时间,请添加注释,这是最终解决方案。
$ \ endgroup $
–user2718
13年4月6日,0:32

#4 楼

(一个月后),任何插值方法都有两个主要选择:
1)最接近要使用的缺失点的数据点数,$ Nnear $ 2 4 6 ...
2)类使用的基本函数:线性,多项式,正弦余弦(傅立叶),分段三次(B样条或插值样条),类似Sinc的...
将直线拟合到$ Nnear $点很容易:
2点[-1,$ y_ { -1} $],[1,$ y_1 $]:
$ \ qquad $估计$ y_0 \ sim(y _ {-1} + y_1)/ 2 $
点$ [x_i,y_i] $平均$ x_i = 0 $:
$ \ qquad y_0 \ sim $平均$ y_i $
一般$ [x_i,y_i] $:
$ \ qquad $参见例如>食谱p。 781:
拟合一条线$ a + bx $
并估计$ y_0 \ sim a $。
$ \ qquad $一个可以拟合二次方,三次方,正弦余弦...

我理解您的数据均匀分布,但缺少一些点,
对吗?
这种情况下线性插值的效果如何?
/>那么,让我们尝试使用$ f $ = 0.25的cos $ 2 \ pi ft $:1 0 -1 0 1 0 -1 0 ...
2个任意点的邻居平均为0,太糟糕了。 /> 4个邻居:[1 0(缺失-1)0 1]的平均值= 1/2,太糟了。
(尝试使用4个邻居过滤器[-1 3 3 -1] /4。)

与4或6或8个邻居进行线性插值可能对您的数据足够好

我建议您从一种您已经理解的方法入手
跳入样条线,类似于Sinc ...虽然它们也很有趣。



另一种完全不同的方法是反距离加权。
易于实现
(请参见idw-interpolation-with-python on SO),工作在2d和3d以上也是如此,
但是在理论上很难分析。

(显然,没有一种插值方法可能适合
现实中出现的成千上万的
[信号,噪声,误差度量,测试函数]的组合。
世界上有比测试功能更多的方法,带有更多的旋钮。
尽管如此,方法和测试功能的库还是有用的。)

#5 楼

如果您使用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


#6 楼

在进行一些特殊处理之前,您可以尝试类似以下的简单操作(伪代码-无插值,但可以添加)

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]
   }
}


#7 楼

恕我直言,Datageist的答案是正确的,Jacob的答案是不正确的。一种简单的验证方法是,保证datageist的建议算法可以对原始样本进行插值(假设数值精度无限大),而Jacob的答案则不能。 Sinc函数的集合是正交的:如果每个移位的Sinc函数在输入样本上离散,则它们将形成一个无限的单位矩阵。这是因为对于除n = 0外的所有n,sin(n pi)/(n pi)均为零。在输入样本上,将产生一个非平凡的矩阵。因此,周围样本的贡献将不会为零,并且重构将不再通过输入样本进行插值。