【移動應(yīng)用開發(fā)技術(shù)】如何搭建一個藍牙定位系統(tǒng)_第1頁
【移動應(yīng)用開發(fā)技術(shù)】如何搭建一個藍牙定位系統(tǒng)_第2頁
【移動應(yīng)用開發(fā)技術(shù)】如何搭建一個藍牙定位系統(tǒng)_第3頁
【移動應(yīng)用開發(fā)技術(shù)】如何搭建一個藍牙定位系統(tǒng)_第4頁
【移動應(yīng)用開發(fā)技術(shù)】如何搭建一個藍牙定位系統(tǒng)_第5頁
已閱讀5頁,還剩12頁未讀 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

【移動應(yīng)用開發(fā)技術(shù)】如何搭建一個藍牙定位系統(tǒng)

本篇文章給大家分享的是有關(guān)如何搭建一個藍牙定位系統(tǒng),在下覺得挺實用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著在下一起來看看吧。1、準備設(shè)備所需硬件設(shè)備:(1)低功率藍牙定位器若干(如:10個),網(wǎng)上有賣(單價從幾十到幾百都有)(2)android設(shè)備一臺,系統(tǒng)版本4.2以上(SDK版本大于17)(3)iOS設(shè)備一臺,支持藍牙4.0BLE2、設(shè)置藍牙定位器移動設(shè)備掃描周邊低功率藍牙設(shè)備,可以獲得藍牙設(shè)備對應(yīng)的ProximityUUID、Major、Minor等屬性信息。而剛采購來的藍牙設(shè)備屬性可能都相同,互相區(qū)別不開,所以我們需要設(shè)置每臺設(shè)備的屬性。設(shè)備廠商都會提供相關(guān)手機應(yīng)用,共用戶設(shè)置屬性信息。給藍牙設(shè)備裝上電池,打開手機應(yīng)用,靠近藍牙設(shè)備就能發(fā)現(xiàn),然后就可以設(shè)置其屬性值了,其中:UUID是一個32位的16進制數(shù),表示設(shè)備廠商,該字段可以沿用出廠設(shè)置Major表示不同區(qū)域(比如:某一樓層、某一地區(qū)),取值范圍0到6萬多Minor表示不同的設(shè)備,取值范圍0到6萬多樣例:UUID=e2c56db5-dffb-48d2-b060-d0f5a71096e0,Major=1001,Minor=10001每臺設(shè)備設(shè)置完屬性后準備一個標簽,填上屬性信息,貼到設(shè)備上,方便以后部署。3、部署藍牙設(shè)備首先,準備目標場地地圖數(shù)據(jù),可以是基于經(jīng)緯度坐標,也可以是簡單圖片坐標,看具體使用情況。接下來,將藍牙設(shè)備挨個部署到場地指定位置上,順便記錄每個設(shè)備地理坐標或圖片坐標。最后,得到一張表格信息,記錄著每臺藍牙設(shè)備屬性和位置信息。這張表就是整個定位系統(tǒng)的指紋庫,為定位算法使用。固定藍牙設(shè)備到場地指定位置比較容易,不過記錄設(shè)備坐標信息可能復(fù)雜一點,需要在地圖或圖片上獲得相應(yīng)位置點??梢蚤_發(fā)一個App從而快速準確地記錄位置信息,順便將相關(guān)信息錄入指紋庫(數(shù)據(jù)庫,比如:SQLite)。部署藍牙設(shè)備還有一個關(guān)注點就是部署間隔。低功率藍牙設(shè)備容易受場地、環(huán)境影響,比較不穩(wěn)定,所以根據(jù)場地條件每隔幾米或十幾米部署一臺藍牙設(shè)備。間隔太大會影響定位精度,不過太密也是資源浪費,不是越密集定位精度越高。4、客戶端App開發(fā)客戶端app主要功能就是掃描周圍藍牙設(shè)備,將設(shè)備列表信息上傳定位服務(wù)器,從而獲得定位效果,并展現(xiàn)給終端用戶。4.1Android應(yīng)用開發(fā)工程所需SDK版本大于17。1.App所需權(quán)限(AndroidManifest.xml文件)<uses-permission

android:name="android.permission.BLUETOOTH"

/>

<uses-permission

android:name="android.permission.BLUETOOTH_ADMIN"

/>2.創(chuàng)建beacon數(shù)據(jù)項類public

class

IBeaconRecord

{

public

String

address; //

設(shè)備地址(Mac)

public

String

uuid; //

Proximity

UUID

public

int

major; //

Major

public

int

minor; //

Minor

public

int

rssi; //

場強

}其中,address屬性可以不要,因為iOS設(shè)備獲取不到該屬性!3.創(chuàng)建掃描工具類import

java.util.ArrayList;

import

java.util.HashMap;

import

java.util.List;

import

java.util.Map;

