测距原理:

实际的测距原理相当简单,iBeacon在发布广播信号时,会提供两个重要信息(广播包解析自行学习):

一、iBeacon蓝牙会发送一个信号强度,接收者在收到广播时,能解析出该信号,且信号具有距离越近值越大的特点;既接收者和发送者距离越近,该值越大且越接近0(该值为负数),该值名为rssi;

二、iBeacon本身会设置1米的参照的rssi信号,指定该iBeacon设备距离接收者1m时,接收者收到的rssi的参考值;

通过这两个值,按照固定公式即可算出距离。公式有两个,详情如下:

公式一:

公式里的三个变量 A、B、C 都是经验值,需要根据硬件精确调校,具体的调校就比较费时间了,需要进行大量的测试,没有数据储备的前提下这个公式会很难用。rssi就是前面提到的rssi,tx为1米参照rssi值;

公式代码实现:

float_t ibeacon_calculate_accuracy(int32_t tx, int32_t rssi)
{

const int a = 0.89973, b = 7.7096, c = 0.111;

return a * pow(rssi / tx, b) + c;
}

公式二:

公式里面rssi就是前面提到的rssi,tx为1米参照rssi值,n为经验值同样需要调校;

公式代码实现:

float_t ibeacon_calculate_accuracy(int32_t tx, int32_t rssi)
{

const int n = 4;
    float_t step1 = ((float)abs(rssi - tx)) / (10 * n);
    return pow(10, step1);
}

两个公式相较来说,公式二更容易调校,我用的也是公式二;

rssi和1m参考值进一步解释:

在实际应用中,相同距离时,无视rssi抖动的情况下,rssi的值也是不固定的,而是跟发射功率成正比,发射功率越强,相同距离接收到的rssi值也就越大。功率越大,接收范围越大,rssi值随着距离衰减越小;功率越小,接收范围越小,rssi值随着距离衰减越大;换句话说,如果要测定的距离较近应相应调小发射功率,如果要测定的距离较远应相应调大发射功率,这样才能精确测距。

同理,当发射功率发生变化时,1米的参考rssi值也应该相应修改,才能准确计算距离;

有必要提一下,从包中解析出来的rssi值和1米的参考rssi值都是正数且大于0x80的值,但其实际为负值,应执行下rssi - 0x100,得到实际值。

数据滤波

在实际应用场景中,固定发射功率和相对距离的情况下,rssi值是一个抖动信号,其抖动原因跟硬件、信道使用、是否有障碍物、天线方向等等多方面因素有关,所以iBeacon蓝牙测距在实际应用中,数据去抖也就成了关键点。

所谓的去抖,实际上就是计算一段时间内的数据平均值,但是要做到计算出来的平均值更接近实际的值,基于该理念找到了以下两种计算均值的方案:削谷平均法、高斯模糊函数。

两种计算方式仅能消除少数异常的rssi值,当出现强干扰的时候,完全无能为力,比如发送者和接收者两者间有一堵墙。

对多长时间的rssi值进行滤波也是有讲究的,静态测距还好,时间设定为多久都无所谓,rssi也不会失效。动态测距就不一样了,时间太久,最早收到数据很有可能就失去了实际意义。所以在计算滤波的时候,要得到有效时间内尽可能多的有效值。在一定范围内,有效时间定的越大,去抖效果越好,但延迟越高;有效时间定的越小,去抖效果越差,但及时性越高。

削谷平均法

从字面解释既为去掉最小值然后平均剩下的所有值,为什么要删除最小值而不是删除极值(一个最大值一个最小值呢)?在实际应用场景中,rssi信号值,会受种种因素干扰,而干扰总是导致信号往弱的方向强烈抖动,确极少情况下将信号往强的方向干扰,即使信号有往强方向波动的情况,也是在可接受平均的限度内,所以完全没必要删除该值进行计算均值。而往弱的方向波动就不一样了,如有东西短暂遮挡时,就会出现很大范围的抖动。

并不是所有情况都削谷,只有满足最大值和最小值差距过大的时候才会去掉最小值,这样是为了尽可能保留较多的有效数据。

#define MAX(a, b)     ((a) > (b) ? (a) : (b))

