转换为浮点数时难以用 pyserial 解释字节流
Difficulty interpreting byte stream with pyserial when casting to floats
我正在使用 pyserial 从 USB 设备获取数据流。
当我使用他们的专有软件时,我得到了一个情节更新和滚动,看起来像:
制造商提供的信息非常有帮助sheet:
所以我编写的代码在我看来完全合乎逻辑并且应该可以工作:
import serial
import struct
device = '/dev/cu.usbserial-DM003616' #osx
ser = serial.Serial(device, 115200, bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE, rtscts=False, dsrdtr=False)
bytes = ser.read(500)
splitby = 11
for i in range(splitby):
offset = i
for i in range(len(bytes)//splitby):
datum = bytes[i*splitby+offset:(i+1)*splitby+offset]
float = datum[2:6]
float = struct.unpack('!f',float)
print(float)
鉴于上图和数据文件中的示例,我希望浮点数在 -100 到 +100 范围内是合理的。
但我完全是胡言乱语:
(5.502899069403557e-41,)
(-2.350988701644575e-38,)
(33.033477783203125,)
(1.408765983841204e-38,)
(0.0,)
(9.839021882302715e-36,)
(-1.1925119585221935e-23,)
(0.0,)
(1.9816933148201242e-21,)
(4.0218751556765575e+23,)
(0.0,)
(98320384.0,)
(-1.894110546575914e-13,)
外部循环的原因是我想看看我是否可以通过将数据偏移最多 11 个字节来理解数据(因为我想知道我是否可能在一个中间开始轮询设备11 byte section),但不管数据都是废话。
有没有人对我如何理解数据有任何建议?
长度为 500 的字节字符串示例为:
b'\xaaUA\xd4,\xc0\xbbL\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd5\x15\xed\xbbM\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd0p\x84\xbbN\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcd\xb3)\xbbO\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xc4\xb74\xbbP\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xca\x118\xbbQ\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcf(A\xbbR\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd6f\x0f\xbbS\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd5\x8f\x97\xbbT\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd0)\xb3\xbbU\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcd-\xd9\xbbV\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcf\x1f\\xbbW\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd6\xbf\xf9\xbbX\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd90\xed\xbbY\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xddp\x15\xbbZ\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd7\x91c\xbb[\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd4$\xad\xbb\\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcf\x88\xa9\xbb]\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcf\xa9\x18\xbb^\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xce\xae{\xbb_\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcc\x89+\xbb`\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd0\x08\x83\xbba\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd4\xdb\xb5\xbbb\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd95a\xbbc\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd70\xa8\xbbd\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd3#`\xbbe\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcd\xa4]\xbbf\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcd\xa0L\xbbg\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcd\xe5\xd7\xbbh\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xce\xd5\xec\xbbi\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcd;#\xbbj\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcd'
附录: 抱歉我没说清楚。它开始工作正常,但随后失败。例如,如果我使用上面的字节串,我们从好的数据开始:
(26.5218505859375,)
但这很快就变成了:
(1.408765983841204e-38,)
(0.0,)
(-16140921856.0,)
(-1.1925119585221935e-23,)
(0.0,)
(-1.0772448149509728e-05,)
(4.021874795388587e+23,)
(0.0,)
附录: 下面标记的答案很好,但在此期间我想出了我自己的使用正则表达式的解决方案,它似乎工作得很好:
class PLD():
def __init__(self, device='COM5'):
self.device = device # self.device = '/dev/cu.usbserial-DM003616' #osx
self.sample_rate = 1/0.0256
self.ser = serial.Serial(device, 115200, bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE,stopbits=serial.STOPBITS_ONE, rtscts=False, dsrdtr=False)
def get_data(self,n=500):
regexp = '\xaa.+?\xbb'
floats = []
serial_data = self.ser.read(n)
for match in re.findall(regexp, serial_data):
serial_data = match[2:6]
try:
datum = struct.unpack('!f', serial_data)[0]
floats.append(datum)
except struct.error:
floats.append(0)
return floats
这个数据看起来对吗?
by=b'\x41\xd4\x2c\xc0'
print(list(by))
print(struct.unpack('!f',by))
输出:
[65, 212, 44, 192]
(26.5218505859375,)
我查看了您的数据,根据您提供的格式信息,第一个数据包似乎编码了一个浮点数 26.521 ...
此外,他们提供的示例数据似乎可以正确解码:
by=b'\xc0\x59\x99\x99'
print(list(by))
print(struct.unpack('!f',by))
输出:
[192, 89, 153, 153]
(-3.3999998569488525,)
一般来说,您需要编写一些软件依次读取每个字节,当您找到 0xaa 字节时,还要读取接下来的 15 个字节并检查所有常量字节是否在正确的位置:([0xaa ,0x55]...[0xbb].[0,0,0,0,0,0,0x99,0x66]) 然后你可以通过 unpack()
.[=15= 传递字节 2..5 ]
问题是你依赖于永远不会丢失传输的一部分或得到任何损坏,这两个都是有缺陷的假设。
您需要为页眉字节实现一个 wait/check,理想情况下对于页脚字节也是如此:
#!python3
import struct
class FakeSerial():
def __init__(self):
self.data = b'\xaaUA\xd4,\xc0\xbbL\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd5\x15\xed\xbbM\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd0p\x84\xbbN\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcd\xb3)\xbbO\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xc4\xb74\xbbP\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xca\x118\xbbQ\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcf(A\xbbR\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd6f\x0f\xbbS\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd5\x8f\x97\xbbT\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd0)\xb3\xbbU\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcd-\xd9\xbbV\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcf\x1f\\xbbW\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd6\xbf\xf9\xbbX\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd90\xed\xbbY\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xddp\x15\xbbZ\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd7\x91c\xbb[\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd4$\xad\xbb\\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcf\x88\xa9\xbb]\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcf\xa9\x18\xbb^\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xce\xae{\xbb_\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcc\x89+\xbb`\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd0\x08\x83\xbba\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd4\xdb\xb5\xbbb\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd95a\xbbc\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd70\xa8\xbbd\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd3#`\xbbe\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcd\xa4]\xbbf\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcd\xa0L\xbbg\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcd\xe5\xd7\xbbh\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xce\xd5\xec\xbbi\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcd;#\xbbj\x00\x00\x00\x00\x00\x00\x99f'
self.index = 0
def read(self, num=1):
data = self.data[self.index : self.index+num]
self.index += num
if self.index > len(self.data):
self.index = 0
return data
s = FakeSerial()
while True:
if s.read() == b'\xaa': # wait for header 1 because of loop
if s.read() == b'\x55': # check header 1 followed immediately by header 2
data = s.read(4) # get the data
print(struct.unpack('!f',data)) # unpack it
# choose one of these methods, not both
# 1
padding = s.read(10) # this should read the tag, count, reserved and footer bytes
# 2
# this instead loops waiting for footer bytes
# while True:
# if s.read() == b'\x99': # footer 1
# if s.read() == b'\x66': # footer 2
# break
我做了一个假的连续剧class所以我可以写一个更接近你实际需要如何使用它的例子,所以你可以忽略那一点,但是请注意我从末尾删除了几个字节您的示例代码是一个不完整的数据框。
这个输出是:
(26.5218505859375,)
(26.635705947875977,)
(26.05493927001953,)
(25.712480545043945,)
(24.589454650878906,)
(25.258407592773438,)
(25.894655227661133,)
(26.799833297729492,)
(26.695112228393555,)
(26.020360946655273,)
(25.64738655090332,)
(25.89031219482422,)
(26.84373664855957,)
(27.148889541625977,)
(27.67972755432129,)
(26.94598960876465,)
(26.517908096313477,)
(25.941728591918945,)
(25.957565307617188,)
(25.835195541381836,)
(25.56697654724121,)
(26.0041561126709,)
(26.60727882385254,)
(27.151063919067383,)
(26.898757934570312,)
(26.39227294921875,)
(25.70525550842285,)
(25.703269958496094,)
(25.737226486206055,)
(25.854454040527344,)
(25.65387535095215,)
我正在使用 pyserial 从 USB 设备获取数据流。 当我使用他们的专有软件时,我得到了一个情节更新和滚动,看起来像:
制造商提供的信息非常有帮助sheet:
所以我编写的代码在我看来完全合乎逻辑并且应该可以工作:
import serial
import struct
device = '/dev/cu.usbserial-DM003616' #osx
ser = serial.Serial(device, 115200, bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE, rtscts=False, dsrdtr=False)
bytes = ser.read(500)
splitby = 11
for i in range(splitby):
offset = i
for i in range(len(bytes)//splitby):
datum = bytes[i*splitby+offset:(i+1)*splitby+offset]
float = datum[2:6]
float = struct.unpack('!f',float)
print(float)
鉴于上图和数据文件中的示例,我希望浮点数在 -100 到 +100 范围内是合理的。
但我完全是胡言乱语:
(5.502899069403557e-41,)
(-2.350988701644575e-38,)
(33.033477783203125,)
(1.408765983841204e-38,)
(0.0,)
(9.839021882302715e-36,)
(-1.1925119585221935e-23,)
(0.0,)
(1.9816933148201242e-21,)
(4.0218751556765575e+23,)
(0.0,)
(98320384.0,)
(-1.894110546575914e-13,)
外部循环的原因是我想看看我是否可以通过将数据偏移最多 11 个字节来理解数据(因为我想知道我是否可能在一个中间开始轮询设备11 byte section),但不管数据都是废话。
有没有人对我如何理解数据有任何建议?
长度为 500 的字节字符串示例为:
b'\xaaUA\xd4,\xc0\xbbL\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd5\x15\xed\xbbM\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd0p\x84\xbbN\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcd\xb3)\xbbO\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xc4\xb74\xbbP\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xca\x118\xbbQ\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcf(A\xbbR\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd6f\x0f\xbbS\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd5\x8f\x97\xbbT\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd0)\xb3\xbbU\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcd-\xd9\xbbV\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcf\x1f\\xbbW\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd6\xbf\xf9\xbbX\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd90\xed\xbbY\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xddp\x15\xbbZ\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd7\x91c\xbb[\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd4$\xad\xbb\\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcf\x88\xa9\xbb]\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcf\xa9\x18\xbb^\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xce\xae{\xbb_\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcc\x89+\xbb`\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd0\x08\x83\xbba\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd4\xdb\xb5\xbbb\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd95a\xbbc\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd70\xa8\xbbd\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd3#`\xbbe\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcd\xa4]\xbbf\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcd\xa0L\xbbg\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcd\xe5\xd7\xbbh\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xce\xd5\xec\xbbi\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcd;#\xbbj\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcd'
附录: 抱歉我没说清楚。它开始工作正常,但随后失败。例如,如果我使用上面的字节串,我们从好的数据开始:
(26.5218505859375,)
但这很快就变成了:
(1.408765983841204e-38,)
(0.0,)
(-16140921856.0,)
(-1.1925119585221935e-23,)
(0.0,)
(-1.0772448149509728e-05,)
(4.021874795388587e+23,)
(0.0,)
附录: 下面标记的答案很好,但在此期间我想出了我自己的使用正则表达式的解决方案,它似乎工作得很好:
class PLD():
def __init__(self, device='COM5'):
self.device = device # self.device = '/dev/cu.usbserial-DM003616' #osx
self.sample_rate = 1/0.0256
self.ser = serial.Serial(device, 115200, bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE,stopbits=serial.STOPBITS_ONE, rtscts=False, dsrdtr=False)
def get_data(self,n=500):
regexp = '\xaa.+?\xbb'
floats = []
serial_data = self.ser.read(n)
for match in re.findall(regexp, serial_data):
serial_data = match[2:6]
try:
datum = struct.unpack('!f', serial_data)[0]
floats.append(datum)
except struct.error:
floats.append(0)
return floats
这个数据看起来对吗?
by=b'\x41\xd4\x2c\xc0'
print(list(by))
print(struct.unpack('!f',by))
输出:
[65, 212, 44, 192]
(26.5218505859375,)
我查看了您的数据,根据您提供的格式信息,第一个数据包似乎编码了一个浮点数 26.521 ...
此外,他们提供的示例数据似乎可以正确解码:
by=b'\xc0\x59\x99\x99'
print(list(by))
print(struct.unpack('!f',by))
输出:
[192, 89, 153, 153]
(-3.3999998569488525,)
一般来说,您需要编写一些软件依次读取每个字节,当您找到 0xaa 字节时,还要读取接下来的 15 个字节并检查所有常量字节是否在正确的位置:([0xaa ,0x55]...[0xbb].[0,0,0,0,0,0,0x99,0x66]) 然后你可以通过 unpack()
.[=15= 传递字节 2..5 ]
问题是你依赖于永远不会丢失传输的一部分或得到任何损坏,这两个都是有缺陷的假设。 您需要为页眉字节实现一个 wait/check,理想情况下对于页脚字节也是如此:
#!python3
import struct
class FakeSerial():
def __init__(self):
self.data = b'\xaaUA\xd4,\xc0\xbbL\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd5\x15\xed\xbbM\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd0p\x84\xbbN\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcd\xb3)\xbbO\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xc4\xb74\xbbP\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xca\x118\xbbQ\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcf(A\xbbR\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd6f\x0f\xbbS\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd5\x8f\x97\xbbT\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd0)\xb3\xbbU\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcd-\xd9\xbbV\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcf\x1f\\xbbW\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd6\xbf\xf9\xbbX\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd90\xed\xbbY\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xddp\x15\xbbZ\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd7\x91c\xbb[\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd4$\xad\xbb\\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcf\x88\xa9\xbb]\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcf\xa9\x18\xbb^\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xce\xae{\xbb_\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcc\x89+\xbb`\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd0\x08\x83\xbba\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd4\xdb\xb5\xbbb\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd95a\xbbc\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd70\xa8\xbbd\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xd3#`\xbbe\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcd\xa4]\xbbf\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcd\xa0L\xbbg\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcd\xe5\xd7\xbbh\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xce\xd5\xec\xbbi\x00\x00\x00\x00\x00\x00\x99f\xaaUA\xcd;#\xbbj\x00\x00\x00\x00\x00\x00\x99f'
self.index = 0
def read(self, num=1):
data = self.data[self.index : self.index+num]
self.index += num
if self.index > len(self.data):
self.index = 0
return data
s = FakeSerial()
while True:
if s.read() == b'\xaa': # wait for header 1 because of loop
if s.read() == b'\x55': # check header 1 followed immediately by header 2
data = s.read(4) # get the data
print(struct.unpack('!f',data)) # unpack it
# choose one of these methods, not both
# 1
padding = s.read(10) # this should read the tag, count, reserved and footer bytes
# 2
# this instead loops waiting for footer bytes
# while True:
# if s.read() == b'\x99': # footer 1
# if s.read() == b'\x66': # footer 2
# break
我做了一个假的连续剧class所以我可以写一个更接近你实际需要如何使用它的例子,所以你可以忽略那一点,但是请注意我从末尾删除了几个字节您的示例代码是一个不完整的数据框。
这个输出是:
(26.5218505859375,)
(26.635705947875977,)
(26.05493927001953,)
(25.712480545043945,)
(24.589454650878906,)
(25.258407592773438,)
(25.894655227661133,)
(26.799833297729492,)
(26.695112228393555,)
(26.020360946655273,)
(25.64738655090332,)
(25.89031219482422,)
(26.84373664855957,)
(27.148889541625977,)
(27.67972755432129,)
(26.94598960876465,)
(26.517908096313477,)
(25.941728591918945,)
(25.957565307617188,)
(25.835195541381836,)
(25.56697654724121,)
(26.0041561126709,)
(26.60727882385254,)
(27.151063919067383,)
(26.898757934570312,)
(26.39227294921875,)
(25.70525550842285,)
(25.703269958496094,)
(25.737226486206055,)
(25.854454040527344,)
(25.65387535095215,)