从可变速率重新采样到固定速率的算法是什么?


27

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

我发现可变利率数据难以处理。大多数过滤器期望固定的采样率。固定采样率也可以更轻松地绘制图形。

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


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

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

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

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

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

Answers:


21

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

技巧是将您的一组非均匀样本视为已经通过sinc插值从均匀样本中重构出来。遵循本文中的注释:

y(t)=k=1Ny(kT)sin(π(tkT)/T)π(tkT)/T=k=1Ny(kT)sinc(tkTT).

请注意,这提供了一组线性方程,每个非均匀样本,其中未知数是等距样本,如下所示:y k T y(t)y(kT)

[y(t0)y(t1)y(tm)]=[sinc(t0TT)sinc(t02TT)sinc(t0nTT)sinc(t1TT)sinc(t12TT)sinc(t1nTT)sinc(tmTT)sinc(tm2TT)sinc(tmnTT)][y(T)y(2T)y(nT)].

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

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

Sinc vs三次样条重构非均匀样本

(此答案的末尾包含用于重现以上情节的代码)

综上所述,对于高质量,鲁棒的方法,从以下一篇论文中开始的内容开始可能会更合适:

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()

好的方法和代码。但是对于且有一些(例如[0 1-3 4 5-7 8] T),我认为这是OP的问题,矩阵中的Sinc s是否都为0?当然有解决方法,但是。tj=jT
丹尼斯

据我了解,OP的问题是关于掉落和/或延迟的样本。此方法基本上只是一个超定方程组,因此删除的样本仅显示为未知数(而不显示为值为0的数据点)。也许这不是您要的?
datageist

如果都是整数(T = 1)会怎样?假设我们有数据点[ ] ,这是一组非零整数,例如{-1 1}或{-2 -1 1 2};插值是否与无关,还是我错过了什么? Ĵ ÿ Ĵ Ĵ Ĵ Ŷ 0 = 0 ÿ Ĵtjj,yjjJy0=0yj
丹尼斯

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

2
这样的答案是纯金的。
艾哈迈德·法西

6

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

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

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

x(t)=n=x[n]sinc(tnTsTs)

其中是你的采样时间。但是,就您而言,采样时间不是固定的。因此,我认为您需要用该样本的采样时间替换它。Ts

x(t)=n=x[n]sinc(tnTs[n]Ts[n])

由此您可以重新采样信号:

y[n]=x(nTns

其中是所需的采样时间。Tns

放在一起,您将得到:

y[m]=n=x[n]sinc(mTnsnTs[n]Ts[n])

由于这不是因果关系或难于处理的,因此可以用有限支持函数代替Sinc函数,并相应地调整求和的极限。

令kernel(t)为窗口Sinc或长度为2k的其他类似函数,则:

y[m]=n=kkx[n]kernel(mTnsnTs[n]Ts[n])

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


使用Sinc函数重建信号的连续版本需要将样本等距放置,因此Sinc函数将必须适应任意光线样本间隔。可能很难实施。
user2718 2013年

是的,要完全按照此处所示进行操作,效率不是很高。它将需要为每个不同的采样时间计算新的内核系数。但是,可以计算几个内核的集合,并将时间量化为其中一个。相对于量化误差会有性能上的损失。
2013年

您还可以预先计算单个sinc查找表,然后在该查找表的各点之间进行插值。
jms

5

我认为雅各布的答案非常可行。

就引入失真而言,一种可能较差的简便方法是进行多项式插值。我将使用线性插值(简单,信号性能不佳)或三次样条曲线(仍然不太硬,信号性能更好)在任意时间从任意时间样本中生成样本。


1
您的回答似乎比Jacob的实现起来容易得多,所以我先讲了它。它似乎正在运行,但是我还没有运行全套测试。
FigBug

1
@FigBug-如果您有时间,请添加注释以作为最终解决方案。
user2718 2013年

2

(一个月后),任何插值方法都有两个主要选择:
1)最要使用的缺失点的数据点数量, 2 4 6 ... 2)要使用的基础函数类别:线性,多项式,正弦余弦(傅立叶),分段三次(B样条或插值样条),类似Sinc的... (选择0是使用别人的方法和代码,还是自己动手做。)Nnear

将一条直线拟合到很容易: 2点[-1, ],[1, ]:估计 点平均值:平均 通用:参见例如 数字食谱 p。781:拟合直线 并估计。可以用相同的方法拟合二次方,三次方,正弦余弦...。Nnear
y1y1
[ X ÿ ] X = 0y0(y1+y1)/2
[xi,yi]xi=0
ÿ [ X ÿ ]y0yi
[xi,yi]
ý 0a+bxy0a

我知道您的数据均匀分布,缺少一些点,对吗?
线性插值在这种情况下的效果如何?
好吧,让我们尝试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。)˚F2πftf


与4个或6个或8个邻居进行线性插补可能足以处理您的数据。
我建议您从一种深入了解的方法开始,然后再进入类似Sinc的样条曲线,尽管这些方法也可能很有趣。


另一种完全不同的方法是 反距离加权。它易于实现(在SO上参见idw-interpolation-with-python),也可在2d,3d及更高版本中工作,但在理论上难以分析。

(显然,NO单的内插方法都不可能适合的组合的不计其数
[信号,噪声,误差度量,测试功能]中出现的现实。
有在世界上多个方法,以多个旋钮,比测试功能。
尽管如此画廊方法和测试功能可能有用。)


1

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

0

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

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

0

恕我直言,Datageist的答案是正确的,Jacob的答案是错误的。一种简单的验证方法是,保证datageist的建议算法可以对原始样本进行插值(假设数值精度无限大),而Jacob的答案则不然。

  • 对于均匀采样情况,一组Sinc函数是正交的:如果每个移位的Sinc函数在输入样本上离散,则它们将形成一个无限的恒等矩阵。这是因为对于除n = 0以外的所有n,sin(n pi)/(n pi)为零。
  • 但是,不能简单地将此特性外推到非均匀情况:在输入样本上离散的一组相似的Sinc函数将产生一个非平凡的矩阵。因此,周围样本的贡献将不会为零,并且重构将不再通过输入样本进行插值。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.