【ROS2】MOMO的鱼香ROS2(四)ROS2入门篇——ROS2节点通信之话题与服务

news2024/12/26 21:21:59

ROS2节点通信之话题与服务点

  • 引言
  • 1 理解从通信开始
    • 1.1 TCP(传输控制协议)
    • 1.2 UDP(用户数据报协议)
    • 1.3 基于共享内存的IPC方式
  • 2 ROS2话题
    • 2.1 ROS2话题指令
    • 2.2 话题之RCLPY实现
      • 2.2.1 编写发布者
      • 2.2 2 编写订阅者
      • 2.2.3 运行测试
  • 3 ROS2服务
    • 3.1 ROS2服务指令
    • 3.2 服务之RCLPY实现
      • 3.2.1 编写客户端
      • 3.2 2 编写服务端
      • 3.2.3 运行测试

引言

笔者跟着鱼香ROS的ROS2学习之旅
学习参考:
【ROS2机器人入门到实战】
笔者的学习目录

  1. MOMO的鱼香ROS2(一)ROS2入门篇——从Ubuntu操作系统开启
  2. 【ROS2】MOMO的鱼香ROS2(二)ROS2入门篇——ROS2初体验
  3. ROS2】MOMO的鱼香ROS2(三)ROS2入门篇——ROS2第一个节点

专业术语认识

  1. TCP(传输控制协议)
  2. UDP(用户数据报协议)

1 理解从通信开始

通信的目的是在计算机系统中实现不同组件、进程或设备之间的信息和数据传递。通过通信,各个实体可以共享信息、协调行动并实现协同工作。在计算机领域,通信是构建分布式系统、网络和协议的基础。
通信的原理涉及两个主要方面:通信协议和通信方式。

通信协议定义了数据的格式、传输方式、错误检测和纠正等规则,以确保可靠的数据传输。
通信方式涉及了不同的通信介质和技术,包括网络通信和进程间通信(IPC)。

1.1 TCP(传输控制协议)

使用ping命令进行基于UDP的网络通信:

# 修改为自己的ip地址
ping 192.168.2.42
ping 192.168.2.36

在这里插入图片描述

1.2 UDP(用户数据报协议)

使用nc命令进行基于TCP的网络通信:

# 终端1
nc -l 1234
# 终端2
echo "Hello, TCP!" | nc 127.0.0.1 1234

在这里插入图片描述
关于TCP的通信可以参考笔者的博客:【Python】基于socket函数的TCP通信

1.3 基于共享内存的IPC方式

基于共享内存的进程间通信(IPC)方式
通过ipcs命令查看当前系统中的共享内存段:

ipcs -m

使用ipcrm命令删除不再需要的共享内存段:

ipcrm -m <shmid>

2 ROS2话题

一个节点发布数据到某个话题上,另外一个节点就可以通过订阅话题拿到数据。

ROS2话题通信其实还可以是1对n,n对1,n对n的。
同一个话题,所有的发布者和接收者必须使用相同消息接口。

在这里插入图片描述

2.1 ROS2话题指令

1.查看所有话题

ros2 topic list
# 增加查看消息类型
ros2 topic list -t

2.打印实时话题内容

ros2 topic echo  <topic_name> 

3.查看主题信息

ros2 topic info  <topic_name> 

2.2 话题之RCLPY实现

创建功能包

cd ROS_WS/colcon_ws/src
ros2 pkg create imu_py  --build-type ament_python --dependencies rclpy

创建节点文件

cd ROS_WS/colcon_ws/src/imu_py
touch imu_publisher.py
touch imu_subscribe.py

2.2.1 编写发布者

imu_publisher.py

# -*- coding: utf-8 -*-
"""
1.查看映射端口
ls /dev/ttyUSB*
2.更改端口的权限
sudo chmod 777 /dev/ttyUSB0
"""
import rclpy
from rclpy.node import Node
# 话题接口
from sensor_msgs.msg import Imu  # imu接口
# Usart Library
import serial


