从数据中去除噪声和极值?

Remove noise and extreme values from data?

我有一个程序可以通过串行方式从 PSoC 上的 ADC 读取数据。

数字以 <uint16> 格式发送,包括“<”和“>”符号,以二进制格式 00111100 XXXXXXXX XXXXXXXX 00111110 传输,其中“X”构成 16 位无符号整数.

有时读取效果不佳,程序使用“>”符号的二进制数据作为其编号的一部分,导致出现故障,如 2500 个样本的屏幕截图所示(忽略样本之间的下降800 到 1500,那是我在玩 ADC 输入):

您可以清楚地看到,每次发生故障时,数据都会采样大致相同的值。

数据每秒发送十次,所以我打算做的是取十个样本,去除任何毛刺(值与其他样本相差很远的地方),然后对剩余值进行平均以平滑出曲线一点。输出可以在 0 到 50000+ 之间的任何地方,所以我不能只删除低于某个数字的值。

我不确定如何删除在 10 样本组中远离其他值范围的值,因为在某些情况下可能有两个样本受此故障影响.也许有一些其他方法可以修复这个有问题的数据,而不仅仅是解决它!

最好的方法是什么?到目前为止,这是我的代码(在 DataReceivedEvent 方法中):

SerialPort sp = (SerialPort)sender; //set up serial port
byte[] spBuffer = new byte[4];
int indata = 0;

sp.Read(spBuffer, 0, 4);
indata = BitConverter.ToUInt16(spBuffer, 1);

object[] o = { numSamples, nudDutyCycle.Value, freqMultiplied, nudDistance.Value, pulseWidth, indata };
lock (dt)    //lock for multithread safety
{
    dt.Rows.Add(o); //add data to datatable
}

工程上常用的方法是增加阻尼功能。阻尼函数基本上作用于参数的微分,即连续值之间的差异。关于如何选择阻尼函数并没有硬性规定,大多数情况下会对其进行调整以产生合理的结果。

因此,在您的情况下,这意味着您将最新值与之前的值进行比较。如果它大于一定数量,要么将最新值默认为前一个值,要么将最新值减少某个固定因子,比如 10% 或 1%。这样你就不会丢失信息,也不会出现突然的跳跃和故障。

我怀疑你的问题可能是因为你从串行端口读取的字节数比你想象的要少。

例如,sp.Read(spBuffer, 0, 4);不一定会读取 4 个字节。它可以读取 1、2、3 或 4 个字节(但绝不是 0)。

如果您知道应该读取一定数量的字节,请尝试这样的操作:

public static void BlockingRead(SerialPort port, byte[] buffer, int offset, int count)
{
    while (count > 0)
    {
        // SerialPort.Read() blocks until at least one byte has been read, or SerialPort.ReadTimeout milliseconds
        // have elapsed. If a timeout occurs a TimeoutException will be thrown.
        // Because SerialPort.Read() blocks until some data is available this is not a busy loop,
        // and we do NOT need to issue any calls to Thread.Sleep().

        int bytesRead = port.Read(buffer, offset, count);
        offset += bytesRead;
        count -= bytesRead;
    }
}

如果在读取过程中出现超时,应该有一个 TimeoutException,因此无需将您自己的超时放在那里。

然后像这样更改调用:

sp.Read(spBuffer, 0, 4);

为此:

BlockingRead(sp, spbuffer, 0, 4);

首先,我强烈建议只解决解析问题,这样你就不用担心错误值了。

但是,如果您之后仍决定继续修复数据: 我看到所有出现故障的数据都在某个值附近:~16000。事实上,从图表来看,我想说每次都几乎相同。您可以简单地忽略在故障值范围内的数据(您必须进行一些测试才能找到确切的范围),并使用最后一个非故障值代替。