这篇博客是 B 站《古月·ROS2入门21讲》的第七个视频的图文记录,主要介绍了一个常规 ROS 节点的结构是怎样的、如何配置 python 节点的程序入口、基于 opencv 本地与摄像头画面的识别实现。原始视频链接如下:
- B 站视频链接:7. 节点:机器人的工作细胞
1. hello world 节点体验
在正式解析节点之前先运行一下古月的官方代码仓库提供的节点,进入到上一篇博客的 dev_ws 工作空间下编译该空间:
bash
$ cd dev_ws
$ colcon build

编译完成后 source 一下 install 目录下的环境变量:
bash
$ source source install/local_setup.sh

使用下面的命令运行节点,该节点会以 1 Hz 的频率在终端打印 Hello World 字段:
bash
$ ros2 run learning_node node_helloworld

2. 节点结构 - [面向过程]
该文件位于 src/ros2_21_tutorials/learning_node/learning_node/node_hello_world.py 处:

你可以用 vim 或者 vscode 打开这个文件以查看该文件中的内容,这个节点的内容非常短:
python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类
import time
def 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接口
核心代码由三部分构成:
- 初始化节点:
rclpy.init、Node(""); - 功能实现:functions;
- 销毁节点:
destory_node()、rclpy.shutdown();

使用下面命令启动节点:
bash
$ ros2 run learning_node node_helloworld

3. 节点结构 - [面向对象]
该文件位于 dev_ws/src/ros2_21_tutorials/learning_node/learning_node/node_helloworld_class.py 处:
python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类
import time
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节点对象并进行初始化
node.destroy_node() # 销毁节点对象
rclpy.shutdown() # 关闭ROS2 Python接口
核心代码由三部分构成:
- 初始化节点:
rclpy.init、Node(""); - 对象实例化:functions;
- 销毁节点:
destory_node()、rclpy.shutdown();

使用下面的命令运行节点:
bash
$ ros2 run learning_node node_helloworld_class

4. python 程序入口点配置
只有当你的节点是以 python 形式实现的时候才需要配置程序入口点,C++ 实现的节点后面会介绍。
该文件位于 /dev_ws/src/ros2_21_tutorials/learning_node/setup.py 处,关注最下方 entry_points 字段:
python
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',
],
},
其配置格式为,通常情况下会以脚本名作为可执行文件名,但这不是强制要求:
python
可执行文件名 = 模块路径:main函数'
'node_name = learning_mix.node_name:main',

所有编译好的 python 节点会存储在 /home/orin/Desktop/dev_ws/install/learning_node/lib/learning_node 位置处:

【Note】:由于 python 本身是一个解释语言,因此即便是编译好的 lib 文件其本质上 仍是一个 python 文件,这和 C++ 生成一个二进制文件之间是有根本差异的。
你可以打开一个文件看看里面的内容,主要是写明了如何找到对应的 python 脚本位置。

5. 本地图像识别节点
作者在 src/ros2_21_tutorials/learning_node/learning_node/node_object.py 处提供了一个图像识别节点,你需要修改代码第 41 行附近的绝对路径,其原始文件就在当前目录下:
python
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') # 读取图像
image = cv2.imread('/home/orin/Desktop/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接口

在你的环境中安装 python3-opencv:
bash
$ sudo apt-get install python3-opencv

运行该节点:
bash
$ ros2 run learning_node node_object
如果你在运行后出现以下报错:
bash
cv2.error: OpenCV(4.5.4) ./modules/imgproc/src/color.cpp:182: error: (-215:Assertion failed) !_src.empty() in function 'cvtColor'

可能是因为修改了 python 文件后没有重新编译的原因(这一点和 ROS1 存在显著区别),重新编译一下工作空间后再运行即可:
bash
$ colcon build
$ ros2 run learning_node node_object

6. 摄像头图像识别节点
作者在该功能包下还提供了一个使用摄像头进行图像识别的节点,但由于我的设备上没有摄像头硬件,因此在这里就不进行演示了,有条件的读者可以直接运行下面的命令:
bash
$ ros2 run learning_node node_object_webcam