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

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.0self.Q1: float = 0.0  # 四元数Q1self.Q2: float = 0.0  # 四元数Q2self.Q3: float = 0.0  # 四元数Q3# 判断串口是否打开成功if self.IMU_Usart.isOpen():print("open success")else:print("open failed")# 回调函数返回周期time_period = 0.001self.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_Ximu_data.linear_acceleration.y = self.ACC_Yimu_data.linear_acceleration.z = self.ACC_Zimu_data.angular_velocity.x = self.GYRO_X * 3.1415926 / 180.0  # unit transfer to rad/simu_data.angular_velocity.y = self.GYRO_Y * 3.1415926 / 180.0imu_data.angular_velocity.z = self.GYRO_Z * 3.1415926 / 180.0imu_data.orientation.x = self.Q0imu_data.orientation.y = self.Q1imu_data.orientation.z = self.Q2imu_data.orientation.w = self.Q3self.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)  # 初始化rclpynode = 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) # 初始化rclpynode = 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 AddTwoIntsclass 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 = arequest.b = bself.client_.call_async(request).add_done_callback(self.result_callback_)def main(args=None):rclpy.init(args=args) # 初始化rclpynode = 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 AddTwoIntsclass 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.breturn responsedef main(args=None):rclpy.init(args=args) # 初始化rclpynode = 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.mzph.cn/news/590336.shtml

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

相关文章

OSG读取和添加节点学习

之前加载了一个模型&#xff0c;代码是&#xff0c; osg::Group* root new osg::Group(); osg::Node* node new osg::Node(); node osgDB::readNodeFile("tree.osg"); root->addChild(node); root是指向osg::Group的指针&#xff1b; node是 osg:…

字节高级Java面试真题

今年IT寒冬&#xff0c;大厂都裁员或者准备裁员&#xff0c;作为开猿节流主要目标之一&#xff0c;我们更应该时刻保持竞争力。为了抱团取暖&#xff0c;林老师开通了《知识星球》&#xff0c;并邀请我阿里、快手、腾讯等的朋友加入&#xff0c;分享八股文、项目经验、管理经验…

appium入门基础

介绍 appium支持在不同平台的UI自动化&#xff0c;如web,移动端,桌面端等。还支持使用java&#xff0c;python&#xff0c;js等语言编写自动化代码。主要用于自动化测试脚本&#xff0c;省去重复的手动操作。 Appium官网 安装 首先必须环境有Node.js用于安装Appium。 总体来…

设计模式—行为型模式之模板方法模式

设计模式—行为型模式之模板方法模式 在模板模式&#xff08;Template Pattern&#xff09;中&#xff0c;一个抽象类公开定义了执行它的方法的方式模板。它的子类可以按需要重写方法实现&#xff0c;但调用将以抽象类中定义的方式进行。 模板方法&#xff08;Template Metho…

TransNeXt:稳健的注视感知ViT学习笔记

论文地址&#xff1a;https://arxiv.org/pdf/2311.17132.pdf 代码地址&#xff1a; GitHub - DaiShiResearch/TransNeXt: Code release for TransNeXt model 可以直接在ImageNet上训练的分类代码&#xff1a;GitHub - athrunsunny/TransNext-classify 代码中读取数据的部分修改…

Rust学习笔记006:代码组织

Crate 在Rust中&#xff0c;“crate” 是指 Rust 的代码单元&#xff0c;它可以包含一个或多个模块&#xff08;modules&#xff09;。Rust 的 crate 分类主要有两个方面&#xff1a;库&#xff08;Library Crates&#xff09;和二进制&#xff08;Binary Crates&#xff09;。…

病理HE学习贴(自备)

目录 正常结构 癌症HE 在线学习 以胃癌的学习为例 正常结构 1&#xff1a;胃粘膜正常结构和细胞分化 ●表面覆盖小凹上皮细胞(主要标志物&#xff1a;MUC5AC)以保护黏膜。 ●胃底腺固有腺体由黏液颈细胞(MUC6)、主细胞(Pepsinogen l)和壁细胞(Proton pump α-subunit)组…

算法练习Day23 (Leetcode/Python-回溯算法)

46. Permutations Given an array nums of distinct integers, return all the possible permutations. You can return the answer in any order. Example 1: Input: nums [1,2,3] Output: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]思路&#xff1a;此题可用回溯…

oracle学习(6)