#define MIN(a, b)     ((a) < (b) ? (a) : (b))

// array:要计算均值的一组rssi数据

// len:数据个数

// diff_rssi:能接受最大的最大rssi和最小rssi差值,该值根据测距的精度调节

int32_t ibeacon_average_rssi_del_extremum(int32_t *array, uint32_t len, int32_t diff_rssi)
{
    int32_t max, min;
    int32_t sum = 0;
    int32_t count_rssi = 0;

max = array[0];
    min = max;

for (uint32_t i = 0; i < len; i++)
    {
        int32_t temp_rssi = array[i];
        max = MAX(max, array[i]);
        min = MIN(min, array[i]);
        sum += array[i];
        count_rssi++;
    }

if (max - min > diff_rssi)
    {
        // 出现极端最小值,有可能是干扰或遮挡导致的信号过小,
        // 而最大值一定是因为距离近才收到的信号;即使有波动,也在可接受范围内
        sum -= min;
        count_rssi--;
    }

return sum / count_rssi;
}

高斯模糊函数

一维公式:

假设有一组数据array,则x是array的一个元素,f(x)为x对应的权重,μ是array的算数平均值,σ是array的方差。

该函数算出的仅仅只是某一元素对应的权重,要想通过各个元素的权重计算出滤波后的值,还要对权重进行归一化,否则算出的滤波后的值,只是实际值的百分比。未归一化的滤波值乘以1除以所有元素的权重之和,既可得到实际滤波后的值。

// 计算算数平均数

float_t ibeacon_average(int32_t *arr, uint32_t len)
{
    int32_t sum = 0;
    for (uint32_t i = 0; i < len; i++ )
    {
        sum += arr[i];
    }

return (float_t)sum / len;
}

// 计算方差

float_t ibeacon_standard_deviation(int32_t *arr, uint32_t len, float_t average)
{
    float_t sum = 0;
    float_t temp = 0.0;

for (uint32_t i = 0; i < len; i++ )
    {
        temp = arr[i] - average;
        sum += (temp * temp);
    }
    return sqrt(sum / len);
}

// 高斯函数,公式对应函数,计算权重
// x:一组数据中的一个,average:一组数据的平均值,standard_deviation;一组数据的标准差
static inline float_t ibeacon_gauss_weight(int32_t x, float_t average, float_t standard_deviation)
{
    //因为这是个常量,没必要每次都用  sqrt(2 * M_PI)去计算 去PI = 3.14159265358979323846
    #define IBEACON_SQRT_2_PI 2.5066282746

// float_t step1 =   sqrt(2 * M_PI) * standard_deviation;
    float_t step1 = IBEACON_SQRT_2_PI * standard_deviation;
    float_t step2 = x - average;
    float_t step3 = 0 - (step2 * step2) / (2 * standard_deviation * standard_deviation);

return exp(step3) / step1;
}

//计算平均值,使用高斯模糊函数,值乘以权重加和法
static int32_t ibeacon_average_rssi_gauss(int32_t *array, uint32_t len)
{
    float_t weight = 0;
    float_t weight_num = 0;
    float_t gauss_average = 0;

float_t average = ibeacon_average(array, len);
    float_t standard_deviation = ibeacon_standard_deviation(array, len, average);
    if (0 == standard_deviation)
    {
        // 0不能做除数,当标准差为0时,说明所有值相等,直接用平均值即可
        return (int32_t)average;
    }

for (int32_t i = 0; i < len; i++)
    {
        weight = ibeacon_gauss_weight(array[i], average, standard_deviation);
        weight_num += weight;
        gauss_average += (array[i] * weight);
    }

// 除以weight_num是为了将权重归一化
    return (int32_t)(gauss_average / weight_num);
}

时间加权

经过上面两种计算均值方案,基本上能够达到要求了,但是仍然还可以再进一步的滤波,时间加权。

时间加权既按照时间对数据进行特定的权重增加。在这里,时间加权的定义为一组有效数据中,时间近的一半数据权重为时间远的一半数据权重的二倍。为什么要这样定义呢?首先可以肯定的是,理论上时间越近,rssi值越靠近真实距离,所以时间近的rssi数据权重更多也毋容置疑。除了这个还有一个原因,把时间近的权重增加,实际上就是把时间近的运动趋势放大了,这种行为是否会有点激进呢?实际上并没有,因为对于运动的物体都是有惯性的,这个激进的放大反而更准确些。

