Robot Operating System——创建可执行文件项目的步骤

2024-08-21 06:28

本文主要是介绍Robot Operating System——创建可执行文件项目的步骤,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

大纲

  • 初始化环境
  • 创建Package
  • 代码
  • 添加依赖(package.xml)
  • 修改编译描述
    • find_package寻找依赖库
    • 指定代码路径和编译类型(可执行文件/动态库)
    • 链接依赖的库
    • 完整文件
  • 编译
  • 测试
  • 总结
  • 参考资料

之前我们看到ROS2中,有的Node的实现逻辑存在于动态库中,而动态库又有隐式加载和手动加载等几种模式。这些例子都比较复杂。本文将介绍如何从0到1创建一个最简单的工程,其Node都在可执行文件中,以保证过程的清晰。

初始化环境

在《Robot Operating System——深度解析自动隐式加载动态库的运行模式》一文中,我们展现了ROS2可执行文件的链接指令。可以看到它依赖了很多ROS2环境相关的动态库,所以我们在创建工程之前也要初始化环境。

source /opt/ros/jazzy/setup.bash

关于环境的安装可以参见《Robot Operating System——Ubuntu上以二进制形式安装环境》。

创建Package

ros2 pkg create --build-type ament_cmake --license Apache-2.0 two_node_pipeline

在这里插入图片描述
可以看到ros2帮我们创建好了相关的目录结构
在这里插入图片描述
剩下的事就是我们要填充这个工程。

代码

在two_node_pipeline/src目录下新建一个main.cpp文件。
代码我们直接借用https://github.com/ros2/demos/blob/jazzy/intra_process_demo/src/cyclic_pipeline/cyclic_pipeline.cpp的源码。它直接在一个可执行文件中编译了两个Node。

