在编写本文的 1 问题描述 和 2 数据分析 时,我是基于串口调试助手得到的数据进行分析的,因此产生了一个问题,就是数据格式看着不对,我自己去猜了猜。
但是当我使用 python 去操作 jetson 时,也就是写本文的 3 使用Python接收数据并筛选 时,发现提取到的数据又和资料中的格式一样了。不过为什么串口调试助手和 jetson 读取到的数据为什么格式有误,我也不太明白。
当然,这也与我的假设和猜想一致。因此,我就不再去大费周章修改本文了,望读者见谅。
1 问题描述
测试一款 UWB 定位模块,模块长下边这样。
卖家所提供的整个资料在 JY1000-SMA。同类型的还有更优惠版的型号 JY1000_BU,不过卖家说这两个并不通用,有点遗憾。
同时还有一个视频,能够教你快速配置并使用 维特智能UWB室内定位传感器模块平面坐标二维厘米级通讯距离测量。
不过遗憾的是,这次做为标签的模块,输出的数据是原始数据的,也就是16进制数据。以前的模块是直接输出测量坐标的字符串的,直接截取更方便一些。不过两种方式各有优缺吧。
接下来就借助 【Python】在 jetson 平台上,使用 python 处理串口设备 将数据提取到并进行处理得到所需要的坐标信息。
2 数据分析
首先,我利用串口调试助手,随机截取了部分数据,如下所示。
01 83 00 21 00 00 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 45 00 47 00 00 FD 43 01 46 FF D0 0E 38 FB 40
01 83 00 21 00 00 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 46 00 48 00 00 FD 43 01 46 FF D0 0E 3B 8E 96
01 83 00 21 00 00 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 4A 00 48 00 00 FD 43 01 46 FF D0 0E 3D 1F 58
01 83 00 21 00 00 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 4B 00 47 00 00 FD 43 01 46 FF D0 0E 39 2C 0E
01 83 00 21 00 00 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 50 00 47 00 00 FD 43 01 46 FF D0 0E 39 08 15
01 83 00 21 00 00 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 51 00 48 00 00 FD 43 01 46 FF D0 0E 3B BB 41
01 83 00 1D 00 00 02 00 04 00 00 00 00 00 00 00 8B 00 00 00 00 00 3F 00 9E 00 00 00 CB 00 DD 00 00 C3 06
01 83 00 21 00 00 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 50 00 49 00 00 FD 43 01 46 FF D0 0E 3D 3D 3E
01 83 00 21 00 00 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 4E 00 4A 00 00 FD 43 01 46 FF D0 0E 39 1B A7
01 83 00 21 00 00 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 4C 00 4C 00 00 FD 43 01 46 FF D0 0E 41 02 4F
01 83 00 21 00 00 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 49 00 4D 00 00 FD 43 01 46 FF D0 0E 3D 0B 57
01 83 00 21 00 00 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 49 00 4D 00 00 FD 43 01 46 FF D0 0E 3D 0B 57
01 83 00 21 00 00 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 4E 00 4D 00 00 FD 42 01 46 FF D0 0E 36 51 17
01 83 00 21 00 00 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 54 00 4D 00 00 FD 42 01 46 FF D0 0E 3D 36 4A
01 83 00 21 00 00 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 56 00 4D 00 00 FD 42 01 46 FF D0 0E 3B B1 0A
01 83 00 21 00 00 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 00 4D 00 00 FD 42 01 46 FF D0 0E 3A 75 09
01 83 00 21 00 00 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 52 00 4E 00 00 FD 42 01 46 FF D0 0E 3D 30 C8
01 83 00 1D 00 00 02 00 04 00 00 00 00 00 00 00 8B 00 00 00 00 00 3F 00 9E 00 00 00 CB 00 DD 00 00 C3 06
01 83 00 21 00 00 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 54 00 4D 00 00 FD 42 01 46 FF D0 0E 3B B6 48
01 83 00 21 00 00 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 57 00 4C 00 00 FD 41 01 46 FF D0 0E 39 05 B6
01 83 00 21 00 00 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 56 00 4C 00 00 FD 41 01 46 FF D0 0E 39 07 37
01 83 00 21 00 00 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 54 00 4C 00 00 FD 41 01 46 FF D0 0E 3B 81 B4
01 83 00 21 00 00 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 51 00 4C 00 00 FD 41 01 46 FF D0 0E 3C CC 73
01 83 00 21 00 00 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 4E 00 4D 00 00 FD 41 01 46 FF D0 0E 3C E2 10
根据资料中提供的 UWB 室内定位协议 对数据进行分析归类。
按照所查到资料的信息包,初步分析其中一条数据,有
01 83 00 21 00 00 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 45 00 47 00 00 FD 43 01 46 FF D0 0E 38 FB 40
按照数据表格再重新做一份表格,有
协议格式 | | 实测数据 | 猜测含义 |
---|
ModbusID | 0x01~0x7F | 01 | | 读写功能码 | 0x03 | 83 | | 数据长度(高位 / 低位) | xx / xx | 00 21 | 等于十进制的33 | 标定模式(高位 / 低位) | 0x00 / 0x00 | 00 00 | | 数据包 | 0x01 | 01 | | 标签数量(高位 / 低位) | 0x00 / 0x01~0x32 | 00 02 | | | | | | 标签X轴坐标(高位 / 低位) | xx / xx | 00 00 | | 标签Y轴坐标(高位 / 低位) | xx / xx | 00 00 | | 标签Z轴坐标(高位 / 低位) | xx / xx | 00 00 | | 标签角度X(高位 / 低位) | xx / xx | 00 00 | | 标签角度Y(高位 / 低位) | xx / xx | 00 00 | | 标签角度Z(高位 / 低位) | xx / xx | 00 00 | | 标签温度(高位 / 低位) | xx / xx | 00 00 | | | | | | … | 根据标签数量决定,14*标签数量,例如5 个标签,则是 14*5=70 个字节 | 00 45 00 47 00 00 FD 43 01 46 FF D0 0E 38 | | | | | | CRCH / CRCL | xx / xx | FB 40 | |
其中 CRC 即循环冗余校验码(Cyclic Redundancy Check):数据通信领域中最常用的一种差错校验码,其信息字段和校验字段长度可以任意指定,但要求通信双方定义的CRC标准一致。更多解释可参考 CRC原理简介。
根据所给的标签信息包格式,把实测的数据放进去,感觉有点迷糊。
思想想后,自己做如下猜测: 数据 … 这一栏应该才是需要用到的数据,也就是说
- 00 45 表示标签 X 轴坐标,转换为10进制等于 69,单位为 cm
- 00 47 表示标签 Y 轴坐标,转换为10进制等于 71,单位为 cm
- 00 00 表示标签 Z 轴坐标,卖家说模块功能并不支持测量,因此没有数据
- FD 43 表示标签绕 X 轴角度不懂
- 01 46 表示标签绕 Y 轴角度不懂
- FF D0 表示标签绕 Z 轴角度不懂
- 0E 38 表示温度,转换为10进制等于 3640,单位为摄氏度
?
0.01
*0.01
?0.01
°
C
^\circ \text{C}
°C 也合理
- 剩下的角度,我暂时不需要用,因此先忽略了,而且我也没看明白对应关系
同时还需要注意的是,程序指令会存在丢包的情况,也就是会产生异常数据,比如说下边两条数据
01 83 00 21 00 00 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 51 00 48 00 00 FD 43 01 46 FF D0 0E 3B BB 41
01 83 00 1D 00 00 02 00 04 00 00 00 00 00 00 00 8B 00 00 00 00 00 3F 00 9E 00 00 00 CB 00 DD 00 00 C3 06
其中稍微短一些的就是异常数据。通过分析发现,异常数据的第4位,也就是数据长度这里是不同的(或者说第3位和第4位,因为这表示同一个含义)。
因此,我们可以增加一个删选条件在数据的第4位这里,如果等于21那么数据可用,否则直接舍弃。
3 使用Python接收数据并筛选
这一步的目标就很明确了,接收16进制数据,排除异常,提取有效,转换合适进制。 这里我们使用了 sleep() 函数和 inwaiting() ,二者最好配对使用,不然返回的待读取数据可能等于0。
3.1 接收16进制数据
通过查询别人的经验,包括 Python3 串口接收与发送16进制数据包,及字符串 和 Python的学习之旅:1.串口16进制发送数据与接收数据,决定采用如下指令来接收16进制的数据。
data_UWB = str(binascii.b2a_hex(ser_UWB.read(wait_Read_UWB_Data_Len)))[2:-1]
3.2 排除异常
上一节已经分析过异常的情况了,我们这里直接使用查找关键字符串的方式来处理。参考经验为 Python判断字符串是否包含特定子串的7种方法。
实现思路为
- 首先多接收几条数据,调节程序指令
time.sleep(0.2) 这样每次接收两条数据包。 - 选择关键字符串为
01830021 ,然后查找这一串字符串在接收到数据的位置。 - 如果没有异常,那么返回的索引值应该为0,否则返回字符串所在正确位置。采用这种方式有两个好处,一可以忽略掉无效异常数据,二可以直接查找到有效数据的起始位置。
idx = data_UWB.find("01830021")
3.3 提取有效
拿到有效数据后,并且明确了起始位置。根据之前的数据解析过程,我们知道标签的 X 轴位置是有效数据的第 18~21 位。
数据无异常时可使用
data_UWB[18:22]
data_UWB[22:26]
数据有异常时可使用
data_UWB[idx+18:idx+22]
data_UWB[idx+22:idx+26]
其实第二个指令就能直接解决所有问题,不过为了方便理解实现思路,这里我们还是分开写吧。
3.4 转换进制
由于提取到的数据仍然是16进制,我们使用这个方法 Python 二进制,十进制,十六进制转换 转换到10进制。
pos_X = int(data_UWB[idx+18:idx+22], 16)
pos_Y = int(data_UWB[idx+22:idx+26], 16)
为了更好的显示结果,我们借助了这个教程 python同时输出字符和数字。
print(f"pos_X: {pos_X}cm, pos_Y: {pos_Y}cm")
至此,实现了对 UWB 定位模块的输出数据的处理。
整合上述过程的示例程序和输出结果如下
import serial
import binascii
import time
ser_UWB = serial.Serial(port="/dev/ttyUSB1", baudrate=115200, timeout=1)
ser_UWB.reset_input_buffer()
time.sleep(0.2)
wait_Read_UWB_Data_Len = ser_UWB.inWaiting()
if wait_Read_UWB_Data_Len:
try:
data_UWB = str(binascii.b2a_hex(ser_UWB.read(wait_Read_UWB_Data_Len)))[2:-1]
except:
str = ser_UWB.read(wait_Read_UWB_Data_Len)
idx = data_UWB.find("01830021")
if idx == 0:
pos_X = int(data_UWB[18:22], 16)
pos_Y = int(data_UWB[22:26], 16)
else:
pos_X = int(data_UWB[idx+18:idx+22], 16)
pos_Y = int(data_UWB[idx+22:idx+26], 16)
print(f"pos_X: {pos_X}cm, pos_Y: {pos_Y}cm")
ser_UWB.close()
pos_X: 98cm, pos_Y: 113cm
|