基于Wi-Fi的室内定位在美团总部的实践和应用(上)
室內(nèi)定位技術(shù)的商業(yè)化必將帶來一波創(chuàng)新高潮,尤其是在O2O領(lǐng)域,各種基于此技術(shù)的應(yīng)用將出現(xiàn)在我們的面前。我們可以想象一些比較常見的應(yīng)用場(chǎng)景,比如在大型商場(chǎng)里面借助室內(nèi)導(dǎo)航快速找到目標(biāo)商鋪,商店根據(jù)用戶的具體位置向用戶推送更多關(guān)于商品的介紹等等,這些應(yīng)用會(huì)極好的服務(wù)于O2O,提高用戶體驗(yàn)。
目前室內(nèi)定位技術(shù)有很多,如A-GPS、藍(lán)牙、超聲,紅外、信標(biāo)、射頻、Wi-Fi、計(jì)算機(jī)視覺等,這些技術(shù)綜合比較,其中以基于Wi-Fi的室內(nèi)定位技術(shù)最為突出,無(wú)論從硬件投入、軟件投入、實(shí)施難度、可控性,還是定位效果方面考察,都是有優(yōu)勢(shì)的。
本文描述了作者在美團(tuán)總部從零開始構(gòu)建基于Wi-Fi的室內(nèi)定位系統(tǒng)的過程,具有廣泛的借鑒意義。
基于Wi-Fi的室內(nèi)定位原理
- 為提供Wi-Fi服務(wù),室內(nèi)會(huì)部署有熱點(diǎn)(AP),每一個(gè)無(wú)線AP都有一個(gè)全球唯一的MAC地址,并且一般來說無(wú)線AP在一段時(shí)間內(nèi)是不會(huì)改變的。
- 設(shè)備可以程序控制掃描并收集周圍的AP信號(hào),無(wú)論是否加密,是否已連接,甚至信號(hào)強(qiáng)度不足以顯示在無(wú)線信號(hào)列表中,都可以獲取到AP廣播出來的MAC地址。
- 對(duì)應(yīng)每個(gè)AP,這里有兩個(gè)重要數(shù)據(jù),AP的MAC地址和信號(hào)強(qiáng)度,MAC地址可以決定是哪個(gè)AP;信號(hào)強(qiáng)度理論上是和AP之間的距離有函數(shù)關(guān)系的,就是根據(jù)信號(hào)強(qiáng)度可以算出和AP的距離。
- 設(shè)備將這些數(shù)據(jù)發(fā)送到位置服務(wù)器,服務(wù)器就可以用一個(gè)算法計(jì)算出設(shè)備的地理位置并返回到用戶設(shè)備。
- 定位的精度取決于AP的個(gè)數(shù),信號(hào)的穩(wěn)定程度,以及算法的選擇。
美團(tuán)總部Wi-Fi部署情況
美團(tuán)總部于2014年1月搬入了望京科技園3期,新的辦公室地上共4層,建筑面積一萬(wàn)多平米,共部署有86臺(tái)無(wú)線AP,覆蓋很充分,沒有死角,這為良好的定位效果打下了基礎(chǔ)。
無(wú)線AP使用的是,ArubA AP-135,這是一款優(yōu)秀的商用無(wú)線路由器,2.4-GHz/5-GHz雙頻。
基礎(chǔ)數(shù)據(jù)測(cè)繪
第一步,建立AP的基礎(chǔ)數(shù)據(jù)庫(kù)是關(guān)鍵,至少需要如下信息:
- AP的MAC地址,這里是雙頻的AP,就是有2個(gè)無(wú)線MAC地址
- AP的物理位置
關(guān)于AP的物理位置,這里因?yàn)榉秶?#xff0c;加之無(wú)法找到足夠精度的參考點(diǎn),所以AP的物理位置無(wú)法使用GPS坐標(biāo),只能使用自定義坐標(biāo)系。 這里有2種選擇:
- 以建筑的東南角為參考點(diǎn)(坐標(biāo)原點(diǎn)),這樣就可以測(cè)繪AP相對(duì)原點(diǎn)的坐標(biāo),包含Z軸,單位是米
- 以測(cè)繪圖的圖片為參考,以AP在圖中的像素位置為坐標(biāo),單位是1像素點(diǎn)
這里選用了后一種方法,因?yàn)楹笠环N方法容易測(cè)繪,大部分工作在電腦上操作即可;前一種方法需要更多的實(shí)地測(cè)繪工作。
關(guān)于AP的MAC地址,從IT那里要到了一個(gè)列表,如圖所示:
但是很不幸,這里的MAC地址是路由器的WAN口的MAC地址,而我們需要的是兩個(gè)無(wú)線模塊的MAC地址。 這里只能自己測(cè)繪了,我寫了一小段android程序,可以排序出最近的AP的MAC地址,然后挨個(gè)跑到各個(gè)AP下,運(yùn)行程序,記下兩個(gè)MAC地址;同時(shí)記錄下AP的真實(shí)物理位置。
WifiManager wm = (WifiManager) getSystemService(Context.WIFI_SERVICE); wm.startScan(); //開始掃描AP //等待一段時(shí)間,時(shí)間可長(zhǎng)可短 List<ScanResult> results = wm.getScanResults(); //拿到掃描的結(jié)果 Collections.sort(results,this); //this是個(gè)Comparator,按照l(shuí)evel排序 //去掉非sankuai的SSID //在UI線程中,顯示到界面上 int max=Math.min(30,results.size()); for(int i=0;i<max;i++) {ScanResult one = results.get(i);text1.append("\n"+one.BSSID+"\t\t"+one.level); }圖中信號(hào)最強(qiáng)的就是當(dāng)前AP的MAC地址,然后地址與它相近的是這個(gè)AP另一個(gè)頻段的MAC地址,兩個(gè)MAC地址都是0結(jié)尾,尾數(shù)相差1,容易辨認(rèn)。 MAC地址后面的數(shù)字是信號(hào)強(qiáng)度,單位是dBm,是個(gè)負(fù)數(shù)。
然后在底圖中標(biāo)注好AP的準(zhǔn)確的物理位置,圖中紅色圓點(diǎn)即是AP位置,其圓心的像素坐標(biāo)當(dāng)作AP的坐標(biāo)。
測(cè)繪的數(shù)據(jù)應(yīng)該存入數(shù)據(jù)庫(kù),這里設(shè)計(jì)了一個(gè)POJO,服務(wù)器端程序可以使用:
public class MtApLoc {private int id; //數(shù)字ID 人工定,有一定含義private String id1; //字符串ID 從IT給表中來private String mac1; //WAN MAC地址,有線口的private String sn; //AP的 SN 從IT給表中來private String sku; //資產(chǎn)編號(hào) N 從IT給表中來private String mac2; //無(wú)線MAC 1 ,測(cè)繪得來private String mac3; //無(wú)線MAC 2 ,測(cè)繪得來private int pn; //圖號(hào) 對(duì)應(yīng)樓層private float x; //物理坐標(biāo) x 自定義坐標(biāo)系中private float y; //物理坐標(biāo) y 自定義坐標(biāo)系中}然后將測(cè)繪的數(shù)據(jù)錄入數(shù)據(jù)庫(kù),最后得到的數(shù)據(jù)如:
其中的x,y是此AP在對(duì)應(yīng)樓層的測(cè)繪圖的圖片中的坐標(biāo)。
MAC2和MAC3是AP的兩個(gè)MAC地址(這里沒有區(qū)分2.4G和5G),和上面的測(cè)繪客戶端的截圖比較,能看出當(dāng)時(shí)我是站在AP7下的。
把所有86個(gè)AP的物理位置和MAC地址測(cè)繪收集全后,測(cè)繪過程完成。
Android客戶端示例
這里寫了一個(gè)Demo用的android客戶端,來測(cè)試定位結(jié)果,先看客戶端運(yùn)行截圖:
點(diǎn)擊定位按鈕,系統(tǒng)會(huì)掃描AP,然后把結(jié)果請(qǐng)求到服務(wù)器。
HttpPost post = new HttpPost(BaseUrl + "/gar/locate/ap-locate.html"); List<NameValuePair> parameters = new ArrayList<NameValuePair>(); for (ScanResult result : results) {parameters.add(new BasicNameValuePair("mac", result.BSSID.toUpperCase()));parameters.add(new BasicNameValuePair("rssi", String.valueOf(result.level))); } post.setEntity(new UrlEncodedFormEntity(parameters, "UTF-8")); String res; synchronized (hc) {HttpResponse response = hc.execute(post);res = EntityUtils.toString(response.getEntity(), "UTF-8").trim(); } Log.w(TAG, res);服務(wù)器返回其所在位置,是一個(gè)JSON字符串
{"accuracy":0.0,"message":"ok Least Squares","pn":1,"status":0,"x":237.97249473061038,"y":1241.8270604002646}然后客戶端顯示pn對(duì)應(yīng)的底圖,然后在底圖的x,y位置上顯示定位到的標(biāo)志,即圖中跳動(dòng)的紅心。 客戶端大部分代碼都是UI相關(guān)代碼,這里不貼出了。
定位算法
常見的室內(nèi)定位的算法主要分為兩類:基于測(cè)距技術(shù)的定位算法和距離無(wú)關(guān)的算法。基于測(cè)距技術(shù)的算法一般是通過節(jié)點(diǎn)之間的距離或者角度來計(jì)算出未知節(jié)點(diǎn)的位置,實(shí)際運(yùn)用中常見的有:基于接收信號(hào)強(qiáng)度指示算法(RSSI)、到達(dá)角度算法(AOA)、到達(dá)時(shí)間算法(TOA)等。距離無(wú)關(guān)的算法有:質(zhì)心法、APIT算法、凸規(guī)劃算法等。這些算法都是利用節(jié)點(diǎn)之間的鄰近關(guān)系實(shí)現(xiàn)定位的。一般來說,基于測(cè)距技術(shù)的算法比無(wú)需測(cè)距的精度要高,這里適合采用。
首先確定一個(gè)信號(hào)強(qiáng)度和距離之間的關(guān)系,這需要了解電波傳播模型。在自由空間環(huán)境中,不考慮阻擋和多徑傳播,設(shè)發(fā)射端與接收端的距離為d,則接收端的接收功率Pr可表示為:
其中Pt為發(fā)射功率;Gt和Gr分別為發(fā)射和接收天線增益;λ為電波波長(zhǎng);Pt和Pr的單位是瓦特;Gt和Gr無(wú)量綱。由上式可以看出,在自由空間中,接收功率與距離d2成反比。
在實(shí)際環(huán)境中,由于存在多徑、障礙物、繞射等隨機(jī)因素,無(wú)線電傳播損耗與上式相比還是有較大變化。此時(shí),常采用對(duì)數(shù)-常態(tài)分布模型更為合理:
其中Pr單位為dBm ,d0一般取1。在一般室內(nèi)定位中,考慮到環(huán)境、成本、定位精度要求等因素,所使用的RSSI測(cè)距信號(hào)衰減模型進(jìn)一步簡(jiǎn)化為:
d為定位節(jié)點(diǎn)與參考點(diǎn)之間的距離,單位m;A為定位節(jié)點(diǎn)與參考點(diǎn)之間的距離d為1m時(shí)測(cè)得的RSSI值;n為信號(hào)衰減因子,范圍一般為2~4。
在美團(tuán)的環(huán)境中,我們?nèi)為-50,n為2.1。
這樣根據(jù)信號(hào)強(qiáng)度,就能估算設(shè)備和AP之間的距離。
定位方法一般是根據(jù)幾何模型建立方程,然后求解方程得到節(jié)點(diǎn)坐標(biāo)。 只有一個(gè)AP的情況:
這里目標(biāo)點(diǎn)坐標(biāo)只能取AP的坐標(biāo),精度取半徑
兩個(gè)AP的情況:
這里取AB的中間位置,精度取AB的長(zhǎng)度。
三個(gè)AP的情況:
這里取三個(gè)圓的一個(gè)共同交點(diǎn)。
不過實(shí)際沒有這么簡(jiǎn)單,因?yàn)榫嚯x都有誤差,兩個(gè)AP時(shí),可能是這種情況:
三個(gè)AP可能是這種情況
甚至這種:
這只是三個(gè)AP,有更多AP時(shí)怎么辦?
這里考慮一般的情況:
考慮一般的情況,設(shè)有n個(gè)AP,AP1,AP2,…,APn,坐標(biāo)是(xi,yi)。目標(biāo)點(diǎn)到這n個(gè)AP的距離是di。 設(shè)目標(biāo)點(diǎn)的坐標(biāo)是(X,Y),則可列一個(gè)方程組,有n個(gè)等式:
大家都減第一個(gè)等式,就消去了二次項(xiàng),得到另一個(gè)方程組,有n-1個(gè)等式:
常數(shù)項(xiàng)換個(gè)名字,得到:
等式除以X的系數(shù)ai,變量換個(gè)名字,得到:
等式有n-1個(gè),現(xiàn)在問題變成了:已知一組點(diǎn)(ui,vi)滿足p+uq=v,求最合適的系數(shù)p,q,這是典型的最小二乘法
Java里可以用Apache Commons Math3這個(gè)library來解決最小二乘法,文檔見 SimpleRegression
這里還有一個(gè)問題,AP的坐標(biāo)(xi,yi)是像素坐標(biāo),那di相應(yīng)的需要是像素距離,需要做一個(gè)比例尺變換
比例很容易算,相關(guān)代碼:
public double getPicLen(double rssi) {double f=(-rssi-50)/22.0;return 41.785*Math.pow(10,f); }服務(wù)器端代碼示例
通過上面的描述,服務(wù)器端代碼就很容易寫了,這里給出主要代碼:
private String[] macs; //輸入mac地址 private float[] rssis; //輸入信號(hào)強(qiáng)度 private int pn; //輸出,樓層 private double x,y,accuracy; //輸出,定位到的坐標(biāo) 和 精度 List<MtApLoc> aps=new ArrayList<>(map.keySet()); MtApLoc first=aps.get(0); //信號(hào)最強(qiáng)的那個(gè)ap for (MtApLoc one : aps) { //以信號(hào)最強(qiáng)的ap的樓層作為最終樓層,因?yàn)榭赡芩训狡渌鼧菍拥男盘?hào)if(one.getPn()!=first.getPn()) { //干掉其它樓層的apmap.remove(one);} } aps.clear(); aps.addAll(map.keySet()); size=aps.size(); this.pn=first.getPn(); if(size==1) {setStatus(0);setMessage("ok one point");this.x=first.getX();this.y=first.getY();this.accuracy=getPicLen(map.get(first).floatValue());return JSON; } else if(size==2) {setStatus(3);setMessage("to impl"); } else {float minRssi=-65; //信號(hào)強(qiáng)大要達(dá)到 -65 才參與運(yùn)算int min=4; //至少需要4個(gè)ap,這個(gè)條件比上個(gè)條件優(yōu)先size=0;for(Iterator<MtApLoc> it = aps.iterator();it.hasNext();) {MtApLoc ap = it.next();if(map.get(ap).floatValue()<minRssi && size>=min) {it.remove();} else {size++;}}//map的key之前是信號(hào)強(qiáng)度,現(xiàn)在變?yōu)?像素距離aps.forEach(ap -> map.put(ap,getPicLen(map.get(ap).floatValue())));double[][] ps=new double[size-1][4]; //看 size-1double r1=map.get(first).doubleValue();r1=r1*r1;double r2=first.getX()*first.getX()+first.getY()*first.getY();int n=0;for (MtApLoc ap : aps) { //生成數(shù)據(jù)if(ap!=first) {ps[n][0]=ap.getX()*ap.getX()+ap.getY()*ap.getY()-r2;ps[n][1]=2*(first.getX()-ap.getX());ps[n][2]=2*(first.getY()-ap.getY());double r=map.get(ap).doubleValue();ps[n][3]=r*r-r1;n++;}}assert n==(size-1);for(int i=0;i<n;i++) { //生成數(shù)據(jù)double k=ps[i][1];ps[i][1]=(ps[i][3]-ps[i][0])/k;ps[i][0]=ps[i][2]/k;}SimpleRegression reg=new SimpleRegression(true); //最小二乘法reg.addData(ps);setStatus(0);setMessage("ok Least Squares");this.x=reg.getIntercept();this.y=reg.getSlope(); }效果檢驗(yàn)
系統(tǒng)完成了,這里需要檢驗(yàn)一下定位效果。為了簡(jiǎn)化過程,我是這樣操作的: 我選擇了一個(gè)固定點(diǎn),就是我的座位(上面客戶端截圖中跳動(dòng)的紅心所在的位置),然后用手機(jī)客戶端做100次定位操作,同時(shí)服務(wù)器做log記錄下100次的定位結(jié)果,然后做分析。
我座位這個(gè)點(diǎn)被3個(gè)AP包圍著,定位效果應(yīng)該不錯(cuò),所以結(jié)論可能會(huì)偏樂觀,實(shí)際應(yīng)該選擇不同的點(diǎn)。 不過選擇不同的點(diǎn)要記錄真實(shí)的點(diǎn)的坐標(biāo),稍顯麻煩。后面做進(jìn)一步改進(jìn)和測(cè)試時(shí),可以選擇不同的點(diǎn)做測(cè)試,這算作一個(gè)todo。 然后就得到100個(gè)定位結(jié)果,然后可以計(jì)算和真實(shí)點(diǎn)的偏差,結(jié)果如:
其中x、y是定位到的坐標(biāo),單位是像素坐標(biāo),diff是計(jì)算出的偏差,單位是米。
然后按距離排序,得到如下表,是全部數(shù)據(jù):
從這個(gè)表可以大致分析定位效果:
- 100個(gè)點(diǎn)中,誤差小于1米的有4個(gè)點(diǎn)
- 大部分點(diǎn)誤差在1米到4米,有93個(gè)點(diǎn),大致呈均勻分布態(tài)勢(shì)
- 誤差大于4米的有3個(gè)點(diǎn),而且誤差極大,明顯屬于失敗的噪聲點(diǎn)
去掉3個(gè)失敗的點(diǎn),剩下的97個(gè)點(diǎn),可以用excel畫一個(gè)分布圖:
分析上面數(shù)據(jù),以及實(shí)際測(cè)試過程,能發(fā)現(xiàn),這個(gè)系統(tǒng)應(yīng)該有一個(gè)系統(tǒng)誤差。就是測(cè)試中,定位結(jié)果總是分布在距我大概2米處的某一點(diǎn)周圍,應(yīng)該是系統(tǒng)編碼某個(gè)地方缺陷造成的。
這是待改進(jìn)的To Do,預(yù)計(jì)找到問題解決后,重復(fù)上面的測(cè)試過程,定位效果能達(dá)到95%的點(diǎn)誤差小于2米的水平。
另外上面我選的點(diǎn)應(yīng)該屬于定位效果較好的點(diǎn),一般情況的點(diǎn)的定位精度,得進(jìn)一步詳細(xì)測(cè)試得出。這里我拍腦袋估計(jì),系統(tǒng)應(yīng)該在90%的點(diǎn)誤差小于5米的水平。
進(jìn)一步工作,改進(jìn)與設(shè)想
整個(gè)系統(tǒng)正在應(yīng)用到移動(dòng)組開發(fā)的一個(gè)找會(huì)議室的手機(jī)應(yīng)用“會(huì)議室”中,為其增加定位自身的功能。 為了完善系統(tǒng),現(xiàn)在能想到的改進(jìn)有:
- 找到并改進(jìn)上面說到的 系統(tǒng)誤差
- 完善后,做進(jìn)一步的評(píng)測(cè)
- 考慮2.4G和5G信號(hào)的定位差別,目前是不區(qū)分的
- 信號(hào)強(qiáng)度和距離的公式的系數(shù)做進(jìn)一步精確
- 核心定位算法目前采用的是最小二乘法,目前在考慮用更智能的一個(gè)方法,叫“位置指紋”,這個(gè)算法預(yù)計(jì)效果更好,也容易實(shí)施
- 目前坐標(biāo)系統(tǒng)用的自定義的坐標(biāo)系,這個(gè)不利于使用者使用,考慮用更好的坐標(biāo)系
- 光有定位接口是不夠的,還應(yīng)該有 坐標(biāo)和地址相互轉(zhuǎn)換的接口;還應(yīng)該有導(dǎo)航的接口
- 推廣應(yīng)用到更多實(shí)際的系統(tǒng)中
這些改進(jìn),會(huì)逐步完善,敬請(qǐng)期待本系列的(下)篇。
總結(jié)
以上是生活随笔為你收集整理的基于Wi-Fi的室内定位在美团总部的实践和应用(上)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 线性代数应该这样讲(二)
- 下一篇: Spring Boot中使用log4j实