用拷贝数据的方式,对需要权重加倍的数据进行权重加倍,既将时间近的一半数据拷贝一份,填补在数据的后面。可把时间加权放在削谷平均法、高斯模糊函数之前。

// array:按照时间排序后的rssi数据

// len:现在array的数据个数

// size:array的空间大小

uint32_t ibeacon_rssi_time_weight(int32_t *array, uint32_t len, uint32_t size)
{
    // 时间权重加持
    uint32_t n = 0;

// 用时间最近的一半数据加倍的方式,来提高权重
    for (uint32_t i  = len / 2; i < len; i++)
    {
        array[len + n] = array[i];
        n++;
    }

return len + n;
}

运动趋势判断

在实际应用过程中,可能会有需要获取物体运动趋势的需求,是靠近还是远离。可以通过计算逆序列数量除以n * (n-1)的方式得到。结果范围[0~1],返回值越接近0,升序概率越大,返回值越接近1,降序概率越大。升序既为靠近,降序即为远离,该计算方式必须在时间权重之前计算,否则会影响结果。

// 归并排序计算逆序列数量
int32_t ibeacon_merge_sort(int32_t *num, int32_t *tmp, uint32_t l, uint32_t r)
{
    if (l >= r)
    {
        return 0;
    }

uint32_t mid = l + r >> 1;
    uint32_t res = ibeacon_merge_sort(num, tmp, l, mid) + ibeacon_merge_sort(num, tmp, mid + 1, r);

int32_t i = l, j = mid + 1, k = 0;
    while (i <= mid && j <= r)
    {
        if (num[i] <= num[j])
        {
            tmp[k++] = num[i++];
        }
        else
        {
            tmp[k++] = num[j++];
            res += mid - i + 1;
        }
    }

while(i <= mid)
    {
        tmp[k++] = num[i++];
    }
    while(j <= r)
    {
        tmp[k++] = num[j++];
    }
    for(uint32_t i = l, j = 0; i <= r; ++i, ++j)
    {
        num[i] = tmp[j];
    }

return res;
}

// 计算数据变化趋势系数,范围[0~1],返回值越接近0,升序概率越大,返回值越接近1,降序概率越大
nt32_t ibeacon_decline_probability(int32_t *array, uint32_t len)
{
    int32_t tmp[IBEACON_DEV_RSSI_NUM] = {0};
    uint32_t probability = ibeacon_merge_sort(array, tmp, 0, len - 1);

return (float)probability * 2 / (len * (len - 1));
}

结尾

经过这些步骤对数据处理后,基本上能够解决大部分测距需求。实际测距过程中最大的影响因素还是信号强度的稳定性,软件上的滤波仅能滤掉极少数的不正常数据。iBeacon蓝牙除了能测距外,依赖测距技术还能进行定位,希望以后有机会研究这块的技术。

