Advertisement

使用 ZeroMQ 和 ROS 实现发送消息和接收消息

阅读量:

文章目录

    • 创建工作空间和功能包

    • ZeroMQ 的头文件和库文件路径

      • CMakeLists.txt
    • 编写代码

      • talker_zmq.cpp
      • listener_zmq.cpp
    • 编写启动文件

      • zeromq_ros_tutorial.launch
  • 运行结果

    • 套接字(Socket)是计算机网络编程中的核心数据传输通道。
    • 它允许不同设备间的程序间建立连接并交换数据。
    • 套接字作为系统间消息传递的主要入口。
    • 通过套接字协议层的数据封装与解封装功能,
    • 借助于socket对象,
    • 应用程序能够实现对网络资源的有效访问与管理。

创建工作空间和功能包

复制代码
    创建工作空间catkin_ws_zmq/src
    mkdir -p zeromq_ros_tutorial/src
    进入zeromq_ros_tutorial/src目录
    cd zeromq_ros_tutorial/src
    
    创建功能包zeromq_ros_tutorial
    catkin_create_pkg  zeromq_ros_tutorial std_msgs rospy roscpp
    
    
      
      
      
      
      
      
      
    
    AI助手

ZeroMQ 的头文件和库文件路径

复制代码
    检查你的计算机上是否安装了 ZeroMQ
    pkg-config --modversion libzmq
    
    通过apt-get命令安装libzmq3-dev软件包
    sudo apt-get install libzmq3-dev
    
    查看头文件和库文件:
    你还可以检查系统上是否存在 ZeroMQ 的头文件和库文件。
    通常,头文件位于 /usr/include 或 /usr/local/include,
    库文件位于 /usr/lib 或 /usr/local/lib。
    你可以执行以下命令来查找:
    
    查找 ZeroMQ 头文件
    find /usr/include /usr/local/include -name zmq.h
    
    查找 ZeroMQ 库文件
    find /usr/lib /usr/local/lib -name libzmq.*
    
    设置 ZeroMQ 头文件路径
    set(ZeroMQ_INCLUDE_DIRS "/usr/include/zmq.h")
    
    设置 ZeroMQ 库文件路径
    set(ZeroMQ_LIBRARIES "/usr/lib/x86_64-linux-gnu/libzmq.so")
    
    include_directories(${ZeroMQ_INCLUDE_DIRS})
    target_link_libraries(${ZeroMQ_LIBRARIES})
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    AI助手

CMakeLists.txt

复制代码
    cmake_minimum_required(VERSION 3.0.2)
    project(zeromq_ros_tutorial)
    
    set(CMAKE_CXX_FLAGS "-std=c++11  ${CMAKE_CXX_FLAGS}")
    set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake CACHE INTERNAL "" FORCE)
    
    
    find_package(catkin REQUIRED COMPONENTS
      roscpp
      rospy
      std_msgs
    )
    
    catkin_package()
    
    include_directories(include ${catkin_INCLUDE_DIRS} )
    include_directories(${ZeroMQ_INCLUDE_DIRS})
    
    # 设置 ZeroMQ 头文件路径
    set(ZeroMQ_INCLUDE_DIRS "/usr/include/zmq.h")
    
    # 设置 ZeroMQ 库文件路径
    set(ZeroMQ_LIBRARIES "/usr/lib/x86_64-linux-gnu/libzmq.so")
    
    add_executable(talker_zmq src/talker_zmq.cpp)
    target_link_libraries(talker_zmq ${catkin_LIBRARIES} ${ZeroMQ_LIBRARIES})
    
    add_executable(listener_zmq src/listener_zmq.cpp)
    target_link_libraries(listener_zmq ${catkin_LIBRARIES} ${ZeroMQ_LIBRARIES})
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    AI助手

编写代码

talker_zmq.cpp

