一、室内定位系统功能:
设计一种基于移动智能终端的室内定位系统,系统训练阶段,通过在用户随身携带的移动智能终端自动采集室内无线信号强度信息,数据经预处理后建立信号强度和位置的指纹库;定位阶段,用户移动智能终端实时采集到的信号强度数据和指纹库中的信息进行相关性匹配来完成定位。在部署实验场景采集数据的基础上,设计建立指纹库,并编程实现HTML界面,构建室内定位系统。系统能够快速且较准确实现用户室内定位。
二、RSSI及KNN算法简介:
RSSI简介
RSSI是一种基于测距的定位技术,其测距原理为接收机通过测量射频信号的能量来确定与发送机的距离。该方法实现简答,因此被广泛应用,但使用时需要注意遮盖或者折射现象会引起接收端产生严重的测量误差,导致精度降低。
KNN算法
在本文中使用到KNN算法(K = 3时)的目的是:根据输入的信号值,在指纹库中找到对应的三个位置信息后,依据KNN算法思想,将三个位置信息中出现次数最多的位置信息输出为最终的定位信息。可以参考下图进行理解:
三、系统的详细设计:
(1)后端
设计一个主页面,实现RSSI室内定位的功能:输入一个data信号值,在指纹库中进行查找数据,找到离该信号值最近的三个点,然后使用KNN算法得到最后的定位信息:
@app.route('/', methods=['GET', 'POST'])
def issa():
place = "暂无" # 第一次进入系统时,GET方式访问,直接给place等变量赋值,然后输出即可
place_01 = "暂无"
place_02 = ""
place_03 = ""
data_dict = {'place_01': place_01, 'place_02': place_02, 'place_03': place_03, 'place': place}
if request.method == 'GET': # 首次访问页面,没有输入值,直接显示默认界面
return render_template("issa.html", data_dict = data_dict)
data = int(request.form.get("index")) # POST方式访问页面,用data来接收输入的信号值
# 1.连接数据库index:
conn = pymysql.connect(host="127.0.0.1", port=3306, user='root', passwd="******", charset='utf8', db="index")
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 2.取得数据
sql = "select * from informations"
cursor.execute(sql)
data_list = cursor.fetchall()
# 3.关闭数据库:
cursor.close()
conn.close()
map_data = []
for item in data_list: # 将距离输入值 2 以内的所有存储值,放到 map_data 中
if ((item["data_median"] >= data - 2) & (item["data_median"] <= data + 2)):
k = [item["data_median"], item["place"]]
map_data.append(k)
if ((item["data_average"] >= data - 2) & (item["data_average"] <= data + 2)):
k = [item["data_average"], item["place"]]
map_data.append(k)
if ((item["data_weightAvg"] >= data - 2) & (item["data_weightAvg"] <= data + 2)):
k = [item["data_weightAvg"], item["place"]]
map_data.append(k)
if ( len(map_data) < 3):
place = "无法获取定位信息!"
else: # 查找到了三个或者超过三个的已存储信息,可以继续使用KNN算法
for issa in map_data:
issa[0] = abs(issa[0] - data) # 得到差的绝对值
map_data = sorted(map_data,key=lambda x:x[0] ) # 排序
place_01 = map_data[0][1] # 获取三个点的位置信息
place_02 = map_data[1][1]
place_03 = map_data[2][1]
if (place_01 == place_02) & (place_01 == place_03): # 三个地址信息相等
place = place_01
elif (place_01 == place_02) | (place_01 == place_03):
place = place_01
elif (place_02 == place_03):
place = place_02
else:
place = "位置不确定!"
data_dict = {'place_01': place_01, 'place_02': place_02, 'place_03': place_03, 'place': place}
return render_template("issa.html", data_dict = data_dict)
设计一个展示数据库的页面,使用户可以直观的看到指纹库中存储的数据:
@app.route("/information", methods=['GET', 'POST'])
def information():
# 1.连接数据库index:
conn = pymysql.connect(host="127.0.0.1", port=3306, user='root', passwd="******", charset='utf8', db="index")
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 2.取得数据
sql = "select * from informations"
cursor.execute(sql)
data_list = cursor.fetchall()
# 3.关闭数据库:
cursor.close()
conn.close()
return render_template("information.html", data_list = data_list)
设计一个页面,可以向指纹库中输入新的数据信息。由于输入的信息需要经过一定的处理,我还另外定义了三个函数来求取16个数的平均值、中值、加权移动平均值,然后在add_details函数中进行引用,并将计算结果存储到数据库中:
def getAvg(signal_data): # 使用平均值方法,求得一个值并返回
total = 0.0
for item in signal_data: # 将所有列表数据累加起来
total = total + float(item)
return total / 16
def getMedian(signal_data): # 使用中值法,求得一个值并返回
signal_data = list(map(float, signal_data))
signal_data.sort() # 将列表进行排序
data_median = (signal_data[int(len(signal_data) / 2)] +
signal_data[int(len(signal_data) / 2 - 1)]) / 2.0
return data_median
def getWeightMovingAvg(signal_data): # 使用加权移动平均法,求得一个值并返回
length = len(signal_data)
signal_data = list(map(float, signal_data))
while True:
s = 0.0 # 保存计算得到值
for k in range(length):
s += (k + 1) * signal_data[k] # s = 1*signal_data + 2*signal_data + ...
s /= (length * (length + 1) / 2.0)
for k in range(length - 1): # 将后一个值赋值给前面
signal_data[k] = signal_data[k + 1]
signal_data[length - 1] = s # 将 s 赋值给最后一个数
nd = signal_data[length - 1] # 获取数组最后一个值
js = 0 # 计数
for j in range(length - 1):
if (nd == signal_data[j]):
js = js + 1
if (js == length - 1): # 所有的数据都一样,停止迭代
break
data_weightAvg = round(s, 4) # 保留 4 位小数
return data_weightAvg
@app.route('/add_details', methods=['GET', 'POST'])
def add_details():
if request.method == 'GET':
return render_template("add_details.html")
place = request.form.get("place") # 拿到用户在网页输入的信息
signal_01 = request.form.get("signal_01")
signal_02 = request.form.get("signal_02")
signal_03 = request.form.get("signal_03")
signal_04 = request.form.get("signal_04")
signal_05 = request.form.get("signal_05")
signal_06 = request.form.get("signal_06")
signal_07 = request.form.get("signal_07")
signal_08 = request.form.get("signal_08")
signal_09 = request.form.get("signal_09")
signal_10 = request.form.get("signal_10")
signal_11 = request.form.get("signal_11")
signal_12 = request.form.get("signal_12")
signal_13 = request.form.get("signal_13")
signal_14 = request.form.get("signal_14")
signal_15 = request.form.get("signal_15")
signal_16 = request.form.get("signal_16")
signal_data = [signal_01, signal_02, signal_03, signal_04, signal_05, signal_06, signal_07, signal_08, signal_09, signal_10, signal_11, signal_12, signal_13, signal_14, signal_15, signal_16]
# 1.连接数据库index:
conn = pymysql.connect(host="127.0.0.1", port=3306, user='root', passwd="******", charset='utf8', db="index")
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 2.将16个测量值存储到information_details中,将三个计算值存储到informations中:
sql_1 = "insert into information_details(place, signal_01, signal_02, signal_03, signal_04, signal_05, signal_06, signal_07, signal_08, signal_09, signal_10, signal_11, signal_12, signal_13, signal_14, signal_15, signal_16) values(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)"
cursor.execute(sql_1, [place, signal_01, signal_02, signal_03, signal_04, signal_05, signal_06, signal_07, signal_08, signal_09, signal_10, signal_11, signal_12, signal_13, signal_14, signal_15, signal_16])
conn.commit()
data_average = getAvg(signal_data) # 调用三个函数,进行计算三个估计值
data_median = getMedian(signal_data)
data_weightAvg = getWeightMovingAvg(signal_data)
sql_2 = "insert into informations(place, data_median, data_weightAvg, data_average) values(%s, %s, %s, %s)"
cursor.execute(sql_2, [place, data_median, data_weightAvg, data_average])
# 3.关闭数据库:
cursor.close()
conn.close()
return "添加测量数据成功!"
至此,后端的设计完成!
(2)前端
设计一个页面,来进行RSSI室内定位系统的使用。需要一个输入信号值的窗口,还有两个面板,来展示KNN算法输出的三个位置信息,以及最后的定位信息:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ISSA定位技术的实现</title>
<link rel="stylesheet" href="../static/bootstrap-3.4.1-dist/bootstrap-3.4.1-dist/css/bootstrap.min.css">
</head>
<body>
<div class="container">
<form method="post" action="/" class="form-inline" style="margin-top: 50px;margin-left: 50px;">
<div class="form-group">
<label class="sr-only" for="exampleInputAmount"></label>
<div class="input-group">
<div class="input-group-addon">请输入所在位置的信号强度:</div>
<input type="text" class="form-control" name="index" placeholder="请输入信号强度">
<div class="input-group-addon">dBm</div>
</div>
</div>
<input type="submit" class="btn btn-info" value="获取定位">
</form>
<div style="margin-top: 20px;margin-left: 1000px;">
<a href="/information" style="margin-top: 10px"><button type="submit" class="btn btn-info">编辑指纹库</button></a>
</div>
<div class="panel panel-default" style="margin-top: 50px;margin-left: 50px;">
<div class="panel-heading">你所在的大致位置是:</div>
<div class="panel-body">
<h3><div>{{ data_dict.place }}</div></h3>
</div>
</div>
<div class="panel panel-default" style="margin-top: 50px;margin-left: 50px;">
<div class="panel-heading">KNN算法返回的三个临界位置信息</div>
<div class="panel-body">
<h3><div>{{ data_dict.place_01 }}</div></h3>
<h3><div>{{ data_dict.place_02 }}</div></h3>
<h3><div>{{ data_dict.place_03 }}</div></h3>
</div>
</div>
</div>
</body>
</html>
设计一个页面,用以展示当前指纹库中的数据,直接使用一个table即可:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>添加信息</title>
<link rel="stylesheet" href="../static/bootstrap-3.4.1-dist/bootstrap-3.4.1-dist/css/bootstrap.min.css">
</head>
<body>
<div class="container">
<table class="table table-hover">
<thead>
<tr>
<th>序号</th>
<th>信号强度指标(中值法)</th>
<th>信号强度指标(均值法)</th>
<th>信号强度指标(加权移动平均法)</th>
<th>所在位置</th>
</tr>
</thead>
<tbody>
{% for item in data_list %}
<tr>
<th scope="row">{{ item.id }}</th>
<td>{{ item.data_median }}</td>
<td>{{ item.data_average }}</td>
<td>{{ item.data_weightAvg }}</td>
<td>{{ item.place }}</td>
</tr>
{% endfor %}
</table>
<a href="/add_details"><button type="button" class="btn btn-info">添加指纹库信息</button></a>
</div>
</body>
</html>
设计一页面,向指纹库中添加新的数据信息,需要一个输入text的文本框来接收位置信息,以及16个输入number的输入框来接收16个测量值:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>添加指纹库信息</title>
<link rel="stylesheet" href="../static/bootstrap-3.4.1-dist/bootstrap-3.4.1-dist/css/bootstrap.min.css">
</head>
<body>
<div class="container">
<form method="post" action="/add_details" class="form-inline" >
<div class="form-group" style="margin-left: 5px; margin-top: 5px;">
<label for="exampleInputName2">当前位置</label>
<input type="text" class="form-control" name="place" placeholder="当前位置">
</div>
<div class="form-group" style="margin-left: 5px; margin-top: 5px;">
<label for="exampleInputName2">信号强度测试值1</label>
<input type="number" class="form-control" name="signal_01" placeholder="信号强度测试值1">
</div>
<div class="form-group" style="margin-left: 5px; margin-top: 5px">
<label for="exampleInputName2">信号强度测试值2</label>
<input type="number" class="form-control" name="signal_02" placeholder="信号强度测试值2">
</div>
<div class="form-group" style="margin-left: 5px; margin-top: 5px">
<label for="exampleInputName2">信号强度测试值3</label>
<input type="number" class="form-control" name="signal_03" placeholder="信号强度测试值3">
</div>
<div class="form-group" style="margin-left: 5px; margin-top: 5px">
<label for="exampleInputName2">信号强度测试值4</label>
<input type="number" class="form-control" name="signal_04" placeholder="信号强度测试值4">
</div>
<div class="form-group" style="margin-left: 5px; margin-top: 5px">
<label for="exampleInputName2">信号强度测试值5</label>
<input type="number" class="form-control" name="signal_05" placeholder="信号强度测试值5">
</div>
<div class="form-group" style="margin-left: 5px; margin-top: 5px">
<label for="exampleInputName2">信号强度测试值6</label>
<input type="number" class="form-control" name="signal_06" placeholder="信号强度测试值6">
</div>
<div class="form-group" style="margin-left: 5px; margin-top: 5px">
<label for="exampleInputName2">信号强度测试值7</label>
<input type="number" class="form-control" name="signal_07" placeholder="信号强度测试值7">
</div>
<div class="form-group" style="margin-left: 5px; margin-top: 5px">
<label for="exampleInputName2">信号强度测试值8</label>
<input type="number" class="form-control" name="signal_08" placeholder="信号强度测试值8">
</div>
<div class="form-group" style="margin-left: 5px; margin-top: 5px">
<label for="exampleInputName2">信号强度测试值9</label>
<input type="number" class="form-control" name="signal_09" placeholder="信号强度测试值9">
</div>
<div class="form-group" style="margin-left: 5px; margin-top: 5px">
<label for="exampleInputName2">信号强度测试值10</label>
<input type="number" class="form-control" name="signal_10" placeholder="信号强度测试值10">
</div>
<div class="form-group" style="margin-left: 5px; margin-top: 5px">
<label for="exampleInputName2">信号强度测试值11</label>
<input type="number" class="form-control" name="signal_11" placeholder="信号强度测试值11">
</div>
<div class="form-group" style="margin-left: 5px; margin-top: 5px">
<label for="exampleInputName2">信号强度测试值12</label>
<input type="number" class="form-control" name="signal_12" placeholder="信号强度测试值12">
</div>
<div class="form-group" style="margin-left: 5px; margin-top: 5px">
<label for="exampleInputName2">信号强度测试值13</label>
<input type="number" class="form-control" name="signal_13" placeholder="信号强度测试值13">
</div>
<div class="form-group" style="margin-left: 5px; margin-top: 5px">
<label for="exampleInputName2">信号强度测试值14</label>
<input type="number" class="form-control" name="signal_14" placeholder="信号强度测试值14">
</div>
<div class="form-group" style="margin-left: 5px; margin-top: 5px">
<label for="exampleInputName2">信号强度测试值15</label>
<input type="number" class="form-control" name="signal_15" placeholder="信号强度测试值15">
</div>
<div class="form-group" style="margin-left: 5px; margin-top: 5px">
<label for="exampleInputName2">信号强度测试值16</label>
<input type="number" class="form-control" name="signal_16" placeholder="信号强度测试值16">
</div>
<div style="margin-left: 15px; margin-top: 5px">
<input type="submit" class="btn btn-primary" value="提交">
</div>
</form>
</div>
</body>
</html>
至此,前端的设计完成!
(3)数据库
数据库的设计,主要包括两个table:information_details 和 informations 的创建,前者用来保存输入的16个测量值以及他们的地理位置,后者用来保存由16个测量值计算出的平均值、中值、带权移动平均值以及他们对应的地理位置。它们的结构如图:
information_details:
informations( information是不可数的,写完才发现,但是不想改了 )
数据库的构建完成!
四、系统的测试使用
(1)向数据库中添加数据,构建指纹库:
运行系统,进入127.0.0.1:5000打开系统,然后点击编辑指纹库进入指纹库展示页面,再点击添加指纹库信息,即可进行测量数据的输入:
进行多次添加操作后,即可构建出本系统的指纹库:
(2)指纹库构建完成后,在系统主页输入一个信号值,单击获取定位,即可使用RSSI室内定位系统来获得该信号值对应的地理位置。如果指纹库中查找不到,则输出无法定位:
输入信号值:-60
输入信号值:-27
五、系统运行说明
本人使用vscode来运行app.py文件,数据库使用的是wampserver中的MYSQL,同时使用了sqlyog来对数据库实行可视化操作(创建新的数据库,新建table等操作)。运行本系统时,推荐使用vscode(或pycharm),数据库可以使用MySQL(或wampserver)以及sqlyog。同时也要将后端Python代码里面,连接数据库的语句中的密码、数据库名称等相关信息进行修改。
系统运行展示:
基于RSSI的室内定位技术
六、源码
百度网盘:百度网盘
提取码:iuxp