// Copyright 2015 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.#include <chrono>
#include <cinttypes>
#include <cstdio>
#include <memory>
#include <string>
#include <utility>#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/int32.hpp"using namespace std::chrono_literals;// Node that produces messages.
struct Producer : public rclcpp::Node
{Producer(const std::string & name, const std::string & output): Node(name, rclcpp::NodeOptions().use_intra_process_comms(true)){// Create a publisher on the output topic.pub_ = this->create_publisher<std_msgs::msg::Int32>(output, 10);std::weak_ptr<std::remove_pointer<decltype(pub_.get())>::type> captured_pub = pub_;// Create a timer which publishes on the output topic at ~1Hz.auto callback = [captured_pub]() -> void {auto pub_ptr = captured_pub.lock();if (!pub_ptr) {return;}static int32_t count = 0;std_msgs::msg::Int32::UniquePtr msg(new std_msgs::msg::Int32());msg->data = count++;printf("Published message with value: %d, and address: 0x%" PRIXPTR "\n", msg->data,reinterpret_cast<std::uintptr_t>(msg.get()));pub_ptr->publish(std::move(msg));};timer_ = this->create_wall_timer(1s, callback);}rclcpp::Publisher<std_msgs::msg::Int32>::SharedPtr pub_;rclcpp::TimerBase::SharedPtr timer_;
};// Node that consumes messages.
struct Consumer : public rclcpp::Node
{Consumer(const std::string & name, const std::string & input): Node(name, rclcpp::NodeOptions().use_intra_process_comms(true)){// Create a subscription on the input topic which prints on receipt of new messages.sub_ = this->create_subscription<std_msgs::msg::Int32>(input,10,[](std_msgs::msg::Int32::UniquePtr msg) {printf(" Received message with value: %d, and address: 0x%" PRIXPTR "\n", msg->data,reinterpret_cast<std::uintptr_t>(msg.get()));});}rclcpp::Subscription<std_msgs::msg::Int32>::SharedPtr sub_;
};int main(int argc, char * argv[])
{setvbuf(stdout, NULL, _IONBF, BUFSIZ);rclcpp::init(argc, argv);rclcpp::executors::SingleThreadedExecutor executor;auto producer = std::make_shared<Producer>("producer", "number");auto consumer = std::make_shared<Consumer>("consumer", "number");executor.add_node(producer);executor.add_node(consumer);executor.spin();rclcpp::shutdown();return 0;
}

添加依赖(package.xml)

package.xml 是 ROS 2 包的元数据文件,定义了包的基本信息和依赖关系。

打开two_node_pipeline/package.xml,添加代码中include头文件(#include "rclcpp/rclcpp.hpp" #include "std_msgs/msg/int32.hpp")所依赖的其他库。

  <depend>rclcpp</depend><depend>std_msgs</depend>

完整文件如下

<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3"><name>two_node_pipeline</name><version>0.0.0</version><description>TODO: Package description</description><maintainer email="f304646673@gmail.com">fangliang</maintainer><license>Apache-2.0</license><buildtool_depend>ament_cmake</buildtool_depend><depend>rclcpp</depend><depend>std_msgs</depend><test_depend>ament_lint_auto</test_depend><test_depend>ament_lint_common</test_depend><export><build_type>ament_cmake</build_type></export>
</package>

修改编译描述

find_package寻找依赖库

再打开two_node_pipeline/CMakeLists.txt中添加上述头文件(#include "rclcpp/rclcpp.hpp" #include "std_msgs/msg/int32.hpp")所依赖的库

find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)

在 CMake 中,find_package 命令用于查找并加载外部包或库。它的主要作用如下:

  • 查找包:find_package 会在系统中查找指定的包或库,并加载其配置文件。这些配置文件通常包含包的路径、库文件、头文件等信息。

  • 设置变量:如果找到包,find_package 会设置一些变量,这些变量可以在后续的 CMake 脚本中使用。例如,<PackageName>_FOUND 变量会被设置为 TRUE,表示找到了包。

  • 导入目标:一些包会定义 CMake 导入目标(imported targets),这些目标可以直接在 target_link_libraries 等命令中使用。

指定代码路径和编译类型(可执行文件/动态库)

设定编译结果名称

set(EXECUTABLE_NAME "two_node_pipeline")

指定编译结果是可执行文件

# Collect all source files in this directory
file(GLOB SOURCES "src/*.cpp")# Add the executable target
add_executable(${EXECUTABLE_NAME} ${SOURCES})

链接依赖的库

最后将代码中用到的库(#include "rclcpp/rclcpp.hpp" #include "std_msgs/msg/int32.hpp")链接到可执行文件中。

ament_target_dependencies(${EXECUTABLE_NAME} rclcpp std_msgs)

完整文件

cmake_minimum_required(VERSION 3.8)
project(two_node_pipeline)if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")add_compile_options(-Wall -Wextra -Wpedantic)
endif()# find dependencies
find_package(ament_cmake REQUIRED)
# uncomment the following section in order to fill in
# further dependencies manually.
# find_package(<dependency> REQUIRED)find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)set(EXECUTABLE_NAME "two_node_pipeline")# Collect all source files in this directory
file(GLOB SOURCES "src/*.cpp")# Add the executable target
add_executable(${EXECUTABLE_NAME} ${SOURCES})ament_target_dependencies(${EXECUTABLE_NAME} rclcpp std_msgs)if(BUILD_TESTING)find_package(ament_lint_auto REQUIRED)# the following line skips the linter which checks for copyrights# comment the line when a copyright and license is added to all source filesset(ament_cmake_copyright_FOUND TRUE)# the following line skips cpplint (only works in a git repo)# comment the line when this package is in a git repo and when# a copyright and license is added to all source filesset(ament_cmake_cpplint_FOUND TRUE)ament_lint_auto_find_test_dependencies()
endif()ament_package()

编译

cd two_node_pipeline/build
cmake ..
make

在这里插入图片描述

测试

./two_node_pipeline 

在这里插入图片描述

总结

  1. 初始化环境source /opt/ros/jazzy/setup.bash,以保证ROS2的基础动态库路径在系统PATH中。
  2. 通过`ros2 pkg create --build-type ament_cmake --license Apache-2.0 <YOUR-Folder-Name>'创建目录结构和基础文件。
  3. 填充代码。
  4. package.xml中添加代码中显式依赖的库。
  5. CMakeLists.txt中寻找和链接代码中显式依赖的库。

参考资料

  • https://docs.ros.org/en/rolling/Tutorials/Beginner-Client-Libraries/Creating-A-Workspace/Creating-A-Workspace.html#new-directory

这篇关于Robot Operating System——创建可执行文件项目的步骤的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1092378

相关文章

用Java打造简易计算器的实现步骤

《用Java打造简易计算器的实现步骤》:本文主要介绍如何设计和实现一个简单的Java命令行计算器程序,该程序能够执行基本的数学运算(加、减、乘、除),文中通过代码介绍的非常详细,需要的朋友可以参考... 目录目标:一、项目概述与功能规划二、代码实现步骤三、测试与优化四、总结与收获总结目标:简单计算器,设计

Python 中 requests 与 aiohttp 在实际项目中的选择策略详解

《Python中requests与aiohttp在实际项目中的选择策略详解》本文主要介绍了Python爬虫开发中常用的两个库requests和aiohttp的使用方法及其区别,通过实际项目案... 目录一、requests 库二、aiohttp 库三、requests 和 aiohttp 的比较四、requ

SpringBoot项目启动后自动加载系统配置的多种实现方式

《SpringBoot项目启动后自动加载系统配置的多种实现方式》:本文主要介绍SpringBoot项目启动后自动加载系统配置的多种实现方式,并通过代码示例讲解的非常详细,对大家的学习或工作有一定的... 目录1. 使用 CommandLineRunner实现方式:2. 使用 ApplicationRunne

Python在固定文件夹批量创建固定后缀的文件(方法详解)

《Python在固定文件夹批量创建固定后缀的文件(方法详解)》文章讲述了如何使用Python批量创建后缀为.md的文件夹,生成100个,代码中需要修改的路径、前缀和后缀名,并提供了注意事项和代码示例,... 目录1. python需求的任务2. Python代码的实现3. 代码修改的位置4. 运行结果5.

使用IntelliJ IDEA创建简单的Java Web项目完整步骤

《使用IntelliJIDEA创建简单的JavaWeb项目完整步骤》:本文主要介绍如何使用IntelliJIDEA创建一个简单的JavaWeb项目,实现登录、注册和查看用户列表功能,使用Se... 目录前置准备项目功能实现步骤1. 创建项目2. 配置 Tomcat3. 项目文件结构4. 创建数据库和表5.

Python项目打包部署到服务器的实现

《Python项目打包部署到服务器的实现》本文主要介绍了PyCharm和Ubuntu服务器部署Python项目,包括打包、上传、安装和设置自启动服务的步骤,具有一定的参考价值,感兴趣的可以了解一下... 目录一、准备工作二、项目打包三、部署到服务器四、设置服务自启动一、准备工作开发环境:本文以PyChar

使用SpringBoot创建一个RESTful API的详细步骤

《使用SpringBoot创建一个RESTfulAPI的详细步骤》使用Java的SpringBoot创建RESTfulAPI可以满足多种开发场景,它提供了快速开发、易于配置、可扩展、可维护的优点,尤... 目录一、创建 Spring Boot 项目二、创建控制器类(Controller Class)三、运行

python安装完成后可以进行的后续步骤和注意事项小结

《python安装完成后可以进行的后续步骤和注意事项小结》本文详细介绍了安装Python3后的后续步骤,包括验证安装、配置环境、安装包、创建和运行脚本,以及使用虚拟环境,还强调了注意事项,如系统更新、... 目录验证安装配置环境(可选)安装python包创建和运行Python脚本虚拟环境(可选)注意事项安装

多模块的springboot项目发布指定模块的脚本方式

《多模块的springboot项目发布指定模块的脚本方式》该文章主要介绍了如何在多模块的SpringBoot项目中发布指定模块的脚本,作者原先的脚本会清理并编译所有模块,导致发布时间过长,通过简化脚本... 目录多模块的springboot项目发布指定模块的脚本1、不计成本地全部发布2、指定模块发布总结多模

SpringBoot项目删除Bean或者不加载Bean的问题解决

《SpringBoot项目删除Bean或者不加载Bean的问题解决》文章介绍了在SpringBoot项目中如何使用@ComponentScan注解和自定义过滤器实现不加载某些Bean的方法,本文通过实... 使用@ComponentScan注解中的@ComponentScan.Filter标记不加载。@C