其他数据库对象 视图&#xff1a; 常见数据库对象——视图&#xff1a;从表中抽出的逻辑上相关的数据集合。 所以&#xff1a;1. 视图基于表。2. 视图是逻辑概念。3. 视图本身没有数据。 创建视图 创建语法与创建表类似&#xff0c;只需要将table → view即可: SQL> …

Selenium教程04:鼠标+键盘网页的模拟操作

在webdriver 中&#xff0c;鼠标操作都封装在ActionChains类中&#xff0c;使用的时候需要导入这个包。 from selenium.webdriver import ActionChainsActionChains方法列表如下&#xff1a; click(on_elementNone) ——单击鼠标左键click_and_hold(on_elementNone) ——点击…

将PPT4页并排成1页

将PPT4页并排成1页打印 解决方法: 方法一 在打印时选择&#xff1a; 打开 PPT&#xff0c;点击文件选项点击打印点击整页幻灯片点击4张水平放置的幻灯平页面就会显示4张PPT显示在一张纸上 方法二 另存为PDF&#xff1a; 打开电脑上的目标PPT文件&#xff0c;点击文件点击…

可拖拽流程图组件开发

效果 说在前面 流程图在技术领域是一种常见的可视化工具&#xff0c;用于展示系统、应用或业务流程的各个步骤以及它们之间的关系。它们可以帮助开发人员和项目团队更好地理解和规划复杂的流程&#xff0c;从而提高工作效率和准确性。但是&#xff0c;传统的静态流程图有时无法…

《深入理解C++11:C++11新特性解析与应用》笔记七

第七章 为改变思考方式而改变 7.1 指针空值--nullptr 7.1.1 指针空值&#xff1a;从0到NULL&#xff0c;再到nullptr 传统C头文件里NULL是一个宏定义&#xff1a; 在函数重载同时出现int和char *参数版本的函数时&#xff0c;使用NULL作为参数调用函数会调用int参数版本&…

elasticsearch系列九:异地容灾-CCR跨集群复制

概述 起初只在部分业务中采用es存储数据&#xff0c;在主中心搭建了个集群&#xff0c;随着es在我们系统中的地位越来越重要&#xff0c;数据也越来越多&#xff0c;针对它的安全性问题也越发重要&#xff0c;那如何对es做异地容灾呢&#xff1f; 今天咱们就一起看下官方提供的…

【电商项目实战】商品详情显示与Redis存储购物车信息

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《电商项目实战》。&#x1f3af;&#x1f3af; &am…

合伙企业有哪些分类

合伙企业分为&#xff1a;普通合伙企业和有限合伙企业。其中&#xff0c;普通合伙企业又包含特殊的普通合伙企业。 1、普通合伙企业由2人以上普通合伙人(没有上限规定)组成。 普通合伙企业中&#xff0c;合伙人对合伙企业债务承担无限连带责任。 特殊的普通合伙企业中&#xf…

软件测试/测试开发丨Python 封装 学习笔记

封装的概念 封装&#xff08;Encapsulation&#xff09; 隐藏&#xff1a;属性和实现细节&#xff0c;不允许外部直接访问暴露&#xff1a;公开方法&#xff0c;实现对内部信息的操作和访问 封装的作用 限制安全的访问和操作&#xff0c;提高数据安全性可进行数据检查&#x…

『番外篇九』SwiftUI 实战:打造一款“五脏俱全”的网络图片显示 App(上)

概览 俗话说得好:“读书破万卷,下笔如有神”。不过如果把这句话放到编程的学习上可就不那么贴切了。 要想熟练掌握一门编程语言,光看书是绝对不够的。我们还需尽可能的多撸码、早撸码,撸到无路可退、海枯石烂才有可能一窥门径。 在本篇和续篇博文中,我们将和小伙伴们一起…

c++_09_继承

1 继承 C的继承是弱继承 继承的语法&#xff1a; class 子类 : 继承方式1 基类1, 继承方式2 基类2, ... { ... }; 继承方式&#xff1a; 共有继承 public 保护继承 protected 私有继承 private 2 继承的基本属性&#xff08;3种继承方式均有&#xff09; 继承所…

Javaweb之JDBC的详细解析

2. JDBC介绍(了解) 2.1 介绍 通过Mybatis的快速入门&#xff0c;我们明白了&#xff0c;通过Mybatis可以很方便的进行数据库的访问操作。但是大家要明白&#xff0c;其实java语言操作数据库呢&#xff0c;只能通过一种方式&#xff1a;使用sun公司提供的 JDBC 规范。 Mybatis…