微信小程序iBeacon测距及稳定程序的实现解析


Posted in Javascript onJuly 31, 2019

前言

iBeacon是苹果公司推出的一项低耗能蓝牙技术,由蓝牙设备发射包含指定信息的信号,再由移动设备接收信号,从而实现近场通信。微信小程序2017年开始支持iBeacon,摇一摇附近就是基于iBeacon实现的,此外iBeacon还可以实现距离测量,本文将介绍如何基于微信小程序实现iBeacon测距。

iBeacon测距原理

蓝牙信标发射的信号强度(rssi)与收发设备之间的距离,某种程度上呈正相关,因此通过合理的运算转化,可以通过rssi的值反推出与接收设备间的距离。

蓝牙信标的rssi值是一个参考值,没有固定标准。想要计算出蓝牙信标的距离,还必须知道这个信标设备的txPower值。txPower是指当距离蓝牙信标1m时的rssi值,不同的蓝牙设备或相同设备不同的工况甚至不同的场地环境,都会影响txPower值,因此这个值虽然可以测量,但一定程度上是个经验值,无法测准。

rssi测距公式

知道rssi和txPower后就可以计算距离了,有两种计算公式:

一、

微信小程序iBeacon测距及稳定程序的实现解析

这个公式里的三个变量A、B、C都是经验值,需要根据手机系统或硬件型号精确调校,通常会将所有设备的校准结果保存成一个设备信息表,移动终端先检测本机型号,然后匹配设备信息调取相应的计算配置,再进行计算。很明显这个公式是比较依赖硬件调校的,没有数据储备的前提下这个公式会很难用。

转换成js代码:

const calculateAccuracy = function (txPower, rssi) {
 return (0.89976) * Math.pow(rssi / txPower, 7.7095) + 0.111
}

未精校情况下的测距表现:

微信小程序iBeacon测距及稳定程序的实现解析

先说这个图怎么看。

  • 纵轴代表测量距离,横轴代表时间,每隔一秒取样一次,图中是近10次取样的数值曲线。
  • 绿线是设备接收到的rssi值,反应硬件真实接收到的数据情况;
  • 红线是套用公式计算得出的瞬时距离;
  • 黄线是微信小程序自带的瞬时测距结果。

蓝牙信标与手机的实际距离1m,测试设备为红米Note7。

从上图可见,rssi值相对稳定,说明硬件没有太大问题。红线和黄线的波动都很大,说明准确度不咋地。二者的波动趋势几乎一致,所以有理由怀疑微信小程序内部也是用的这个测距公式。从结果来看,这个公式的准确度比较差,可能是因为没有精校的原因。

二、

微信小程序iBeacon测距及稳定程序的实现解析

这个公式里的A就是rssi,tx是txPower,n是经验值,n的取值跟物理环境有关。

const calculateAccuracy = function (txPower, rssi) {
 return Math.pow(10, Math.abs(rssi - txPower) / (10 * 4))
}

公式二的测距表现:

微信小程序iBeacon测距及稳定程序的实现解析

人比人得死,货比货得扔啊。

图中黄线还是波动的那么疯狂,但红线却异常稳定,而且呈现出跟绿线一致的波动幅度,说明测距精度靠谱。这个公式只有一个参数,生产环境中的调校相对简单,这里我们选择公式二作为测距公式。

iBeacon测距稳定程序

蓝牙信号本身就有波动性,加上现实环境中的很多因素也会影响到信号强度,比如物体遮挡、设备方向变化、硬件自身的稳定性等,所以接收设备检测到的rssi值通常是“跳动”的,直接使用测距公式算出的结果,往往不可用。必须实现一个稳定程序,让计算结果呈现出连续性和稳定性。

数据滤波

稳定程序主要做的事就是对波段数据“削峰填谷”,也可以称作数据滤波。最简单的滤波处理,就是收集一段时间的值求平均,只要硬件不出问题,固定距离的蓝牙信标rssi值总是会在一个相对稳定的区间内变化,采样时间越长,采样的平均值就会越接近真实值,因此在静态测距场景中,求平均是最佳方式。

//求数组平均值
const arrayAverage = arr => arr.reduce((acc, val) => acc + val, 0) / arr.length;
return arrayAverage([...])

具体实现是,当程序源源不断的接收到信标的rssi时,先用公式计算出瞬时测距结果,然后将结果存进一个数组,然后计算这个数组的平均值。静态测距时,测量结果还是非常准的,2m以内的距离误差可以低至0.1m。

实际应用中往往都是动态测距,所以采样数据的长度要加以限制,比如按后进先出的顺序,取最近10组数据。具体采样队列设为多长,要根据项目实际需求而定。采样队列的长度越长,测距结果越平滑,但对移动端的动态捕捉越迟钝;反之采样队列越短,结果越锐利,对移动端的动态捕捉越灵敏。

有时因为一些偶然因素,采样队列中会出现个别大幅偏离真实值的“燥音”数据,即使求平均也难以有效抹除影响,为消除这种影响,可以在求平均前先用高斯模糊算法对“偏大值”和“偏小值”做平滑处理,最大限度的降低数据噪音的干扰。

高斯模糊算法的关键是根据平均差求权重,一维高斯模糊的权重计算公式:

微信小程序iBeacon测距及稳定程序的实现解析