# imu接收数据类型
class IMUPublisher(Node):

    def __init__(self,name):
        super().__init__(name)  # 继承父类,初始化名称
        self.get_logger().info("大家好,我是%s!" % name)
        self.publisher_node = self.create_publisher(Imu, 'imu_data', 1)  # 创建发布imu数据的发布者到话题:imu_data上

        # 串口初始化
        self.IMU_Usart = serial.Serial(
            port='/dev/ttyUSB0',  # 串口
            baudrate=115200,  # 波特率
            timeout=0.001  # 由于后续使用read_all按照一个timeout周期时间读取数据
            # imu在波特率115200返回数据时间大概是1ms,9600下大概是10ms
            # 所以读取时间设置0.001s
        )
        # 接收数据初始化
        self.ACC_X: float = 0.0  # X轴加速度
        self.ACC_Y: float = 0.0  # Y轴加速度
        self.ACC_Z: float = 0.0  # Z轴加速度
        self.GYRO_X: float = 0.0  # X轴陀螺仪
        self.GYRO_Y: float = 0.0  # Y轴陀螺仪
        self.GYRO_Z: float = 0.0  # Z轴陀螺仪
        self.roll: float = 0.0  # 横滚角
        self.pitch: float = 0.0  # 俯仰角
        self.yaw: float = 0.0  # 航向角
        self.leve: float = 0.0  # 磁场校准精度
        self.temp: float = 0.0  # 器件温度
        self.MAG_X: float = 0.0  # 磁场X轴
        self.MAG_Y: float = 0.0  # 磁场Y轴
        self.MAG_Z: float = 0.0  # 磁场Z轴
        self.Q0: float = 0.0  # 四元数Q0.0
        self.Q1: float = 0.0  # 四元数Q1
        self.Q2: float = 0.0  # 四元数Q2
        self.Q3: float = 0.0  # 四元数Q3
        # 判断串口是否打开成功
        if self.IMU_Usart.isOpen():
            print("open success")
        else:
            print("open failed")

        # 回调函数返回周期
        time_period = 0.001
        self.timer = self.create_timer(time_period, self.timer_callback)


    def timer_callback(self):
        """
        定时器回调函数
        """

        # ----读取IMU的内部数据-----------------------------------
        try:
            count = self.IMU_Usart.inWaiting()
            if count > 0:
                # 发布sensor_msgs/Imu 数据类型
                imu_data = Imu()
                imu_data.header.frame_id = "map"
                imu_data.header.stamp = self.get_clock().now().to_msg()
                imu_data.linear_acceleration.x = self.ACC_X
                imu_data.linear_acceleration.y = self.ACC_Y
                imu_data.linear_acceleration.z = self.ACC_Z
                imu_data.angular_velocity.x = self.GYRO_X * 3.1415926 / 180.0  # unit transfer to rad/s
                imu_data.angular_velocity.y = self.GYRO_Y * 3.1415926 / 180.0
                imu_data.angular_velocity.z = self.GYRO_Z * 3.1415926 / 180.0
                imu_data.orientation.x = self.Q0
                imu_data.orientation.y = self.Q1
                imu_data.orientation.z = self.Q2
                imu_data.orientation.w = self.Q3
                self.publisher_node.publish(imu_data)  # 发布imu的数据
                self.get_logger().info(f'发布了指令:{imu_data.header.frame_id}')    #打印一下发布的数据
                # --------------------------------------------------------
                # print('读取中')

        except KeyboardInterrupt:
            if serial != None:
                print("close serial port")
                self.IMU_Usart.close()

def main(args=None):
    """
    ros2运行该节点的入口函数
    编写ROS2节点的一般步骤
    1. 导入库文件
    2. 初始化客户端库
    3. 新建节点对象
    4. spin循环节点
    5. 关闭客户端库
    """
    rclpy.init(args=args)  # 初始化rclpy
    node = IMUPublisher("imu_publisher")  # 新建一个节点
    rclpy.spin(node)  # 保持节点运行,检测是否收到退出指令(Ctrl+C)
    rclpy.shutdown()  # 关闭rclpy

2.2 2 编写订阅者

imu_subscribe.py

# -*- coding: utf-8 -*-
import rclpy
from rclpy.node import Node
# 话题接口
from sensor_msgs.msg import Imu  # imu接口


