【问题标题】:Signal processing (in Java)信号处理(Java)
【发布时间】:2014-06-23 19:34:30
【问题描述】:

我有一个正在读取的传感器,目前代码是 Java,但我不认为问题是特定于语言的,更多的方法相关。

传感器产生高低脉冲信号,大致类似于心跳。然而,“高”脉冲并不总是相同的电平,“低”也不是。我感兴趣的是相对差异。但是,仅此还不够,因为在单个“会话”内,高值和低值也可能发生变化(想想弯曲的中点)

我已经包含了我希望能够处理的 4 种“类型”信号的图像。左上角是“理想”,我很确定我已经可以处理了,遗憾的是其他三个更常见且不太容易处理。

我目前的方法是寻找数据的平均值,看看有多少次跨越该点,这将告诉我有多少高脉冲和低脉冲。

我想知道是否有一种简单的方法来检测高低脉冲,而不使用平均方法。

【问题讨论】:

  • 我认为您将需要某种类型的平均方法,但您不一定必须对整个信号进行平均。您也许可以使用 boxcar 过滤器。
  • 那么,您想知道紧随其后的波峰和波谷之间的平均差值(反之亦然),还是想知道波的频率?
  • 它是我需要的波的频率

标签: java algorithm signals


【解决方案1】:

当您说要提取波的频率时,我首先想到的是傅立叶变换;这将信号从时域转换到频域。给定以下示例波形:

这是我添加噪声和趋势的正弦方式。底层正弦波的频率为 1.5Hz

你得到这个傅立叶变换

在这里您可以看到 0hz 处的大响应,这是线性趋势,在这种情况下我们可以忽略它。之后,您可以在 1.5Hz(我们输入信号的频率)处看到响应中的一个峰值。换句话说;一旦你进行了傅立叶变换,你的结果就是具有最大值的数据点(在你删除了非常低的频率结果之后)

Java 代码

Apache commons 有一个快速傅立叶变换类,我用它来创建这个变换。它将波形的采样数据作为输入并输出一个复数,复数的模数(实部平方的平方根加上虚部平方的平方根)为equal to the energy at that frequency。输出数组中的每个条目i 引用i*samplingFrequency/noOfSamples 处的频率。

但是,下面的 java 代码很大程度上会为您处理这些问题。快速傅立叶变换的唯一问题是输入条目的数量必须是 2 的幂。

import org.apache.commons.math3.complex.Complex;
import org.apache.commons.math3.transform.DftNormalization;
import org.apache.commons.math3.transform.FastFourierTransformer;
import org.apache.commons.math3.transform.TransformType;

public class FourierTest {

    public static void main(String[] args) {
      
        double samplingFrequency=10; //hz, You will know this from your data and need to set it here
        
        

        double[] frequencyDomain = new double[input.length];

        FastFourierTransformer transformer = new FastFourierTransformer(DftNormalization.STANDARD);
        try {           
            Complex[] complex = transformer.transform(input, TransformType.FORWARD);
            
            for (int i = 0; i < complex.length; i++) {               
                double real = (complex[i].getReal());
                double imaginary = (complex[i].getImaginary());

                frequencyDomain[i] = Math.sqrt((real * real) + (imaginary * imaginary));
            }

        } catch (IllegalArgumentException e) {
            System.out.println(e);
        }

        //only to frequencyDomain.length/2 since second half is mirror image or first half
        for(int i=0;i<frequencyDomain.length/2;i++){
            double frequency=samplingFrequency*i/frequencyDomain.length;
            System.out.println("Frequency: " + frequency + "\t\tEnergyComponent: " + frequencyDomain[i]);
        }
    }
    
