前言
当前主要是分享在modbus tcp协议采集设备数据时,如何确保数据集采的效率,刷新频率的一点小小的技术分享。通过在程序采集逻辑,顺序等方式的修正,在同等技术参数设备的情况下,满足大家对于数据采集精度的高要求。
一、为什么对于数据采集的精度有一定的要求?
设备通过标准协议实现数据的集采,传输;再通过不同的界面,图标,曲线展示其运行状态,反应设备数据的演变状态,这是当下数据集采的基本要求。
当设备增多,数据增多时,随着数据量的累积,如果不在编程上进行优化,就会存在数据采集堵塞,数据采集频率无法跟上实际需求的问题。
例如:日常的温度采集需要的精度是 ℃ / min ; 电流的精度是 A / s。
二、举例说明
1.单个数据集采的示例
通过modbus tcp 采集设备中的单个寄存器的数据,寄存器地址为 01B0H
from pymodbus.client.sync import ModbusTcpClient
import struct
def read_float_from_modbus(ip, port, unit_id, register_address):
try:
# 创建 Modbus TCP 客户端实例
client = ModbusTcpClient(ip, port=port)
# 尝试连接到 Modbus TCP 服务器
if client.connect():
# 浮点数占 2 个寄存器
num_registers = 2
# 读取保持寄存器
result = client.read_holding_registers(register_address, num_registers, unit=unit_id)
if not result.isError():
registers = result.registers
# 合并两个 16 位寄存器为 32 位值
combined_value = (registers[0] << 16) + registers[1]
# 将 32 位值转换为字节数据
byte_data = combined_value.to_bytes(4, byteorder='big')
# 使用 struct 模块将字节数据解包为浮点数
float_value = struct.unpack('!f', byte_data)[0]
return float_value
else:
print(f"读取寄存器时出错: {result}")
else:
print("无法连接到 Modbus TCP 服务器。")
except Exception as e:
print(f"发生异常: {e}")
finally:
if client.is_open():
# 关闭客户端连接
client.close()
# 示例使用
ip = '127.0.0.1'
port = 502
unit_id = 1
# 十六进制 01B0H 转换为十进制是 432
register_address = 432
float_data = read_float_from_modbus(ip, port, unit_id, register_address)
if float_data is not None:
print(f"从寄存器地址 {hex(register_address)} 读取到的浮点数数据为: {float_data}")
根据当前的模式,若是采用多个寄存器地址的数据,则不断调用函数read_float_from_modbus(ip, port, unit_id, register_address)即可,不停的修改register_address的值。
但是实际的运行中会发现,在刷新频率提升之后,由于不停的调用整个函数,就会发生,数据堵塞,卡顿的情况。做到秒级的刷新频率,就会发生数据间隔时间段的整体刷新,而不是按照时间频率准时刷新的情况。
2.程序优化
以上主要的问题是在于,读取寄存器地址内的数据内容,消耗能源是一定的,但是不断地调用 client = ModbusTcpClient(ip, port=port) # 创建 Modbus TCP 客户端,会占用大量的设备资源,此处可以在创建客户端下一步采集数据时进行多个数据的循环集采,就可以节省大量的设备资源。
程序如下:
from pymodbus.client.sync import ModbusTcpClient
import struct
def read_float_from_modbus(ip, port, unit_id, register_addresses):
float_values = []
try:
# 创建 Modbus TCP 客户端实例
client = ModbusTcpClient(ip, port=port)
# 尝试连接到 Modbus TCP 服务器
if client.connect():
for register_address in register_addresses:
# 浮点数占 2 个寄存器
num_registers = 2
# 读取保持寄存器
result = client.read_holding_registers(register_address, num_registers, unit=unit_id)
if not result.isError():
registers = result.registers
# 合并两个 16 位寄存器为 32 位值
combined_value = (registers[0] << 16) + registers[1]
# 将 32 位值转换为字节数据
byte_data = combined_value.to_bytes(4, byteorder='big')
# 使用 struct 模块将字节数据解包为浮点数
float_value = struct.unpack('!f', byte_data)[0]
float_values.append(float_value)
else:
print(f"读取寄存器 {register_address} 时出错: {result}")
else:
print("无法连接到 Modbus TCP 服务器。")
except Exception as e:
print(f"发生异常: {e}")
finally:
if client.is_open():
# 关闭客户端连接
client.close()
return float_values
# 示例使用
ip = '127.0.0.1'
port = 502
unit_id = 1
# 寄存器地址列表,示例中包含十六进制 01B0H(十进制 432)和 01B2H(十进制 434)
register_addresses = [432, 434]
float_data = read_float_from_modbus(ip, port, unit_id, register_addresses)
if float_data:
for i, address in enumerate(register_addresses):
print(f"从寄存器地址 {hex(address)} 读取到的浮点数数据为: {float_data[i]}")
此处就是采集两个寄存器的数据,十六进制 01B0H(十进制 432)和 01B2H(十进制 434)一次采集两个数据 , client = ModbusTcpClient(ip, port=port) 只打开了一次。
实际测试中将 寄存器数列 register_addresses = [432, 434] 中的寄存器地址拓展到10+,采集精度任然可以保持到10ms甚至更高。当然此处也要考虑设备的高强度数据集采情况的使用寿命,散热等情况。
实测的视频如下:
modbus tcp 数据集采 精度10ms
总结
在成本,技术,以及实际情况固定的情况下,不同的采集策略,不同的代码,不同的逻辑思维对于最终的成品有着重大的影响。更多的时候,合理的设计,优质的设计会让不多的资源,发挥出意想不到的效果。