ROS2 -03-工作空间与功能包
文章目录ROS2 工作空间与功能包完全指南一、ROS2 工作空间Workspace1. 什么是工作空间2. 工作空间的目录结构3. 工作空间的类型Overlay 与 Underlay4. 创建工作空间5. 编译工作空间二、ROS2 功能包Package1. 什么是功能包2. 功能包的最小结构3. 创建功能包4. 功能包的依赖管理5. 编译功能包三、功能测试python代码示例发布者与订阅者1. 创建功能包2. 编写发布者节点3. 编写订阅者节点4. 配置 setup.py5. 编译与运行6. 查看节点和话题ROS2 工作空间与功能包C 示例一、准备工作二、创建 C 功能包三、编写发布者节点四、编写订阅者节点五、修改构建文件六、编译与运行七、验证与调试运行结果ROS2 工作空间与功能包完全指南在 ROS2 中工作空间workspace和功能包package是组织和管理代码的核心概念。理解它们对于开发 ROS2 应用程序至关重要。本文将全面介绍这两个概念并提供一个完整的 Python 发布-订阅示例每行代码都附带详细注释。一、ROS2 工作空间Workspace1. 什么是工作空间工作空间是一个包含 ROS2 功能包的目录它提供了一种结构化的方式来组织、构建和运行多个相互关联的功能包。通过工作空间你可以将自定义的代码与 ROS2 基础环境分离方便管理和复用。2. 工作空间的目录结构一个典型的 ROS2 工作空间包含以下子目录workspace_name/ ├── src # 存放所有功能包的源代码 ├── build # 存放编译过程中的中间文件colcon 自动生成 ├── install # 存放编译后的可执行文件、库、配置文件等colcon 自动生成 └── log # 存放编译日志colcon 自动生成src你手动创建并放置自定义功能包的地方。build、install、log由构建工具colcon自动生成通常不需要手动修改。3. 工作空间的类型Overlay 与 UnderlayUnderlay基础的 ROS2 安装环境如系统安装的/opt/ros/humble。每个新工作空间都可以叠加在现有环境之上。Overlay你正在开发的本地工作空间它依赖于 underlay 提供的依赖但可以覆盖或扩展其中的功能包。当你source一个工作空间的install/setup.bash后该工作空间就成为了当前终端的 overlay。4. 创建工作空间使用以下命令创建一个新的工作空间例如ros2_wsmkdir-p~/ros2_ws/src# 创建工作空间根目录和 src 文件夹cd~/ros2_ws# 进入工作空间此时工作空间还是空的接下来我们需要在src文件夹中创建功能包。5. 编译工作空间ROS2 的官方构建工具是colcon。在工作空间根目录下运行colcon build常用选项--packages-select package_name仅编译指定的功能包。--symlink-install使用符号链接安装修改 Python 脚本后无需重新编译。--cmake-args -DCMAKE_BUILD_TYPERelease传递参数给 CMake。编译完成后需要 source 安装文件才能使用该工作空间sourceinstall/setup.bash可以将其添加到~/.bashrc中自动加载二、ROS2 功能包Package1. 什么是功能包功能包是 ROS2 中代码组织的最小单元。它包含节点、库、配置文件、启动文件等并且必须包含描述包信息和依赖关系的package.xml文件以及一个构建文件CMakeLists.txt用于 Csetup.py或setup.cfg用于 Python。2. 功能包的最小结构以 Python 功能包为例my_package/ ├── package.xml # 包清单文件必须 ├── setup.py # Python 包的安装脚本必须 ├── setup.cfg # 配置文件通常用于声明 entry points ├── resource/ # 标记文件夹通常包含一个空文件 my_package ├── test/ # 测试代码文件夹可选 └── my_package/ # 与包同名的 Python 模块存放源代码 └── __init__.pypackage.xml包含包名、版本、作者、许可证以及依赖项。setup.py用于安装 Python 包定义入口点entry points以便将脚本作为节点运行。setup.cfg通常告诉 setuptools 如何安装脚本。resource标记该文件夹为 ROS2 包通常包含一个与包名相同的空文件。my_package实际的 Python 模块放置节点代码。对于 C 包则使用CMakeLists.txt和include/、src/等目录。3. 创建功能包使用ros2 pkg create命令创建功能包。例如创建一个 Python 包cd~/ros2_ws/src ros2 pkg create my_package --build-type ament_python--dependenciesrclpy std_msgs--build-type指定构建类型ament_python用于 Pythonament_cmake用于 C。--dependencies列出该包依赖的其他 ROS2 包如rclpy、std_msgs。这些依赖会被自动添加到package.xml中。创建一个C包cd~/ros2_ws/src ros2 pkg create cpp_pubsub--build-type ament_cmake--dependencies rclcpp std_msgscpp_pubsub/├── CMakeLists.txt # 构建配置文件├── package.xml # 包清单文件├── include/ # 存放头文件可选│ └── cpp_pubsub/└── src/ # 存放源文件4. 功能包的依赖管理在package.xml中依赖通过以下标签声明depend构建和运行时都需要。build_depend仅构建时需要。build_export_depend导出到依赖此包的其他包所需的依赖。exec_depend仅运行时需要。test_depend仅测试时需要。例如package.xml中的依赖部分可能如下dependrclpy/dependdependstd_msgs/depend5. 编译功能包在工作空间根目录使用colcon build可以指定只编译该包colcon build --packages-select my_package三、功能测试python代码示例发布者与订阅者下面我们创建一个简单的发布者-订阅者示例使用 Python 编写并逐行注释。该示例包含两个节点一个发布字符串消息一个订阅并打印消息。1. 创建功能包首先确保你已经创建了工作空间如~/ros2_ws然后在src目录下创建包cd~/ros2_ws/src ros2 pkg create py_pubsub --build-type ament_python--dependenciesrclpy std_msgs这条命令会生成一个名为py_pubsub的 Python 包并声明依赖rclpyROS2 Python 客户端库和std_msgs标准消息类型。2. 编写发布者节点在py_pubsub/py_pubsub目录下创建publisher.py文件cd~/ros2_ws/src/py_pubsub/py_pubsubtouchpublisher.pychmodx publisher.py# 赋予执行权限可选编辑publisher.py内容如下#!/usr/bin/env python3# 指定解释器为 Python3使得可以直接执行该脚本importrclpy# 导入 ROS2 Python 客户端库fromrclpy.nodeimportNode# 导入 Node 类所有 ROS2 节点都继承自它fromstd_msgs.msgimportString# 导入标准字符串消息类型classMinimalPublisher(Node): 自定义的发布者节点类继承自 Node。 def__init__(self):# 调用父类 Node 的构造函数节点名称为 minimal_publishersuper().__init__(minimal_publisher)# 创建一个发布者发布到 topic 话题消息类型为 String队列长度为 10self.publisher_self.create_publisher(String,topic,10)# 创建一个定时器每隔 0.5 秒调用一次 timer_callback 函数# 参数定时器周期秒回调函数self.timerself.create_timer(0.5,self.timer_callback)# 初始化计数器self.i0deftimer_callback(self): 定时器回调函数创建消息并发布。 # 创建 String 类型的消息对象msgString()# 设置消息内容包含计数器的当前值msg.dataHello World: %d%self.i# 调用发布者的 publish 方法发布消息self.publisher_.publish(msg)# 使用节点的日志系统打印信息相当于 ROS1 的 ROS_INFOself.get_logger().info(Publishing: %s%msg.data)# 计数器递增self.i1defmain(argsNone): 主函数初始化节点进入事件循环最后清理。 # 初始化 ROS2 客户端库rclpy.init(argsargs)# 创建 MinimalPublisher 类的实例minimal_publisherMinimalPublisher()# 进入事件循环等待回调函数被触发此处会阻塞直到节点被关闭rclpy.spin(minimal_publisher)# 当 spin 结束后例如按 CtrlC销毁节点可选但推荐minimal_publisher.destroy_node()# 关闭 ROS2 客户端库rclpy.shutdown()if__name____main__:main()3. 编写订阅者节点同样在py_pubsub/py_pubsub目录下创建subscriber.py文件touchsubscriber.pychmodx subscriber.py编辑subscriber.py#!/usr/bin/env python3importrclpyfromrclpy.nodeimportNodefromstd_msgs.msgimportStringclassMinimalSubscriber(Node): 自定义的订阅者节点类继承自 Node。 def__init__(self):# 节点名称为 minimal_subscribersuper().__init__(minimal_subscriber)# 创建一个订阅者订阅 topic 话题消息类型为 String队列长度为 10# 当收到消息时调用 listener_callback 函数self.subscriptionself.create_subscription(String,topic,self.listener_callback,10)# 为了防止 unused variable 警告可以将 subscription 保存为成员变量如上# 但实际上 self.subscription 被使用了所以没问题deflistener_callback(self,msg): 消息回调函数收到消息时打印内容。 # 使用节点的日志系统打印收到的消息self.get_logger().info(I heard: %s%msg.data)defmain(argsNone):rclpy.init(argsargs)minimal_subscriberMinimalSubscriber()rclpy.spin(minimal_subscriber)# 持续等待消息minimal_subscriber.destroy_node()rclpy.shutdown()if__name____main__:main()4. 配置 setup.py为了使ros2 run能够找到我们的节点需要在setup.py的entry_points中添加控制台脚本入口。编辑py_pubsub/setup.pyfromsetuptoolsimportsetupimportosfromglobimportglob package_namepy_pubsubsetup(namepackage_name,version0.0.0,packages[package_name],data_files[# 安装 package.xml 到 share 目录以便 ROS2 发现该包(os.path.join(share,package_name),[package.xml]),# 如果有启动文件或其他资源也可以在这里添加],install_requires[setuptools],zip_safeTrue,maintaineryour_name,maintainer_emailyour_emailexample.com,descriptionPython publisher and subscriber example,licenseApache License 2.0,# 或其他许可证tests_require[pytest],entry_points{console_scripts:[# 格式: 命令名 包名.模块名:主函数名publisher py_pubsub.publisher:main,subscriber py_pubsub.subscriber:main,],},)5. 编译与运行编译工作空间cd~/ros2_ws colcon build --packages-select py_pubsub加载工作空间环境如果尚未加载sourceinstall/setup.bash运行发布者打开终端1source~/ros2_ws/install/setup.bash ros2 run py_pubsub publisher你将看到每隔 0.5 秒打印一条发布消息。运行订阅者打开终端2source~/ros2_ws/install/setup.bash ros2 run py_pubsub subscriber订阅者会收到发布者发送的消息并打印。6. 查看节点和话题使用ros2 node list查看运行中的节点。使用ros2 topic list查看可用话题。使用ros2 topic echo /topic直接查看话题上的消息。ROS2 工作空间与功能包C 示例在前面的回答中我们已经详细介绍了 ROS2 工作空间和功能包的概念。本文将重点聚焦于C 功能包的创建与测试代码提供一个完整的发布者-订阅者示例并逐行注释代码。通过这个示例你将学会如何组织 C 节点、配置构建文件并成功运行。一、准备工作确保你已经安装了 ROS2如 Humble、Iron 或 Rolling并创建了一个工作空间例如ros2_wsmkdir-p~/ros2_ws/srccd~/ros2_ws二、创建 C 功能包使用ros2 pkg create命令创建一个 C 包并指定依赖项rclcppROS2 C 客户端库和std_msgs标准消息类型cd~/ros2_ws/src ros2 pkg create cpp_pubsub --build-type ament_cmake--dependenciesrclcpp std_msgs--build-type ament_cmake指定构建类型为 CMakeC 包的标准。--dependencies自动将依赖添加到package.xml和CMakeLists.txt中。是在 ROS 2 环境中创建一个功能包package 的命令用于开发 C 语言编写的发布-订阅pub-sub通信程序。下面逐部分解释其含义ros2 pkg create这是 ROS 2 提供的命令行工具用于创建新的功能包。功能包是 ROS 2 中组织代码、配置、依赖的基本单元类似于一个项目文件夹。cpp_pubsub这是你创建的功能包的名称可自定义。命名遵循 ROS 惯例[语言]_[功能]这里表示这是一个用 C编写的“发布-订阅”publisher-subscriber示例功能包。–build-type ament_cmake指定功能包的构建类型Build Type。ROS 2 支持多种构建系统ament_cmake是其中一种适用于 C 项目ament_cmake基于 CMake 的构建系统适合编写 C 节点Node。其他可选类型ament_pythonPython 项目、cmake兼容旧版 CMake、ament_cmake_python混合C 和 Python等。这里选择 ament_cmake说明你要开发一个 C 编写的 ROS 2 节点。–dependencies rclcpp std_msgs声明功能包的依赖项Dependencies。ROS 2 节点运行时需要依赖其他库或消息类型这里指定了两个核心依赖1rclcpp是 ROS 2 的 C 客户端库ROS Client Library for C提供了编写 ROS 2 节点的核心API例如创建节点rclcpp::Node创建发布者create_publisher创建订阅者create_subscription处理回调函数、定时器等功能。没有 rclcpp就无法用 C 编写 ROS 2 节点。2std_msgs是 ROS 2 的 标准消息包包含了常用的基础数据类型消息例如std_msgs/msg/String字符串消息std_msgs/msg/Int32整数消息std_msgs/msg/Float64浮点数消息等。如果你的节点需要发送/接收消息比如发布一个字符串就需要依赖 std_msgs或其他自定义消息包。生成的目录结构如下cpp_pubsub/ ├── CMakeLists.txt # 构建配置文件 ├── package.xml # 包清单文件 ├── include/ # 存放头文件可选 │ └── cpp_pubsub/ └── src/ # 存放源文件三、编写发布者节点在src目录下创建publisher.cpp文件cd~/ros2_ws/src/cpp_pubsub/srctouchpublisher.cpp编辑publisher.cpp内容如下每一行都有详细注释// 包含必要的头文件#includechrono// 时间相关用于定时器周期#includefunctional// 用于 std::bind#includememory// 用于 std::make_shared#includestring// 字符串处理#includerclcpp/rclcpp.hpp// ROS2 C 客户端库#includestd_msgs/msg/string.hpp// 标准字符串消息类型usingnamespacestd::chrono_literals;// 允许使用 500ms 这样的字面量/* 发布者节点类继承自 rclcpp::Node */classMinimalPublisher:publicrclcpp::Node{public:MinimalPublisher():Node(minimal_publisher),count_(0)// 节点名称并初始化计数器{// 创建发布者话题名称 topic消息类型 std_msgs::msg::String队列大小 10publisher_this-create_publisherstd_msgs::msg::String(topic,10);// 创建定时器每隔 500ms 调用一次 timer_callback 函数timer_this-create_wall_timer(500ms,std::bind(MinimalPublisher::timer_callback,this));}private:// 定时器回调函数voidtimer_callback(){// 创建一个 String 消息对象automessagestd_msgs::msg::String();message.dataHello, world! std::to_string(count_);// 使用 RCLCPP_INFO 宏打印日志类似 ROS1 的 ROS_INFORCLCPP_INFO(this-get_logger(),Publishing: %s,message.data.c_str());// 发布消息publisher_-publish(message);}// 成员变量rclcpp::Publisherstd_msgs::msg::String::SharedPtr publisher_;// 发布者智能指针rclcpp::TimerBase::SharedPtr timer_;// 定时器智能指针size_t count_;// 消息计数器};/* 主函数 */intmain(intargc,char*argv[]){// 初始化 ROS2rclcpp::init(argc,argv);// 创建节点实例并进入事件循环spin 会一直阻塞直到节点关闭rclcpp::spin(std::make_sharedMinimalPublisher());// 关闭 ROS2rclcpp::shutdown();return0;}四、编写订阅者节点在src目录下创建subscriber.cpp文件touchsubscriber.cpp编辑subscriber.cpp内容如下#includememory#includerclcpp/rclcpp.hpp#includestd_msgs/msg/string.hppusingstd::placeholders::_1;// 用于 std::bind 占位符/* 订阅者节点类 */classMinimalSubscriber:publicrclcpp::Node{public:MinimalSubscriber():Node(minimal_subscriber){// 创建订阅者话题 topic消息类型 std_msgs::msg::String队列大小 10// 当收到消息时调用 topic_callback 函数subscription_this-create_subscriptionstd_msgs::msg::String(topic,10,std::bind(MinimalSubscriber::topic_callback,this,_1));}private:// 消息回调函数voidtopic_callback(conststd_msgs::msg::String::SharedPtr msg)const{// 打印接收到的消息RCLCPP_INFO(this-get_logger(),I heard: %s,msg-data.c_str());}// 成员变量订阅者智能指针rclcpp::Subscriptionstd_msgs::msg::String::SharedPtr subscription_;};intmain(intargc,char*argv[]){rclcpp::init(argc,argv);rclcpp::spin(std::make_sharedMinimalSubscriber());rclcpp::shutdown();return0;}五、修改构建文件C 包使用CMakeLists.txt来定义如何编译可执行文件。打开cpp_pubsub/CMakeLists.txt并进行如下修改cmake_minimum_required(VERSION 3.8) project(cpp_pubsub) # 默认使用 C17 标准ROS2 要求 if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES Clang) add_compile_options(-Wall -Wextra -Wpedantic) endif() # 查找依赖包 find_package(ament_cmake REQUIRED) find_package(rclcpp REQUIRED) # 查找 rclcpp find_package(std_msgs REQUIRED) # 查找 std_msgs # 编译发布者节点可执行文件 add_executable(publisher src/publisher.cpp) # 链接需要的库 ament_target_dependencies(publisher rclcpp std_msgs) # 编译订阅者节点可执行文件 add_executable(subscriber src/subscriber.cpp) ament_target_dependencies(subscriber rclcpp std_msgs) # 安装可执行文件到 lib 目录使得 ros2 run 可以找到 install(TARGETS publisher subscriber DESTINATION lib/${PROJECT_NAME}) # 如果存在 launch 文件或其他资源也可以在这里安装 # ament 包相关设置 ament_package()注意ament_target_dependencies是 ROS2 提供的便捷函数它会自动处理头文件路径和依赖库链接。package.xml文件在创建包时已自动添加了依赖检查一下确保包含以下内容dependrclcpp/dependdependstd_msgs/depend六、编译与运行编译工作空间cd~/ros2_ws colcon build --packages-select cpp_pubsub加载工作空间环境每次新终端都需要执行或添加到~/.bashrcsource~/ros2_ws/install/setup.bash运行发布者节点打开终端1ros2 run cpp_pubsub publisher你将看到每 0.5 秒输出一条发布日志。运行订阅者节点打开终端2ros2 run cpp_pubsub subscriber订阅者会打印接收到的消息。七、验证与调试使用ros2 node list查看正在运行的节点应看到/minimal_publisher和/minimal_subscriber。使用ros2 topic list查看话题应看到/topic。使用ros2 topic echo /topic直接查看话题上的消息流。如果需要重新编译可以在工作空间根目录再次执行colcon build或者只编译修改过的包colcon build --packages-select cpp_pubsub。运行结果
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2418398.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!