class IMUSubscribe(Node):
    def __init__(self,name):
        super().__init__(name)
        self.get_logger().info("大家好,我是%s!" % name)
        self.imu_subscribe_node = self.create_subscription(Imu, 'imu_data',self.imu_callback, 1)  # 创建发布imu数据的发布者到话题:imu_data上
        
    def imu_callback(self, imu_data):
         self.get_logger().info(f'收到[{imu_data.header.frame_id}]命令')

def main(args=None):
    rclpy.init(args=args) # 初始化rclpy
    node = IMUSubscribe("imu_subscribe")  # 新建一个节点
    rclpy.spin(node) # 保持节点运行,检测是否收到退出指令(Ctrl+C)
    rclpy.shutdown() # 关闭rclpy

2.2.3 运行测试

修改下setup.py

entry_points={
        'console_scripts': [
            "imu_publisher=imu_py.imu_publisher:main",  
            "imu_subscribe=imu_py.imu_subscribe:main",
        ],
    },

发布节点

colcon build --packages-select imu_py
source install/setup.bash
ros2 run imu_py imu_publisher

订阅节点

source install/setup.bash
ros2 run imu_py imu_subscribe

在这里插入图片描述
可视化

rqt

在这里插入图片描述
上图中的节点名称取决于下图
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

3 ROS2服务

服务分为客户端和服务端,服务-客户端模型也可以称为请求-响应模型,不同于话题是没有返回的,适用于单向或大量的数据传递。而服务是双向的,客户端发送请求,服务端响应请求。

注意:
同一个服务(名称相同)有且只能有一个节点来提供
同一个服务可以被多个客户端调用

3.1 ROS2服务指令

启动服务端

ros2 run examples_rclpy_minimal_service service

1.查看所有服务

ros2 service list

2.手动调用服务

ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts "{a: 5,b: 10}"

3.查看服务接口类型

ros2 service type   <service_name> 

4.查找使用某一接口的服务

ros2 service find example_interfaces/srv/AddTwoInts

3.2 服务之RCLPY实现

创建节点文件

cd ROS_WS/colcon_ws/src/imu_py
touch imu_client.py
touch imu_server.py

3.2.1 编写客户端

imu_client.py

# -*- coding: utf-8 -*-
import rclpy
from rclpy.node import Node
from example_interfaces.srv import AddTwoInts

class ServiceClient(Node):
    def __init__(self,name):
        super().__init__(name)
        self.get_logger().info("节点已启动:%s!" % name)
        self.client_ = self.create_client(AddTwoInts,"add_two_ints_srv") 

    def result_callback_(self, result_future):
        response = result_future.result()
        self.get_logger().info(f"收到返回结果:{response.sum}")
    
    def send_request(self, a, b):
        while rclpy.ok() and self.client_.wait_for_service(1)==False:
            self.get_logger().info(f"等待服务端上线....")
            
        request = AddTwoInts.Request()
        request.a = a
        request.b = b
        self.client_.call_async(request).add_done_callback(self.result_callback_)
        
def main(args=None):
    rclpy.init(args=args) # 初始化rclpy
    node = ServiceClient("service_client")  # 新建一个节点
    # 调用函数发送请求
    node.send_request(3,4)
    rclpy.spin(node) # 保持节点运行,检测是否收到退出指令(Ctrl+C)
    rclpy.shutdown() # 关闭rclpy

3.2 2 编写服务端

imu_server.py

# -*- coding: utf-8 -*-
import rclpy
from rclpy.node import Node
# 导入接口
from example_interfaces.srv import AddTwoInts

class ServiceServer(Node):
    def __init__(self,name):
        super().__init__(name)
        self.get_logger().info("节点已启动:%s!" % name)
        self.add_ints_server_ = self.create_service(AddTwoInts,"add_two_ints_srv", self.handle_add_two_ints) 

    def handle_add_two_ints(self,request, response):
        self.get_logger().info(f"收到请求,计算{request.a} + {request.b}")
        response.sum = request.a + request.b
        return response

def main(args=None):
    rclpy.init(args=args) # 初始化rclpy
    node = ServiceServer("service_server")  # 新建一个节点
    rclpy.spin(node) # 保持节点运行,检测是否收到退出指令(Ctrl+C)
    rclpy.shutdown() # 关闭rclpy

