You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用PySerial/PyVisa经RS232连接McPherson 747设备通信报错问题

问题背景
  • 对接设备:McPherson 747滤光轮控制器,连接方式为RS232转USB
  • 通信逻辑:读请求需要先发查询指令+报文头,设备返回ACK应答后才会返回业务数据
  • 协议规则:全ASCII格式交互,3字节查询指令十六进制为4E2105(对应N!<ENQ>),应答指令十六进制为4E2106(对应N!<ACK>),报文头根据读写目标不同由多组十六进制ASCII编码拼接组成
  • 现存问题:发送查询指令始终报通信错误,不确定查询指令和报文头发送方式,PySerial、PyVisa实现均报错
第一步:先解决端口找不到的基础错误

你现在贴出的报错和指令逻辑没有关系,是最基础的端口配置错误:

  • PySerial硬编码COM1、PyVisa硬编码ASRL1::INSTR是完全错误的,RS232转USB线接入系统后会动态分配串口号,不会固定为COM1。Windows下去设备管理器的「端口(COM和LPT)」分类下找对应USB转串口设备的COM编号;Linux下查看/dev/ttyUSB*设备名即可。
  • PyVisa代码里虽然调用了rm.list_resources(),但没有打印返回结果就直接硬写资源地址,先把这个方法的返回值打印出来,选中对应串口的资源地址再连接。
第二步:修正现有代码的逻辑错误

PySerial代码问题

  1. 初始化serial.Serial()对象时默认就会打开端口,你后面先ser.close()再ser.open()属于多余操作,反而可能触发端口占用错误
  2. ser.read_until(size=None)用法错误,这个方法必须指定预期的终止字节,要读取所有缓冲区数据直接用ser.read(ser.in_waiting)即可
  3. 超时设为None会导致程序永久卡在读操作,改成1-2秒的超时值适配设备响应速度

PyVisa代码问题

  1. 没有导入pyvisa.constants模块,直接调用visa.constants会触发名称错误
  2. 重复发送查询指令:先调用inst.write(enquire)发了一次,又调用inst.query("N!<ENQ>")再发一次,query方法本身就包含发送指令+读取返回的逻辑,重复发报文会导致设备判定通信异常
  3. 手动加了\r作为读写终止符是多余操作,手册没有要求报文末尾加回车,额外加字符会导致报文格式不符合协议要求
第三步:指令发送规则说明

查询指令和报文头必须拼接为连续的单条字节流一次性发送,绝对不能拆分多次发送。
手册标注的十六进制ASCII编码,指的是每个十六进制值对应一个ASCII字符:0x4E对应字符N,0x21对应字符!,0x05是ASCII控制符ENQ,0x06是ASCII控制符ACK。如果拆分两次发送,设备会把两段报文分别判定为无效格式,直接返回通信错误。

修正后可运行参考代码

PySerial版本

import serial
import time

# 替换为设备管理器中查到的实际串口号
PORT = "COM3"
# 4E2105是查询指令,后面拼接你实际要使用的报文头十六进制字符串
send_hex = "4E2105" + "替换为实际报文头十六进制内容"
send_bytes = bytes.fromhex(send_hex)

if __name__ == "__main__":
    try:
        ser = serial.Serial(
            port=PORT,
            baudrate=9600,
            timeout=2,
            parity=serial.PARITY_NONE,
            stopbits=serial.STOPBITS_ONE,
            bytesize=serial.EIGHTBITS,
            xonxoff=False
        )
        # 清空收发缓冲区残留数据
        ser.reset_input_buffer()
        ser.reset_output_buffer()
        # 一次性发送完整报文
        ser.write(send_bytes)
        # 留100ms等待设备返回
        time.sleep(0.1)
        # 读取所有返回数据
        recv_data = ser.read(ser.in_waiting)
        print(f"接收数据(十六进制): {recv_data.hex(' ')}")
        # 校验ACK应答
        if recv_data.hex().startswith("4e2106"):
            print("设备ACK应答正常,可继续后续交互")
        else:
            print("设备应答异常,检查报文格式或串口参数")
        ser.close()
    except Exception as e:
        print(f"通信异常: {str(e)}")

PyVisa版本

import pyvisa
from pyvisa import constants

rm = pyvisa.ResourceManager()
# 打印所有可用资源,找到对应串口的资源地址
all_res = rm.list_resources()
print(all_res)
# 替换为上面打印结果中对应串口的资源地址,例如"ASRL3::INSTR"
inst = rm.open_resource("ASRL3::INSTR")

# 串口参数配置
inst.baud_rate = 9600
inst.data_bits = 8
inst.parity = constants.Parity.none
inst.stop_bits = constants.StopBits.one
inst.flow_control = constants.VI_ASRL_FLOW_NONE
# 清空默认终止符,不额外添加字符
inst.write_termination = ""
inst.read_termination = ""
inst.timeout = 2000

send_hex = "4E2105" + "替换为实际报文头十六进制内容"
send_bytes = bytes.fromhex(send_hex)
# 用write_raw发送原始字节,避免编码问题
inst.write_raw(send_bytes)
recv_data = inst.read_raw()
print(f"接收数据(十六进制): {recv_data.hex(' ')}")

rm.close()

调试建议:正式接设备前,可以先用串口调试助手自发自收(短接串口TX、RX引脚),确认代码发送的字节流是连续的4E 21 05加报文头内容,没有多余的回车、换行或者其他乱码字符,排除发送端问题后再接设备测试。

内容的提问来源于stack exchange,提问作者Olivia Jones

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 18:16:01