问题描述
|
机器正在测量并连续给我离散数字,如下所示:
1 2 5 7 8 10 11 12 13 14 18
我们可以说这些测量值可以相差2点,并且每5秒生成一次测量值。我想忽略可能相同的度量
像连续2和3可能是相同的,因为误差范围是2,所以如何对数据进行分区,以便仅获得不同的测量值,但我还想处理测量值不断增加的情况,如下所示:
1 2 3 4 5 6 7 8 9 10
在这种情况下,如果我们继续忽略小于2的连续数字,则可能会丢失实际测量值。
为此有一类算法吗?您将如何解决?
解决方法
在某些工作中,处理此类问题的标准方法是使用卡尔曼滤波器。
引用维基百科:
其[卡尔曼滤波器]的目的是使用测量
随着时间的推移观察到,包含噪音
(随机变化)和其他
错误,并产生可以
趋于接近真实值
测量值及其
相关的计算值。
滤波器本身很容易实现,但需要校准。,只需删除上一个(保留的)“范围内”的数字即可。它应该简单地工作。
对于不断增加的示例:
保留1,因为在1的范围内将2删除,将3丢弃,因为将其在1范围内,然后将4保留,将5和6丢弃在4的范围内,然后将7保留,依此类推,因此如果它足够大,仍然保持增长趋势(这就是您想要的,对吗?
对于原始示例,结果为1、5、8、11、14、18。,我将有两个队列:
临时队列
最终队列/列表
您的第一个值将进入临时队列和最终列表。输入新值时,请检查新值是否在列表中最后一个值的死区之内。如果是,则将其添加到临时队列。如果没有,则将其添加到最终列表中。如果临时队列的大小在死区之外获得新值之前开始增加,那么一旦您处于死区之外,请检查值是否在整个时间内单调增加或减少。如果它们总是在增加或减少,则将队列的内容添加到最终列表,否则只需将单个新值添加到最终列表。这是它的总要旨。
这是我快速编写的一些代码,这些代码实现了一个类来完成我上面描述的操作:
public class MeasurementsFilter
{
private Queue<int> tempQueue = new Queue<int>();
private List<int> finalList = new List<int>();
private int deadband;
public MeasurementsFilter(int deadband)
{
this.deadband = deadband;
}
public void Reset()
{
finalList.Clear();
tempQueue.Clear();
}
public int[] FinalValues()
{
return finalList.ToArray();
}
public void AddNewValue(int value)
{
// if we are just starting then the first value always goes in the list and queue
if (tempQueue.Count == 0)
{
tempQueue.Enqueue(value);
finalList.Add(value);
}
else
{
// if the new value is within the deadband of the last value added to the final list
// then enqueue the value and wait
if ((tempQueue.Peek() - deadband <= value) && (value <= tempQueue.Peek() + deadband))
{
tempQueue.Enqueue(value);
}
// else the new value is outside of the deadband of the last value added to the final list
else
{
tempQueue.Enqueue(value);
if (QueueIsAlwaysIncreasingOrAlwaysDecreasing())
{
//dequeue first item (we already added it to the list before,but we need it for comparison purposes)
int currentItem = tempQueue.Dequeue();
while (tempQueue.Count > 0)
{
// if we are not seeing two in a row of the same (i.e. they are not duplicates of each other)
// then add the newest value to the final list
if (currentItem != tempQueue.Peek())
{
currentItem = tempQueue.Dequeue();
finalList.Add(currentItem);
}
// otherwise if we are seeing two in a row (i.e. duplicates)
// then discard the value and loop to the next value
else
{
currentItem = tempQueue.Dequeue();
}
}
// add the last item from the final list back into the queue for future deadband comparisons
tempQueue.Enqueue(finalList[finalList.Count - 1]);
}
else
{
// clear the queue and add the new value to the list and as the starting point of the queue
// for future deadband comparisons
tempQueue.Clear();
tempQueue.Enqueue(value);
finalList.Add(value);
}
}
}
}
private bool QueueIsAlwaysIncreasingOrAlwaysDecreasing()
{
List<int> queueList = new List<int>(tempQueue);
bool alwaysIncreasing = true;
bool alwaysDecreasing = true;
int tempIncreasing = int.MinValue;
int tempDecreasing = int.MaxValue;
int i = 0;
while ((alwaysIncreasing || alwaysDecreasing) && (i < queueList.Count))
{
if (queueList[i] >= tempIncreasing)
tempIncreasing = queueList[i];
else
alwaysIncreasing = false;
if (queueList[i] <= tempDecreasing)
tempDecreasing = queueList[i];
else
alwaysDecreasing = false;
i++;
}
return (alwaysIncreasing || alwaysDecreasing);
}
}
这是一些可以放入Winform Load事件或单击按钮的测试代码:
int[] values = new int[] { 1,2,1,4,8,3,6 };
MeasurementsFilter filter = new MeasurementsFilter(2);
for (int i = 0; i < values.Length; i++)
{
filter.AddNewValue(values[i]);
}
int[] finalValues = filter.FinalValues();
StringBuilder printValues = new StringBuilder();
for (int i = 0; i < finalValues.Length; i++)
{
printValues.Append(finalValues[i]);
printValues.Append(\" \");
}
MessageBox.Show(\"The final values are: \" + printValues);