3.2.3 运行测试

修改下setup.py

entry_points={
        'console_scripts': [
            "imu_client=imu_py.imu_client:main",  
            "imu_server=imu_py.imu_server:main"
        ],
    },

发布节点

colcon build --packages-select imu_py
source install/setup.bash
ros2 run imu_py imu_client

订阅节点

source install/setup.bash
ros2 run imu_py imu_server

在这里插入图片描述
可视化

rqt

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1350513.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Unity美术】Unity工程师对3D模型需要达到的了解【二】

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;Uni…

基于ThinkPHP的云盘系统Cloudreve本地搭建并实现远程访问

文章目录 1、前言2、本地网站搭建2.1 环境使用2.2 支持组件选择2.3 网页安装2.4 测试和使用2.5 问题解决 3、本地网页发布3.1 cpolar云端设置3.2 cpolar本地设置 4、公网访问测试5、结语 1、前言 自云存储概念兴起已经有段时间了&#xff0c;各互联网大厂也纷纷加入战局&#…

扫码看图时,多图如何用轮播排列展示?

在扫描二维码看图时&#xff0c;一般图片大多会通过上下排列的方式来展示&#xff0c;如果图片的数量太多&#xff0c;就需要在手机上不断地下滑才能看到所有内容&#xff0c;这种方式会导致在查看图片时感觉疲劳或者眼花的情况。那么想要解决这个问题&#xff0c;我们可以在生…

flutter接入扫码枪的扫描结果,其实就是监听键盘输入,从测试到页面显示出来

检测设备是否正常 首先一定要测试一下你的硬件设备是否正常&#xff0c;虽然有的设备看着插入usb后指示灯什么都亮了&#xff0c;但是不一定就说明设备没问题&#xff0c;这就需要先验证一下&#xff0c;比如打开记事本或者doc文档&#xff0c;然后扫描一下条形码&#xff0c;…

electron 主进程对预加载脚本和渲染进程通信

知识整理 主进程main.js node环境可以使用node的方法预加载脚本可以使用部分node方法,可以理解为是主进程和渲染进程之间的一个桥梁渲染进程属于浏览器环境,不可以使用node方法,可以操作dom等js方法 主进程对渲染进程通信 上一篇文章实现了自定义菜单栏功能,上上篇实现了预加…

程序员30而立的北京之路

作为一名程序员&#xff0c;职业规划和心灵成长是我工作和生活中不可或缺的部分。30岁是一个人生中的重要节点&#xff0c;也是所谓的“而立之年”&#xff0c;在这个阶段&#xff0c;我开始更加关注自己的职业发展和内心成长。在这篇文章中&#xff0c;我将分享我在北京这座城…

YOLOv8改进 | 细节创新篇 | iAFF迭代注意力特征融合助力多目标细节涨点

一、本文介绍 本文给大家带来的改进机制是iAFF&#xff08;迭代注意力特征融合&#xff09;&#xff0c;其主要思想是通过改善特征融合过程来提高检测精度。传统的特征融合方法如加法或串联简单&#xff0c;未考虑到特定对象的融合适用性。iAFF通过引入多尺度通道注意力模块(我…

花几分钟整点jmeter花活,轻松超越90%软件测试

jmeter 可以做性能测试&#xff0c;这个很多人都知道&#xff0c;那你知道&#xff0c;jmeter 可以在启动运行时&#xff0c;指定线程数和运行时间&#xff0c;自定义性能场景吗&#xff1f; 前言 jmeter 性能测试&#xff0c;动态设定性能场景 平时&#xff0c;我们使用 jmet…

使用.Net nanoFramework 驱动ESP32的OLED显示屏

本文介绍如何使用.Net nanoFramework 驱动ESP32的OLED显示屏。我们将会从最基础的部分开始&#xff0c;逐步深入&#xff0c;让你能够理解并实现整个过程。无论你是初学者还是有一定经验的开发者&#xff0c;这篇文章都会对你有所帮助。 1. 硬件准备 1.1 ESP32开发板 这里我们…

安装中望CAD2023 SP2