import

com.example.vo.IBeaconRecord;

import

android.bluetooth.BluetoothAdapter;

import

android.bluetooth.BluetoothDevice;

import

android.bluetooth.BluetoothManager;

import

android.content.Context;

import

android.os.Build;

import

android.os.Handler;

public

class

BLEPositioning

{

private

Context

m_ctx;

private

Handler

handler;

private

BluetoothManager

bluetoothManager;

private

BluetoothAdapter

mBluetoothAdapter;

//

存儲藍牙掃描結(jié)果,key

-

name_address,

value

-

List<IBeaconRecord>

private

Map<String,

List<IBeaconRecord>>

mapBltScanResult;

public

BLEPositioning(Context

ctx)

{

super();

this.m_ctx

=

ctx;

initParam();

}

/**

*

初始化

*/

private

void

initParam()

{

handler

=

new

Handler();

mapBltScanResult

=

new

HashMap<String,

List<IBeaconRecord>>();

//

設(shè)備SDK版本大于17(Build.VERSION_CODES.JELLY_BEAN_MR1)才支持BLE

4.0

if

(Build.VERSION.SDK_INT

>=

Build.VERSION_CODES.JELLY_BEAN_MR1)

{

bluetoothManager

=

(BluetoothManager)

this.m_ctx

.getSystemService(Context.BLUETOOTH_SERVICE);

mBluetoothAdapter

=

bluetoothManager.getAdapter();

}

}

/**

*

開始掃描藍牙設(shè)備

*/

public

void

startScan()

{

mapBltScanResult.clear();

if

(mBluetoothAdapter

!=

null

&&

mBluetoothAdapter.isEnabled())

{

//

5秒后停止掃描,畢竟掃描藍牙設(shè)備比較費電,根據(jù)定位及時性自行調(diào)整該值

handler.postDelayed(new

Runnable()

{

@Override

public

void

run()

{

mBluetoothAdapter.stopLeScan(bltScanCallback);

}

},

5

*

1000);

mBluetoothAdapter.startLeScan(bltScanCallback); //

開始掃描

}

}

/**

*

請求定位服務(wù),由你們完成,

*

如果指紋數(shù)據(jù)在本地,定位算法就在當(dāng)前App里完成

*/

public

void

requestServer()

{

//

TODO

//

利用mapBltScanResult(藍牙掃描結(jié)果)請求定位服務(wù)或本地計算定位

}

/**

*

藍牙掃描回調(diào),獲取掃描獲得的藍牙設(shè)備信息

*/

private

BluetoothAdapter.LeScanCallback

bltScanCallback

=

new

BluetoothAdapter.LeScanCallback()

{

@Override

public

void

onLeScan(final

BluetoothDevice

device,

int

rssi,

byte[]

scanRecord)

{

/**

*

參數(shù)列表描述

*

1.device -

BluetoothDevice類對象,

*

通過該對象可以得到硬件地址(比如"00:11:22:AA:BB:CC")、設(shè)備名稱等信息

*

2.rssi

-

藍牙設(shè)備場強值,小于0的int值

*

3.scanRecord

-

這里內(nèi)容比較豐富,像UUID、Major、Minor都在這里

*/

IBeaconRecord

record

=

new

IBeaconRecord();

if

(fromScanData(scanRecord,

record))

{

String

address

=

device.getAddress(); //

獲取Mac地址

String

name

=

device.getName(); //

獲取設(shè)備名稱

String

key

=

name

+

"_"

+

address;

record.address

=

address; //

Mac地址

record.rssi

=

rssi; //

場強

if

(mapBltScanResult.containsKey(key))

{

mapBltScanResult.get(key).add(record);

}

else

{

ArrayList<IBeaconRecord>

list

=

new

ArrayList<IBeaconRecord>();

list.add(record);

mapBltScanResult.put(key,

list);

}

}

}

};

/**

*

解析藍牙信息數(shù)據(jù)流

*

注:該段代碼是從網(wǎng)上看到的,來源不詳

*

@param

scanData

*

@param

record

*

@return

*/

private

boolean

fromScanData(byte[]

scanData,

IBeaconRecord

record)

{

int

startByte

=

2;

boolean

patternFound

=

false;

while

(startByte

<=

5)

{

if

(((int)

scanData[startByte

+

2]

&

0xff)

==

0x02

&&

((int)

scanData[startByte

+

3]

&

0xff)

==

0x15)

{

//

yes!

This

is

an

iBeacon

patternFound

=

true;

break;

}

else

if

(((int)

scanData[startByte]

&

0xff)

==

0x2d

&&

((int)

scanData[startByte

+

1]

&

0xff)

==

0x24

&&

((int)

scanData[startByte

+

2]

&

0xff)

==

0xbf

&&

((int)

scanData[startByte

+

3]

&

0xff)

==

0x16)

{

return

false;

}

else

if

(((int)

scanData[startByte]

&

0xff)

==

0xad

&&

((int)

scanData[startByte

+

1]

&

0xff)

==

0x77

&&

((int)

scanData[startByte

+

2]

&

0xff)

==

0x00

&&

((int)

scanData[startByte

+

3]

&

0xff)

==

0xc6)

{

return

false;

}

startByte++;

}

if

(patternFound

==

false)

{

//

This

is

not

an

iBeacon

return

false;

}

//

獲得Major屬性

record.major

=

(scanData[startByte

+

20]

&

0xff)

*

0x100

+

(scanData[startByte

+

21]

&

0xff);

//

獲得Minor屬性

record.minor

=

(scanData[startByte

+

22]

&

0xff)

*

0x100

+

(scanData[startByte

+

23]

&

0xff);

//

record.tx_power

=

(int)

scanData[startByte

+

24];

//

this

one

is

//

signed

//

record.accuracy

=

calculateAccuracy(record.tx_power,

record.rssi);

//

if

(record.accuracy

<

0)

{

//

return

false;

//

}

try

{

byte[]

proximityUuidBytes

=

new

byte[16];

System.arraycopy(scanData,

startByte

+

4,

proximityUuidBytes,

0,

16);

String

hexString

=

bytesToHex(proximityUuidBytes);

StringBuilder

sb

=

new

StringBuilder();

sb.append(hexString.substring(0,

8));

sb.append("-");

sb.append(hexString.substring(8,

12));

sb.append("-");

sb.append(hexString.substring(12,

16));

sb.append("-");

sb.append(hexString.substring(16,

20));

sb.append("-");

sb.append(hexString.substring(20,

32));

//

beacon.put("proximity_uuid",

sb.toString());

//

獲得UUID屬性

record.uuid

=

sb.toString();

}

catch

(Exception

e)

{

e.printStackTrace();

}

return

true;

}

private

char[]

hexArray

=

{

'0',

'1',

'2',

'3',

'4',

'5',

'6',

'7',

'8',

'9',

'a',

'b',

'c',

'd',

'e',

'f'

};

private

String

bytesToHex(byte[]

bytes)

{

char[]

hexChars

=

new

char[bytes.length

*

2];

int

v;

for

(int

j

=

0;

j

<

bytes.length;

j++)

{

v

=

bytes[j]

&

0xFF;

hexChars[j

*

2]

=

hexArray[v

>>>

4];

hexChars[j

*

2

+

1]

=

hexArray[v

&

0x0F];

}

return

new

String(hexChars);

}

}掃描結(jié)果放在mapBltScanResult里,該HashMap的key由設(shè)備Mac地址和名稱組成(address_name),value是個ArrayList,記錄著該藍牙設(shè)備多次掃描得到的信息(IBeaconRecord)序列,請求定位服務(wù)或本地計算定位之前,這些序列要進行平均處理(其實只是平均rssi值)。經(jīng)過RSSI值多次平均處理后,一定程度上減小藍牙設(shè)備不穩(wěn)定因素。關(guān)于請求定位服務(wù),展現(xiàn)定位效果,還有定位算法都不是本文重點!關(guān)于藍牙定位算法也可以參考其他文獻資料!4.2iOS應(yīng)用開發(fā)iOS部分參考了AirLocate源碼(蘋果官方藍牙樣例工程)。1.引用基礎(chǔ)配置類“APLDefaults”(來自AirLocate)APLDefaults.h文件/*

File:

APLDefaults.h

Abstract:

Contains

default

values

for

the

application.

Version:

1.1

Copyright

(C)

2014

Apple

Inc.

All

Rights

Reserved.

*/