复制代码
    #include <ros/ros.h>
    #include <zmq.hpp>
    #include <std_msgs/String.h>
    
    int main(int argc, char** argv) {
    // 初始化ROS节点
    ros::init(argc, argv, "talker_zmq");
    
    // 初始化ROS时间系统
    ros::Time::init();
    
    // 创建一个期望频率为10Hz的ros::Rate对象
    ros::Rate loop_rate(10);
    
    // 创建一个包含一个I/O线程的ZeroMQ上下文
    zmq::context_t context(1);
    
    // 创建一个发布者类型的ZeroMQ套接字
    zmq::socket_t publisher(context, ZMQ_PUB);
    
    // 将套接字绑定到某个地址(例如,tcp://*:5555)
    publisher.bind("tcp://*:6666");
    
    // 主循环,只要ROS节点在运行,就一直执行
    while (ros::ok()) {
        // 创建一个 ROS String 消息
        std_msgs::String ros_message;
        ros_message.data = "xiaoqiuslam";
    
        // 输出信息到ROS日志(INFO级别)
        ROS_INFO_STREAM("Publishing: " << ros_message.data);
    
        // 将 ROS 消息转换为 ZeroMQ 消息
        zmq::message_t zmq_message(ros_message.data.size());
        memcpy(zmq_message.data(), ros_message.data.c_str(), ros_message.data.size());
    
        // 将消息发送给所有订阅者
        publisher.send(zmq_message);
    
        // 等待一段时间,控制循环的频率
        loop_rate.sleep();
    }
    
    // 关闭套接字
    publisher.close();
    
    // 关闭上下文
    context.close();
    
    //  等待 ROS 节点被关闭
    ros::waitForShutdown();
    
    return 0;
    }
    
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    AI助手

listener_zmq.cpp

复制代码
    #include <ros/ros.h>
    #include <zmq.hpp>
    
    int main(int argc, char** argv) {
    ros::init(argc, argv, "listener_zmq");
    zmq::context_t context(1);
    
    // 创建订阅者套接字
    zmq::socket_t subscriber(context, ZMQ_SUB);
    
    // 设置订阅主题,这里使用空字符串表示接收所有消息
    subscriber.setsockopt(ZMQ_SUBSCRIBE, "", 0);
    
    // 设置 linger 时间为 0,立即关闭套接字
    int linger = 0;
    subscriber.setsockopt(ZMQ_LINGER, &linger, sizeof(linger));
    
    // 连接到发布者的地址
    subscriber.connect("tcp://localhost:6666");
    
    while (ros::ok()) {
        zmq::message_t message;
        // 接收消息
        int rc = subscriber.recv(&message);
        if (rc) {
            // 将消息数据转换为字符串
            std::string recv_string(static_cast<char*>(message.data()), message.size());
    
            // 使用 std::istringstream 进行字符串解析
            std::istringstream iss(recv_string);
            std::string parsed_string;
            if (std::getline(iss, parsed_string)) {
                ROS_INFO_STREAM("I heard " << parsed_string);
            } else {
                ROS_WARN_STREAM("Failed to parse the received message");
            }
        }
    }
    
    subscriber.close();
    context.close();
    ros::waitForShutdown();
    return 0;
    }
    
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    AI助手

编写启动文件

复制代码
    source devel/setup.bash
    执行setup.bash脚本以设置环境变量
    
    publisher 
    rosrun ros_zeromq_tutorial talker_zmq
    使用rosrun命令运行ros_zeromq_tutorial包中的talker_zmq节点
    
    
    source devel/setup.bash
    执行setup.bash脚本以重新设置环境变量
    
    rosrun ros_zeromq_tutorial listener_zmq
    使用rosrun命令运行ros_zeromq_tutorial包中的listener_zmq节点
    
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    AI助手

zeromq_ros_tutorial.launch

