一、模拟PLC数据
二、建立客户端程序并发布
#!/usr/bin/env python
import rospy
from pymodbus.client import ModbusTcpClient
from pymodbus.exceptions import ConnectionException
from std_msgs.msg import Float32MultiArray
class PLCDataPublisher:
def __init__(self):
# 初始化 ROS 节点
rospy.init_node('plc_data_publisher', anonymous=True)
# 配置 PLC 参数
self.plc_ip = rospy.get_param('~plc_ip', '192.168.18.209') # 默认 PLC IP 地址
self.plc_port = rospy.get_param('~plc_port', 502) # 默认 MODBUS TCP 端口
self.points_count = rospy.get_param('~points_count', 100) # 默认读取的点位数量
# 创建 MODBUS TCP 客户端
self.client = ModbusTcpClient(self.plc_ip, self.plc_port)
# 创建发布者,发布 PLC 数据到话题 "plc_data"
self.pub = rospy.Publisher('plc_data', Float32MultiArray, queue_size=10)
# 设置连接超时时间
self.connect_timeout = 5 # 单位:秒
# 设置重连间隔
self.reconnect_interval = 5 # 单位:秒
# 连接到 PLC
self.connect_to_plc()
# 设置发布频率
self.rate = rospy.Rate(10) # 10Hz
def connect_to_plc(self):
"""连接到 PLC"""
rospy.loginfo("尝试连接到 PLC %s:%d...", self.plc_ip, self.plc_port)
try:
self.client.connect()
if self.client.connected:
rospy.loginfo("成功连接到 PLC")
else:
rospy.logwarn("连接到 PLC 失败,将在 %d 秒后重试...", self.reconnect_interval)
rospy.sleep(self.reconnect_interval)
self.connect_to_plc()
except ConnectionException as e:
rospy.logerr("连接到 PLC 失败: %s", e)
rospy.logwarn("将在 %d 秒后重试...", self.reconnect_interval)
rospy.sleep(self.reconnect_interval)
self.connect_to_plc()
def read_plc_data(self):
"""读取 PLC 数据"""
try:
# 读取保持寄存器中的数据(假设数据存储在保持寄存器中)
result = self.client.read_holding_registers(address=0, count=self.points_count, slave=1)
# if not result.isError():
# values = result.registers
if result.isError():
#rospy.logerr("读取 PLC 数据失败: %s", result.exception_code)
#rospy.logerr("读取 PLC 数据失败")
self.connect_to_plc()
return None
return result.registers
except ConnectionException as e:
rospy.logerr("与 PLC 的连接已断开: %s", e)
self.connect_to_plc()
return None
def publish_plc_data(self):
"""发布 PLC 数据"""
while not rospy.is_shutdown():
data = self.read_plc_data()
if data is not None:
# 创建消息并填充数据
msg = Float32MultiArray()
msg.data = [float(value) for value in data]
# 发布消息
self.pub.publish(msg)
rospy.loginfo("发布 PLC 数据成功,数据点数: %d", len(data))
self.rate.sleep()
def shutdown(self):
"""关闭连接"""
self.client.close()
rospy.loginfo("已关闭与 PLC 的连接")
if __name__ == '__main__':
try:
plc_data_publisher = PLCDataPublisher()
plc_data_publisher.publish_plc_data()
except rospy.ROSInterruptException:
pass
finally:
plc_data_publisher.shutdown()
增加重连机制,初始链接时,如果连接不上,或者连接上中途断开,则每5秒重连一次
#!/usr/bin/env python
import rospy
from pymodbus.client import ModbusTcpClient
from pymodbus.exceptions import ConnectionException
from std_msgs.msg import Float32MultiArray
class PLCDataPublisher:
def __init__(self):
# 初始化 ROS 节点
rospy.init_node('plc_data_publisher', anonymous=True)
# 配置 PLC 参数
self.plc_ip = rospy.get_param('~plc_ip', '192.168.18.240') # 默认 PLC IP 地址
self.plc_port = rospy.get_param('~plc_port', 502) # 默认 MODBUS TCP 端口
self.points_count = rospy.get_param('~points_count', 100) # 默认读取的点位数量
# 创建 MODBUS TCP 客户端
self.client = ModbusTcpClient(self.plc_ip, self.plc_port)
# 创建发布者,发布 PLC 数据到话题 "plc_data"
self.pub = rospy.Publisher('plc_data', Float32MultiArray, queue_size=10)
# 设置连接超时时间
self.connect_timeout = 5 # 单位:秒
# 设置重连间隔
self.reconnect_interval = 5 # 单位:秒
# 连接到 PLC
self.connect_to_plc()
# 设置发布频率
self.rate = rospy.Rate(10) # 10Hz
def connect_to_plc(self):
"""连接到 PLC"""
rospy.loginfo("尝试连接到 PLC %s:%d...", self.plc_ip, self.plc_port)
try:
self.client.connect()
if self.client.connected:
rospy.loginfo("成功连接到 PLC")
else:
rospy.logwarn("连接到 PLC 失败,将在 %d 秒后重试...", self.reconnect_interval)
rospy.sleep(self.reconnect_interval)
self.connect_to_plc()
except ConnectionException as e:
rospy.logerr("连接到 PLC 失败: %s", e)
rospy.logwarn("将在 %d 秒后重试...", self.reconnect_interval)
rospy.sleep(self.reconnect_interval)
self.connect_to_plc()
def read_plc_data(self):
"""读取 PLC 数据"""
try:
# 读取保持寄存器中的数据(假设数据存储在保持寄存器中)
result = self.client.read_holding_registers(address=0, count=self.points_count, slave=1)
# if not result.isError():
# values = result.registers
if result.isError():
rospy.logerr("读取 PLC 数据失败: %s", result.exception_code)
return None
return result.registers
except ConnectionException as e:
rospy.logerr("与 PLC 的连接已断开: %s", e)
self.connect_to_plc()
return None
def publish_plc_data(self):
"""发布 PLC 数据"""
while not rospy.is_shutdown():
data = self.read_plc_data()
if data is not None:
# 创建消息并填充数据
msg = Float32MultiArray()
msg.data = [float(value) for value in data]
# 发布消息
self.pub.publish(msg)
rospy.loginfo("发布 PLC 数据成功,数据点数: %d", len(data))
self.rate.sleep()
def shutdown(self):
"""关闭连接"""
self.client.close()
rospy.loginfo("已关闭与 PLC 的连接")
if __name__ == '__main__':
try:
plc_data_publisher = PLCDataPublisher()
plc_data_publisher.publish_plc_data()
except rospy.ROSInterruptException:
pass
finally:
plc_data_publisher.shutdown()
三、订阅话题
rostopic echo /plc_modbus_data