extern

NSString

*BeaconIdentifier;

@interface

APLDefaults

:

NSObject

+

(APLDefaults

*)sharedDefaults;

@property

(nonatomic,

copy,

readonly)

NSArray

*supportedProximityUUIDs;

@property

(nonatomic,

copy,

readonly)

NSUUID

*defaultProximityUUID;

@property

(nonatomic,

copy,

readonly)

NSNumber

*defaultPower;

@endAPLDefaults.m文件/*

File:

APLDefaults.m

Abstract:

Contains

default

values

for

the

application.

Version:

1.1

Copyright

(C)

2014

Apple

Inc.

All

Rights

Reserved.

*/

#import

"APLDefaults.h"

NSString

*BeaconIdentifier

=

@"com.example.apple-samplecode.AirLocate";

@implementation

APLDefaults

-

(id)init

{

self

=

[super

init];

if(self)

{

//

uuidgen

should

be

used

to

generate

UUIDs.

_supportedProximityUUIDs

=

@[[[NSUUID

alloc]

initWithUUIDString:@"E2C56DB5-DFFB-48D2-B060-D0F5A71096E0"],

[[NSUUID

alloc]

initWithUUIDString:@"5A4BCFCE-174E-4BAC-A814-092E77F6B7E5"],

[[NSUUID

alloc]

initWithUUIDString:@"74278BDA-B644-4520-8F0C-720EAF059935"]];

_defaultPower

=

@-59;

}

return

self;

}

