1755|7

15

帖子

0

TA的资源

一粒金砂(中级)

楼主
 

ROS2基础:节点、功能包、工作空间与其编译运行 [复制链接]

ROS2基础:节点、功能包、工作空间与其编译运行

src:ROS2新书测评活动Launch官仓-Github
QS:需要下载编译器:sudo apt install python3-colcon-common-extensions

[TOCM]

介绍:本篇博客将会介绍ROS2创建/编译/运行一个节点必备的知识,包括使用Launch的节点批量启动
注意:命令行代码默认项目已经source install/setup.bash,执行的工作目录为工作空间my_project

从创建一个节点开始

ROS2将节点-Node视为机器人的细胞,每个节点都是一个独立的可执行文件/功能部件。比如对于一个摄像头节点,他的职能就是拍摄图片发布到数据空间-DataBus,之后,需要这些数据的节点,比如显示屏节点,或者实体分割节点会订阅这些数据进行显示或处理。在ROS2中,每个节点独占一个进程,且由于ROS2的节点管理是基于哈希表名称映射的,所以每个节点必须有一个唯一的名称。

工作空间

工作空间-WorkSpace,就类比一个项目目录-ProjectDir,或者一个工作台,或者一个工具箱。如果拿其他技术栈来比喻:

ROS2 前端页面 后端服务 游戏开发 建模渲染 智能项目
工作空间 Project Project Project Project 工作空间
功能包 View App/Service 场景 渲染链 Notebook
节点 Component Router/API 单个脚本材质贴图等 合成器节点 单模型

即:节点可以独立存在,且可以独立运行,但是得借由多个节点组成的包才是一个完整的系统,一个包可以有一个或多个节点,一个工作空间涵盖了多个包以及其之下的节点。

创建一个工作空间就是新建一个存放此新工程文件的文件夹:

  1. mkdir -p my_project/src # 之后的命令行操作默认在my_project目录

如果是新工程,则可以colcon build --packages-select=false初始化此my_project工作空间,初始化会创建空的install, build, log目录,并设置初始的install/setup.bash,加载此bash环境后,ros2命令将会额外检测此工作空间下定义的消息、节点等等。

  1. source install/setup.bash

如果是旧工程,可以使用ROS2自带的rosdep命令自动安装src代码空间中各功能包的依赖库:

  1. rosdep install --from-paths src --ignore-src -r -y

功能包

一个功能包-Package,一般是一个可独立运行的完整组件,内含有一个或者多个节点,比如移动控制、视觉感知、自主导航等等,将完整机器人分割为功能包使用了解耦的思想,功能包可以独立方向至其他机器人项目。

  1. # 创建功能包
  2. ros2 pkg create --build-type <build-type> <package-name>
  3. build-type: ament_cmake | ament_python,根据适合/喜欢的语言进行选择
  4. package-name: 功能包名,即src文件夹下将会新建什么名字的文件夹(功能包)
  5. # 比如创建一个位于 my_project/src 下的使用Python风格API的 my_package:
  6. ros2 pkg create --build-type ament_python my_package

一个功能包含有一个描述元信息的package.xml,包括包的版权、作者信息、开发日期、功能描述等等。另外还有一个本地化包含有的包构建配置文件,比如Cmake包含有的CMakeLists.txt,Python包含有的setup.py。其中使用方式与各语言通用包构建发布的配置一致。

节点

节点是一个单独的可执行文件,其中无论是C++还是Py,此文件将会启动一个Ros::Node对象,比如:

  1. from rcipy.node import Node
  2. if __name__ == '__main__':
  3. # 一定注意:节点名称得保证唯一性
  4. node = Node("node_name")
  5. # 循环等待ROS退出
  6. rclpy.spin(node)
  7. # 退出后将会销毁节点实例,关闭ROS接口
  8. node.destory_node()
  9. rclpy.shutdown()
  1. #include <rclcpp/rclcpp.hpp>
  2. int main(int argc, char* argv[]) {
  3. rclcpp::init(argc, argv);
  4. auto node = rclcpp::Node("node_name");
  5. rclcpp::spin(std::shared_ptr(node));
  6. rclcpp::shutdown();
  7. return 0;
  8. }

