Ibeacon小项目

概述

这个项目主要是定位50米走廊的位置,共有5个Ibeacon设备,每隔10米放置一个。显示也不像之前放在pc端上显示,而是实时显示在手机屏幕上。Android扫描到Ibeacon设备的RSSi后,先进行卡尔曼滤波,得到一个相对稳定的RSSI,再根据可调的距离算法(该距离算法是买IBeacon设备厂家提供的算法,其实也是一个衰减模式)获得距离。距离进行sma平滑后,通过5个ibeacon设备相互迭代(最小二乘法),得到坐标。

需要源码的小伙伴:留言

Appconfig类

该类是APP所有配置的配置类,可以根据实际环境,调节这个类。配置内容有:绘图坐标点大小,蓝牙扫描时间,5个ibeacon部署位置,sma平滑精度,卡尔曼算法的误差调节以及距离衰减模型的参数调节。(注意::使用时候,由于beacon设备uuid,蓝牙地址等不同,需要在ibeacondevice中,更改你们的ibeacon设备的uuid,major,minor,以及蓝牙地址,否者不会识别你们的beacon设备)如下:代码

public class AppConfig {
    /*
    MAIN_ACTIVITY
     */
    static private  int MAIN_ACTIVITY_SCAN_TIME=3000;

    /*
    POINT
    */
    static private  int POINT_RADIUS=25;//绘制点半径

    /*
     CALMAN
     */
    static private double KALMAN_Q1=16; //卡尔曼预测误差的方差
    static private double KALMAN_R1=100; //卡尔曼噪声误差的方差

Point类

存储坐标点的数据类

public class Point {
    //点的X坐标
    private int x;
    //点的Y坐标
    private int y;
    //点的半径,默认为10像素
    private int radius = AppConfig.getPointRadius();
    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
    public Point(int x, int y, int radius) {
        this.x = x;
        this.y = y;
        this.radius = radius;
    }
    public int getX() {
        return x;
    }
.......
}

queue类

一个自己写的队列类,用于sma平滑数据,队列没满则会一直放即时的距离值进去,满了则计算队列(默认5个值)的平均数,为当前的距离值,然后出一个值,再进最新值

public class queue {
    private float[] data ;// 队列
    private int front;// 队列头,允许删除
    private int rear;// 队列尾,允许插入
    private int LENGTH=AppConfig.getQueueLength();
    private int full=0;
    private int t=0;//判断队列是否装满
    private  int relute=0;
    public queue() {
        data = new float[LENGTH];
        front = rear = 0;
    }
    // 入队
    public void offer(float date) {
        if (rear>=LENGTH)
        {
            rear=0;
        }
        data[rear++] = date;

        if (t++>=LENGTH)
        {
            full=1;
            //relute=(int)(data[0]+data[1]+data[2]+data[3]+data[4])/5;
            for (int i=0;i<LENGTH;i++)
            {
                relute=0;
                relute += data[i];
            }
        }
    }
    // 出队
    public float poll() {

        if (front<LENGTH)
        {
            float value = data[front];// 保留队列的front端的元素的值

            front++;
            return value;
        }
        else
        {
            front=0;
            float value = data[front];
            return  value;
        }
    }
    public boolean full()
    {
        if (full==1)
            return  true;
        else
            return false;
    }
    public int getRelute() {
        return relute;
    }
}

ibeacondevice类

该类是五个Ibeacon设备数据的类,存放五个Ibeacon的uuid,蓝牙地址等信息

public class IbeaconDevice {
        public int major;
        public int minor;
        public String proximityUuid;
        public String bluetoothAddress;
        int x;
        int y;
        public  IbeaconDevice(String bluetoothAddress,int x,int y)
        {
              this.bluetoothAddress=bluetoothAddress;
              this.x=x;
              this.y=y;
              major=10;
              minor=7;
              proximityUuid="fda50693-a4e2-4fb1-afcf-c6eb07647825";
        }
        public int getMajor() {
            return major;
        }
        public int getX() {
            return x;
        }
        public int getY() {
            return y;
        }

ibeacon类

该类存放扫描获得的实时beacon对象,与ibeacondevice最大的区别在于RSSI

public class iBeacon {
    public String name;
    public int major;
    public int minor;
    public String proximityUuid;
    public String bluetoothAddress;
    public int txPower;
    public int rssi;
    public double distance=0;
    public int x;
    public int y;
}

Mainactivity类

UI界面,主要进行图像的显示和蓝牙的扫描。
敲击扫描按钮,开始扫描

 //开启蓝牙扫描
        findViewById(R.id.mStartBtn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mBLE.startLeScan(mScanCallback);
            }
        });

将我的蓝牙显示的函数:

    private void getBlueToothDetail() {
        if (mBLE == null) return;
        mBluetoothAdapter = mBLE.getBluetoothAdapter();
        if (mBluetoothAdapter == null) return;
        Textview1.setText("我的蓝牙:" + mBluetoothAdapter.getName() + "\n地址:" + mBluetoothAdapter.getAddress());
    }

