基于WiFi的室内定位在美团总部的实践和应用Word文档下载推荐.docx
《基于WiFi的室内定位在美团总部的实践和应用Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《基于WiFi的室内定位在美团总部的实践和应用Word文档下载推荐.docx(18页珍藏版)》请在冰豆网上搜索。
但是很不幸,这里的MAC地址是路由器的WAN口的MAC地址,而我们需要的是两个无线模块的MAC地址。
这里只能自己测绘了,我写了一小段Android程序,可以排序出最近的AP的MAC地址,然后挨个跑到各个AP下,运行程序,记下两个MAC地址;
同时记录下AP的真实物理位置。
WifiManagerwm=(WifiManager)getSystemService(Context.WIFI_SERVICE);
wm.startScan();
//开始扫描AP//等待一段时间,时间可长可短List<
ScanResult>
results=wm.getScanResults();
//拿到扫描的结果Collections.sort(results,this);
//this是个Comparator,按照level排序//去掉非sankuai的SSID//在UI线程中,显示到界面上intmax=Math.min(30,results.size());
for(inti=0;
i<
max;
i++){
ScanResultone=results.get(i);
text1.append("
\n"
+one.BSSID+"
\t\t"
+one.level);
}
图中信号最强的就是当前AP的MAC地址,然后地址与它相近的是这个AP另一个频段的MAC地址,两个MAC地址都是0结尾,尾数相差1,容易辨认。
MAC地址后面的数字是信号强度,单位是dBm,是个负数。
然后在底图中标注好AP的准确的物理位置,图中红色圆点即是AP位置,其圆心的像素坐标当作AP的坐标。
测绘的数据应该存入数据库,这里设计了一个POJO,服务器端程序可以使用:
publicclassMtApLoc{
privateintid;
//数字ID
人工定,有一定含义
privateStringid1;
//字符串ID
从IT给表中来
privateStringmac1;
//WANMAC地址,有线口的
privateStringsn;
//AP的SN
privateStringsku;
//资产编号N
privateStringmac2;
//无线MAC1,测绘得来
privateStringmac3;
//无线MAC2,测绘得来
privateintpn;
//图号
对应楼层
privatefloatx;
//物理坐标x
自定义坐标系中
privatefloaty;
//物理坐标y
自定义坐标系中}
然后将测绘的数据录入数据库,最后得到的数据如:
其中的x,y是此AP在对应楼层的测绘图的图片中的坐标。
MAC2和MAC3是AP的两个MAC地址(这里没有区分2.4G和5G),和上面的测绘客户端的截图比较,能看出当时我是站在AP7下的。
把所有86个AP的物理位置和MAC地址测绘收集全后,测绘过程完成。
Android客户端示例
这里写了一个Demo用的android客户端,来测试定位结果,先看客户端运行截图:
点击定位按钮,系统会扫描AP,然后把结果请求到服务器。
HttpPostpost=newHttpPost(BaseUrl+"
/gar/locate/ap-locate.html"
);
List<
NameValuePair>
parameters=newArrayList<
();
for(ScanResultresult:
results){
parameters.add(newBasicNameValuePair("
mac"
result.BSSID.toUpperCase()));
rssi"
String.valueOf(result.level)));
post.setEntity(newUrlEncodedFormEntity(parameters,"
UTF-8"
));
Stringres;
synchronized(hc){
HttpResponseresponse=hc.execute(post);
res=EntityUtils.toString(response.getEntity(),"
).trim();
Log.w(TAG,res);
服务器返回其所在位置,是一个JSON字符串
{"
accuracy"
:
0.0,"
message"
"
okLeastSquares"
"
pn"
1,"
status"
0,"
x"
237.97249473061038,"
y"
1241.8270604002646}
然后客户端显示pn对应的底图,然后在底图的x,y位置上显示定位到的标志,即图中跳动的红心。
客户端大部分代码都是UI相关代码,这里不贴出了。
定位算法
常见的室内定位的算法主要分为两类:
基于测距技术的定位算法和距离无关的算法。
基于测距技术的算法一般是通过节点之间的距离或者角度来计算出未知节点的位置,实际运用中常见的有:
基于接收信号强度指示算法(RSSI)、到达角度算法(AOA)、到达时间算法(TOA)等。
距离无关的算法有:
质心法、APIT算法、凸规划算法等。
这些算法都是利用节点之间的邻近关系实现定位的。
一般来说,基于测距技术的算法比无需测距的精度要高,这里适合采用。
首先确定一个信号强度和距离之间的关系,这需要了解电波传播模型。
在自由空间环境中,不考虑阻挡和多径传播,设发射端与接收端的距离为d,则接收端的接收功率Pr可表示为:
其中Pt为发射功率;
Gt和Gr分别为发射和接收天线增益;
λ为电波波长;
Pt和Pr的单位是瓦特;
Gt和Gr无量纲。
由上式可以看出,在自由空间中,接收功率与距离d2成反比。
在实际环境中,由于存在多径、障碍物、绕射等随机因素,无线电传播损耗与上式相比还是有较大变化。
此时,常采用对数-常态分布模型更为合理:
其中Pr单位为dBm,d0一般取1。
在一般室内定位中,考虑到环境、成本、定位精度要求等因素,所使用的RSSI测距信号衰减模型进一步简化为:
d为定位节点与参考点之间的距离,单位m;
A为定位节点与参考点之间的距离d为1m时测得的RSSI值;
n为信号衰减因子,范围一般为2~4。
在美团的环境中,我们取A为-50,n为2.1。
这样根据信号强度,就能估算设备和AP之间的距离。
定位方法一般是根据几何模型建立方程,然后求解方程得到节点坐标。
只有一个AP的情况:
这里目标点坐标只能取AP的坐标,精度取半径。
两个AP的情况:
这里取AB的中间位置,精度取AB的长度。
三个AP的情况:
这里取三个圆的一个共同交点。
不过实际没有这么简单,因为距离都有误差,两个AP时,可能是这种情况:
三个AP可能是这种情况:
甚至这种:
这只是三个AP,有更多AP时怎么办?
这里考虑一般的情况:
考虑一般的情况,设有n个AP,AP1,AP2,...,APn,坐标是(xi,yi)。
目标点到这n个AP的距离是di。
设目标点的坐标是(X,Y),则可列一个方程组,有n个等式:
大家都减第一个等式,就消去了二次项,得到另一个方程组,有n-1个等式:
常数项换个名字,得到:
等式除以X的系数ai,变量换个名字,得到:
等式有n-1个,现在问题变成了:
已知一组点(ui,vi)满足p+uq=v,求最合适的系数p,q,这是典型的最小二乘法。
Java里可以用ApacheCommonsMath3这个library来解决最小二乘法,文档见SimpleRegression。
这里还有一个问题,AP的坐标(xi,yi)是像素坐标,那di相应的需要是像素距离,需要做一个比例尺变换。
比例很容易算,相关代码:
publicdoublegetPicLen(doublerssi){
doublef=(-rssi-50)/22.0;
return41.785*Math.pow(10,f);
服务器端代码示例
通过上面的描述,服务器端代码就很容易写了,这里给出主要代码:
privateString[]macs;
//输入mac地址privatefloat[]rssis;
//输入信号强度privateintpn;
//输出,楼层privatedoublex,y,accuracy;
//输出,定位到的坐标和精度
MtApLoc>
aps=newArrayList<
>
(map.keySet());
MtApLocfirst=aps.get(0);
//信号最强的那个apfor(MtApLocone:
aps){
//以信号最强的ap的楼层作为最终楼层,因为可能搜到其它楼层的信号
if(one.getPn()!
=first.getPn()){
//干掉其它楼层的ap
map.remove(one);
aps.clear();
aps.addAll(map.keySet());
size=aps.size();
this.pn=first.getPn();
if(size==1){
setStatus(0);
setMessage("
okonepoint"
this.x=first.getX();
this.y=first.getY();
this.accuracy=getPicLen(map.get(first).floatValue());
returnJSON;
}elseif(size==2){
setStatus(3);
toimpl"
}else{
floatminRssi=-65;
//信号强大要达到-65才参与运算
intmin=4;
//至少需要4个ap,这个条件比上个条件优先
size=0;
for(Iterator<
it=aps.iterator();
it.hasNext();
){
MtApLocap=it.next();
if(map.get(ap).floatValue()<
minRssi&
&
size>
=min){
it.remove();
}else{
size++;
}
//map的key之前是信号强度,现在变为像素距离
aps.forEach(ap->
map.put(ap,getPicLen(map.get(ap).floatValue())));
double[][]ps=newdouble[size-1][4];
//看size-1
doubler1=map.get(first).doubleValue();
r1=r1*r1;
doubler2=first.getX()*first.getX()+first.getY()*first.getY();
intn=0;
for(MtApLocap:
//生成数据
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());
doubler=map.get(ap).doubleValue();
ps[n][3]=r*r-r1;
n++;
assertn==(size-1);
n;
i++){
doublek=ps[i][1];
ps[i][1]=(ps[i][3]-ps[i][0])/k;
ps[i][0]=ps[i][2]/k;
SimpleRegressionreg=newSimpleRegression(true);
//最小二乘法
reg.addData(ps);
this.x=reg.getIntercept();
this.y=reg.getSlope();
效果检验
系统完成了,这里需要检验一下定位效果。
为了简化过程,我是这样操作的:
我选择了一个固定点,就是我的座位(上面客户端截图中的红心所在的位置),然后用手机客户端做100次定位操作,同时服务器做log记录下100次的定位结果,然后做分析。
我座位这个点被3个AP包围着,定位效果应该不错,所以结论可能会偏乐观,实际应该选择不同的点。
不过选择不同的点要记录真实的点的坐标,稍显麻烦。
后面做进一步改进和测试时,可以选择不同的点做测试,这算作一个todo。
然后就得到100个定位结果,然后可以计算和真实点的偏差,结果如:
其中x、y是定位到的坐标,单位是像素坐标,diff是计算出的偏差,单位是米。
然后按距离排序,得到如下表,是全部数据:
从这个表可以大致分析定位效果:
∙100个点中,误差小于1米的有4个点
∙大部分点误差在1米到4米,有93个点,大致呈均匀分布态势
∙误差大于4米的有3个点,而且误差极大,明显属于失败的噪声点
去掉3个失败的点,剩下的97个点,可以用excel画一个分布图:
分析上面数据,以及实际测试过程,能发现,这个系统应该有一个系统误差。
就是测试中,定位结果总是分布在距我大概2米处的某一点周围,应该是系统编码某个地方缺陷造成的。
这是待改进的todo,预计找到问题解决后,重复上面的测试过程,定位效果能达到95%的点误差小于2米的水平。
另外上面我选的点应该属于定位效果较好的点,一般情况的点的定位精度,得进一步详细测试得出。
这里我拍脑袋估计,系统应该在90%的点误差小于5米的水平。
进一步工作,改进与设想
整个系统正在应用到移动组开发的一个找会议室的手机应用“会议室”中,为其增加定位自身的功能。
为了完善系统,现在能想到的改进有:
∙找到并改进上面说到的系统误差
∙完善后,做进一步的评测
∙考虑2.4G和5G信号的定位差别,目前是不区分的
∙信号强度和距离的公式的系数做进一步精确
∙核心定位算法目前采用的是最小二乘法,目前在考虑用更智能的一个方法,叫“位置指纹”,这个算法预计效果更好,也容易实施
∙目前坐标系统用的自定义的坐标系,这个不利于使用者使用,考虑用更好的坐标系
∙光有定位接口是不够的,还应该有坐标和地址相互转换的接口;
还应该有导航的接口
∙推广应用到更多实际的系统中