Node.js 与 Raspberry Pi 打造 Beacon 追踪器

在过去的几周里,我们一走在工作的 Beacon 上。我们将它们用于具有室内近距离跟踪功能的项目。 我们需要跟踪 Beacon 本身在空间中移动,然后在屏幕上显示输出。作为一个概念的证明,我们决定用 Raspberry Pi 和 Node.js 来滚动我们自己的 Beacon 扫描器。结果如下步骤所示:

https://vimeo.com/114711115

在项目开始的时候,我们有了蓝牙智能(蓝牙低功耗)的基本工作知识,在我们投入生产时,我们很快遇到了一些意想不到的有趣的障碍。 在这篇文章中,我们将介绍我们的过程以及迄今为止收集到的一些见解。

硬件综述

在我们完成解决方案之前,我们通过了很多硬件。这是个亮点。

Raspberry Pi B+

我们决定使用 Raspberry PI B+ 作为扫描仪的基础硬件。它适合我们需求的形状和在 Raspbian 发行版上获取 Node.js 也是一件轻而易举的事情。我们还增加了一个基本的 wifi 和蓝牙 dongle。

Beacon 与 Raspberry Pi

Nordic Semiconductor nRF beacon

我们从 Nordic Semiconductor nRF 的 beacon 开始。这些是与 Estimotes 相同的芯片,我们喜欢他们没有任何盒子,我们想在以后使用 3D 打印制作一个。关于这些信标的一个重要的事情是,它们没有被配置为立即广播 iBeacon 消息。 这首先把我们困住了,因为它们没有出现在我们的一些蓝牙实用程序扫描仪中(Beacon 如下所示)。

Nordic Semiconductor nRF beacons

Estimotes

我们有这些坏男孩坐在我们的办公室大约 8 个月,最后得到他们的考验。Estimaotes 是 iBeacon 认证的,我们所有的蓝牙 BLE 实用程序都能马上检测到它们。他们似乎也是与报道的数据最一致的。Estimote iOS 应用程序是一流的,整体硬件构建质量非常好。唯一的缺点是它们的大小。 为了我们的目的,我们正在寻找更小的东西。 我们很高兴看到他们的 Beacon 贴纸在运送时如何运作。

Estimotes Beacons

高通的 Gimbal

我们还注册了高通开发者帐号,并获得了 3 个免费的 Gimbal Beacon,感谢 Qualcomm!钥匙扣离我们的演示所需要的更近,而且不能超过 $5 的价位。 这些信标附带 iOS 应用程序和 Web 应用程序配置工具,可以轻松更新设备的固件。

关于这些信标唯一令人讨厌的事情是:他们从盒子里拿出一个安全功能,每当他们发布广告时就改变 mac 地址。所以当我们打开蓝牙扫描仪时,我们会看到大约 20 个独特的蓝牙智能设备。除此之外,形状因素是好的,Gimbal 给你的信标的配置体面控制。

Gimbal Beacon

拿走硬件

总的来说,我们感觉到有很多人在做针对支持苹果 iBeacon 格式的设备的工作。 苹果似乎是在比赛中领先,而 iBeacon 得到了广泛的支持(与非 iBeacon 配置相比)。 而且,当我们有问题的时候,使用我们的 Google-fu 找到社区的答案是相对容易的。

未使用 iBeacon 配置进行广播的信标似乎难以处理和追踪文档,但这主要是因为我们不耐烦。 总的来说,Estimotes 是我们最喜欢的 Beacon 硬件。

软件

Raspberry Pi 上的蓝牙追踪非常简单,最大的障碍(也是最耗时的)是获得所有正确的软件。 以下是我们用来获取 POC 工作的软件列表。

Bluez

Bluez 是 Linux 的核心蓝牙栈,Raspberry Pi 需要开始扫描蓝牙智能设备。 本文中提到的所有后续库和实用程序都依赖于安装了 Bluez。

安装 Bluez 并不难。Jared Wolff 的教程帮助我完成了这一过程。拿一杯咖啡,这个安装需要大约 90 分钟的时间。

hcitool & gatttool

一旦安装了 Bluez,您可以使用这两个实用程序来扫描并连接到蓝牙和智能设备。Hcitool 成为一个非常有用的工具,可以快速扫描信标硬件,而无需编写任何自定义代码。 安装 Bluez 后,您可以使用以下命令获取附近的硬件地址列表:

$ sudo hcitool lescan


LE Scan ...
7C:66:9D:9B:2D:DA (unknown)
7C:66:9D:9B:2D:DA Estimote
7C:66:9D:9B:2D:DA (unknown)
7C:66:9D:9B:2D:DA (unknown)

一旦你找到你正在寻找的硬件地址,你可以使用 gatttol 连接到设备,以获得更多的信息,包括制造商的数据和可用的服务。如果你想深入挖掘一下 gatttol,我会推荐Mike Ryan 的博客 文章......它帮助我们揭开了神秘面纱。

Node.js

下一步是让 Node.js 在 Raspberry Pi 上运行,以便我们可以开始将数据抽到服务器进行分析。谢天谢地,随着 ARM 版本 Node.js 的发布,这个过程变得更容易了。只需输入以下命令,大约 20 分钟后就可以开始了。

sudo wget http://node-arm.herokuapp.com/node_latest_armhf.deb 
sudo dpkg -i node_latest_armhf.deb

Noble

在安装 Node.js 的时间,我们搜索 Github 以获取基于 Node 的 Beacon 库,并通过 SandeepMistry 找到 Noble。 Sandeep还写了一些其他的蓝牙库,包括 BlenoBleacon,但为了我们的目的,我们继续使用 Noble。

Noble 使扫描蓝牙设备和访问元数据变得非常简单。 使用下面的代码,我们能够扫描附近的设备,并打印出他们的硬件地址,rssi和本地名称。

noble.startScanning();

noble.on(‘discover’, function(peripheral) { 

  var macAddress = peripheral.uuid;
  var rss = peripheral.rssi;
  var localName = advertisement.localName; 
  console.log('found device: ', macAdress, ' ', localName, ' ', rss);   
}

注意 - 在 Noble 中“perihperal.uuid” 实际上是 mac 地址,实际的 uuid 可以通过制造商数据有效载荷(通常看起来像(4C00 02 15 585 CDE931B0142CC9A1325009BEDC65E 0000 0000 C5))被解析出广告数据包。 请参考这个博客了解更多关于有效载荷的信息

Socket.io

一旦我们能够可靠地跟踪给定 Beacon 的 rssi,我们就通过网络套接字将数据推送出去。 我们使用了独立的 socket.io-client,它允许我们将消息传递给另一个节点服务器进行分析。 代码看起来像这样:

var noble = require('noble');

//replace localhost with your server's IP;
var socket = require('socket.io-client')('http://localhost/scanner');

//replace with your hardware address
var addressToTrack = '7c669d9b2dda'; 

socket.on('connect', function(){  
  console.log('connected to server');
});

noble.on('discover', function(peripheral){
  if(peripheral.uuid == addressToTrack){
    socket.emit('deviceData', {mac: peripheral.uuid, rssi:peripheral.rssi});    
  }
});

noble.startScanning([], true) //allows dubplicates while scanning

Node.js 服务器

我们的服务器应用程序(也运行带有socket.io的node.js)负责捕获从 Raspberry Pi 发送的数据。 我们试图保留任何 Raspberry Pi 的计算,以减少 CPU 负载,并将原始数据传递给我们的服务器(在这种情况下,我的 MacBook Pro)。 服务器等待 Raspberry Pi 连接,然后在发送消息到客户端之前捕获数据做一些计算。 下面的代码显示了我们如何设置服务器,以接收来自 Raspberry Pi 的消息的基本结构。

var express = require('express');
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);

var scanner = io.of('/scanner'); 

scanner.on('connection', function(socket) {

    console.log('Scanner Connected');

    socket.on('message', function(msg) {
        //recived message from scanner
        //do some processing here
    });

    socket.on('disconnect', function() {
        console.log('Scanner Disconnected');
    });
});

http.listen(3000, function() {
    console.log('listening on *:3000');
});

HTML/CSS/JS (客户端)

最后一个难题是在客户端网页上,渲染 Beacon 的位置。用户界面本身非常简单 - 我们再次使用 socket.io,并将套接字分配给 “客户端” 空间(room)。 当服务器发送距离值时,我们将其传递给线性比例尺(使用 d3.js),将其以米为单位的位置转换为屏幕上的像素。在这种情况下,我们根据要跟踪的最小/最大距离值(0-20米)和屏幕上可用的像素(0 - 1000像素)进行硬编码。 最后,我们用 TweenLite 调用视觉的位置,使得 beacon 视觉动画成为新的位置。 代码的要点如下所示:

$(document).ready(function() {

   var socket = io('http://localhost/client');
   var linearScale = d3.scale.linear()
        .domain([0, 20])
        .range([20, 1000]);   

    socket.on('connected', function(msg) {
        console.log('connected to server');
    });   

    socket.on('message', function(msg) {

        var yVal = filter(linearScale(msg.accuracy));

        TweenLite.to(user, 2, {
            y: yVal,
            ease: 'easeOutExpo'
        });
    });
});

网页结果

接近跟踪

在这一点上,我们到位一切的东西了,可以开始做一些近距离跟踪。应该是容易实现的? 呃,比我想象的要多一点。 我们需要克服两大障碍:

  • 估计距 rssi 的距离
  • 平滑来自传感器的噪音数据

估计距 rssi 的距离

起初有点混淆,但经过一番探索,我们意识到很多社区上的人都在寻找同样的东西。这个 stackoverflow 的帖子 包含了我们正在寻找的魔法。我们很快将代码转换为 JavaScript 并进行了测试。算法背后的思想是计算 rssi 信号和测量功率之间的比率,然后为信号衰减添加一些衰减。我不会声称确切地知道下面算法中的数字在做什么,但经过一系列的测试之后,我们觉得它对我们的 POC 来说足够准确了。

function calculateDistance(rssi) {

  var txPower = -59 //hard coded power value. Usually ranges between -59 to -65

  if (rssi == 0) {
    return -1.0; 
  }

  var ratio = rssi*1.0/txPower;
  if (ratio < 1.0) {
    return Math.pow(ratio,10);
  }
  else {
    var distance =  (0.89976)*Math.pow(ratio,7.7095) + 0.111;    
    return distance;
  }
} 

平滑来自传感器的噪音数据

当我们在办公室里徘徊在我们口袋里的 beacon 时,我们惊讶地发现报告的数据是如何不可靠和嘈杂的。 我们观察到几个导致主要信号干扰或吸收的事物:

  • 蓝牙信号不能很好地穿过身体 - 面向扫描仪,正面的口袋里的信号灯按预期工作。背对扫描仪导致数据量大幅下降,rssi 信号非常低。
  • 跟踪变得越来越不可靠越远 - 数据吞吐量在6-8英尺之后显着下降,并且报告值的变化显着增加。
  • 扫描仪的放置很重要 - 经过一系列测试之后,我们确定扫描仪的最佳结果是放置在距离地面 5 英尺至 6.5 英尺之间的墙上(而非天花板)。

这些都是好东西,但是我们仍然需要平滑扫描仪的值,所以我们可以在屏幕上渲染一个 beacon 的位置,看起来不像是 beacon 在各处抖动。

我们通过计算 rssi 值的滚动平均值开始。不幸的是,这并不奏效,因为在远离一段时间之后,随着beacon 在扫描仪附近移动,平均时间过长。所以我们实现了一个不超过 10 个值的滚动窗口,并且每秒取平均值。这工作相当好,但视觉效果还是有点“跳跃”。

输入卡尔曼滤波器 - 此算法通常用于使用不可靠的 GPS 信号估算车辆位置。它通常也用于信号处理。它需要一系列噪声测量并估计最佳输出值。 这似乎是理想的,幸运的是我们发现这个 fiddle,可以用于我们的这个目的。结合我们的滚动窗口方法,我们终于看到了一些可观的结果。

https://vimeo.com/114583701

下一步

虽然我们迄今为止的结果是非常基本的,但是我们觉得在展示我们的流程方面有价值,因为从零开始拼凑这些东西相当耗时。

对于接下来的步骤,我们将继续进行三边测量以跟踪多个扫描仪的多个 beacon。 如果您在室内 beacon 跟踪方面取得了成功,或者对我们的室内 beacon 追踪内容有任何建议,我们很乐意听到。 在 Twitter 上通过 @truthlabschi 联系我们

原文链接:https://blog.truthlabs.com/beacon-tracking-with-node-js-and-raspberry-pi-794afa880318

尚未评分
您的评分将帮助我们做出更好的玩法

观光\评论区

Copyright © 2017 玩点什么. All Rights Reserved.