但是,启动一个空节点没有太大的实际意义,所以一般启动都是实现特定功能的节点子类:

  1. class MyNode(rclpy.Node):
  2. def __init__(self, name: str, ...):
  3. super.__init__(name)
  4. ...
  5. ...
  1. class MyNode: public rclcpp::Node
  2. {
  3. public:
  4. MyNode(const string &name, ...): Node(name)
  5. {
  6. ...
  7. }
  8. ...
  9. }

编译运行

启动节点不是单纯的运行写好的节点文件,而是需要合理配置编译选项后,编译,通过ros命令来启动:

  1. ros2 run <工作空间名字> <节点启动名>

其中需要设置的编译选项:

  1. ## Python: setup.py
  2. ...
  3. entry_points = { # 设置程序入口
  4. 'console_scripts': [
  5. # 格式为:`节点启动名 = 相对于工作空间下的main所在位置-my_node.py`
  6. "node_class_name = my_package.my_node:main"
  7. # 此时启动此节点:ros2 run my_project node_class_name
  8. ],
  9. }
  10. ...
  1. // Cpp: CMakeLists.txt
  2. ...
  3. # 查找依赖的功能包 rclcpp 提供ROS2 C++的基础接口
  4. find_package(rclcpp REQUIRED)
  5. # 添加一个可执行文件,节点启动名字为`emm`,对应源码src/my_node.cpp
  6. add_executable(emm src/my_node.cpp)
  7. # 配置编译时需要链接的库,比如 rclcpp 库:
  8. ament_target_dependencies(emm rclcpp)
  9. # 将编译生成的可执行文件`emm`拷贝到`install/lib`:
  10. install(
  11. TARGETS
  12. emm
  13. DESTINATION lib/${PROJECT_NAME}
  14. )
  15. # 编译后将可以这样运行此节点:ros2 run my_project emm
  16. ...

编译操作:

  1. colcon build # 默认编译,直接编译此工作目录下的所有包的所有节点
  2. colcon build --packages-select <package_name> # 只编译指定功能包
  3. colcon build --packages-up-to <package_name> # 编译指定功能包及其依赖

清除编译:

  1. # 直接了当删除除了源码的目录即可
  2. rm -rf install/ build/ log/

[==========]

到批量启动多个节点

前情提要:节点是一个独立进程 =意味着=> 每启动一个节点都会占用一个shell,除非nohup后台启动或者使用容器等等虚拟化手段启动。当你需要启动的节点大于5个时,你会发现tmux也会变得不好使,那么如何使用一个命令行启动一键多个节点?且可以不用每次启动都麻烦的配置每个节点的启动配置?哈哈,ROS人有自己的Makefile:Launch

Launch

Launch,多节点启动与配置脚本,官方仓库:Github,官方说明:design.ros2.org。Launch启动文件使用Python进行描述,可以配置命令行输入的各项参数,并同时使用Python原有的编程功能。注意:Launch的功能是利用模板生成命令行命令,类似于模板引擎展开为界面代码一样,并不影响节点源码的行为!但是可以通过条件判断来在生成命令的同时更改命令行参数(类似于条件编译),或者使用循环批量生成大量的节点进行启动。

基础使用示例

  1. import os
  2. from launch import LaunchDescription
  3. # 注意:这里的Node是用来生成模板命令字符串的,而不是ros2用于启动的Node
  4. from launch_ros.actions import Node
  5. # 下面导入的这个方法用来查询功能包路径
  6. from ament_index_python.packages import get_package_share_directory
  7. def generate_launch_description():
  8. return LaunchDescription([
  9. # 启动第一个Python节点,相当于:ros2 run my_py_package1 my_node1
  10. # 但是指定了别名:my_node1
  11. Node(
  12. package='my_py_package1',
  13. executable='my_node1',
  14. name='my_node1',
  15. output='screen'
  16. ),
  17. # 启动第二个Python节点
  18. Node(
  19. package='my_py_package1',
  20. executable='my_node2',
  21. name='my_node2',
  22. output='screen'
  23. ),
  24. # 使用循环生成3个节点
  25. *[Node(
  26. package='my_cpp_package',
  27. executable='my_node3',
  28. name=f'cpp_node{i}',
  29. output='screen'
  30. ) for i in range(3)],
  31. ])