+

(APLDefaults

*)sharedDefaults

{

static

id

sharedDefaults

=

nil;

static

dispatch_once_t

onceToken;

dispatch_once(&onceToken,

^{

sharedDefaults

=

[[self

alloc]

init];

});

return

sharedDefaults;

}

-

(NSUUID

*)defaultProximityUUID

{

return

_supportedProximityUUIDs[0];

}

@end2.

定義變量

//

存儲掃描獲得的藍牙設(shè)備信息

//

key

-

proximityUUID_Major_Minor

//

value

-

NSArray

(CLBeacon)

NSMutableDictionary

*dicBeacons;

CLLocationManager

*locationManager;

NSMutableDictionary

*rangedRegions;

//

要掃描的region

NSTimer

*timerPos;

//

定時器,用于控制掃描時間長短3.初始化

dicBeacons

=

[[NSMutableDictionary

alloc]

init];

locationManager

=

[[CLLocationManager

alloc]

init];

locationManager.delegate

=

self;

//

當(dāng)前類接收回調(diào),從而獲得藍牙設(shè)備信息

//

Populate

the

regions

we

will

range

once.

rangedRegions

=

[[NSMutableDictionary

alloc]

init];

for

(NSUUID

*uuid

in

[APLDefaults

sharedDefaults].supportedProximityUUIDs)

{

CLBeaconRegion

*region

=

[[CLBeaconRegion

alloc]

initWithProximityUUID:uuid

identifier:[uuid

UUIDString]];

rangedRegions[region]

=

[NSArray

array];

}4.開始掃描、停止掃描和請求定位服務(wù)//

開始掃描藍牙

-

(void)startScanning

{

//

定時3.0秒后請求定位服務(wù),時間間隔自行設(shè)置,只要有足夠的掃描時間即可

timerPos

=

[NSTimer

scheduledTimerWithTimeInterval:3.0

target:self

selector:@selector(startPositioning)

userInfo:nil

repeats:NO];

[dicBeacons

removeAllObjects];

//

開始掃描

for

(CLBeaconRegion

*region

in

rangedRegions)

{

[locationManager

startRangingBeaconsInRegion:region];

}

}

//

停止掃描藍牙

-

(void)stopScanning

{

//

停止掃描

for

(CLBeaconRegion

*region

in

rangedRegions)

{

[locationManager

stopRangingBeaconsInRegion:region];

}

}

//

請求定位服務(wù)

-

(void)startPositioning

{

[self

stopScanning];

//

停止掃描

//

以下根據(jù)掃描結(jié)果dicBeacons來請求定位服務(wù)

//

}其中,請求定位服務(wù)部分每個人都不一樣,依賴自身定位服務(wù)。5.監(jiān)聽回調(diào),解析掃描獲得的藍牙設(shè)備信息,存入dicBeacons變量#pragma

mark

-

Location

manager

delegate

-

(void)locationManager:(CLLocationManager

*)manager

didRangeBeacons:(NSArray

*)beacons

inRegion:(CLBeaconRegion

*)region

{

/*

CoreLocation

will

call

this

delegate

method

at

1

Hz

with

updated

range

information.

Beacons

will

be

categorized

and

displayed

by

proximity.

A

beacon

can

belong

to

multiple

regions.

It

will

be

displayed

multiple

times

if

that

is

the

case.

If

that

is

not

desired,

use

a

set

instead

of

an

array.

*/

for

(NSNumber

*range

in

@[@(CLProximityUnknown),

@(CLProximityImmediate),

@(CLProximityNear),

@(CLProximityFar)])

{

NSArray

*proximityBeacons

=

[beacons

filteredArrayUsingPredicate:[NSPredicate

predicateWithFormat:@"proximity

=

%d",

[range

intValue]]];

for

(int

i

=

0;

i

<

[proximityBeacons

count];

i++)

{

CLBeacon

*beacon

=

[proximityBeacons

objectAtIndex:i];

//

場強過濾,RSSI值要在-90到0之間

if

(beacon.rssi

<

0

&&

beacon.rssi

>

-90)

{

NSString

*strKey

=

[NSString

stringWithFormat:@"%@_%@_%@",[ximityUUID

UUIDString],

beacon.major,

beacon.minor];

if

([dicBeacons

objectForKey:strKey])

{

[[dicBeacons

objectForKey:strKey]

addObject:beacon];

}

else

{

NSMutableArray

*arrBeacons

=

[[NSMutableArray

alloc]

init];

[arrBeac

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論