十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Modbus数据字节序处理:高低字节、浮点数据转换、数据校准

Modbus数据字节序处理:高低字节、浮点数据转换、数据校准 11-Modbus数据字节序处理高低字节、浮点数据转换、数据校准写 Modbus 程序十个里面有八个被字节序坑过。不是读了半天数据是乱码就是解析出来的温度 -327 度。真不是设备坏了是你字节序没对齐。这篇把字节序这层窗户纸捅破从 16 位高低字节到 32 位浮点的四种排列方式配上 Python 和 Java 的实操代码最后再讲数据校准一条龙给你讲明白。一、先搞懂字节序到底是啥字节序Byte Order就是多字节数据在内存/传输中按什么顺序排。计算机有大端Big-Endian和小端Little-Endian之分大端高位字节在前。数字0x1234存储为12 34小端低位字节在前。数字0x1234存储为34 12。Modbus 协议规定用大端Big-Endian也叫网络字节序。所以一个 16 位寄存器值0x1234在 RTU 报文里就是按高字节0x12在前、低字节0x34在后的顺序发送的。寄存器 0x1234 RTU数据字段: 12 34 - 高字节在前(大端)新手误区01 03 02 12 34 B9 B2这个报文0x12是温度的高字节0x34是低字节拼起来是0x1234 4660。千万别把它当成两个独立字节分别去算——那就会得到 12 和 34 两个温度。二、16 位数据的组合与拆解读取时要把两个字节拼成一个数hi,lo0x12,0x34value(hi8)|lo# 大端组合print(hex(value))# 0x1234写成通用函数支持大端/小端两种有些非标设备会反着发defcombine16(hi,lo,big_endianTrue):return(hi8)|loifbig_endianelse(lo8)|hi写入时反过来拆开defsplit16(value,big_endianTrue):ifbig_endian:return(value8)0xFF,value0xFF# 返回(hi, lo)returnvalue0xFF,(value8)0xFF三、32 位数据跨两个寄存器AB/CD 还是 CD/AB当物理量超过 16 位比如光照 200000 Lux、大流量计累积量、CO2 高精度型号厂商就用两个寄存器拼一个 32 位整数。但哪个寄存器在前就乱了套常见两种AB/CD高字在前寄存器0 是高 16 位寄存器1 是低 16 位。符合 Modbus 大端规范最标准CD/AB低字在前寄存器0 是低 16 位寄存器1 是高 16 位。很多国产设备图省事就这么干。解析代码必须兼容两种defcombine32(reg0,reg1,high_word_firstTrue):ifhigh_word_first:return(reg016)|reg1# AB/CDreturn(reg116)|reg0# CD/AB实际项目里的痛苦在于同一条总线上传感器 A 用 AB/CD传感器 B 用 CD/AB。所以点位表里必须给每个点标注字节序解析按点位配置走不能写死。四、IEEE754 浮点数在 Modbus 中的传输温度、EC、PH 这些带小数的高精度量很多高端传感器直接传32 位 IEEE754 浮点。一个浮点数占 4 字节 2 个寄存器。回顾一下 IEEE754 单精度结构SEEEEEEE EMMMMMMM MMMMMMMM MMMMMMMM 1位符号 8位阶码 23位尾数比如25.5℃其二进制表示0x41CC0000验证(10.59375)×2^425.5。Modbus 传输时就是把这 4 个字节分成两半放进两个寄存器。坑就在这4 个字节在两个寄存器里怎么排共有四种排列方式排列方式寄存器0(高字)寄存器1(低字)字节流ABCDA BC D最标准高字在前CDABC DA B低字在前BADCB AD C高字在前字节交换DCBAD CB A低字在前字节交换A最高字节符号位阶码高位D最低字节。国内国产传感器CDAB 最常见欧洲大牌多用 ABCD还有一些把每个寄存器的字节内再交换BADC/DCBA。五、Python struct 处理字节序Pythonstruct模块是处理二进制字节序的神器格式字符表示大端importstruct# 假设读回两个寄存器: reg00x41CC, reg10x0000reg0,reg10x41CC,0x0000# 1) ABCD: 高字在前datastruct.pack(HH,reg0,reg1)# b\x41\xCC\x00\x00value_abcdstruct.unpack(f,data)[0]# 25.5# 2) CDAB: 低字在前国内设备常见datastruct.pack(HH,reg1,reg0)# b\x00\x00\x41\xCCvalue_cdabstruct.unpack(f,data)[0]# 25.5print(value_abcd,value_cdab)# 都是 25.5# 3) 字节序配置驱动解析推荐做法defparse_float_regs(regs,byte_orderABCD):regs: [reg0, reg1]byte_order: ABCD/CDAB/BADC/DCBAifbyte_orderABCD:rawstruct.pack(HH,*regs)elifbyte_orderCDAB:rawstruct.pack(HH,regs[1],regs[0])elifbyte_orderBADC:rawstruct.pack(HH,swap16(regs[0]),swap16(regs[1]))elifbyte_orderDCBA:rawstruct.pack(HH,swap16(regs[1]),swap16(regs[0]))returnstruct.unpack(f,raw)[0]defswap16(v):return((v0xFF)8)|(v8)六、Java ByteBuffer 处理字节序Java 侧用ByteBuffer的order()方法切换大小端importjava.nio.ByteBuffer;importjava.nio.ByteOrder;publicclassModbusFloat{// 两个寄存器拼 float支持四种字节序publicstaticfloatregsToFloat(intreg0,intreg1,Stringorder){ByteBufferbufByteBuffer.allocate(4);buf.order(ByteOrder.BIG_ENDIAN);switch(order){caseABCD:buf.putShort((short)reg0).putShort((short)reg1);break;caseCDAB:buf.putShort((short)reg1).putShort((short)reg0);break;caseBADC:buf.putShort((short)swap16(reg0)).putShort((short)swap16(reg1));break;caseDCBA:buf.putShort((short)swap16(reg1)).putShort((short)swap16(reg0));break;}buf.flip();returnbuf.getFloat();// ByteBuffer默认就是大端}privatestaticintswap16(intv){return((v0xFF)8)|((v8)0xFF);}publicstaticvoidmain(String[]args){// 某传感器回读 reg00x41CC, reg10x0000System.out.println(regsToFloat(0x41CC,0x0000,ABCD));// 25.5}}经验排查浮点乱码时先把 4 种字节序各解一遍看哪个落在合理物理范围。正常值是 25.5乱序解出来往往是天文数字几千万或极小的次正规数。用合理性筛选可以自动确定字节序我在调试现场就是这么干的。七、数据校准零点、满量程、线性拟合传感器出厂标定过了但现场探头老化、线缆衰减、环境漂移读数会偏。Modbus 读回来的是原始值能不能直接用取决于校准。1. 零点偏移Offset读数整体偏移一个固定值比如温度读数整体高 0.5℃calibratedraw_value-0.52. 满量程系数Gain线性缩放比如 EC 传感器读数偏高 5%calibratedraw_value*0.953. 线性两点校准推荐工程上最靠谱的是两点线性校准取标准源的两个点比如用标准温度计测 10℃ 和 30℃记录传感器读数为 R1、R2得直线真值 K × 读数 B K (V2 - V1) / (R2 - R1) # 斜率 B V1 - K × R1 # 截距deflinear_calibrate(raw,k,b):returnk*rawb# 例标准25℃时传感器读24.3标准45℃时读44.1k(45-25)/(44.1-24.3)# 1.0101b25-k*24.3# 0.4545print(linear_calibrate(30.0,k,b))# 30.76℃两点校准公式在 Python 里一行就能算配合numpy.polyfit([R1,R2],[V1,V2],1)也是同款效果。4. 什么时候做校准新探头首次安装零点偏移校准换线缆、换继电器模块后满量程校准定期如每月与标准设备比对偏差超过阈值如温度 0.5℃、PH 0.3就做两点校准注意区分校准和单位换算缩放系数除回来是单位换算K/B 修正是设备误差补偿两者串联工程值 K × (原始值×缩放系数) B。八、实战案例解析某品牌温湿度传感器 32 位浮点值现场一例某国产大棚温湿度传感器文档说温度寄存器0x0000~0x0001IEEE754 浮点CDAB 序湿度0x0002~0x0003同上。轮询读回原始寄存器温度寄存器: reg00x0000, reg10x41CC用我们的parse_float_regs试 4 种字节序regs[0x0000,0x41CC]fororderin[ABCD,CDAB,BADC,DCBA]:vparse_float_regs(regs,order)print(f{order}:{v})输出ABCD: 1.0e-41 - 次正规数不可能 CDAB: 25.5 - 合理室温 BADC: 4.6e-41 - 不可能 DCBA: 4.6e-41 - 不可能一秒钟锁定CDAB25.5℃ 到手。这方法比对着文档猜半天高效多了强烈安利。九、小结字节序处理就三个要点16 位默认大端、32 位整数看清 AB/CD 还是 CD/AB、32 位浮点试出 ABCD/CDAB/BADC/DCBA 四种之一。把字节序做成点位配置项解析引擎统一处理以后接新设备就是填个配置的事。再配合零点/满量程/两点线性校准原始值到工程量的链路就完全打通了。
返回列表