游乐游手机版
首页/编程语言/文章详情

pymodbus模块实现Modbus通讯的方法与实践

时间:2026-08-15 17:04
PyModbus支持ModbusRTU、ASCII及TCP IP模式,用于PLC等设备通信。通过客户端连接,可读取保持寄存器、写入单个或批量寄存器,需处理ModbusException异常,并利用struct模块进行数据类型转换。

在工业自动化和设备通信领域,Modbus 协议几乎是无法绕开的基础协议——它发展时间长、应用范围广、生态完善、实现门槛低,至今仍然是 PLC、传感器、变频器、驱动器等设备之间通信的“通用语言”。而 PyModbus 作为 Python 生态中非常常用的 Modbus 通讯库,支持 RTU(串行通信)、ASCII 以及 TCP/IP 三种主流模式,基本能够覆盖绝大多数工业现场的 Modbus 设备对接需求。下面就从最基础的连接建立开始,系统梳理一遍使用 pymodbus 模块实现 Modbus 通讯的完整流程。

使用pymodbus模块实现Modbus通讯方式

1. 建立通讯

要让程序与 Modbus 设备正常交互,第一步就是先建立连接。PyModbus 提供的客户端接口比较直观,下面以 Modbus TCP 通讯为例:

from pymodbus.client import ModbusTcpClient

client = ModbusTcpClient('localhost', port=502)
client.connect()

如果目标设备是串口通信设备,例如 RS232 或 RS485,则可以改用ModbusSerialClient,并配置串口号、波特率、校验位、停止位等参数。连接建立成功后,就可以继续执行寄存器读取、写入等 Modbus 数据交互操作。

2. 读取数据

2.1 读取寄存器

在 Modbus 协议中,常见的数据区域主要包括保持寄存器(Holding Register)和输入寄存器(Input Register)。其中保持寄存器支持读写,输入寄存器通常为只读。下面先看一个读取保持寄存器的示例:

response = client.read_holding_registers(address=1, count=10, unit=1)
if not response.isError():
    print("Register Values: ", response.registers)
else:
    print("Failed to read registers")

这里address=1表示起始寄存器地址(需要特别注意:很多 Modbus 设备的地址映射从 0 开始,也有部分设备手册会按 1 开始标注,是否存在地址偏移一定要以设备说明书为准),count=10表示连续读取 10 个寄存器,unit=1表示设备 ID,也就是常说的从站地址。

另外还有一个版本兼容细节需要注意:在某些 PyModbus 版本中,unit参数已经调整为sla ve。如果你当前使用的是较新的版本,写unit通常依然可以正常运行,因为库本身做了兼容处理;不过为了避免后续升级带来问题,最好还是先查看当前版本文档,确认实际参数名称,减少踩坑概率。

3. 写入数据

3.1 写入单个寄存器

当需要向设备写入单个寄存器的值时,可以使用write_register方法:

write_response = client.write_register(address=1, value=25, unit=1)
if not write_response.isError():
    print("Written successfully")
else:
    print("Failed to write register")

这里的参数含义与读取寄存器时一致,分别是寄存器地址、写入值和设备 ID。是否写入成功,同样可以通过isError()来判断返回结果。

3.2 写入多个寄存器

如果业务场景中需要一次性写入多个连续寄存器,那么可以使用write_registers方法(注意函数名末尾多了一个 s):

values = [20, 40, 60, 80, 100]
write_response = client.write_registers(address=1, values=values, unit=1)
if not write_response.isError():
    print("Multiple registers written successfully")
else:
    print("Failed to write multiple registers")

这里传入的是一个列表,程序会从address指定的起始地址开始,按顺序把列表中的值依次写入对应的连续寄存器中。这种方式在批量下发参数、配置设备时非常常见。

4. 处理异常

工业现场的通信环境通常没有想象中那么稳定——线路干扰、设备掉线、地址配置错误、帧校验异常等情况都可能随时出现。因此,更稳妥的做法是使用try-except来捕获 Modbus 通讯异常:

from pymodbus.exceptions import ModbusException

try:
    # 尝试执行Modbus操作
    response = client.read_holding_registers(address=1, count=10, unit=1)
except ModbusException as ex:
    print("An error occurred:", str(ex))

这样即使读取失败或写入异常,程序也不会直接中断退出,至少可以先记录错误日志,或者进一步做重试、告警、断线重连等处理,从而提高整个 Modbus 通讯程序的稳定性和可靠性。

5. 数据类型转换

