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

news2024/11/14 1:21:02

大纲

  • 初始化环境
  • 创建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 files
  set(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 files
  set(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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2057451.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

案例 | 生产制造中的直线度测量

关键词&#xff1a;直线度测量仪,直线度 生产中不仅需要评价产品的外观尺寸&#xff0c;还需要对直线度&#xff08;弯曲度&#xff09;等尺寸加以测量。作为一种评价产品直度的重要指标——直线度&#xff0c;能够对其进行检测是非常重要的。 关于直线度&#xff0c;对于一些弯…

初学者使用WordPress可能会遇到的问题以及如何解决

WordPress 作为一个普及度相当广的内容管理系统 (CMS)&#xff0c;对于刚刚开始建立自己第一个网站的初学者来说是非常合适的选择。它不需要你懂编写代码&#xff0c;且对 SEO 友好&#xff0c;管理起来也很方便。然而&#xff0c;许多初学者在使用 WordPress 时会犯一些错误&a…

各厂家BI对比

帆软BI、奥威BI、永洪BI、思迈特BI、亿信华辰BI是国内知名的BI产品&#xff0c;不少企业在选型BI软件时都需要对这些BI软件进行了解&#xff0c;从中选择适合自己的一款。经过过年的发展&#xff0c;这些BI&#xff08;商业智能&#xff09;软件各自在多个行业中都有广泛的应用…

Anti-Bandit Neural Architecture Search for Model Defense

模型防御的Anti-Bandit网络架构搜索 论文链接&#xff1a;https://arxiv.org/abs/2008.00698(ECCV2020) 项目链接&#xff1a;https://github.com/bczhangbczhang/ABanditNAS 模型防御的Anti-Bandit网络架构搜索Abstract1 Introduction2 Related Work3 Anti-Bandit网络架构搜…

前端项目重新打包部署后如何通知用户更新

前端项目重新打包部署后如何通知用户更新 前端项目重新打包部署后如何通知用户更新常用的webSocket解决方案纯前端方案路由拦截多线程main.ts中 创建多线程多线程逻辑处理 前端项目重新打包部署后如何通知用户更新 前端项目重新打包部署后&#xff0c;由于用户没及时更新页面&…

Vue 自定义文字提示框

目录 前言代码演示相关代码文字提示框组件定义组件调用前言 今天开发遇上了一个新的问题,要求写一个带着滑动动画的文字提示框。但是我经常使用的Element-UI组件库只有淡入淡出效果,并且想要修改样式只能全局修改,非常不利于后期的开发。因此,我最终选择直接自定义一个符合…

VAuditDemo文件漏洞

目录 VAuditDemo文件漏洞 一、首页文件包含漏洞 包含图片马 利用伪协议phar:// 构造shell.inc被压缩为shell.zip&#xff0c;然后更改shell.zip 为 shell.jpg上传 二、任意文件读取漏洞 avatar.php updateAvatar.php logCheck.php 任意文件读取漏洞利用 VAuditDemo文件…

Python中使用SQLite数据库的方法4-3

对于数据库的操作&#xff0c;主要包括“增”、“删”、“改”、“查”四种。在Python中使用SQLite数据库的方法4-1_python的sqlite怎么打开-CSDN博客和Python中使用SQLite数据库的方法4-2_python2 sqlite2-CSDN博客中实现增”、“删”和“查”三种操作。 1 带过滤条件的“查”…

C语言基础(七)

1、二维数组&#xff1a; C语言中的数组是一种基本的数据结构&#xff0c;用于在计算机内存中连续存储相同类型的数据。 数组中的每个元素可以通过索引&#xff08;或下标&#xff09;来访问&#xff0c;索引通常是从0开始的。数组的大小在声明时确定&#xff0c;并且之后不能…

在Linux下搭建go环境

下载go go官网&#xff1a;All releases - The Go Programming Language 我们可以吧压缩包下载到Windows上再传到Linux上&#xff0c;也可以直接web下载&#xff1a; wget https://golang.google.cn/dl/go1.23.0.linux-amd64.tar.gz 解压 使用命令解压&#xff1a; tar -x…

Leetcode JAVA刷刷站(57)插入区间

一、题目概述 二、思路方向 为了解决这个问题&#xff0c;我们可以遍历给定的区间列表 intervals&#xff0c;并同时构建一个新的列表来存储最终的合并结果。遍历过程中&#xff0c;我们检查当前区间是否与 newInterval 重叠或相邻&#xff0c;并根据需要进行合并。如果不重叠…

虚拟化平台kvm架构 部署kvm虚拟化平台

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…

在HarmonyOS中使用RelativeContainer实现相对布局

在应用开发中&#xff0c;布局设计至关重要&#xff0c;尤其是当我们需要处理复杂的界面时&#xff0c;合理的布局设计不仅能够提升界面的美观性&#xff0c;还能够提高应用的性能。在HarmonyOS中&#xff0c;RelativeContainer是一个强大的布局容器&#xff0c;它允许开发者通…

【Qt】 对象树 与 乱码问题

文章目录 1. 对象树在堆上开辟空间 并管理栈上开辟 与 堆上开辟 的区别 2. 乱码问题的解释编码方式的区分出现乱码的原因查看当前文件的编码方式如何处理 文件与 终端 编码方式 不统一 1. 对象树 在堆上开辟空间 并管理 该代码只进行new(在堆上开辟空间) 而没有delete 正常来说…

ES系列二之CentOS7安装ES head插件

CentOS7安装ES head插件 附&#xff1a;Centos7中安装Node出现Cannot find module ‘…/lib/utils/unsupported.js‘问题 删除原本的的npm连接&#xff0c;重新建一个即可。 1、先cd到该node版本中的bin文件夹下,这里装的是12.16.2版本&#xff1a; cd /usr/local/soft/nod…

C语言 之 字符串函数strncpy、ctrncat、strncmp函数的使用

文章目录 strncpy函数的使用strncat函数的使用strncmp函数的使用 strncpy函数的使用 函数原型&#xff1a; char * strncpy ( char * destination, const char * source, size_t num); strncpy与strcpy的区别是&#xff0c;strncpy可以控制需要拷贝的字符数量 1.能够拷贝num个…

为什么使用HTTPS?

HTTPS现在是所有Web活动的首选协议&#xff0c;因为它是用户保护敏感信息的最安全方式。 HTTPS不仅对请求用户信息的网站至关重要。除了用户直接发送的信息外&#xff0c;攻击者还可以从不安全的连接中跟踪行为和身份数据。 HTTP为网站所有者带来的好处除了数据安全之外&…

【Linux网络编程入门】Day5_socket编程基础

socket 编程基础 Linux 下的网络编程&#xff1a;socket 编程&#xff1b; socket是内核向应用层提供的一套网络编程接口&#xff0c;用户基于 socket 接口可开发自己的网络相关应用程序。 ⚫ socket 简介 ⚫ socket 编程 API 介绍 ⚫ socket 编程实战 socket 简介 ​ 套…

微信小程序引入全局环境变量

有时候一套代码要在多个小程序appId下使用,其中又有一些数据(文字)需要做区分.可以使用下面的方法 把要配置的数据以export default 形式导出 在app.js中,引入project.config.0.js文件,将导出的数据放在globalData中 在页面目录中,即可利用getApp()方法使用全局变量 也可以放数…

LM4863 带立体声耳机功能的双 2.2W音频功率放大器芯片IC

一般概述 LM4863是双桥接的音频功率放大器。当电源电压为5V时&#xff0c;在保证总谐波失真、噪声失真之和小于1.0%的情况下&#xff0c;4Ω负载提供2.2W的输出功率或者可向3Ω负载提供2.5W的输出功率。另外&#xff0c;当驱动立体声耳机时&#xff0c;耳机输入端允许放…