对了,这次使用的扫描是使用的第三方库,扫描更快更稳定,如下是扫描的回调

   private PeriodScanCallback mScanCallback = new PeriodScanCallback(TIME_OUT_SCAN) {
        @Override
        public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
            logDevice(device, rssi, scanRecord);
        }
        @Override
        public void onScanTimeout() {
            mBLE.startLeScan(mScanCallback);
            toast("扫描超时");
        }
    };
    
 private void logDevice(BluetoothDevice device, int rssi, byte[] scanRecord) {
        ibeacon = iBeaconClass.fromScanData(device, rssi, scanRecord,sma1);//先通过ibeaconclass获得ibeacon类
        if (ibeacon == null) return;
        Textview1.setText("\n" +"蓝牙地址"+ibeacon.bluetoothAddress);
        Textview1.append("\n" + "rssi" + ibeacon.rssi);
        Textview1.append("\n" + "distance" + ibeacon.distance);
        double t=mtwoPointAlgorithm.algorithm(ibeacon);
        Textview1.append("\nt=="+t);
        mPointView.drawPoint(new Point(mPointView.getWidth()/2+30,(int)((1-t/50)*mPointView.getHeight())));
        //绘制坐标,x坐标默认是图像宽度的一半。走道使用x,y和只使用y差距不大
    }

Ibeaconclass类

主要是将获得的广播数据,解析出一个ibeacon类对象。在最开始,会调用卡尔曼算法的类,先过滤RSSi值。

class iBeaconClass {
    static Kalman kalman1=new Kalman(AppConfig.getKalmanQ1(),AppConfig.getKalmanR1());//5个卡尔曼滤波器分别对5个ibeacon滤波
    static Kalman kalman2=new Kalman(AppConfig.getKalmanQ1(),AppConfig.getKalmanR1());
    static Kalman kalman3=new Kalman(AppConfig.getKalmanQ1(),AppConfig.getKalmanR1());
    static Kalman kalman4=new Kalman(AppConfig.getKalmanQ1(),AppConfig.getKalmanR1());
    static Kalman kalman5=new Kalman(AppConfig.getKalmanQ1(),AppConfig.getKalmanR1());
    static IbeaconDevice dev1 = new IbeaconDevice("88:3F:4A:EA:50:8E", 0, AppConfig.getBeaconDevice1Y());
    static IbeaconDevice dev2 = new IbeaconDevice("88:3F:4A:EA:4D:CE", 0, AppConfig.getBeaconDevice2Y());
    static IbeaconDevice dev3 = new IbeaconDevice("88:3F:4A:EA:4D:D6", 0, AppConfig.getBeaconDevice3Y());
    static IbeaconDevice dev4 = new IbeaconDevice("88:3F:4A:EA:4D:B3", 0, AppConfig.getBeaconDevice4Y());
    static IbeaconDevice dev5 = new IbeaconDevice("88:3F:4A:EA:50:85", 0, AppConfig.getBeaconDevice5Y());

