Удалить шум и экстремальные значения из данных?
У меня есть программа, которая читает данные по последовательному с АЦП на PSoC.
Числа отправляются в формате <uint16>
включая символы "<" и ">", передаваемые в двоичном формате 00111100 XXXXXXXX XXXXXXXX 00111110
где 'X' составляют 16-битный без знака int.
Иногда чтение не работает очень хорошо, и программа использует двоичные данные для символа ">" как часть его числа, что приводит к сбою, как показано на этом снимке экрана с 2500 выборками (игнорируйте падение между выборками от 800 до 1500, что Был ли я играть с входом АЦП):
Вы можете ясно видеть, что сбой приводит к тому, что данные каждый раз примерно одинаково выбираются.
Данные отправляются десять раз в секунду, поэтому я планировал взять десять выборок, удалить все сбои (если значение находится далеко от других выборок), а затем усреднить оставшиеся значения, чтобы немного сгладить кривую., Выход может быть в любом месте от 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
}
3 ответа
Я подозреваю, что ваша проблема может быть в том, что вы читаете из последовательного порта меньше байтов, чем вы думаете.
Например, 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. На самом деле, судя по графику, я бы сказал, что он почти всегда одинаков. Вы можете просто игнорировать данные, находящиеся в диапазоне сбитых значений (вам придется провести некоторое тестирование, чтобы найти точные границы), и использовать вместо этого последнее не сбитое значение.
Распространенным методом в инженерии является добавление демпфирующей функции. Функция демпфирования в основном действует на дифференциал параметра, то есть на разницу между последовательными значениями. Не существует жестких и быстрых правил о том, как выбрать функцию демпфирования, и в основном они настроены для получения разумного результата.
Так что в вашем случае это означает, что вы сравниваете последнее значение с предыдущим значением. Если оно больше определенной величины, либо по умолчанию последнее значение по умолчанию равно предыдущему, либо уменьшите последнее значение на некоторый фиксированный коэффициент, скажем, на 10% или 1%. Таким образом, вы не теряете информацию, но и не получаете внезапных скачков и глюков.