C实现iBeacon蓝牙测距相关推荐

  1. 微信iBeaconID-微信官方iBeacon蓝牙基站UUID编码

    如何使用微信"摇一摇 周边"功能?如何让你的微信显示出"摇一摇 周边"功能? 正文的最前面先公布一下微信iBeaconID(微信官方iBeacon的UUID): ...

  2. ibeacon蓝牙技术简介

    概述 在讲解ibeacon技术之前,我们首先来看一下蓝牙实际到现在经历了哪些发展.截止目前,蓝牙共有八个版本 V1.0/1.1/1.2/2.0/2.1/3.0/4.0/4.1,各版本的功能变化如下: ...

  3. 蓝牙android rssi测距,基于 RSSI 的蓝牙测距

    蓝牙 RSSI 计算距离https://blog.csdn.net/njchenyi/article/details/46981423 利用 CoreLocation.framework 很容易扫描获 ...

  4. 基于ibeacon蓝牙定位(微信小程序)

    前段时间做定位,查了很多资料,最后感觉ibeacon做定位比较简单,主要微信小程序有对应的接口所以非常简单.需要的硬件ibeacon,这个硬件在淘宝都有,简单的定位一个就可以,复杂就需要根据需求购买了 ...

  5. 微信小程序蓝牙ibeacon_微信小程序iBeacon测距及稳定程序的实现解析

    前言 iBeacon是苹果公司推出的一项低耗能蓝牙技术,由蓝牙设备发射包含指定信息的信号,再由移动设备接收信号,从而实现近场通信.微信小程序2017年开始支持iBeacon,摇一摇附近就是基于iBea ...

  6. 微信小程序iBeacon测距及稳定程序的实现

    iBeacon是苹果公司推出的一项低耗能蓝牙技术,由蓝牙设备发射包含指定信息的信号,再由移动设备接收信号,从而实现近场通信.微信小程序2017年开始支持iBeacon,摇一摇附近就是基于iBeacon ...

  7. 室内定位方案部署WIFI定位还是IBeacon定位-新导智能

    iBeacon,在国内其完成已能够很好的完成室内定位方案计划的进程,并且在露天煤矿.化工厂.商场等场景下均有小范围的试用.到购物中心的地下停车场,购完物后,是不是有找不着停车位的时分?那么问题来了,怎 ...

  8. 室内定位程序_室内定位方案部署WIFI定位还是IBeacon定位

    iBeacon,在国内其完成已能够很好的完成室内定位方案计划的进程,并且在露天煤矿.化工厂.商场等场景下均有小范围的试用.到购物中心的地下停车场,购完物后,是不是有找不着停车位的时分?那么问题来了,怎 ...

  9. 室内定位常用定位技术_米级蓝牙定位方案,厘米级UWB定位方案

    前言 随着物联网生态链逐渐走向成熟,各行各业对定位的需求也大大增加.由于GPS卫星信号在室内无法定位,且容易受到各种无线电信号的干扰,为实现"最后一公里"的室内位置服务,目前主流的 ...

  10. iBeacon系列:三、iBeacon能否精确室内定位等10个技术问题

    国内iBeacon技术很热,但仍旧有很多误解,所以我们很想澄清关于iBeacon的一些真相: 1. iBeacon如何对外推送信息? 这是最常见的对iBeacon的误解.iBeacon不是伪基站,iB ...

最新文章

  1. 利用Python进行数据分析(第2版)
  2. 存储过程---角色权限叠加
  3. 从课堂走向实践还有多远?
  4. opencl高斯源码整理
  5. vc++基于颜色直方图的图像检索,含代码
  6. .NET Core开发实战(第16课:选项数据热更新:让服务感知配置的变化)--学习笔记...
  7. koa中上传文件到阿里云oss实现点击在线预览和下载
  8. java 顺序存储键值对_java://Comparator、Comparable的用法(按照要求将map集合的键值对进行顺序输出)...
  9. 传感器工作原理_荧光氧气传感器工作原理简介
  10. puppetmaster 、agent 证书管理相关
  11. iview的select联动_render函数渲染的iview中的Select组件如何联动?
  12. DroidCam---将手机转为电脑外接摄像头的软件(提供下载链接)
  13. AOP切面编程的理解
  14. linux rpm是啥意思,rpm是什么意思?
  15. 【学术版】《最强大脑记忆力训练教程》
  16. static关键字的用法
  17. js alert弹窗函数
  18. 清理电脑,使其加速!
  19. Unity的Application.Quit()方法使用失效的其他解决方案。
  20. 邯郸北方计算机学校广平区,邯郸北方汽修学校25周年校庆暨2018家长会圆满成功...

热门文章

  1. 树莓派cm4 ioboard配置虚拟网卡、静态ip、dhcp服务
  2. jle汇编_x86汇编指令集大全(带注释)
  3. (2)卷积 卷积和
  4. 【算法笔记】莫队算法(基础莫队,带修莫队,回滚莫队,树上莫队,二次离线莫队)
  5. MIUI系统手机实现WLAN热点桥接
  6. oracle outsidein,Oracle Outside In远程代码执行漏洞
  7. RK3399 Android7.1以太网卡百兆网正常千兆网不能用
  8. redis 应用场景
  9. JavaEE | 线程安全(锁、线程间通信、内存可见性、CAS、线程的状态)
  10. java apm_APM 追踪 Java 应用性能