分享 | 机器人室内定位技术 机器人位置

分享 | 机器人室内定位技术 机器人位置

解决方案goocz2024-12-22 17:35:2620A+A-

室内环境下的定位一直是一个很多问题未被解决的领域。由于信号的严重衰减和多径效应,通用的室外定位设施(比如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屏幕将会实时显示坐标点。

移动定位装置,将会呈现出以下效果:

点击这里复制本文地址 以上内容由goocz整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!

果子教程网 © All Rights Reserved.  蜀ICP备2024111239号-5