您好,登錄后才能下訂單哦!
前言
iBeacon是蘋果公司推出的一項低耗能藍牙技術,由藍牙設備發射包含指定信息的信號,再由移動設備接收信號,從而實現近場通信。微信小程序2017年開始支持iBeacon,搖一搖附近就是基于iBeacon實現的,此外iBeacon還可以實現距離測量,本文將介紹如何基于微信小程序實現iBeacon測距。
iBeacon測距原理
藍牙信標發射的信號強度(rssi)與收發設備之間的距離,某種程度上呈正相關,因此通過合理的運算轉化,可以通過rssi的值反推出與接收設備間的距離。
藍牙信標的rssi值是一個參考值,沒有固定標準。想要計算出藍牙信標的距離,還必須知道這個信標設備的txPower值。txPower是指當距離藍牙信標1m時的rssi值,不同的藍牙設備或相同設備不同的工況甚至不同的場地環境,都會影響txPower值,因此這個值雖然可以測量,但一定程度上是個經驗值,無法測準。
rssi測距公式
知道rssi和txPower后就可以計算距離了,有兩種計算公式:
一、
這個公式里的三個變量A、B、C都是經驗值,需要根據手機系統或硬件型號精確調校,通常會將所有設備的校準結果保存成一個設備信息表,移動終端先檢測本機型號,然后匹配設備信息調取相應的計算配置,再進行計算。很明顯這個公式是比較依賴硬件調校的,沒有數據儲備的前提下這個公式會很難用。
轉換成js代碼:
const calculateAccuracy = function (txPower, rssi) { return (0.89976) * Math.pow(rssi / txPower, 7.7095) + 0.111 }
未精校情況下的測距表現:
先說這個圖怎么看。
藍牙信標與手機的實際距離1m,測試設備為紅米Note7。
從上圖可見,rssi值相對穩定,說明硬件沒有太大問題。紅線和黃線的波動都很大,說明準確度不咋地。二者的波動趨勢幾乎一致,所以有理由懷疑微信小程序內部也是用的這個測距公式。從結果來看,這個公式的準確度比較差,可能是因為沒有精校的原因。
二、
這個公式里的A就是rssi,tx是txPower,n是經驗值,n的取值跟物理環境有關。
const calculateAccuracy = function (txPower, rssi) { return Math.pow(10, Math.abs(rssi - txPower) / (10 * 4)) }
公式二的測距表現:
人比人得死,貨比貨得扔啊。
圖中黃線還是波動的那么瘋狂,但紅線卻異常穩定,而且呈現出跟綠線一致的波動幅度,說明測距精度靠譜。這個公式只有一個參數,生產環境中的調校相對簡單,這里我們選擇公式二作為測距公式。
iBeacon測距穩定程序
藍牙信號本身就有波動性,加上現實環境中的很多因素也會影響到信號強度,比如物體遮擋、設備方向變化、硬件自身的穩定性等,所以接收設備檢測到的rssi值通常是“跳動”的,直接使用測距公式算出的結果,往往不可用。必須實現一個穩定程序,讓計算結果呈現出連續性和穩定性。
數據濾波
穩定程序主要做的事就是對波段數據“削峰填谷”,也可以稱作數據濾波。最簡單的濾波處理,就是收集一段時間的值求平均,只要硬件不出問題,固定距離的藍牙信標rssi值總是會在一個相對穩定的區間內變化,采樣時間越長,采樣的平均值就會越接近真實值,因此在靜態測距場景中,求平均是最佳方式。
//求數組平均值 const arrayAverage = arr => arr.reduce((acc, val) => acc + val, 0) / arr.length; return arrayAverage([...])
具體實現是,當程序源源不斷的接收到信標的rssi時,先用公式計算出瞬時測距結果,然后將結果存進一個數組,然后計算這個數組的平均值。靜態測距時,測量結果還是非常準的,2m以內的距離誤差可以低至0.1m。
實際應用中往往都是動態測距,所以采樣數據的長度要加以限制,比如按后進先出的順序,取最近10組數據。具體采樣隊列設為多長,要根據項目實際需求而定。采樣隊列的長度越長,測距結果越平滑,但對移動端的動態捕捉越遲鈍;反之采樣隊列越短,結果越銳利,對移動端的動態捕捉越靈敏。
有時因為一些偶然因素,采樣隊列中會出現個別大幅偏離真實值的“燥音”數據,即使求平均也難以有效抹除影響,為消除這種影響,可以在求平均前先用高斯模糊算法對“偏大值”和“偏小值”做平滑處理,最大限度的降低數據噪音的干擾。
高斯模糊算法的關鍵是根據平均差求權重,一維高斯模糊的權重計算公式:
轉換成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直接算出來的瞬時測距結果,黃線是加入穩定程序后的測距結果。
第一張圖是相對靜止的條件,可以看到黃線相對紅線明顯更加平穩,說明穩定程序還是起作用的。
第二張圖是模擬快速遠離的場景,可以看到黃線在保證平穩的前提下緊跟紅線,沒有被甩掉,主要體現的是穩定程序的動態跟進效果。
第三張圖是掄胳膊甩手機+遮擋信號模擬出的場景,貌似穩定程序也架不住了,有點飄忽。
以上是關于穩定程序的簡要實現思路,生產環境中肯定會面臨更加復雜的情況,免不了還要做大量調試,這里只是拋磚引玉。
總結
藍牙測距簡單來說就是一個公式的應用,本身比較簡單,基于測距可以實現很多近場應用,比如近場簽到、近場推送等等,更進一步甚至可以實現對移動設備的定位,有了定位信息,很多室內定位、室內導航相關的應用就都可以實現了。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。