ROS利用Modbus协议,读取PLC数据并发布

一、模拟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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值