ROS2入门21讲__第07讲__节点:机器人的工作细胞
目录
前言
通信模型
案例一:Hello World节点(面向过程)
运行效果
代码解析
创建节点流程
案例二:Hello World节点(面向对象)
运行效果
代码解析
创建节点流程
案例三:物体识别节点
运行效果
代码解析
案例四:机器视觉识别节点
运行效果
代码解析
节点命令行操作
思考题
前言
机器人是各种功能的综合体,每一项功能就像机器人的一个工作细胞,众多细胞通过一些机制连接到一起,成为了一个机器人整体。
在ROS中,我们给这些 “细胞”取了一个名字,那就是节点。
通信模型
完整的机器人系统可能并不是一个物理上的整体,比如这样一个的机器人:
在机器人身体里搭载了一台计算机A,它可以通过机器人的眼睛——摄像头,获取外界环境的信息,也可以控制机器人的腿——轮子,让机器人移动到想要去的地方。除此之外,可能还会有另外一台计算机B,放在你的桌子上,它可以远程监控机器人看到的信息,也可以远程配置机器人的速度和某些参数,还可以连接一个摇杆,人为控制机器人前后左右运动。
这些功能虽然位于不同的计算机中,但都是这款机器人的工作细胞,也就是节点,他们共同组成了一个完整的机器人系统。
- 节点在机器人系统中的职责就是执行某些具体的任务,从计算机操作系统的角度来看,也叫做进程;
- 每个节点都是一个可以独立运行的可执行文件,比如执行某一个python程序,或者执行C++编译生成的结果,都算是运行了一个节点;
- 既然每个节点都是独立的执行文件,那自然就可以想到,得到这个执行文件的编程语言可以是不同的,比如C++、Python,乃至Java、Ruby等更多语言。
- 这些节点是功能各不相同的细胞,根据系统设计的不同,可能位于计算机A,也可能位于计算机B,还有可能运行在云端,这叫做分布式,也就是可以分布在不同的硬件载体上;
- 每一个节点都需要有唯一的命名,当我们想要去找到某一个节点的时候,或者想要查询某一个节点的状态时,可以通过节点的名称来做查询。
节点也可以比喻是一个一个的工人,分别完成不同的任务,他们有的在一线厂房工作,有的在后勤部门提供保障,他们互相可能并不认识,但却一起推动机器人这座“工厂”,完成更为复杂的任务。
接下来,我们就来看看, 节点这个工作细胞,到底该如何实现。
案例一:Hello World节点(面向过程)
ROS2中节点的实现当然是需要编写程序了,我们从Hello World例程开始,先来实现一个最为简单的节点,功能并不复杂,就是循环打印一个“Hello World”字符串到终端中。
运行效果
大家先不要着急看代码,是骡子是马,先拉出来溜溜,我们通过ros2 run命令,运行编译好的课程代码,看下这个节点执行的效果如何,然后再来分析代码的实现过程,做到知其然也知其所以然。
ros2 run learning_node node_helloworld
运行成功后,可以在终端中看到循环打印“Hello World”字符串的效果。
代码解析
这个节点是如何实现的呢?我们来看下代码。
learning_node/node_helloworld.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-"""
@作者: 古月居(www.guyuehome.com)
@说明: ROS2节点示例-发布“Hello World”日志信息, 使用面向过程的实现方式
"""import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类
import timedef main(args=None): # ROS2节点主入口main函数rclpy.init(args=args) # ROS2 Python接口初始化node = Node("node_helloworld") # 创建ROS2节点对象并进行初始化while rclpy.ok(): # ROS2系统是否正常运行node.get_logger().info("Hello World") # ROS2日志输出time.sleep(0.5) # 休眠控制循环时间node.destroy_node() # 销毁节点对象 rclpy.shutdown() # 关闭ROS2 Python接口
完成代码的编写后需要设置功能包的编译选项,让系统知道Python程序的入口,打开功能包的setup.py文件,加入如下入口点的配置:
entry_points={'console_scripts': ['node_helloworld = learning_node.node_helloworld:main',],
创建节点流程
代码中出现的函数大家未来会经常用到,大家先不用纠结函数的具体使用方法,更重要的是理解节点的编码流程。
总结一下,想要实现一个节点,代码的实现流程是这样做:
- 编程接口初始化
- 创建节点并初始化
- 实现节点功能
- 销毁节点并关闭接口
大家如果有学习过C++或者Pyhton的话,应该可以发现这里我们使用的是面向过程的编程方法,这种方式虽然实现简单,但是对于稍微复杂一点的机器人系统,就很难做到模块化编码。
案例二:Hello World节点(面向对象)
所以在ROS2的开发中,我们更推荐大家使用面向对象的编程方式,比如刚才的代码就可以改成这样,虽然看上去复杂了一些,但是代码会具备更好的可读性和可移植性,调试起来也会更加方便。
运行效果
接下来运行一下调整后的节点:
ros2 run learning_node node_helloworld_class
代码解析
功能虽然一样,但是程序的结构发生了变化,我们具体看一下这份代码。
learning_node/node_helloworld_class.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-"""
@作者: 古月居(www.guyuehome.com)
@说明: ROS2节点示例-发布“Hello World”日志信息, 使用面向对象的实现方式
"""import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类
import time"""
创建一个HelloWorld节点, 初始化时输出“hello world”日志
"""
class HelloWorldNode(Node):def __init__(self, name):super().__init__(name) # ROS2节点父类初始化while rclpy.ok(): # ROS2系统是否正常运行self.get_logger().info("Hello World") # ROS2日志输出time.sleep(0.5) # 休眠控制循环时间def main(args=None): # ROS2节点主入口main函数rclpy.init(args=args) # ROS2 Python接口初始化node = HelloWorldNode("node_helloworld_class") # 创建ROS2节点对象并进行初始化rclpy.spin(node) # 循环等待ROS2退出node.destroy_node() # 销毁节点对象rclpy.shutdown() # 关闭ROS2 Python接口
完成代码的编写后需要设置功能包的编译选项,让系统知道Python程序的入口,打开功能包的setup.py文件,加入如下入口点的配置:
entry_points={'console_scripts': ['node_helloworld = learning_node.node_helloworld:main','node_helloworld_class = learning_node.node_helloworld_class:main',],
创建节点流程
所以总体而言,节点的实现方式依然是这四个步骤,只不过编码方式做了一些改变而已。
- 编程接口初始化
- 创建节点并初始化
- 实现节点功能
- 销毁节点并关闭接口
到这里为止,大家是不是心里还有一个疑惑,机器人中的节点不能只是打印Hello World吧,是不是得完成一些具体的任务。
案例三:物体识别节点
没错,接下来我们就以机器视觉的任务为例,模拟实际机器人中节点的实现过程。
我们先从网上找到一张苹果的图片,通过编写一个节点来识别图片中的苹果。
运行效果
在这个例程中,我们将用到一个图像处理的库——OpenCV,运行前请使用如下指令安装:
sudo apt install python3-opencv
然后就可以运行例程啦:
ros2 run learning_node node_object #注意修改图片路径后重新编译
运行前需要将learning_node/node_object.py代码中的图片路径,修改为实际路径,修改后重新编译运行即可:
image = cv2.imread('/home/hcx/dev_ws/src/ros2_21_tutorials/learning_node/learning_node/apple.jpg')
例程运行成功后,会弹出一个可视化窗口,可以看到苹果被成功识别啦,一个绿色框会把苹果的轮廓勾勒出来,中间的绿点表示中心点。
代码解析
在这个例程中,我们加入了图像识别的处理过程,模拟一个节点的功能,关于图像处理的具体实现,并不是此处的重点,大家更多要关注我们是如何通过节点的概念来实现一个具体的机器人功能。
learning_node/node_object.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-"""
@作者: 古月居(www.guyuehome.com)
@说明: ROS2节点示例-通过颜色识别检测图片中出现的苹果
"""import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类import cv2 # OpenCV图像处理库
import numpy as np # Python数值计算库lower_red = np.array([0, 90, 128]) # 红色的HSV阈值下限
upper_red = np.array([180, 255, 255]) # 红色的HSV阈值上限def object_detect(image):hsv_img = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) # 图像从BGR颜色模型转换为HSV模型mask_red = cv2.inRange(hsv_img, lower_red, upper_red) # 图像二值化contours, hierarchy = cv2.findContours(mask_red, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE) # 图像中轮廓检测for cnt in contours: # 去除一些轮廓面积太小的噪声if cnt.shape[0] < 150:continue(x, y, w, h) = cv2.boundingRect(cnt) # 得到苹果所在轮廓的左上角xy像素坐标及轮廓范围的宽和高cv2.drawContours(image, [cnt], -1, (0, 255, 0), 2)# 将苹果的轮廓勾勒出来cv2.circle(image, (int(x+w/2), int(y+h/2)), 5, (0, 255, 0), -1)# 将苹果的图像中心点画出来cv2.imshow("object", image) # 使用OpenCV显示处理后的图像效果cv2.waitKey(0)cv2.destroyAllWindows()def main(args=None): # ROS2节点主入口main函数rclpy.init(args=args) # ROS2 Python接口初始化node = Node("node_object") # 创建ROS2节点对象并进行初始化node.get_logger().info("ROS2节点示例:检测图片中的苹果")image = cv2.imread('/home/hcx/dev_ws/src/ros2_21_tutorials/learning_node/learning_node/apple.jpg') # 读取图像object_detect(image) # 苹果检测rclpy.spin(node) # 循环等待ROS2退出node.destroy_node() # 销毁节点对象rclpy.shutdown() # 关闭ROS2 Python接口
完成代码的编写后需要设置功能包的编译选项,让系统知道Python程序的入口,打开功能包的setup.py文件,加入如下入口点的配置:
entry_points={'console_scripts': ['node_helloworld = learning_node.node_helloworld:main','node_helloworld_class = learning_node.node_helloworld_class:main','node_object = learning_node.node_object:main',],
案例四:机器视觉识别节点
用图片进行识别好像还不太合理,机器人应该有眼睛呀,没问题,接下来我们就让节点读取摄像头的图像,动态识别其中的苹果,或者类似颜色的物体。
运行效果
启动一个终端,运行如下节点:
ros2 run learning_node node_object_webcam #注意设置摄像头
如果是在虚拟机中操作,需要进行以下设置: 1. 把虚拟机设置为兼容USB3.1; 2. 在可移动设备中将摄像头连接至虚拟机。
运行成功后,该节点就可以驱动摄像头,并且实时识别摄像头中的红色物体啦。
代码解析
相比之前的程序,这里最大的变化是修改了图片的来源,使用OpenCV中的VideoCapture()来驱动相机,并且周期read摄像头的信息,并进行识别。
learning_node/node_object_webcam.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-"""
@作者: 古月居(www.guyuehome.com)
@说明: ROS2节点示例-通过摄像头识别检测图片中出现的苹果
"""import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类import cv2 # OpenCV图像处理库
import numpy as np # Python数值计算库lower_red = np.array([0, 90, 128]) # 红色的HSV阈值下限
upper_red = np.array([180, 255, 255]) # 红色的HSV阈值上限def object_detect(image):hsv_img = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) # 图像从BGR颜色模型转换为HSV模型mask_red = cv2.inRange(hsv_img, lower_red, upper_red) # 图像二值化contours, hierarchy = cv2.findContours(mask_red, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE) # 图像中轮廓检测for cnt in contours: # 去除一些轮廓面积太小的噪声if cnt.shape[0] < 150:continue(x, y, w, h) = cv2.boundingRect(cnt) # 得到苹果所在轮廓的左上角xy像素坐标及轮廓范围的宽和高cv2.drawContours(image, [cnt], -1, (0, 255, 0), 2) # 将苹果的轮廓勾勒出来cv2.circle(image, (int(x+w/2), int(y+h/2)), 5, (0, 255, 0), -1) # 将苹果的图像中心点画出来cv2.imshow("object", image) # 使用OpenCV显示处理后的图像效果cv2.waitKey(50)def main(args=None): # ROS2节点主入口main函数rclpy.init(args=args) # ROS2 Python接口初始化node = Node("node_object_webcam") # 创建ROS2节点对象并进行初始化node.get_logger().info("ROS2节点示例:检测图片中的苹果")cap = cv2.VideoCapture(0)while rclpy.ok():ret, image = cap.read() # 读取一帧图像if ret == True:object_detect(image) # 苹果检测node.destroy_node() # 销毁节点对象rclpy.shutdown() # 关闭ROS2 Python接口
完成代码的编写后需要设置功能包的编译选项,让系统知道Python程序的入口,打开功能包的setup.py文件,加入如下入口点的配置:
entry_points={'console_scripts': ['node_helloworld = learning_node.node_helloworld:main','node_helloworld_class = learning_node.node_helloworld_class:main','node_object = learning_node.node_object:main','node_object_webcam = learning_node.node_object_webcam:main',],
节点命令行操作
节点命令的常用操作如下:
$ ros2 node list # 查看节点列表
$ ros2 node info <node_name> # 查看节点信息
思考题
现在,大家应该熟悉节点这个工作细胞的概念和实现方法了,回到这个机器人系统的框架图,我们还会发现另外一个问题。
电脑B中的摇杆,要控制机器人运动,这两个节点岂不是应该有某种连接,比如摇杆节点发送一个速度指令给运动节点,收到后机器人开始运动。
同理,如果我们想要改变机器人的速度,负责配置参数的节点就得发送一个指令给运动节点,如果电脑B想要显示机器人看到的图像,电脑A中的摄像头节点就得把图像发送过来。
没错,在一个ROS机器人的系统中,节点并不是孤立的,他们之间会有很多种机制保持联系,下一节,我们将给大家介绍这些机制中最为常用的一种。
相关文章:

ROS2入门21讲__第07讲__节点:机器人的工作细胞
目录 前言 通信模型 案例一:Hello World节点(面向过程) 运行效果 代码解析 创建节点流程 案例二:Hello World节点(面向对象) 运行效果 代码解析 创建节点流程 案例三:物体识别节点 …...

k8s node NotReady后会发生什么?
K8s 是一种强大的容器编排和管理平台,能够高效地调度、管理和监控容器化应用程序;其本身使用声明式语义管理着集群内所有资源模型、应用程序、存储、网络等多种资源,Node 本身又属于 K8s 计算资源,上面承载运行着各种类型的应用程…...

uni-starter创建App项目最全流程(日后还有其他功能会不断更新)
一、创建项目 在HbuilderX中点击创建项目,选择uni-starter模板,选择阿里云、Vue3,填写项目名称后点击创建。如果没有下载过uni-starter会自动下载该插件,如下图: 二、 创建云服务器并关联项目 如果是第一次使用&#…...
动态IP和静态IP区别
1.可变性:当设备重新连接时,动态IP将分配新的IP地址,静态IP将保持不变。 2.适用场景:动态IP适用于普通用户或小型办公室,静态IP适用于需要特定IP地址的服务或应用。 3.价格:动态IP通常比静态IP更经济。 4.管理和配置:动…...

蓝牙(2):BR/EDR的连接过程;查询(发现)=》寻呼(连接)=》安全建立=》认证=》pair成功;类比WiFi连接过程。
4.2.1 BR/EDR 流程: 查询(发现)》寻呼(连接)》安全建立》认证》pair成功 4.2.1.1 查询(发现)流程Inquiry (discovering) 类比WiFi的probe request/response 蓝牙设备使用查询流程来发现附近的…...
源码部署EFK
目录 资源列表 基础环境 关闭防护墙 关闭内核安全机制 修改主机名 添加hosts映射 一、部署elasticsearch 修改limit限制 部署elasticsearch 修改配置文件 单节点 集群(3台节点集群为例) 启动 二、部署filebeat 部署filebeat 添加配置文件 启动 三、部署kiban…...

CSDN智能总结助手
github项目地址: https://github.com/anjude/little-demo/tree/master 获取CSDN的user name和user token 打开csdn,打开控制台 - Application - Cookies,找到domain为blog.csdn.net的cookie,复制user_name和user_token的值 把上…...
setImmediate是在当前事件循环的所有周期的末尾执行,还是再当前事件循环的当前周期的下一个周期执行?
实际上,setImmediate 的回调函数会在当前事件循环的当前周期的末尾执行,而不是下一个周期。 在事件循环中,任务分为宏任务(macrotask)和微任务(microtask)。setImmediate 的回调函数属于宏任务…...
建材行业工程设计资质动态核查不通过怎么办
详细了解核查结果:首先,需要仔细阅读核查结果,了解不通过的具体原因。这些原因可能涉及企业基本情况、技术负责人情况、主要人员情况、设备和厂房情况、业绩和信誉等方面。 针对问题制定整改计划:根据核查结果,针对存…...

二叉数之插入操作
首先是题目 给定二叉搜索树(BST)的根节点 root 和要插入树中的值 value ,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。 注意,可能存在多种有效…...
【Python】全局变量与init的区别
一个脚本里,设置全局变量,和初始化类时__init__中加载,有什么区别? 在Python脚本中,使用全局变量和在类的__init__方法中加载数据有几个关键区别: 作用域: 全局变量:全局变量在整个…...
JAVA学习-练习试用Java实现“位1的个数”
问题: 编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 1 的个数(也被称为汉明重量)。 提示: 请注意,在某些语言(如…...

HTML静态网页成品作业(HTML+CSS)——魅族商城首页网页(1个页面)
🎉不定期分享源码,关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 🏷️本套采用HTMLCSS,未使用Javacsript代码,共有1个页面。 二、作品演示 三、代…...

Windows DNS 服务器配置转发器
DNS服务器转发器 在企业中由于自身条件的限制, 可能本身的DNS新能并不是很好,这个时候通过使用转发器功能, 将收到的DNS请求转发给另外一台高性能的DNS服务器,让其做后面的迭代查询。 1. 选择DNS服务器, 右击选择属性…...

基于FPGA的VGA协议实现----条纹-文字-图片
基于FPGA的VGA协议实现----条纹-文字-图片 引言: 随着数字电子技术的飞速发展,现场可编程门阵列(FPGA)因其高度的灵活性和并行处理能力,在数字系统设计中扮演着越来越重要的角色。FPGA能够实现复杂的数字逻辑&#…...
hdfs中MapReduce中的shuffle,combine和partitioner(hadoop,Hdfs)
1- MapReduce中shuffle阶段的工作流程以及何如优化该阶段? 分区 ,排序 ,溢写 ,拷贝到对应reduce机器上 ,增加combiner ,压缩溢写的文件 2-MapReduce中combine的作用,一般使用情景,…...

Linux应用入门(二)
1. 输入系统应用编程 1.1 输入系统介绍 常见的输入设备有键盘、鼠标、遥控杆、书写板、触摸屏等。用户经过这些输入设备与Linux系统进行数据交换。这些设备种类繁多,如何去统一它们的接口,Linux为了统一管理这些输入设备实现了一套能兼容所有输入设备的…...

高仿果汁导航模板
参考原文:果汁导航风格模板_1234FCOM专注游戏工具及源码例子分享 极速云...
机器学习之一分类支持向量机(One-class SVM)
一分类支持向量机(One-class SVM)是一种用于异常检测(outlier detection)和新颖性检测(novelty detection)的无监督学习算法。与传统的SVM不同,一分类SVM仅使用一种类别的数据进行训练,目的是在高维空间中找到一个最大边界超平面,将大部分数据点包含在超平面的一侧,从…...

签发免费https证书的方式
目录 http访问和https访问的区别 实现https后有哪些好处: 如何申请、安装部署免费https证书: 在浏览网页时,最常见的是http访问,但是也有一部分网站前缀是https,且浏览器网址栏会出现“安全”字样,或是绿…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...

STM32F4基本定时器使用和原理详解
STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...

ElasticSearch搜索引擎之倒排索引及其底层算法
文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...

华为OD机考-机房布局
import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...
Python Einops库:深度学习中的张量操作革命
Einops(爱因斯坦操作库)就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库,用类似自然语言的表达式替代了晦涩的API调用,彻底改变了深度学习工程…...