复制代码
    <!-- zeromq_ros_tutorial.launch -->
    
    <launch>
      <!-- 启动listener_zmq节点 -->
      <node pkg="zeromq_ros_tutorial" type="listener_zmq" name="listener_zmq" output="screen">
      </node>
    
      <!-- 启动talker_zmq节点 -->
      <node pkg="zeromq_ros_tutorial" type="talker_zmq" name="talker_zmq" output="screen">
      </node>
    </launch>
    
    
      
      
      
      
      
      
      
      
      
      
      
    
    AI助手

在launch文件中,
该标签的pkg属性应指定你的ROS功能包名称,
type属性应设为你项目的可执行文件名。
name属性则用于给节点命名(如有必要)。

在同一个launch文件中同时启动多个具有相同类型的角色: 通过为每个角色设置不同的name属性能够显著提升配置效率和可管理性。 这样的做法不仅有助于避免角色冲突还能确保系统运行稳定性和可扩展性。

这样一来,在同一个功能包内就可以运行多台配置相同的设备实例,并且每台设备都有独特的标识。

复制代码
    <launch>
      <!-- 启动两个talker节点,但它们具有不同的名称 -->
      <node pkg="my_package" type="talker_node" name="talker_1" output="screen" />
      <node pkg="my_package" type="talker_node" name="talker_2" output="screen" />
    </launch>
    
    
      
      
      
      
      
    
    AI助手

举例来说,在这个案例中,有两个 talker_node 节点具有不同的名称:分别是 talker_1 和 talker_2。通过这种方式,在ROS图中可以保证每个节点的唯一性。

运行结果

复制代码
    cd zeromq_ros_tutorial
    source devel/setup.bash
    roslaunch zeromq_ros_tutorial zeromq_ros_tutorial.launch
    
    
      
      
      
    
    AI助手
在这里插入图片描述

socket(Socket)是计算机网络编程中一种核心通信机制,在多台不同的计算机之间允许程序间进行数据交换。socket作为一个抽象的通信接口,在实际应用中被广泛采用以实现跨平台的数据传输功能。借助socket这一抽象接口,应用程序能够发送数据至指定服务器并接收服务器返回的数据。

具体含义如下:

通信端点: 套接字被视为通信的起点,在网络架构中扮演着关键角色。它被用来建立并维护数据传输通道。在编程实现中,通常会为每个参与通信的过程分配一个独特的套接字以确保数据传输的安全性和可靠性。

通信机制: 套接字采用了标准化的通信机制,在支持进程之间实现数据发送和接收功能。该机制既可实现单向通信(如服务器向客户端发送数据),也可支持双方进行数据交换(实现双向通信)。

套接字概念: 套接字被分为多种类别,其中流式套接字(TCP)主要负责高效的数据传输,在可靠传输的同时支持双向通信连接;而数据报套接字(UDP)则以简洁高效著称,在实时性要求较高的场景中表现出色。各类别套接字均能实现高效的双向通信连接并支持相应的端到端业务功能。

套接字地址:该套接字由特定的标识符进行编码,在编程应用中通常表示为统一资源 locator(URL)。一般情况下,在网络架构中这一标识符由 IP 地址字段和端口号参数共同构成。

套接字 API: 套接字API是由操作系统提供的系统调用或库函数集合,在程序开发中用于执行一系列与数据传输相关的操作。它支持创建、绑定、连接以及发送和接收数据等基本操作。在多种编程语言中均设有对应的套接字接口或库模块以实现类似功能。例如,在C语言中使用的是socket库API,在Python中则提供了socket模块作为实现socket功能的基础组件。

从网络编程的角度来看,在构建网络通信系统时,套接字被视为基础组件。它使得不同计算机上的进程能够通过网络实现数据的交互与共享。通过套接字机制的设计与应用,在实际开发中可以覆盖多种复杂的网络应用场景。例如基于客户端-服务器模式以及点对点(P2P)通信架构的方案。

全部评论 (0)

还没有任何评论哟~