Launch的编译和启动:

Launch文件在使用前需要移动至install,但是直接在此文件夹下创建launch.py文件,在清理编译文件时会一不小心全删掉,所以一般放在功能包-Package目录下的launch文件夹下,但是如果图省事直接放于功能包-Package根目录,则常习惯命名为xxx.launch.py来表示这是一个launch描述文件,之后配置相应的编译指导文件,将其作为资源文件直接拷贝到install/share即可。

  1. ## Python: setup.py
  2. ...
  3. data_files = [
  4. # data file将会在编译时被原样拷贝到lib
  5. (
  6. os.path.join('share', package_name, 'launch'),
  7. glob(os.path.join('launch', '*.launch.py'))
  8. ), ...
  9. ]
  10. ...
  1. // Cpp: CMakeLists.txt
  2. ...
  3. install(DIRECTORY
  4. launch
  5. DESTINATION share/${PROJECT_NAME}/
  6. )
  7. ...
  1. # Launch命令行操作,在编译-build后:
  2. ros2 launch <工作空间名称> <launch文件名> # eg. ros2 launch my_project emm.launch.py

常用描述方法

综上所述,一个Launch启动文件可以用以下模板描述:

  1. def generate_launch_description():
  2. return LaunchDescription([ ... ])
  3. # 即:在文件中定义 `generate_launch_description` 函数,返回启动文件的描述类

其中常用的描述方法有:

  1. from launch import LaunchDescription
  2. from launch.actions import (
  3. GroupAction,
  4. IncludeLaunchDescription,
  5. )
  6. from launch_ros.actions import (
  7. Node,
  8. PushRosNamespace,
  9. )
  10. from launch.launch_description_sources import PythonLaunchDescriptionSource
  11. from ament_index_python.packages import get_package_share_directory
  12. def generate_launch_description():
  13. # 普通的加载此工作空间下节点示例:
  14. node1 = Node(
  15. package='my_py_package1',
  16. executable='my_node1',
  17. )
  18. # 当某些节点已经被另外一个Launch定义的时候,可以直接加载此Launch定义的节点
  19. others = IncludeLaunchDescription(
  20. PythonLaunchDescriptionSource([
  21. os.path.join(
  22. # get_package_share_directory可以获取环境包所在的目录
  23. get_package_share_directory("环境中的其他包的包名"),
  24. # 习惯将launch文件放于launch之下,且名字为*.launch.py
  25. "launch", "xxx.launch.py"
  26. )
  27. ])
  28. )
  29. # 通常无法保证外部Launch不与当前的名称冲突,所以常常给外部Launch套一层命名空间:
  30. other2 = IncludeLaunchDescription(
  31. PythonLaunchDescriptionSource([
  32. os.path.join(
  33. get_package_share_directory("环境中的其他包的包名"),
  34. "launch", "xxx.launch.py"
  35. )
  36. ])
  37. )
  38. other2_with_ns = GroupAction(
  39. actions=[
  40. PushRosNamespace('namespace_233'),
  41. other2,
  42. ]
  43. )
  44. # 最后返回集合后的描述(如果仅仅一个Node,直接返回Node也可)
  45. return LaunchDescription([node1, others, other2_with_ns])

常用参数配置

  1. Node(
  2. package="节点所在的功能包",
  3. executable="编译后的可执行文件名",
  4. namespace="节点所在命名空间",
  5. name="对节点进行重命名",
  6. parameters=[...], # 加载参数 / 参数文件路径
  7. # 资源重映射,比如将话题名为"/emm"映射到话题名为"/asd/qwe"
  8. remappings=[("原资源路径", "映射资源路径"), ("/emm", "/asd/qwe"), ...],
  9. arguments=['-d', '命令行参数'],
  10. )

