分享 | 机器人室内定位技术 机器人位置
室内环境下的定位一直是一个很多问题未被解决的领域。由于信号的严重衰减和多径效应,通用的室外定位设施(比如GPS)并不能在建筑物内有效地工作。定位准确性也是一个问题,GPS也许可以指出移动设备在哪一个建筑物,但是室内场景下,人们希望得到更精确的室内位置,这需要更精密的地图信息和更高的定位精度。
我们可以在室内搭建一套完整的基础设施用来定位,但是这样需要很大的代价,包括定位信号占用的频谱资源、用于感知定位信号的嵌入在移动设备中的额外硬件、安装在固定位置的用来发送定位信号的锚节点。因此,大家倾向于使用那些已有的被广泛部署的无线设备去实现室内定位。
(几种定位技术的特点)
今天给大家带来1个成本低、易实现的室内定位方法。
WiFi广泛使用在家庭、旅馆、咖啡馆、机场、商场等各类大型或小型建筑物内,这样使得WiFi成为定位领域中一个最引人注目的无线技术。通常,一个WiFi系统由一些固定的接入点(AP)组成,它们部署在在室内一些便于安装的位置,系统或网络管理员通常知道这些AP的位置。能连接WiFi的移动设备(比如笔记本电脑、移动电话)相互之间可以直接或间接地(通过AP)通信,因此可以考虑在通信功能外同时实现定位功能。
(基于WIFI的三点定位实现)
本文将以Arduino单片机作为载体,以RSSI技术作为基础来分享一下WIFI室内定位的技术。
01
准备环节
需要准备以下几种实验器材:Basra控制板(ArduinoUNO)*4、扩展板*4、锂电池*4、ESP8266WIFI模块*4、OLED显示屏*1、Mini数据线*1。
02
技术梳理
时代
在本章的开头中,大家可能大概了解了室内定位的概念。那么要具体实现的话你还需要学习基于RSSI的三点定位技术、OLED屏幕显示、串口通信等。
由于文章篇幅有限,此处将详细介绍基于RSSI的三点定位技术实现,其他技术点大家可以自行学习。
RSSI的全称是Received Signal Strength Indication接收的信号强度指示。释义为无线发送层的可选部分,用来判定链接质量,以及是否增大广播发送强度。
RSSI是射频信号理论术语,主要应用于发射机和接收机之间的距离测量。该方法是依据接收信号能量强度确定距离,对通信信道参数要求较高。
如何实现基于RSSI的三点定位技术
基于rssi的三点定位算法,是已知三个点的坐标和未知点到这三个点的rssi的信号值,求解未知点的坐标。
首先是将rssi信号转换为距离:
d=10^((ABS(RSSI)-A)/(10*n))
其中d为距离,单位是m。
RSSI为rssi信号强度,为负数。
A为距离探测设备1m时的rssi值的绝对值,最佳范围在45-49之间。
n为环境衰减因子,需要测试矫正,最佳范围在3.25-4.5之间。
在获取未知点到三个点的距离后,剩下的就是求解未知点的坐标。我们都知道两个圆会相交于一个或者两个点(如果相交),那么三个圆如果相交的话,必然会交于一个点(三个探测设备在一条直线上的情况下有可能相交于两个点,这里不考虑),所以我们要求解的未知点便是以三个已知点为圆心,以他们与未知点之间的距离为半径画出的三个圆的交点。那么这个问题就转化为了求三个已知圆的交点,然后如果根据圆的方程:
(x1 – x)^2 + (y1-y)^2 = r1^2
(x2 – x)^2 + (y2-y)^2= r2^2
(x3 – x)^2 + (y3-y)^2= r3^2
求解的话,是非常难求出未知点的坐标的。
这里介绍另一种程序容易实现的计算方法:
判断任意两个圆是否相切(内切或外切),这里可以设定一个误差允许值d,也就是
(x1 – x2)^2 + (y1-y2)^2= (r1+r2+d)^2
满足上述公式时就认为两个圆相切,其中d为误差值,可以是正数或者负数。如果两个圆相切的话,那么交点就比较好求解了:
x = x1+ (x2 - x1)*(r1/(r1 + r2));
y = y1 + (y2- y1)*(r1/(r1 + r2));
求解到x和y的坐标后,只需要用第三个圆进行验证,即求出这个点到第三个圆的圆心的距离,再和第三个圆的半径做比较,如果在误差允许范围内,那么就可以认为求得的x,y是三个圆的交点,也就是未知点的坐标。
没有任意两个圆相切,那么就先用两个圆求解两个交点,如下图:
其中A,B是两个圆心,坐标分别为(xa,ya)和(xb,yb),C,D是两个圆的焦点,E为AB与CD的交点。
其中
AB^2 = (xa – xb)^2 +(ya-yb)^2
其中
AC^2 =AE^2 + CE^2……………………1
BC^2 = BE^2+ CE^2……………………2
AC = ra
BC= rb
AE + BE = AB = (xa – xb)^2+ (ya – yb)^2
等式2转换为
BC^2 = (AB - AE)^2 + CE^2
BC^2 = AB^2 + AE^2 – 2*AB*AE + CE^2 …………………3
等式3减去等式1:
BC^2 - AC^2 = AB^2 – 2*AB*AE
AE = (rb ^2 - ra ^2 -AB^2)/( – 2*AB)
于是可以根据以下公式求得CE
CE^2 = AC^2 – AE^2
我们还可以获取E点的坐标(xE, yE)
xE = xa + ( (xb- xa)*AE )/AB
yE = ya + ( (yb- ya)*AE )/AB
然后我需要求得AB和CD的斜率KAB和KCD
kAB = (yb - ya)/(xb - xa)
kCD = (-1)/kAB //这里要注意kAB为0的情况
然后求得CD和x轴的夹角
∠CDX = atan(kCD)
这时候就可以求得C (xc, yc)和D(xd, yd)的坐标
xc = xE + CE*cos(∠CDXs)
yc = yE + CE*sin (∠CDXs)
xd = xE - CE*cos(∠CDXs)
yd = yE - CE*sin (∠CDXs)
至此,我们就求得了两个圆的两个交点坐标,然后只需要用这两个点去第三个圆做验证,就可以获得三个圆的交点,也就是我们要求的未知点。
03
功能实现
ESP8266模块操作
①将Basra控制板通过数据线连接至电脑,使用Arduino IDE给控制板烧录1套空程序(初始打开软件或新建项目都是空程序,选择端口号、版型烧录即可)。
(Arduino IDE初始打开界面)
②将Esp8266模块连接到Ares-DC扩展板上,并将扩展板插到控制板上。打开arduino的Seiral Monitor,输入AT指令集,观察模块的相应。
③描述 Espressif AT 指令集功能以及使用方法 指令集主要分为:基础 AT 命令、Wifi 功能 AT 命令、TCP/IP 工具箱 AT 命令等。
WIFI定位实现
将以下程序烧录至主控板中,移动中心未知点模块位置,观察oled模块显示数值,目标物体会显示相应的位置。
/*将与主控板相连接的wifi模块使用“esp8266调试工具软件”设置为Station模式
将其余三个模块设置为AP模式,并记录其NAME,存储在程序中*/
//#define DEBUG
#define ESP_AP_NUMBER 3
#include <SoftwareSerial.h>
#include <RssiPositionComputer.h>
#include <Wire.h>
#include <MultiLCD.h>
SoftwareSerial myESP(2,3);
RssiPositionComputer myPositionComputer;
Point2D master_point;
LCD_SSD1306 lcd;
char esp_ap_name[ESP_AP_NUMBER][10] = {"ESP826601","ESP826602","ESP826603"};
int rssi[ESP_AP_NUMBER];
float distance[ESP_AP_NUMBER];
void setup()
{
delay(1000);
Serial.begin(115200);
myESP.begin(9600);
#ifdef DEBUG
Serial.println("begin");
#endif
while(myESP.available()&&myESP.read());
while(!myESP.available())
{
myESP.println("AT");
delay(1000);
}
while(myESP.available()&&myESP.read());
#ifdef DEBUG
Serial.println("Resonse ok");
#endif
lcd.begin();
lcd.clear();
lcd.setCursor(30,4);
lcd.print("Hello, world!");
}
void loop()
{
int n = searchESPAP(esp_ap_name,rssi);
for(int i=0;i<ESP_AP_NUMBER;i++)
{
distance[i] = rssiToDistance(rssi[i]);
Serial.print(distance[i]);
Serial.print('\t');
}
Serial.print(n);
Serial.print('\t');
if(myPositionComputer.distanceToPoint(distance[0],distance[1],distance[2],&master_point)==true)
{
Serial.print(master_point.x);
Serial.print('\t');
Serial.print(master_point.y);
Serial.print('\t');
Serial.println("position okok");
lcd.clear();
lcd.setCursor(30,2);
lcd.printLong(master_point.x*100,FONT_SIZE_LARGE); //按厘米输出
lcd.setCursor(30,5);
lcd.printLong(master_point.y*100,FONT_SIZE_LARGE);
}
else
{
lcd.clear();
lcd.setCursor(30,4);
lcd.print("position ERROR!");
Serial.println("position ERROR");
}
}
int nameToNumber(char in[],char name[][10])
{
for(int i=0;i<3;i++)
{
for(int j=0;j<9;j++)
{
if(in[j] != name[i][j])
break;
if(j==8)
return(i);
}
}
return(-1);
}
byte searchESPAP(char name[][10], int rs[])
{
byte search_result_number = 0;
int state = 0;
int n;
int ap_n;
char name_string[10];
char rssi_string[4];
while(myESP.available()&&myESP.read());
myESP.println("AT+CWLAP");
delay(100);
while(myESP.available()&&myESP.read());
unsigned long t = millis();
while(!myESP.available())
{
if(millis()-t<3000)
delay(5);
else
return(0);
}
#ifdef DEBUG
Serial.println("received........");
#endif
t = millis();
while(myESP.available()||(millis()-t<3000))
{
if(!myESP.available())
continue;
char in_char = myESP.read();
#ifdef DEBUG
Serial.print(in_char);
#endif
if( (state == 0)&&(in_char=='(') )
{
state = 1;
n = -4;
}
else if(state == 1)
{
n++;
if(n>=0)
name_string[n] = in_char;
if(n == 8)
{
n = -3;
ap_n = nameToNumber(name_string,name);
if(ap_n != -1)
{
state = 2;
#ifdef DEBUG
Serial.print('\n');
Serial.print("ap_n:"); Serial.println(ap_n);
#endif
}
else
{
state = 0;
}
}
}
else if(state==2)
{
n++;
if(n>=0)
rssi_string[n] = in_char;
if(n == 2)
{
rs[ap_n] = atof(rssi_string);
state = 0;
search_result_number++;
#ifdef DEBUG
Serial.print('\n');
Serial.print("rssi["+String(ap_n)+"]:"); Serial.println(rs[ap_n]);
#endif
}
}
}
return(search_result_number);
}
float rssiToDistance(int rssi)
{
float dis = 0;
dis = pow(10.0,((abs(rssi)-47)/10.0/2.212));
return dis;
程序烧录完成之后,根据现有条件选出一块无遮挡的空地,将3个信号基站呈三角形放置,将烧录显示坐标程序的需要定位的装置放置在3个基站中间,分别打开电源,待连接成功之后移动坐标显示装置,OLED屏幕将会实时显示坐标点。
移动定位装置,将会呈现出以下效果: