一、前言
笔者最近入手了UWB进行室内二维定位,手头只有Arduino UNO,没有官网用的stm32的板子,于是乎,找到了Arduino库——“arduino-dw1000-ng-master”,所带的案例也不多:
简单的连接检测,数据收发,二维定位,其他的笔者还没注意。
本来就是想用用二维定位的功能,于是乎,小编就详细的走了一遍程序流程,并将它的通讯协议记录了下来。
二、二维定位基本原理
有了解过UWB的同学,就能知道,UWB是个高带宽模块,其核心芯片是DW1000。定位采用的是三点定位的方式,即3个基站分别测目标标签的距离,已知3个基站分别的坐标,各自以测量的数据为半径画圆,交点位置就是目标标签的坐标。
注:基站通常是固定位置,坐标固定且已知;标签即移动终端。
那么,单个基站与标签之间是如何完成测距的呢?官方文档就有解释了,图:
每个环节都采用定时器进行计时,再通过如下公式即可得到单次传输所用的时间:
至于为啥这样算就能得到单次的时间,有心的同学可以自己推导一下,这里就不过多叙述了。然后,时间乘以波速就是距离了撒。
那么,已知3个测距数据和基站的坐标,又怎么方便快速的求得坐标呢?
假设标签坐标为(x,y),基站1坐标为(x1,y1),基站2坐标(x2,y2),基站3坐标(x3,y3),分别测到的距离数据为 r1,r2,r3。分别画圆可以得到如下公式:
算式展开可以得到:
再由第一个式子加第二个式子,第二个式子加第三个式子可以得到如下算式:
这是一个由两个未知数的两个方程组成的系统:
其中,x和y就是待测目标,即有:
可见,ABCDEFD都是可以通过已知数据计算得出的值,因此就可以再计算得出x和y的值:
到此处,我们已经说明了二维测距的基本理论以及计算过程,那么接下来我们就来了解一下这个通信协议吧。
三、通信协议基本流程
这里就直接拉干货吧,就不做过多的阐述,这里重点是要理解这流程处理的机制,领悟了这个过程后,就可以自己搭建了哦。
基站默认都已上电工作。
此时,标签上电了,开始工作,发出了设备识别BLINK请求(10字节),其数据结构如下:
[0]BLINK
[1]SEQ_NUMBER(初识状态为0,当执行完这步骤后 SEQ_NUMBER+=1,后文直接写作SEQ_NUMBER++)
[2-9] tag_eui码(这是设备特有的8字节身份识别码,因此要求每个设备不一样)
基站1作为主基站,识别到 [0]BLINK 消息后,发送初识化请求(18字节),其数据结构如下:
[0]DATA
[1]SHORT_SRC_LONG_DEST
[2]SEQ_NUMBER++
[3-4]NetworkID
[5-12]tag_eui码
[13-14]device_address(基站的短地址)
[15]RANGING_INITIATION
[16-17]tag_short_address(通过获取到的tag_eui码得到设备短地址)
标签识别到 [15]RANGING_INITIATION 消息后,发送POLL消息(10字节),其数据结构如下:
[0]DATA
[1]SHORT_SRC_AND_DEST
[2]SEQ_NUMBER++
[3-4]NetworkID
[5-6]target_anchor_address(目标基站的短地址)
[7-8]tag_short_address(标签自身的短地址)
[9]RANGING_TAG_POLL
基站识别到 [9]RANGING_TAG_POLL 消息后,发送CONTINUE消息(13字节),其数据结构如下:
[0]DATA
[1]SHORT_SRC_AND_DEST
[2]SEQ_NUMBER++
[3-4]NetworkID
[5-6]tag_short_address(标签的短地址)
[7-8]device_address(基站的短地址)
[9]ACTIVITY_CONTROL
[10]RANGING_CONTINUE
[11]0
[12]0
标签识别到 [10]RANGING_CONTINUE 消息后,发送 FINAL消息(22字节),其数据结构如下:
[0]DATA
[1]SHORT_SRC_AND_DEST
[2]SEQ_NUMBER++
[3-4]NetworkID
[5-6]target_anchor_address(目标基站的短地址)
[7-8]tag_short_address(标签自身的短地址)
[9]RANGING_TAG_FINAL_RESPONSE_EMBEDDED
[10-13]timePollsent(测时数据)
[14-17]timeResponseToPollReceived(测时数据)
[18-21]timeFinalMessageSent(测时数据)
基站接收到这个 [9]RANGING_TAG_FINAL_RESPONSE_EMBEDDED 就会记录下[10-21]中的3组时间数据,然后发送 RANGING_CONFIRM消息(13字节),其数据结构如下:
[0]DATA
[1]SHORT_SRC_AND_DEST
[2]SEQ_NUMBER++
[3-4]NetworkID
[5-6]tag_short_address(标签的短地址)
[7-8]device_address(基站的短地址)
[9]ACTIVITY_CONTROL
[10]RANGING_CONFIRM
[11-12]next-anchor-DeviceAddress(下一个基站的短地址,在次处就进行了对话转移,接下来就是标签和其他基站的对话)
消息发送完成后,基站就进入了等待模式,等待其他基发送测距数据回来,此期间,基站不会和其他标签对话。
标签收到到 [10]RANGING_CONFIRM 消息后,识别到 [11-12]next-anchor-DeviceAddress,这是下一个基站的短地址,接下来就开始对基站2进行对话,对基站2开始发送POLL消息(10字节),基站2识别到消息后,开始重复以上步骤,当基站2发送出RANGING_CONFIRM消息(13字节)时,携带的[11-12]next-anchor-DeviceAddress数据即为基站3的短地址;当基站3发送出RANGING_CONFIRM消息(13字节)时,携带的[11-12]next-anchor-DeviceAddress数据即为基站1的短地址。发送后,基站2会将所测得的数据发送给基站(12字节),其数据结构如下:
[0]DATA
[1]SHORT_SRC_AND_DEST
[2]SEQ_NUMBER--(这里变成 -- 了哦)
[3-4]NetworkID
[5-6]MainAnchorAddress(基站1的短地址)
[7-8]device_address(基站自身的短地址)
[9]RTLS_APP_ID_HIGH
[10-11]range(距离数据)
[12-13]tag_short_address(测量标签的短地址)
标签识别到 Confirm消息中 [11-12]所携带的信息变为 基站1 时,结束本轮定位对话,等待一段时间后,就会开启下一轮定位BLINK请求。
基站1根据 [9]RTLS_APP_ID_HIGH 消息得到基站2和基站3的测距信息后,经过已知数据进行计算,最会得出定位信息,结束等待模式,开启下一轮标签定位。
四、总结
这过程还算是挺严密的,不过在设计的时候要注意一点,就是开启下一轮回话的等待时长问题,各个设备之间不要发生抢占线程问题。