有时因为一些偶然因素,采样队列中会出现个别大幅偏离真实值的“燥音”数据,即使求平均也难以有效抹除影响,为消除这种影响,可以在求平均前先用高斯模糊算法对“偏大值”和“偏小值”做平滑处理,最大限度的降低数据噪音的干扰。
高斯模糊算法的关键是根据平均差求权重,一维高斯模糊的权重计算公式:
转换成js代码:
//求一维队列某点的高斯模糊权重 @param(队列长度,目标位置, 平均差) const getOneGuassionArray = function (size, kerR, sigma) { if (size % 2 > 0) { size -= 1 } if (!size) { return [] } if (kerR > size-1){ return [] } let sum = 0; let arr = new Array(size); for (let i = 0; i < size; i++) { arr[i] = Math.exp(-((i - kerR) * (i - kerR)) / (2 * sigma * sigma)); sum += arr[i]; } return arr.map(e => e / sum); }
关于“偏大值”和“偏小值”的概念将在下文介绍,这里只要知道我们的模糊目标是那些“极端数据”就行了。
时间加权
基于采样队列求平均的处理方式,不可避免的会让结果产生滞后性,这时可以引入时间加权的补偿算法。
所谓时间加权,是指在求平均值的时候,给距离当前时间较近的值更高的计算权重,反之给距离当前时间较远的值较低的计算权重,实现起来也非常简单。
以最简单的权重分配为例,将采样队列一分为二,按时间远近定位为“当前组”和“过去组”,比如说我想让当前组的权重是过去组的2倍,那么只要将当前组数据全部复制一份加入队列,然后再计算新队列的平均值。
//时间加权处理 queue = queue.slice(0, parseInt(queue.length / 2)).concat(queue) //求平均 return arrayAverage(queue)
动态跟进
经过时间加权处理后,数据的滞后性会得到一定的抑制,但如果遇到比较“陡峭”的距离变化,这种处理仍然会给出一个相对“平滑”的反馈,为了让稳定程序能更好的感知动态变化,并且做出跟进反应,还需要人为的设置一些特殊条件。
首先,如何判断移动设备正在远离或靠近?
这里有一个简单的思路,可以先找出采样队列中的最大值和最小值,然后以一定的阈值找出偏大值和偏小值。比如队列中的最大值是3,最小值是1,阈值设置为0.1m,那么大于2.9m的数据都算偏大值,小于1.1m的数据都算偏小值。偏大值和偏小值的队列长度最长不超过总队列的二分之一。
然后,如果偏大值集中在队列的前三分之一部分,那么我们可以认为移动设备正在果断远离;反之偏小值集中在队列的前三分之一部分,则可以认为移动设备正在靠近。
//maxCount为偏大值的序号数组 //minCount为偏小值的序号数组 //queueLength为队列长度 if (arrayAverage(maxCount) < parseInt(queueLength / 3)) { console.log(`正在远离`) } else if (arrayAverage(minCount) < parseInt(queueLength / 3)) { console.log(`正在靠近`) }
基于这种远离和靠近的趋势判断,我们可以人为的让数据向运动方向做更激进的倾斜。怎么做呢?跳过时间加权逻辑,如果判断为正在远离,那么就将队列中的偏小值过滤掉,反之则将偏大值过滤掉,只计算剩下的数据;这种处理会得到一个明显过激的结果,但考虑到现实世界中的运动往往具有惯性,这种激进处理,可能会更贴合真实的运动情况,而且让数据的响应更“灵敏”。
效果检验
做到目前为止效果怎么样呢,直接看图吧。
下图中,绿线依然是rssi值,红线是根据rssi直接算出来的瞬时测距结果,黄线是加入稳定程序后的测距结果。
第一张图是相对静止的条件,可以看到黄线相对红线明显更加平稳,说明稳定程序还是起作用的。
第二张图是模拟快速远离的场景,可以看到黄线在保证平稳的前提下紧跟红线,没有被甩掉,主要体现的是稳定程序的动态跟进效果。
第三张图是抡胳膊甩手机+遮挡信号模拟出的场景,貌似稳定程序也架不住了,有点飘忽。
以上是关于稳定程序的简要实现思路,生产环境中肯定会面临更加复杂的情况,免不了还要做大量调试,这里只是抛砖引玉。
总结