Modbus 寄存器本质上通常以 16 位无符号整数(UInt16)的形式存储数据,但在真实项目中,经常还需要解析浮点数、32 位整数、有符号整数等业务数据。这时候就需要借助 Python 的struct模块来完成寄存器数据转换。下面的代码示例展示了一组常见的转换方法,实际开发中通常建议封装成工具类或公共函数,便于复用:

 def _convert_to_float(self, registers):
        if len(registers) != 2:
            print("Invalid register length for float")
            return None
        combined = (registers[0] << 16) + registers[1]
        return struct.unpack(">f", combined.to_bytes(4, byteorder="big"))[0]

    def _convert_to_int32(self, registers):
        if len(registers) != 2:
            print("Invalid register length for int32")
            return None
        combined = (registers[0] << 16) + registers[1]
        return struct.unpack(">i", combined.to_bytes(4, byteorder="big"))[0]

    def _convert_to_uint32(self, registers):
        if len(registers) != 2:
            print("Invalid register length for uint32")
            return None
        combined = (registers[0] << 16) + registers[1]
        return struct.unpack(">I", combined.to_bytes(4, byteorder="big"))[0]

    def _convert_to_int16(self, registers):
        if len(registers) != 1:
            print("Invalid register length for int16")
            return None
        return struct.unpack(">h", struct.pack(">H", registers[0]))[0]

    def _convert_to_uint16(self, registers):
        if len(registers) != 1:
            print("Invalid register length for uint16")
            return None
        return struct.unpack(">H", struct.pack(">H", registers[0]))[0]

除了读取后的解析,写入寄存器前的数据反向转换同样很关键:

 def _int32_to_registers(self, value):
        packed = struct.pack(">i", value)
        high_register, low_register = struct.unpack(">HH", packed)
        return [high_register, low_register]

    def _uint32_to_registers(self, value):
        packed = struct.pack(">I", value)
        high_register, low_register = struct.unpack(">HH", packed)
        return [high_register, low_register]

    def _uint16_to_register(self, value):
        packed = struct.pack(">H", value)
        register = struct.unpack(">H", packed)[0]
        return [register]

    def _int16_to_register(self, value):
        packed = struct.pack(">h", value)
        register = struct.unpack(">h", packed)[0]
        return [register]

这里使用的是大端序(>),因为很多 Modbus 设备默认采用大端字节序进行数据传输。如果你的 PLC、仪表或其他从站设备采用的是小端序,那么记得把格式前缀改成<,否则解析出来的数据很可能不正确。

总结

总体来看,PyModbus 已经把 Modbus 通讯涉及的很多底层细节封装得比较完善,无论是建立连接、读取保持寄存器、写入寄存器、异常处理,还是数据类型转换,都可以通过较少的 Python 代码快速实现。掌握这些核心操作之后,就能更高效地把 Python 与 PLC、传感器、工业设备连接起来,应用到设备控制、数据采集、远程监控、自动化测试等场景中。需要注意的是,实际项目中的难点往往不在 pymodbus 库本身,而更多集中在设备文档细节,例如寄存器地址偏移、字节序定义、功能码限制、读写权限以及厂商私有协议扩展等。想写出真正稳定可靠的 Modbus 通讯程序,最有效的方法仍然是认真阅读设备手册,并结合抓包和现场测试进行验证。

来源:https://www.jb51.net/python/364973y6d.htm
上一篇CentOS系统中优化PHP配置的实用方法与技巧 下一篇CentOS环境下C++代码编译选项优化方法
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
Python应用打包与部署入门教程:核心概念、操作步骤与结果验证
编程语言 · 2026-10-01

Python应用打包与部署入门教程:核心概念、操作步骤与结果验证

从 Python 应用打包的基本概念入手,介绍项目环境准备、依赖管理、构建发布包、安装部署以及运行结果验证,并梳理常见打包失败与部署问题,帮助初学者完成从源码到可部署应用的完整流程。

Python CLI 开发避坑指南:从环境配置到参数解析的实战排查
编程语言 · 2026-10-01

Python CLI 开发避坑指南:从环境配置到参数解析的实战排查

本文聚焦 Python 命令行工具(CLI)开发中最高频的故障点,按执行链路梳理从环境配置、参数解析、路径处理到异常调试的完整排查流程。通过具体代码示例与终端输出对照,提供可复现的修复方案,帮助开发者快速定位 ModuleNotFoundError、参数校验失败及跨平台兼容性问题,构建更健壮的命令行

Python CLI 开发:从参数解析到工程化发布的完整路径
编程语言 · 2026-10-01

Python CLI 开发:从参数解析到工程化发布的完整路径

本文以 Python 命令行工具开发为切入点,从项目结构搭建与虚拟环境配置入手,深入讲解 argparse 参数解析与子命令设计。通过一个完整的日志分析工具案例,演示输入校验、错误处理与异常捕获的最佳实践,最后覆盖打包发布流程与常见排查技巧,帮助开发者构建健壮、易用的 CLI 应用。

Python 模块与包的工程化实践:结构、依赖与排错指南
编程语言 · 2026-10-01

Python 模块与包的工程化实践:结构、依赖与排错指南

本文从项目目录规范与模块导入机制切入,详细阐述虚拟环境的配置、第三方包的管理策略以及完整案例的模块化拆分方法。通过具体代码示例展示如何构建高内聚低耦合的代码结构,并针对 ModuleNotFoundError、ImportError 及依赖冲突等常见工程问题提供系统化的排查与解决方案,帮助开发者建立

Python 函数参数与返回值:从环境搭建到实战避坑
编程语言 · 2026-10-01

Python 函数参数与返回值:从环境搭建到实战避坑

本文从搭建 Python 运行环境入手,详细解析函数定义、参数传递机制及返回值处理。通过电商订单计算的完整案例,展示如何模块化组织业务逻辑,并针对参数数量、作用域及返回值缺失等常见错误提供排查方案,帮助开发者写出健壮且可维护的代码。