如何使用Python将16位十六进制数值正确转换为十进制值
Python解码CAN总线16位电池电压值方法
之前解码结果不符合逻辑,基本是踩了两个最常见的坑:一是字节序搞反,二是直接把十六进制转出来的裸整数当成实际电压值,漏掉了CAN信号必备的缩放、偏移转换。
核心解码逻辑
CAN传输的16位模拟量解码固定分两步:
- 按照传输字节序把十六进制字符串拼接为正确的原始整数值
- 按照信号定义的分辨率(缩放系数)、偏移量做线性转换,得到实际物理电压值
你这个场景明确是高字节先发、低字节后发,对应大端(Motorola格式)字节序,不需要调换两个字节的顺序。
可直接运行的代码
def decode_batt_voltage(hex_str: str) -> float: # 清洗输入:去掉多余空格、0x前缀,统一转大写 hex_clean = hex_str.strip().removeprefix("0x").upper() # 大端序转16位无符号原始整数,如果信号定义为有符号值,把signed改成True raw_val = int.from_bytes( bytes.fromhex(hex_clean), byteorder="big", signed=False ) # 线性转换:物理值 = 原始值 * 分辨率 + 偏移量 # 以下系数根据你给出的A8F5≈13V反推,拿到对应ECU的DBC信号定义后替换为官方值即可 resolution = 0.0003006 # 单位:V/bit offset = 0 return round(raw_val * resolution + offset, 2) # 测试你给出的示例值 print(decode_batt_voltage("A8F5")) # 输出约13.0
如果需要从文本文件批量读取数值解码,用下面的逻辑即可:
# 假设文本文件中每行存一个十六进制电压值 with open("batt_hex_log.txt", "r", encoding="utf-8") as f: for line in f: cur_hex = line.strip() if not cur_hex: continue cur_voltage = decode_batt_voltage(cur_hex) print(f"原始值:{cur_hex},电池电压:{cur_voltage}V")
常见踩坑说明
- 不要乱套小端序解析逻辑:网上很多简易CAN解码示例默认用小端(Intel格式),会把低字节当高字节计算,出来的原始值完全错误,你这个场景必须指定
byteorder="big"。 - 不要直接把裸整数当电压:所有CAN模拟量都不是裸值直接对应物理单位,必须乘分辨率加偏移。常见车载12V电池电压信号的分辨率多为0.001V/bit、0.01V/bit,偏移量多为0或-40V,最准确的参数以对应车型的DBC文件定义为准,你也可以多采集几组“十六进制值-实测电压”的配对数据,校准出准确的分辨率和偏移参数。
- 注意符号位:如果该信号定义为有符号16位整数,转原始值时必须把
signed参数设为True,否则负电压区间的数值会被解析成异常大的正数。
内容的提问来源于stack exchange,提问作者Tom
相关产品推荐
相关产品推荐

