优化crc速度

This commit is contained in:
cyy_mac
2026-07-26 20:51:57 +08:00
parent cd9f8e204d
commit 265173e746
3 changed files with 60 additions and 12 deletions

View File

@@ -50,9 +50,12 @@ def build_low_cmd_plain(lowcmd: LowCmd) -> bytes:
# 20..22 bandWidth BE # 20..22 bandWidth BE
cmd[20:22] = struct.pack('>H', lowcmd.bandWidth) cmd[20:22] = struct.pack('>H', lowcmd.bandWidth)
# 22..562 motorCmd[20] × 27B # 22..562 motorCmd[20] × 27B
motor_bytes = b''.join(m.to_bytes() for m in lowcmd.motorCmd) offset = 22
assert len(motor_bytes) == 540, f'motorCmd 序列化长度错: {len(motor_bytes)}' for motor in lowcmd.motorCmd:
cmd[22:562] = motor_bytes motor_bytes = motor.to_bytes()
assert len(motor_bytes) == 27, f'motorCmd 序列化长度错: {len(motor_bytes)}'
cmd[offset:offset + 27] = motor_bytes
offset += 27
# 562..566 bms (默认 0) # 562..566 bms (默认 0)
# 566..606 wirelessRemote # 566..606 wirelessRemote
cmd[566:606] = lowcmd.wirelessRemote cmd[566:606] = lowcmd.wirelessRemote
@@ -60,7 +63,7 @@ def build_low_cmd_plain(lowcmd: LowCmd) -> bytes:
cmd[606:610] = lowcmd.reserve cmd[606:610] = lowcmd.reserve
# 610..612 固定填充 0x0000 # 610..612 固定填充 0x0000
# 612..616 CRC # 612..616 CRC
cmd[612:616] = gen_crc(bytes(cmd[:612])) cmd[612:616] = gen_crc(memoryview(cmd)[:612])
return bytes(cmd) return bytes(cmd)

View File

@@ -15,20 +15,38 @@ import binascii
# ===== CRC32 (用于 LowCmd 末尾 4 字节, PRO 不 XOR, EDU 才 XOR encryptCrc) ===== # ===== CRC32 (用于 LowCmd 末尾 4 字节, PRO 不 XOR, EDU 才 XOR encryptCrc) =====
_CRC32_POLY = 0x04c11db7
def _make_crc32_table():
table = []
for byte in range(256):
crc = byte << 24
for _ in range(8):
if crc & 0x80000000:
crc = ((crc << 1) ^ _CRC32_POLY) & 0xFFFFFFFF
else:
crc = (crc << 1) & 0xFFFFFFFF
table.append(crc)
return tuple(table)
_CRC32_TABLE = _make_crc32_table()
_CRC_PACK = struct.Struct('<I').pack
def gen_crc(data: bytes) -> bytes: def gen_crc(data: bytes) -> bytes:
"""CRC-32 with polynomial 0x04c11db7 (custom 实现, 跟 Unitree MCU 对齐). """CRC-32 with polynomial 0x04c11db7 (custom 实现, 跟 Unitree MCU 对齐).
跟 Python zlib.crc32 不同 (zlib 用反射 + 0xEDB88320), 所以这个手动实现保留. 跟 Python zlib.crc32 不同 (zlib 用反射 + 0xEDB88320), 所以这个手动实现保留.
""" """
table = _CRC32_TABLE
crc = 0xFFFFFFFF crc = 0xFFFFFFFF
for j in struct.unpack("<%dI" % (len(data) // 4), data): for (word,) in struct.iter_unpack('<I', data):
for b in range(32): crc = ((crc << 8) & 0xFFFFFFFF) ^ table[((crc >> 24) ^ ((word >> 24) & 0xff)) & 0xff]
x = (crc >> 31) & 1 crc = ((crc << 8) & 0xFFFFFFFF) ^ table[((crc >> 24) ^ ((word >> 16) & 0xff)) & 0xff]
crc <<= 1 crc = ((crc << 8) & 0xFFFFFFFF) ^ table[((crc >> 24) ^ ((word >> 8) & 0xff)) & 0xff]
crc &= 0xFFFFFFFF crc = ((crc << 8) & 0xFFFFFFFF) ^ table[((crc >> 24) ^ (word & 0xff)) & 0xff]
if x ^ (1 & (j >> (31 - b))): return _CRC_PACK(crc)
crc ^= 0x04c11db7
return struct.pack('<I', crc)
# ===== float ↔ hex 字段 (Unitree 用 big-endian float 但小端整数读) ===== # ===== float ↔ hex 字段 (Unitree 用 big-endian float 但小端整数读) =====

27
tests/test_common.py Normal file
View File

@@ -0,0 +1,27 @@
"""Common codec utility tests."""
import struct
from go1_pro_sdk.utils.common import gen_crc
def _gen_crc_bitwise(data: bytes) -> bytes:
crc = 0xFFFFFFFF
for (word,) in struct.iter_unpack("<I", data):
for bit in range(32):
x = (crc >> 31) & 1
crc <<= 1
crc &= 0xFFFFFFFF
if x ^ (1 & (word >> (31 - bit))):
crc ^= 0x04c11db7
return struct.pack("<I", crc)
def test_gen_crc_matches_reference_bitwise():
samples = [
b"",
bytes(4),
bytes(range(64)),
b"\xfe\xef\xff\x00" + bytes(range(252)) * 2,
]
for sample in samples:
assert gen_crc(sample) == _gen_crc_bitwise(sample)