    static double[]  input = new double[]{
            0.017077407 , //sample at 0 seconds
            1.611895528 , //sample at 0.1 seconds
            2.063967663 , //sample at 0.2 seconds
            1.598492541 , //etc
            0.184678933 ,
            0.02654732  ,
            0.165869218 ,
            1.026139745 ,
            1.914179294 ,
            2.523684208 ,
            1.71795312  ,
            0.932131202 ,
            1.097366772 ,
            1.107912105 ,
            2.843777623 ,
            2.503608192 ,
            2.540595787 ,
            2.048111122 ,
            1.515498608 ,
            1.828077941 ,
            2.400006658 ,
            3.562953532 ,
            3.34333491  ,
            2.620231348 ,
            2.769874641 ,
            2.423059324 ,
            2.11147835  ,
            3.473525478 ,
            4.504105599 ,
            4.325642774 ,
            3.963498242 ,
            2.842688545 ,
            2.573038184 ,
            3.434226007 ,
            4.924115479 ,
            4.876122332 ,
            4.553580015 ,
            3.92554604  ,
            3.804585546 ,
            3.476610932 ,
            4.535171252 ,
            5.398007229 ,
            5.729933758 ,
            5.573444511 ,
            4.487695977 ,
            4.133046459 ,
            4.796637209 ,
            5.091399617 ,
            6.420441446 ,
            6.473462022 ,
            5.663322311 ,
            4.866446009 ,
            4.840966187 ,
            5.329697081 ,
            6.746910181 ,
            6.580067494 ,
            7.140083322 ,
            6.243532245 ,
            4.960520462 ,
            5.100901901 ,
            6.794495306 ,
            6.959324497 ,
            7.194674358 ,
            7.035874424 

        };
}

【讨论】:

  • 这很有趣,我现在没有时间深入回答我的回答,但是我看到的一些可能会导致问题:如果波的频率小于1?是否可以区分真实结果和“极低频结果”?
  • @ZackNewsham 傅立叶的好处在于它自然而然地采用任意单位。重要的是采样频率高于您关心的频率,您在采样时至少有几个周期的波并且线性部分不会消除波(例如,波从 0 变为1 在 20 秒内,但线性运动在那段时间内达到 1000 是行不通的)。因此,如果它看起来像我的第一张图,无论单位如何;秒,毫秒,千年你会得到一个看起来像秒的图表;但其单位将是 KHz、Hz 或 nHz
【解决方案2】:

找到变化方向改变的点。在微积分方面,这是二阶导数。

基本上,您正在寻找sign(f(x)-f(x-1))sign(f(x+1)-f(x)) 相对的点

根据您的数据采集,有几种方法可以做到这一点。如果您添加有关您的特定问题的更多信息,我将编辑答案以提供更多帮助。

【讨论】:

  • 如果信号不对称,这仍然有效吗? IE。上坡比下坡更陡,反之亦然?
  • 是的,您所做的只是寻找拐点。请注意,此方法假定您沿曲线获取数据点,而不是高点、低点、高点模式。
  • 仅仅改变方向是不够的(由于信号上的噪声)我最终寻找了一个实质性的方向改变,结果证明这是相对准确的。
【解决方案3】:

看起来,您只需要高频。使用high-pass filter

【讨论】:

  • 看来我需要知道波的频率才能使用它。
  • 是的,但是您可以从低截止频率开始并增加它,直到只通过非常高的频率。
【解决方案4】:

据我了解,这里需要的是峰相对于相邻波谷的高度。我创建了一个库,可以使用我所说的尖峰检测来处理此类场景。该库名为 jDSP,并提供了一个名为 Spike Detection 的实用程序。

可以在here 找到尖峰检测的演示,可以做的是识别相对于相邻波谷的峰高,然后使用特定值进行过滤。

橙色十字代表检测到的峰。

【讨论】:

  • 反对票背后的任何原因将不胜感激。我理解这个问题是检测局部峰值之一,这个特定库的实用程序可以解决这个问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-09-07
  • 2010-10-12
  • 1970-01-01
  • 1970-01-01
  • 2021-09-07
相关资源
最近更新 更多