1.下载中望CAD2023 SP2&#xff0c;并安装&#xff1b; 2.把“flxNetCommon.dll”拷贝到安装目录&#xff08;与“ZWCAD.exe”同一个目录&#xff09;&#xff1b; 3.运行“ZwLicenseManager.exe” 4.点击“激活许可证”&#xff1b; 5.点击“浮动许可” ->“仅配置不查询…

Hotspot源码解析-第十一章

第十一章 11.1 线程 11.1.1 线程的概念 说起线程&#xff0c;首先得提起进程&#xff0c;相信很面试者在回答进程与线程的区别时都会用一句话&#xff1a;“进程是操作系统资源分配的基本单位&#xff0c;而线程是任务调度和执行的基本单位”&#xff0c;只能说这句话部分正…

十大排序总结之——冒泡排序、插入排序

同样&#xff0c;这两几乎也是被淘汰了的算法&#xff0c;尽管它们是稳定的&#xff0c;但是时间复杂度没人喜欢&#xff0c;了解一下就好&#xff0c;没啥好说的&#xff0c;注意最后一句话就行了 一&#xff0c;冒泡排序 1. 算法步骤 共n-1趟&#xff0c;谁两敢冒泡就换了…

SpringBoot多环境配置,让你部署无忧

✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉 🍎个人主页:Leo的博客 💞当前专栏: 循序渐进学SpringBoot ✨特色专栏: MySQL学习 🥭本文内容:SpringBoot多环境配置,让你部署无忧 📚个人知识库: Leo知识库,欢迎大家访…

vite项目中动态引入src失败的问题解决:require is not defined

问题复现 静态引入路径(无问题) <el-menu-item v-for"(item,index) in menuList" :index"item.name" :key"index"><img class"menuItemImg" src"../svg/router/homePage.svg" alt"">{{ item.meta.c…

vue中使用echarts实现省市地图绘制,根据数据显示省市天气图标及温度信息

一、实现效果 使用echarts实现省市地图绘制根据数据显示省下市的天气图标根据数据显示省下市的温度信息 二、实现方法 1、安装echarts插件 npm install echarts --save2、获取省市json数据 https://datav.aliyun.com/portal/school/atlas/area_selector 通过 阿里旗下的高…

hubSpot有哪些功能?

HubSpot是一款综合性的市场营销、销售和服务软件平台&#xff0c;旨在帮助企业实现更有效的客户关系管理和增长。以下是HubSpot的一些主要功能&#xff1a; 市场营销自动化&#xff1a; 制定和执行多渠道的市场营销活动。 创建和管理电子邮件营销、社交媒体发布和广告活动。 …

WMS仓储管理系统可以处理哪些仓库异常情况

随着现代物流行业的快速发展&#xff0c;仓库管理作为其中的核心环节&#xff0c;面临着越来越大的挑战。传统的仓库管理方式已经难以满足日益复杂的需求&#xff0c;因此&#xff0c;WMS仓储管理系统解决方案应运而生。WMS仓储管理系统不仅简化了仓库的日常操作&#xff0c;更…

Redis:原理+项目实战——Redis实战1(session实现短信登录(并剖析问题))

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位大四、研0学生&#xff0c;正在努力准备大四暑假的实习 &#x1f30c;上期文章&#xff1a;Redis&#xff1a;原理速成项目实战——Redis的Java客户端 &#x1f4da;订阅专栏&#xff1a;Redis速成 希望文章对你们有所帮助…

拓扑排序【邻接矩阵邻接表】

拓扑排序 TuoPuSort 一、概念 我们再说拓扑排序时,我们首先了解下​DAG​ 将有向无环图G(V,E)中的所有顶点排成一个线性序列,使图中任意一对顶点u,v,之间不存在环路 DAG是一种特殊的有向图&#xff0c;它由一组顶点和一组有向边组成&#xff0c;且不存在任何环路。 每个顶点表…

华脉智联融合通讯基础引擎平台V2.0发布

华脉智联始于2012年&#xff0c;成立于2017年。历经11年在融合通讯、指挥调度领域的持续研发&#xff0c;于2024年发布华脉智联融合通讯基础引擎平台V2.0。 系统组成 1 核心平台 2 WEB调度台 3 PC调度台 4 云集群APP 5 云执法APP 系统特点 1 多网融合 可以支持多种网络接入和融…