使用PySerial读取RS485数据时的一致性问题(数据丢失与序列异常)排查求助
我看你首次用Python结合PySerial做RS485开发,读取BLDC电机驱动器转速时遇到了数据丢失、序列异常的问题,先把你的代码和输出整理出来,再一步步分析可能的原因:
你的RS485读取函数
from threading import Thread from time import sleep def v_read_left(): b = 0 packet = bytearray() packet.append(0x01) # packet.append(0x03) packet.append(0x20) packet.append(0xab) packet.append(0x00) packet.append(0x02) a = crc_find(packet) packet += a ser.flush() while True: ser.write(bytes(packet)) # write request to motor driver sleep(0.1) # for frequency control b = ser.read(9) # read RS485 received #print(b) if len(b) > 0: print(b) thread = Thread(target = v_read_left, args = ( )) thread.start() thread.join()
当前输出结果
b'\x00fj\x06\x01\x03\x04\x00c' b'\x00a\xcb\xc5\x01\x03\x04\x00h' b'\x00b\xfa\x06\x01\x03\x04\x00`' b'\x00`\xfa\x05\x01\x03\x04\x00^' b'\x00c\xdb\xc8\x01\x03\x04\x00l' b'\x00e\xfa\x05\x01\x03\x04\x00b' b'\x00_\x1b\xd5\x01\x03\x04\x00]' b'\x00e\xab\xca\x01\x03\x04\x00o' b'\x00m\x0b\xc3\x01\x03\x04\x00h' b'\x00_;\xd7\x01\x03\x04\x00`'
结合你提供的BLDC电机驱动器RS485指令参考文档,我梳理了几个核心排查方向:
1. 串口基础配置是否匹配
首先要确认你的ser对象初始化时的参数(波特率、奇偶校验、停止位、数据位)和驱动器文档完全一致。比如如果驱动器用的是9600波特率、无校验、1停止位、8数据位,你需要这样初始化:
import serial ser = serial.Serial( port='COM3', # 替换成你的串口端口 baudrate=9600, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_1, bytesize=serial.EIGHTBITS, timeout=0.1 # 建议设置合理的超时时间,避免阻塞 )
如果参数不匹配,大概率会出现乱码、数据错位的情况,就是你现在看到的开头有随机字节的问题。
2. RS485半双工收发控制是否缺失
RS485是半双工通信,很多模块需要手动控制DE/RE引脚来切换收发模式:
- 发送数据前,要把DE/RE置为高电平(使能发送)
- 发送完成后,延迟一小段时间(确保数据完全发送),再把DE/RE置为低电平(切换到接收模式)
如果没有这个控制,发送和接收会冲突,导致驱动器返回的数据被干扰,出现异常序列。你可以用GPIO库(比如RPi.GPIO)来控制引脚,示例逻辑:
import RPi.GPIO as GPIO # 初始化引脚 GPIO.setmode(GPIO.BCM) GPIO.setup(18, GPIO.OUT) def v_read_left(): # ... 之前的packet构建代码 ... while True: # 切换到发送模式 GPIO.output(18, GPIO.HIGH) ser.write(bytes(packet)) ser.flush() # 确保数据发送完成 # 延迟切换回接收,时间根据波特率调整,比如9600波特率延迟0.01秒足够 sleep(0.01) GPIO.output(18, GPIO.LOW) sleep(0.1) b = ser.read(9) if len(b) > 0: print(b)
3. CRC校验计算是否正确
从你注释掉的packet.append(0x03)和输出里的0x03功能码来看,这应该是Modbus RTU协议。你需要确认crc_find函数是否正确实现了Modbus CRC16算法。
比如你构建的请求包(未加CRC)是[0x01, 0x20, 0xab, 0x00, 0x02],用标准Modbus CRC16计算的结果应该是特定的十六进制值,你可以把crc_find的结果打印出来对比,或者用在线工具验证。如果CRC错误,驱动器不会返回正确的响应,甚至会返回错误帧。
4. 接收逻辑的合理性问题
你当前用ser.read(9)固定读取9个字节,但如果驱动器返回的帧长度不是9,或者串口超时设置不合理,就会读到不完整的帧或者残留的旧数据:
- 建议设置
ser.timeout=0.1,这样读取时如果超时会返回已读到的字节,不会无限阻塞 - 可以先读取1字节判断是否是驱动器的地址(比如0x01),再读取后续的功能码、数据长度等,按需读取剩余字节,而不是固定长度
- 可以在读取前调用
ser.flushInput()清空接收缓冲区,避免残留之前的错误数据
5. 线程与时序问题
你的线程用了thread.join(),这会导致主线程一直阻塞,而且死循环里的sleep(0.1)是否匹配驱动器的响应频率?如果驱动器响应较慢,可能还没返回数据就开始下一次发送,导致接收缓冲区堆积错误数据。可以调整sleep时间,或者在发送后等待响应,而不是固定间隔发送。
快速验证步骤
- 先用串口助手(比如SSCOM)发送你构建的请求包(带正确CRC),看驱动器返回的数据包是否正常,如果正常,说明硬件和协议没问题,问题出在代码上
- 检查串口初始化参数和DE/RE控制逻辑
- 验证CRC计算的正确性
内容的提问来源于stack exchange,提问作者Maharshi Oza