    //解析数据
    static iBeacon fromScanData(BluetoothDevice device, int rssi, byte[] scanData,sma sma1) {
      // rssi=sma1.run(device.getAddress(),rssi);//平滑数据算法
        //卡尔曼滤波
        if (device.getAddress().equals(iBeaconClass.dev1.bluetoothAddress))
         rssi=(int)iBeaconClass.kalman1.KalmanFilter((double) rssi);
        if (device.getAddress().equals(iBeaconClass.dev2.bluetoothAddress))
            rssi=(int)iBeaconClass.kalman2.KalmanFilter((double) rssi);
        if (device.getAddress().equals(iBeaconClass.dev3.bluetoothAddress))
            rssi=(int)iBeaconClass.kalman3.KalmanFilter((double) rssi);
        if (device.getAddress().equals(iBeaconClass.dev4.bluetoothAddress))
            rssi=(int)iBeaconClass.kalman4.KalmanFilter((double) rssi);
        if (device.getAddress().equals(iBeaconClass.dev5.bluetoothAddress))
            rssi=(int)iBeaconClass.kalman5.KalmanFilter((double) rssi);
  //这个类代码很多。。。。。。。

sma类

简单平均算法,用来平滑滤波后得到的距离。

public class sma {
    IbeaconDevice dev1, dev2, dev3, dev4, dev5;

    public ArrayList<queue> arr_queue;//存放 5个ibeacon的动态数组 每个队列有5个值
   public  ArrayList<IbeaconDevice> arr_ineacon_device;
    sma()
    {
        arr_queue=new ArrayList<>();
        arr_ineacon_device=new ArrayList<>();
        dev1 = new IbeaconDevice("88:3F:4A:EA:50:8E", 0, AppConfig.getBeaconDevice1Y());
        dev2 = new IbeaconDevice("88:3F:4A:EA:4D:CE", 0,  AppConfig.getBeaconDevice2Y());
        dev3 = new IbeaconDevice("88:3F:4A:EA:4D:D6", 0,  AppConfig.getBeaconDevice3Y());
        dev4 = new IbeaconDevice("88:3F:4A:EA:4D:B3", 0,  AppConfig.getBeaconDevice4Y());
        dev5 = new IbeaconDevice("88:3F:4A:EA:50:85", 0,  AppConfig.getBeaconDevice5Y());
        arr_queue.add(new queue());
        arr_queue.add(new queue());
        arr_queue.add(new queue());
        arr_queue.add(new queue());
        arr_queue.add(new queue());
        arr_ineacon_device.add(dev1);
        arr_ineacon_device.add(dev2);
        arr_ineacon_device.add(dev3);
        arr_ineacon_device.add(dev4);
        arr_ineacon_device.add(dev5);
    }

    public  int run(String address,int rssi)
    {
        if (address.equals(dev1.bluetoothAddress))
        {
            arr_queue.get(0).offer(rssi);
                return 0;
        }
        else if (address.equals(dev2.bluetoothAddress))
        {
            arr_queue.get(1).offer(rssi);
                return 0;
        }
        else if (address.equals(dev3.bluetoothAddress))
        {
            arr_queue.get(2).offer(rssi);
                return 0;
        }
        else if (address.equals(dev4.bluetoothAddress))
        {
            arr_queue.get(3).offer(rssi);
                return 0;
        }
        else if (address.equals(dev5.bluetoothAddress))
        {
            arr_queue.get(4).offer(rssi);
                return 0;
        }
        else {
            return 0;
        }
    }

Kalman类

使用的是之前写好的类

卡尔曼类

Algorithm类

类中有,在滤波和平滑后进行的 迭代,两点距离算法,最后获得的是显示的坐标。外部调用algorithm,并传入一个即时ibeacon值,先判断队列是否满了(sma算法),满了则执行迭代算法。

public double algorithm(iBeacon now_ibeacon) {
       sma1.run(now_ibeacon.bluetoothAddress,now_ibeacon.rssi);//入队
       double buff=0;
       int temp=0;
       for(int i=0;i<5;i++)//循环遍历5个队列
       {
          if(sma1.arr_queue.get(i).full())//如果大于5)
          {
              for (int j = i + 1; j < 5; j++)
                  if (sma1.arr_queue.get(j).full())//如果大于5
                  {
                      buff+=twopoint(sma1.arr_ineacon_device.get(i),sma1.arr_ineacon_device.get(j),sma1.arr_queue.get(i).getRelute(),sma1.arr_queue.get(j).getRelute());
                      temp++;
                  }
          }
       }
        buff=buff/temp;
      return buff;
        }
        public  double twopoint(IbeaconDevice now_ibeacon,IbeaconDevice last_ibeacon,double now_dis,double last_dis)
        {
            double relute = 0;
            double buf1, buf2;
            if (now_dis + last_dis > Math.abs(now_ibeacon.y - last_ibeacon.y))//如果是在两点外
            {
                if (now_ibeacon.y > last_ibeacon.y)//now是距离更远的信标
                {
                    buf1 = now_ibeacon.y + now_dis;
                    buf2 = last_ibeacon.y + last_dis;
                    relute = (buf1 + buf2) / 2;
                } else//now是距离更近的信标
                {
                    buf1 = now_ibeacon.y - now_dis;
                    buf2 = last_ibeacon.y - last_dis;
                    relute = (buf1 + buf2) / 2;
                }
            } else//在两点内
            {
                if (now_ibeacon.y > last_ibeacon.y)//now是距离更远的信标
                {
                    buf1 = now_ibeacon.y - now_dis;
                    buf2 = last_ibeacon.y + last_dis;
                    relute = (buf1 + buf2) / 2;
                } else//last是距离更远的信标
                {
                    buf1 = now_ibeacon.y + now_dis;
                    buf2 = last_ibeacon.y - last_dis;
                    relute = (buf1 + buf2) / 2;
                }
            }
            return relute;
        }

PointImageView类

用来显示的类

public class PointImageView extends AppCompatImageView {
    public PointImageView(Context context) {
        super(context);
    }
    public PointImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public PointImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    private Point mPoint = null;

    private Paint mPointPaint = new Paint();

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        mPointPaint.setColor(Color.RED);

        if (mPoint != null) {
            canvas.drawCircle(mPoint.getX(), mPoint.getY(), mPoint.getRadius(), mPointPaint);
        }
    }
    public void drawPoint(Point p) {
        mPoint = p;
        invalidate();
    }

猜你喜欢

转载自blog.csdn.net/qq_35651984/article/details/84933400