我通过以太网连接(UDP协议)从PC到传感器之间的连接向我发送了数据。 数据看起来是一个长字节数组,像这样
data
Out[260]: b'03000248023003e802a003a002f8044003c80478038802f002d8024002b00258030003a80300035002a803c0031002e802c8030802e001f8029002a003c8045002d803f003100378038002a002d803700308029003e00260032002e0027002c0028002a802e80338036804c803300398'
此数据实际上是经过编码的,我想从中生成由有符号整数组成的numpy.ndarray。每个ndarray元素一个数据样本。 解码后的每个数组元素代表一个ADC数据样本:12位+符号整数。 编码如下:字节必须按4分组。每个字节实际上代表一个十六进制数。然后将4个十六进制数字放在一起,除以8,我们得到2的补码。
我到目前为止使用的代码工作正常,如下所示:
j = 0
while j < nb_sample: # loop on each adc data sample
adc_sample_str = chr(udp_frame[4*j]) + chr(udp_frame[4*j+1]) + chr(udp_frame[4*j+2]) + chr(udp_frame[4*j+3]) # create the sample by concatenating 4 hexadecimals characters (2 bytes)
adc_sample_int = int(adc_sample_str, 16) # convert into hexa number
adc_sample_int = (adc_sample_int >> 3) # right shift by 3 (divide by 8)
if adc_sample_int > 2047: # check if it is a negative value (2048 = 0b 0000 1000 0000 0000)
adc_sample_int = (adc_sample_int - (2*4096) + 1) # 2's complement
result[j] = adc_sample_int
j+=1
最大的问题是此循环超级慢。
所以我正在寻找另一种更快的方法(〜10倍) 因此,我尝试了很多事情:使用.decode('UTF-8')转换为字符串或使用具有许多不同dtypes的numpy.frombuffer。 但是我找不到可以读取我奇怪格式的编码(dtype)。
有人知道我应该朝哪个方向看吗? 也许我应该为.decode编写定制编码方案?但是我不知道如何表达我的编码方案。 还是我宁愿转换为字符串?但是之后 ...? 到目前为止,我所做的一切都使我陷入困境。 任何提示都会帮助我... 谢谢
循环代码的结果如下:
result[0:260]
Out[268]:
array([ 96, 73, 70, 125, 84, 116, 95, 136, 121, 143, 113, 94, 91,
72, 86, 75, 96, 117, 96, 106, 85, 120, 98, 93, 89, 97,
92, 63, 82, 84, 121, 138, 91, 126, 98, 111, 112, 84, 91,
110, 97, 82, 124, 76, 100, 92, 78, 88, 80, 85, 93, 103,
109, 153, 102, 115, 89, 134, 105, 108, 84, 100, 76, 101, 81,
96, 98, 106, 98, 116, 109, 98, 93, 118, 111, 94, 95, 98,
91, 141, 76, 97, 110, 92, 104, 103, 89, 86, 101, 85, 114,
82, 83, 104, 72, 103, 118, 92, 133, 111, 104, 85, 101, 92,
108, 108, 108, 100, 81, 102, 99, 102, 125, 121, 68, 75, 104,
85, 90, 96, 127, 102, 112, 118, 106, 92, 78, 98, 98, 96,
105, 77, 79, 107, 100, 88, 89, 115, 86, 98, 106, 100, 105,
79, 121, 109, 115, 80, 113, 84, 131, 91, 114, 126, 93, 95,
119, 73, 100, 121, 102, 98, 100, 117, 111, 63, 99, 97, 108,
109, 95, 75, 102, 93, 127, 112, 91, 86, 79, 68, 104, 104,
84, 116, 85, 79, 120, 95, 91, 75, 135, 116, 115, 119, 102,
90, 131, 57, 102, 86, 104, 99, 106, 97, 95, 116, 116, 123,
99, 87, 61, 105, 81, 104, 91, 108, 114, 82, 122, 84, 108,
107, 93, 101, 95, 76, 84, 74, 104, 113, 110, 104, 123, 91,
99, 120, 92, 107, 120, 97, 119, 76, 87, 118, 73, 85, 113,
104, 123, 99, 94, 101, 97, 103, 65, 103])
答案 0 :(得分:0)
您没有提供完整的最小可重现性问题,因此我临时改善了您提供的代码的工作。
data = b"03000248023003e802a003a002f8044003c80478038802f002d8024002b00258030003a80300035002a803c0031002e802c8030802e001f8029002a003c8045002d803f003100378038002a002d803700308029003e00260032002e0027002c0028002a802e80338036804c803300398"
from numpy import array
import numpy as np
def func(data):
nb_sample = len(data) // 4
udp_frame = data
result = array(range(nb_sample))
j = 0
while j < nb_sample: # loop on each adc data sample
adc_sample_str = (
chr(udp_frame[4 * j])
+ chr(udp_frame[4 * j + 1])
+ chr(udp_frame[4 * j + 2])
+ chr(udp_frame[4 * j + 3])
) # create the sample by concatenating 4 hexadecimals characters (2 bytes)
adc_sample_int = int(adc_sample_str, 16) # convert into hexa number
adc_sample_int = adc_sample_int >> 3 # right shift by 3 (divide by 8)
if (
adc_sample_int > 2047
): # check if it is a negative value (2048 = 0b 0000 1000 0000 0000)
adc_sample_int = adc_sample_int - (2 * 4096) + 1 # 2's complement
result[j] = adc_sample_int
j += 1
return result
def func4(data):
for j in range(len(data) // 4):
adc_sample = int(data[4 * j : 4 * j + 4], 16) >> 3
if adc_sample > 2047:
adc_sample -= 2 * 2096 + 1
yield adc_sample
def func5(data):
result = np.fromiter(func4(data), int)
return result
这些是我通过timeit模块得到的结果
>>> import timeit
>>> from functools import partial
>>> def avg(seq): return sum(seq) / len(seq)
...
>>> avg(timeit.repeat(partial(func, data), number=10_000))
0.9369430501999887
>>> avg(timeit.repeat(partial(func5, data), number=10_000))
0.3753632108000602
所以我对numpy有点陌生。在我的原始代码中有一个错误,
要从生成器创建numpy数组,应使用np.fromiter
而不是np.array
我在测试中遇到问题,导致我没有发现无效的func5。
修复代码会导致性能急剧下降, 现在, func5的速度仅是func的两倍,我认为大部分性能提升来自内部循环的重组。 例如使用data [j:j + 4]代替data [j] + data [j + 1] + data [j + 2] + data [j + 3]
我也误解了何时使用发电机, 生成器更多的是关于空间性能,而不是时间性能。 对于您的应用程序来说可能仍然是一个好主意,但这超出了此问题的范围