使用参数-Parameters:

  1. # 参数指定的可以是yaml文件的路径,也可以是Launch使用DeclareLaunchArgument定义的参数:
  2. from launch.actions import DeclareLaunchArgument
  3. from launch.substitutions import LaunchConfiguration, TextSubstitution
  4. def generate_launch_description():
  5. # 以初始的海龟的背景颜色参数为示例
  6. background_r_arg = DeclareLaunchArgument(
  7. 'background_r', default_value=TextSubstitution(text='0')
  8. )
  9. """假设剩余的background_g和background_b参数在config/emm.yaml文件:
  10. /turtlesim2/sim:
  11. ros__parameters:
  12. background_g: 0
  13. background_b: 0
  14. """
  15. config_gb_yaml_path = os.path.join(
  16. get_package_share_directory("my_project"),
  17. 'config', 'emm.yaml'
  18. )
  19. return LaunchDescription([
  20. background_r_arg, # 定义的参数得加入列表才能使用
  21. Node(
  22. ...,
  23. parameters=[
  24. config_gb_yaml_path, # yaml文件路径形式加载参数
  25. {
  26. # 使用之前Launch文件内定义的参数
  27. 'background_r': LaunchConfiguration('background_r')
  28. },
  29. ]
  30. ),
  31. ])

最新回复

又是rust,,,这个怕是成熟度更底   详情 回复 发表于 2025-2-24 13:47
点赞 关注

回复
举报

7793

帖子

2

TA的资源

五彩晶圆(高级)

沙发
 

ROS2成熟了吗?前几年关注的时候,还说ROS2不太得行

点评

哦对了,机器人操作系统还有一个近年来随着Rust推广的[DORA](https://dora-rs.ai/)[-RS](https://github.com/dora-rs),这位才是真正的“默默无闻”  详情 回复 发表于 2025-2-21 16:35
看需求用不就行了,“成熟”这一词没有定论,毕竟一直在发展,换个思路,Python现在[2025.02.21]都3.13了,他成熟了吗,不一定,未来可能会更改各种特性,还会有不少的功能加入或者删减  详情 回复 发表于 2025-2-21 16:27
 
个人签名

默认摸鱼,再摸鱼。2022、9、28

 

回复

15

帖子

0

TA的资源

一粒金砂(中级)

板凳
 
freebsder 发表于 2025-2-21 16:11 ROS2成熟了吗?前几年关注的时候,还说ROS2不太得行

看需求用不就行了,“成熟”这一词没有定论,毕竟一直在发展,换个思路,Python现在[2025.02.21]都3.13了,他成熟了吗,不一定,未来可能会更改各种特性,还会有不少的功能加入或者删减

 
 
 

回复

15

帖子

0

TA的资源

一粒金砂(中级)

4
 

补充:包的依赖记得配置到`package.xml`!在命令行添加依赖在pkg create时使用`--dependencies`选项,或者直接手动在package.xml添加:<depend>依赖名</depend>。与配置maven差不多……

 
 
 

回复

15

帖子

0

TA的资源

一粒金砂(中级)

5
 
freebsder 发表于 2025-2-21 16:11 ROS2成熟了吗?前几年关注的时候,还说ROS2不太得行

哦对了,机器人操作系统还有一个近年来随着Rust推广的[DORA](https://dora-rs.ai/)[-RS](https://github.com/dora-rs),这位才是真正的“默默无闻”

点评

又是rust,,,这个怕是成熟度更底  详情 回复 发表于 2025-2-24 13:47
 
 
 

回复

15

帖子

0

TA的资源

一粒金砂(中级)

6
 

补充:一定注意,若编译包之后运行节点报错找不到包,一定先确定自己source了install/setup.bash启用了当前工作空间的环境!

 
 
 

回复

15

帖子

0

TA的资源

一粒金砂(中级)

7
 
查看本帖全部讨论,请登录或者注册
 
 
 

回复

7793

帖子

2

TA的资源

五彩晶圆(高级)

8
 
查看本帖全部讨论,请登录或者注册
 
个人签名

默认摸鱼,再摸鱼。2022、9、28

 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条
有奖直播:当AI遇见仿真,会有什么样的电子行业革新之路?
首场直播:Simcenter AI 赋能电子行业研发创新
直播时间:04月15日14:00-14:50

查看 »

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网 9

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表