|
汽车零部件采购、销售通信录 填写你的培训需求,我们帮你找 招募汽车专业培训老师
1、前言
前一期文章“终于有人把ROS(机器人操作系统)讲明白了”从宏观层面科普了下ROS2的基本情况,我已经迫不及待要用它做点事情了。从网上检索了一大圈,甚至买了本ROS2的书回来,看后依然觉得很迷茫,不知道从什么地方开始,于是又结合官网文档、ChatGPT、还有网络文章折腾了两周,才终于调通了一个非常基础的用Python写节点程序的小栗子。这才有了今天这篇记录贴。
由于知识点有点多,还涉及到代码编写,我尽量用最简单的语言描述,代码也尽量做到最简。希望大家能看得轻松些,但有所学,皆是本文之幸。
以下简述,安装好ROS2之后,如何开始创建第一个ROS2应用,运行多个Python程序节点。
2、ROS2创建一个节点
开始之前,先交代下我的软件情况:
操作系统:Ubuntu20.04
编程语言:Python 3.8.10(Ubuntu20.04自带)
我在home目录下专门建立了一个文件夹“ROS2_study”,后续将在这里创建一系列子文件夹以用作不同的ROS2案例的工作空间,方便实现不同的小栗子。
首先打开终端,切换到ROS2_study这个目录下。
cd ~/ROS2_study
然后创建我们的第一个文件夹(也即我们的第一个工作空间):
mkdir ros2_ws_demo001/
接下来,切换到该工作空间下,本次ROS2的所有操作都将在该目录下操作。
cd ros2_ws_demo001
确定了工作空间,就像盖房子已经选好了地址,打好了地基。
然后在这个地基上,就要开始搭建房子了,并且对房子进行各种定义。
ROS2是怎么搭建这个房子的呢?它是要先创建一个统一的固定名字的文件夹“src”,然后在这个文件夹下面再去具体定义各个房间。
因此,要先创建一个src文件夹,并切换到这个目录下。
mkdir src
cd src
在src文件夹下面定义各个房间早已经工程化了,直接在命令行属于ros2相关命令就能快速搭建好一个基础框架出来。使用如下命令就可以创建包,这个包就是功能包。
ros2 pkg create package_001 --build-type ament_python --dependencies rclpy
运行后的效果如下:
这个命令会在src目录下再创建一个package_001的文件夹(称为功能包),并且自动在package_001下生成一系列内容。到目前为止,工作空间(ros2_ws_demo001)下的文件目录如下所示:
.
└── src
└── package_001
├── package_001
│ └── __init__.py
├── package.xml
├── resource
│ └── package_001
├── setup.cfg
├── setup.py
└── test
├── test_copyright.py
├── test_flake8.py
└── test_pep257.py
5 directories, 8 files
虽然一下多了很多文件夹和文件,但是这些都是一个功能包的标配,就像住酒店,选择一个标间,其实到哪个地方都基本长得差不多:主卧有床、桌子、电视,卫生间有浴盆、浴霸、镜子,等等。以后等我们创建其他功能包,内部的结构也都还是这样。
不过这次,我们并不需要搞懂所有的房间布置内容,只需要在package_001中定义好一个节点,然后再在setup.py文件中配置好我们要调用这个节点就可以了。
怎么定义一个python程序的节点呢?
首先新建一个python文件,使用gedit工具新建并打开:
gedit src/package_001/package_001/node_001.py
然后填入如下的内容:
import rclpy
from rclpy.node import Node
def main(args=None):
rclpy.init(args=args) # init rclpy
my_node = Node("node_001") # to create a Node object
my_node.get_logger().info("hello,I am node_001") # to print a message.
rclpy.spin(my_node) # to keep Node running
rclpy.shutdown() # to close Node
保存文件后,再更改下配置文件setup.py:
gedit src/package_001/setup.py
console_scripts的值原本为空,这里需要将console_scripts的值添加上我们新建的节点,如下所示:
'console_scripts': [
"node_001 = package_001.node_001:main"
],
保存之后退出setup.py文件。
到这里终于把源码部分定义好了(相当与定义好了房间的框架),接下来,只需要编译一下这个工作空间(相当于快速填充墙体),一个最简单的房子就搭建好了。
因此我们回到工作空间目录下,执行如下命令进行编译就行了。
cd ~/ROS2_study/ros2_ws_demo001
colcon build
编译完成后,我们发现工作空间下又多出了几个文件夹。分别是build、install、和log。而在这几个文件夹下面又很多其他文件夹和文件生成,此处不细表。
接下来,终于可以召唤神兽啦,来检查下我们的劳动成果吧。
要运行我们自定义的python节点程序,命令行的执行方式如下:
source install/setup.bash
ros2 run package_001 node_001
如下是我这里的运行界面,可以看到屏幕上打印了“hello,I am node_001”,这个正是我们在自定义节点里做的事。
然后,新开一个终端,输入如下指令,可以检查下我们正在运行的节点列表(虽然目前只有一个节点在运行):
ros2 node list
到此,我们来稍微总结下使用python创建一个ros2节点并运行的整个过程:
1、创建一个独立的工作空间(其实就是创建了一个文件夹);
2、在工作空间下创建src文件夹(源码文件夹),并在src下创建功能包(非常简单,使用ros2命令行工具直接就生成了);
3、在功能包里面编辑一个节点python程序,唯一需要你动动脑子写的东西,但我们这个例子也才区区8行代码,已经简单到了极致;
4、在功能包里面配置setup.py文件,将我们上一步创建的节点程序调用起来;
5、回到工作空间,使用colcon工具编译(build)整个代码;
6、运行编译后的代码;
整个一套下来就像打拳一样,按照固定的套路就能完成一套组合拳。
3、ROS2创建两个节点
接下来,稍微扩展下上面的代码,做两个ROS2节点,然后运行起来。毕竟ROS2的核心优势就是管理多个节点,我们这就直达ROS2核心功能了。
为了方便学习备份,我不想破坏上面的工作空间,这里我将上一节的工作空间复制出一个新的工作空间,接下来我将在这个新的工作空间里完成我新的测试。
cd ~/ROS2_study
cp -rf ros2_ws_demo001/ ros2_ws_demo002_2_nodes/
cd ros2_ws_demo002_2_nodes
现在我们在新的工作空间里面了。接下来要做的事情也是非常简单,将第一个节点文件复制生成第二个节点文件,并稍作编辑以作区分。
cp src/package_001/package_001/node_001.py src/package_001/package_001/node_002.py
gedit src/package_001/package_001/node_002.py
这里我用gedit打开了新的节点文件,说是编辑内容其实也仅仅是修改下节点名称和打印内容:
import rclpy
from rclpy.node import Node
def main(args=None):
rclpy.init(args=args) # init rclpy
my_node = Node("node_002") # to create a Node object
my_node.get_logger().info("hello,I am node_002") # to print a message.
rclpy.spin(my_node) # to keep Node running
rclpy.shutdown() # to close Node
就这样,第二个节点文件也创建好了,我们把它配置到setup.py文件中:
gedit src/package_001/setup.py
编辑配置文件内容如下所示:
'console_scripts': [
"node_001 = package_001.node_001:main",
"node_002 = package_001.node_002:main"
],
然后,退回到新的工作空间下,进行编译。
cd ~/ROS2_study/ros2_ws_demo002_2_nodes
colcon build
编译完成后,在当前终端直接启动第一个节点:
source install/setup.bash
ros2 run package_001 node_001
然后新打开一个终端,运行第二个节点:
source install/setup.bash
ros2 run package_001 node_002
两个终端的运行界面如下,我们看到两个节点都打印出了我们定义的内容:
然后再来打开一个终端确认下目前正在运行的节点有哪些:
ros2 node list
至此,我们实现了两个ROS2节点的创建并完成了调用。
4、ROS2使用launch文件启动多个节点
效仿上述做法我们其实可以创建3个、4个、5个、好多好多个节点,但难道每启动一个节点都要新打开一个终端在命令行输入以下两段指令吗?
source install/setup.bash
ros2 run <package_name> <node_name>
这也太傻了,幸好ROS2为我们考虑到了这一点,通过配置launch文件,即可一次性将所有节点都设置好,然后统一启动。
接下来我们来实操一下。
首先备份上一节的工作空间,复制生成新的工作空间并进入。
cd ~/ROS2_study/
cp -rf ros2_ws_demo002_2_nodes/ ros2_ws_demo003_2_nodes_launch/
cd ros2_ws_demo003_2_nodes_launch/
然后在功能包下面手动创建一个launch文件夹:
mkdir src/package_001/launch
在launch文件夹下新建一个python文件用作启动文件,这里我随意命令为了“my_multi_nodes_launch.py”,内容如下:
# my_multi_nodes_launch.py
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(
package='package_001',
executable='node_001',
name='node_001',
output='screen',
),
Node(
package='package_001',
executable='node_002',
name='node_002',
output='screen',
),
])
然后,依然需要在setup.py文件中引入该launch文件以保证编译的时候能找到它。setup.py更改后内容如下:
from setuptools import setup
package_name = 'package_001'
setup(
name=package_name,
version='0.0.0',
packages=[package_name],
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
('share/' + package_name, ['launch/my_multi_nodes_launch.py']),
],
install_requires=['setuptools'],
zip_safe=True,
maintainer='goodman',
maintainer_email='goodman@todo.todo',
description='TODO: Package description',
license='TODO: License declaration',
tests_require=['pytest'],
entry_points={
'console_scripts': [
"node_001 = package_001.node_001:main",
"node_002 = package_001.node_002:main"
],
},
)
看起来很长,但其实这里只添加了一句话:
('share/' + package_name, ['launch/my_multi_nodes_launch.py']),
最后,回到工作空间编译一下:
cd ~/ROS2_study/ros2_ws_demo003_2_nodes_launch/
colcon build
然后运行这个launch文件(注意这次的运行方式有些不同了):
source install/setup.bash
ros2 launch package_001 my_multi_nodes_launch.py
新的运行界面如下:
然后用ros2 node list确认下正在运行的节点:
OK,至此我们通过launch方法实现了一次性启动多个节点。
5、下一步计划
通过本案例的学习,我们初步成功实现了多个节点的配置及启动,对工作空间、功能包、节点等概念有了初步的了解。成就感满满哒。
但是哦,本案例还是有点简单的,两个节点只是各自在干各自的活儿(打印消息),怎么让两个节点之间通信起来互相配合着干活呢?这就是接下来要学的了。
持续分享中,欢迎大家的持续关注。 |
|