转换成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直接算出来的瞬时测距结果,黄线是加入稳定程序后的测距结果。

第一张图是相对静止的条件,可以看到黄线相对红线明显更加平稳,说明稳定程序还是起作用的。

微信小程序iBeacon测距及稳定程序的实现解析

第二张图是模拟快速远离的场景,可以看到黄线在保证平稳的前提下紧跟红线,没有被甩掉,主要体现的是稳定程序的动态跟进效果。

微信小程序iBeacon测距及稳定程序的实现解析

第三张图是抡胳膊甩手机+遮挡信号模拟出的场景,貌似稳定程序也架不住了,有点飘忽。

微信小程序iBeacon测距及稳定程序的实现解析

以上是关于稳定程序的简要实现思路,生产环境中肯定会面临更加复杂的情况,免不了还要做大量调试,这里只是抛砖引玉。

总结

蓝牙测距简单来说就是一个公式的应用,本身比较简单,基于测距可以实现很多近场应用,比如近场签到、近场推送等等,更进一步甚至可以实现对移动设备的定位,有了定位信息,很多室内定位、室内导航相关的应用就都可以实现了。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JavaScript高级程序设计
Dec 29 Javascript
6款新颖的jQuery和CSS3进度条插件推荐
Mar 05 Javascript
使用js实现的简单拖拽效果
Mar 18 Javascript
javascript实现全角半角检测的方法
Jul 23 Javascript
jQuery多条件筛选如何实现
Nov 04 Javascript
JavaScript基于扩展String实现替换字符串中index处字符的方法
Jun 13 Javascript
vue组件实现可搜索下拉框扩展
Oct 23 Javascript
vuejs选中当前样式active的实例
Aug 22 Javascript
vue实现父子组件之间的通信以及兄弟组件的通信功能示例
Jan 29 Javascript
vue.js中ref及$refs的使用方法解析
Oct 08 Javascript
Vue中computed及watch区别实例解析
Aug 01 Javascript
js获取url页面id,也就是最后的数字文件名
Sep 25 Javascript
JS获取动态添加元素的方法详解
Jul 31 #Javascript
JS/jQuery实现超简单的Table表格添加,删除行功能示例
Jul 31 #jQuery
详解Vuex下Store的模块化拆分实践
Jul 31 #Javascript
ES6 Iterator接口和for...of循环用法分析
Jul 31 #Javascript
Vuex 模块化使用详解
Jul 31 #Javascript
判断“命令按钮”是否被鼠标单击详解
Jul 31 #Javascript
express框架下使用session的方法
Jul 31 #Javascript
You might like
双料怀旧--SHARP GF515的维护、修理和简单调试
2021/03/02 无线电
咖啡知识大全
2021/03/03 新手入门
php URL编码解码函数代码
2009/03/10 PHP
使用WordPress发送电子邮件的相关PHP函数用法解析
2015/12/15 PHP
基于PHP后台的Android新闻浏览客户端
2016/05/23 PHP
浅谈PHP安全防护之Web攻击
2017/01/03 PHP
Laravel如何实现自动加载类
2019/10/14 PHP
jquery UI 1.72 之datepicker
2009/12/29 Javascript
JSQL  一个 web DB 的封装
2010/05/05 Javascript
javascript与CSS复习(《精通javascript》)
2010/06/29 Javascript
jquery toolbar与网页浮动工具条具体实现代码
2014/01/12 Javascript
js中string转int把String类型转化成int类型
2014/08/13 Javascript
js和jquery中循环的退出和继续下一个循环
2014/09/03 Javascript
jQuery实现转动随机数抽奖效果的方法
2015/05/21 Javascript
Jquery数字上下滚动动态切换插件
2015/08/08 Javascript
js 判断各种数据类型的简单方法(推荐)
2016/08/29 Javascript
用JavaScript和jQuery实现瀑布流
2017/03/19 Javascript
Bootstrap datepicker日期选择器插件使用详解
2017/07/26 Javascript
关于JavaScript中forEach和each用法浅析
2017/07/27 Javascript
微信小程序 上传头像的实例详解
2017/10/27 Javascript
Layui 解决表格异步调用后台分页的问题
2019/10/26 Javascript
JS精确判断数据类型代码实例
2019/12/18 Javascript
js this 绑定机制深入详解
2020/04/30 Javascript
[05:31]DOTA2英雄梦之声_第08期_莉娜
2014/06/23 DOTA
Python抓取Discuz!用户名脚本代码
2013/12/30 Python
浅析Python中的序列化存储的方法
2015/04/28 Python
python 实现识别图片上的数字
2019/07/30 Python
Django rstful登陆认证并检查session是否过期代码实例
2019/08/13 Python
利用python在大量数据文件下删除某一行的例子
2019/08/21 Python
OpenCV哈里斯(Harris)角点检测的实现
2020/01/15 Python
学生如何注册Pycharm专业版以及pycharm的安装
2020/09/24 Python
维多利亚的秘密官方旗舰店:VICTORIA’S SECRET
2018/04/02 全球购物
英国综合网上购物商城:The Hut
2018/07/03 全球购物
英国门把手公司:Door Handle Company
2019/05/12 全球购物
某公司面试题
2012/03/05 面试题
彩色的非洲教学反思
2014/02/18 职场文书