Robot Operating System——创建动态链接文件项目的步骤

news2024/11/25 3:53:45

大纲

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

在 《Robot Operating System——创建可执行文件项目的步骤》一文中,我们熟悉了如何创建一个ROS2的可执行文件。但是在对之前诸多案例分析后,我们发现动态链接库的形式更加普遍。这是因为一个Node可以以一个动态链接库的形式存在。这样我们一个进程便可以灵活地加载多个动态链接库,从而实现在一个进程中运行多个Node。

初始化环境

由于编译和运行需要依赖很多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_dynamic_library

在这里插入图片描述
可以看到ros2帮我们创建好了相关的目录结构
在这里插入图片描述

代码

我们准备在一个动态链接库中包含两个Node ,然后使用ROS2 Demo中的manual_composition来加载并测试它们。
在include/two_node_dynamic_library下新建visibility_control.h文件,填入以下内容

// Copyright 2016 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.

#ifndef DEMO_NODES_CPP__VISIBILITY_CONTROL_H_
#define DEMO_NODES_CPP__VISIBILITY_CONTROL_H_

#ifdef __cplusplus
extern "C"
{
#endif

// This logic was borrowed (then namespaced) from the examples on the gcc wiki:
//     https://gcc.gnu.org/wiki/Visibility

#if defined _WIN32 || defined __CYGWIN__
  #ifdef __GNUC__
    #define DEMO_NODES_CPP_EXPORT __attribute__ ((dllexport))
    #define DEMO_NODES_CPP_IMPORT __attribute__ ((dllimport))
  #else
    #define DEMO_NODES_CPP_EXPORT __declspec(dllexport)
    #define DEMO_NODES_CPP_IMPORT __declspec(dllimport)
  #endif
  #ifdef DEMO_NODES_CPP_BUILDING_DLL
    #define DEMO_NODES_CPP_PUBLIC DEMO_NODES_CPP_EXPORT
  #else
    #define DEMO_NODES_CPP_PUBLIC DEMO_NODES_CPP_IMPORT
  #endif
  #define DEMO_NODES_CPP_PUBLIC_TYPE DEMO_NODES_CPP_PUBLIC
  #define DEMO_NODES_CPP_LOCAL
#else
  #define DEMO_NODES_CPP_EXPORT __attribute__ ((visibility("default")))
  #define DEMO_NODES_CPP_IMPORT
  #if __GNUC__ >= 4
    #define DEMO_NODES_CPP_PUBLIC __attribute__ ((visibility("default")))
    #define DEMO_NODES_CPP_LOCAL  __attribute__ ((visibility("hidden")))
  #else
    #define DEMO_NODES_CPP_PUBLIC
    #define DEMO_NODES_CPP_LOCAL
  #endif
  #define DEMO_NODES_CPP_PUBLIC_TYPE
#endif

#ifdef __cplusplus
}
#endif

#endif  // DEMO_NODES_CPP__VISIBILITY_CONTROL_H_

在src目录下,我们新建一个talker.cpp,填入以下的代码

// Copyright 2014 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 <cstdio>
#include <memory>
#include <utility>

#include "rclcpp/rclcpp.hpp"
#include "rclcpp_components/register_node_macro.hpp"

#include "std_msgs/msg/string.hpp"

#include "two_node_dynamic_library/visibility_control.h"

using namespace std::chrono_literals;

namespace two_node_dynamic_library
{
// Create a Talker class that subclasses the generic rclcpp::Node base class.
// The main function below will instantiate the class as a ROS node.
class Talker : public rclcpp::Node
{
public:
  DEMO_NODES_CPP_PUBLIC
  explicit Talker(const rclcpp::NodeOptions & options)
  : Node("talker", options)
  {
    // Create a function for when messages are to be sent.
    setvbuf(stdout, NULL, _IONBF, BUFSIZ);
    auto publish_message =
      [this]() -> void
      {
        msg_ = std::make_unique<std_msgs::msg::String>();
        msg_->data = "Hello World: " + std::to_string(count_++);
        RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", msg_->data.c_str());
        // Put the message into a queue to be processed by the middleware.
        // This call is non-blocking.
        pub_->publish(std::move(msg_));
      };
    // Create a publisher with a custom Quality of Service profile.
    // Uniform initialization is suggested so it can be trivially changed to
    // rclcpp::KeepAll{} if the user wishes.
    // (rclcpp::KeepLast(7) -> rclcpp::KeepAll() fails to compile)
    rclcpp::QoS qos(rclcpp::KeepLast{7});
    pub_ = this->create_publisher<std_msgs::msg::String>("chatter", qos);

    // Use a timer to schedule periodic message publishing.
    timer_ = this->create_wall_timer(1s, publish_message);
  }

private:
  size_t count_ = 1;
  std::unique_ptr<std_msgs::msg::String> msg_;
  rclcpp::Publisher<std_msgs::msg::String>::SharedPtr pub_;
  rclcpp::TimerBase::SharedPtr timer_;
};

}  // namespace two_node_dynamic_library

RCLCPP_COMPONENTS_REGISTER_NODE(two_node_dynamic_library::Talker)

再新建一个文件listener.cpp,填入以下内容

// Copyright 2014 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 "rclcpp/rclcpp.hpp"
#include "rclcpp_components/register_node_macro.hpp"

#include "std_msgs/msg/string.hpp"

#include "two_node_dynamic_library/visibility_control.h"

namespace two_node_dynamic_library
{
// Create a Listener class that subclasses the generic rclcpp::Node base class.
// The main function below will instantiate the class as a ROS node.
class Listener : public rclcpp::Node
{
public:
  DEMO_NODES_CPP_PUBLIC
  explicit Listener(const rclcpp::NodeOptions & options)
  : Node("listener", options)
  {
    // Create a callback function for when messages are received.
    // Variations of this function also exist using, for example UniquePtr for zero-copy transport.
    setvbuf(stdout, NULL, _IONBF, BUFSIZ);
    auto callback =
      [this](std_msgs::msg::String::ConstSharedPtr msg) -> void
      {
        RCLCPP_INFO(this->get_logger(), "I heard: [%s]", msg->data.c_str());
      };
    // Create a subscription to the topic which can be matched with one or more compatible ROS
    // publishers.
    // Note that not all publishers on the same topic with the same type will be compatible:
    // they must have compatible Quality of Service policies.
    sub_ = create_subscription<std_msgs::msg::String>("chatter", 10, callback);
  }

private:
  rclcpp::Subscription<std_msgs::msg::String>::SharedPtr sub_;
};

}  // namespace two_node_dynamic_library

RCLCPP_COMPONENTS_REGISTER_NODE(two_node_dynamic_library::Listener)

此时目录结构如下
在这里插入图片描述

添加依赖(package.xml)

package.xml 是 ROS 2 包的元数据文件,定义了包的基本信息和依赖关系。
打开two_node_pipeline/package.xml,添加代码中include头文件(#include “rclcpp/rclcpp.hpp”、#include "std_msgs/msg/int32.hpp"和#include “rclcpp_components/register_node_macro.hpp”)所依赖的其他库。

  <build_depend>rclcpp</build_depend>
  <build_depend>std_msgs</build_depend>
  <build_depend>rclcpp_components</build_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_dynamic_library</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>

  <build_depend>rclcpp</build_depend>
  <build_depend>std_msgs</build_depend>
  <build_depend>rclcpp_components</build_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寻找依赖库

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

find_package(rclcpp REQUIRED)
find_package(rclcpp_components REQUIRED)
find_package(std_msgs REQUIRED)

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

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

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

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

指定代码路径和编译类型(动态库)

设定编译结果名称

set(DYNAMIC_LIBRARY_NAME "two_node_dynamic_library")

指定编译结果是动态库文件

# Collect all source files in this directory
file(GLOB SOURCES "src/*.cpp")

# Add the library target
add_library(${DYNAMIC_LIBRARY_NAME} SHARED ${SOURCES})

设置头文件路径

target_include_directories(${DYNAMIC_LIBRARY_NAME} PRIVATE "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>")

链接依赖的库

最后将代码中用到的库(#include “rclcpp/rclcpp.hpp”、#include "std_msgs/msg/int32.hpp"和#include “rclcpp_components/register_node_macro.hpp”)和生成的动态库关联。

target_link_libraries(${DYNAMIC_LIBRARY_NAME} PRIVATE rclcpp::rclcpp ${std_msgs_TARGETS} rclcpp::rclcpp rclcpp_components::component)

编译

cd two_node_dynamic_library/
mkdir build
cd build/
cmake ..
make

在这里插入图片描述

测试

我们使用《Robot Operating System——初探可执行文件、动态库运行模式》一文中的manual_composition 来加载生成的动态库。

./manual_composition ~/github/ros2-examples/two_node_dynamic_library/build/libtwo_node_dynamic_library.so 

可以看到,进程中有两个Node在通信。
在这里插入图片描述

参考资料

  • https://docs.ros.org/en/rolling/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Cpp-Publisher-And-Subscriber.html

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

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

相关文章

大数据-93 Spark 集群 Spark SQL 概述 基本概念 SparkSQL对比 架构 抽象

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

VMware虚拟机nat无法联通主机

VMware在nat模式下主机无法ping通虚拟机 原因&#xff1a; 虚拟机和对应的网卡不在一个网段 虚拟机开启了防火墙 解决方法: 首先判断虚拟机的网络ip是否和网卡在一个网段上 判断虚拟机使用的网卡 nat模式在VMware虚拟机中一般只有一个对应的网卡 如图笔者的nat网卡为VM…

基于机器学习的二手房房价数据分析与价格预测模型

有需要本项目的可以私信博主&#xff0c;提供远程部署讲解 本研究聚焦重庆二手房市场&#xff0c;通过创新的数据采集和分析方法&#xff0c;深入探讨影响房价的关键因素&#xff0c;并开发了预测模型。 我们首先利用Python编写的爬虫程序&#xff0c;巧妙规避了链家网站的反…

ClickHouse实时探索与实践 京东云

1 前言 京喜达技术部在社区团购场景下采用JDQFlinkElasticsearch架构来打造实时数据报表。随着业务的发展 Elasticsearch开始暴露出一些弊端&#xff0c;不适合大批量的数据查询&#xff0c;高频次深度分页导出导致ES宕机、不能精确去重统计&#xff0c;多个字段聚合计算时性能…

初识Linux · 权限

目录 前言&#xff1a; 1 预备知识 2 权限 2.1 文件的基本权限 2.2 修改权限的第一种做法 2.3 修改权限的第二种做法 2.4 权限的对比 2.5 文件类型 前言&#xff1a; 继上文我们将常用的指令介绍的七七八八了&#xff0c;本文着重探索Linux文件中的权限部分&#xff0…

docker部署postgresSQL 并做持久化

先安装docker&#xff0c;安装docker 方法自行寻找方法 然后安装pgsql 拉取镜像 docker pull registry.cn-hangzhou.aliyuncs.com/qiluo-images/postgres:latest运行容器 docker run -it --name postgres --privileged --restart always -e POSTGRES_PASSWORDZ6n8g4zJzC3mr…

手动与自动修复mfc140u.dll丢失的解决方法,mfc140u.dll在电脑中是什么样的存在

当您遇到“mfc140u.dll丢失”的错误时&#xff0c;通常意味着计算机上缺少Microsoft Foundation Class (MFC) 库的特定版本&#xff0c;该库是Visual Studio 2015的一部分。这种问题往往在启动某些应用程序或游戏时出现&#xff0c;并显示如“无法启动该程序&#xff0c;因为计…

可变参数模板(C++11)

这篇文章讲解的是C11的特性之一——可变参数模板&#xff0c;适合有一定基础的同学学习&#xff0c;如果是刚入门的同学可以看我过往的文章&#xff1a;C基础入门 可变参数模板&#xff08;Variadic Templates&#xff09;是C的一种高级特性&#xff0c;它允许你编写接受任意数…

8.20T3 无损加密(线性代数转LGV+状压dp+高维前缀和)

http://cplusoj.com/d/senior/p/NODSX2301C 对于式子&#xff1a; 这个神秘的线性代数形式比较难处理&#xff0c;但我们可以考虑其组合意义。行列式现存的可用组合意义之一就是LGV&#xff08;矩阵式不太可用&#xff09; 先把原先的矩阵转化为一个有向图。现在我们要构造一…

笔记本电脑无线网卡突然没有了

目录 笔记本电脑无线网卡突然没有了最优解决方案 笔记本电脑无线网卡突然没有了 记录一次笔记本无线网卡突然没有了的解决方案 显示黄色感叹号&#xff0c;试了几个安装驱动的软件都不行 最优解决方案 找到网卡的厂商官网&#xff0c;官网上下载驱动 比如我的无线网卡是Int…

【Hot100】LeetCode—146. LRU 缓存

目录 1-思路1-1 LRU知识点1-2 实现思路LRU的子数据结构① 双向链表 DLinkedNode 结点定义② 其他字段 LRU实现的方法① 初始化——LRUCache中初始化② public int get(int key) 取元素方法③ public void put(int key, int value) 存元素方法 2-实现⭐146. LRU 缓存——题解思路…

rufus制作ubantu的U盘安装介质时,rufus界面上的分区类型选什么?

rufus制作ubantu的U盘安装介质时&#xff0c;rufus软件界面上的分区类型选什么(如下图&#xff09;&#xff1f; 在使用Rufus制作Ubuntu的U盘安装介质时&#xff0c;分区类型的选择取决于我们的计算机的引导方式。 以下是具体的选择建议&#xff1a; 1、查看计算机的引导方式…

JAVA设计模式之【单例模式】

1 类图 2 饿汉式单例 例如&#xff1a;静态块、静态成员 2.1 概念 类加载的时候就立即初始化&#xff0c;并且创建单例对象 2.2 优点 没有加任何的锁、执行效率比较高 2.3 缺点 类加载的时候就初始化&#xff0c;不管用与不用都占着空间&#xff0c;浪费了内存。 3 懒汉…

Java之迭代器的使用

Java之迭代器的使用 摘要基础知识List迭代器Map迭代器 摘要 本博客主要讲解容器的迭代器的使用&#xff0c;包括List、Set和Map等容器 基础知识 这是类的继承关系图 迭代器的原理(一开始迭代器并不指向任何有效元素)&#xff1a; List迭代器 public class TestIterator …

VMware vSphere Client无法访问和连接ESXi虚拟主机解决思路

文章目录 前言1. 问题现象2. 问题原因3. 解决方法4. 参考文章 前言 注意 : 可以先看看参考文章那里&#xff0c;在回过来看 1 、 2 、3 1. 问题现象 版本&#xff1a;VMware vCenter Server 5.5.0 build-2442329 问题描述&#xff1a;用VMware vSphere Client 登录ESXI主机出…

【Linux —— 线程互斥】

Linux —— 线程互斥 1. 临界资源与临界区2. 互斥的定义3. 原子性4. 互斥量(Mutex)5. 互斥的实现示例 1. 临界资源与临界区 临界资源: 指的是多个线程或进程共享的资源&#xff0c;例如全局变量、文件、数据库等。由于这些资源的共享&#xff0c;可能会导致数据不一致或程序崩…

git commit 时发生:fatal: cannot lock HEAD ref

.git目录探析_.git文件在哪-CSDN博客https://blog.csdn.net/luofeng457/article/details/117577275 tree .git .git ├── branches ├── COMMIT_EDITMSG ├── config ├── description ├── FETCH_HEAD ├── HEAD ├── hooks │ ├── applypatch-msg.sample…

STM32的GPIO

GPIO基本控制 GPIO(General-Purpose input/output,通用输入/输出接口) 用于感知外部信号&#xff08;输入模式&#xff09;和控制外部设备&#xff08;输出模式&#xff09; 简单模块&#xff1a;LED,按键&#xff0c;蜂鸣器&#xff0c;温度传感器&#xff0c;使用一个GPIO…

qt-PLC可视化编辑器

qt-PLC可视化编辑器 一、演示效果二、核心代码三、下载链接 一、演示效果 二、核心代码 #include "diagramitem.h" #include "arrow.h"#include <QDebug> #include <QGraphicsScene> #include <QGraphicsSceneContextMenuEvent> #includ…

1 Kubeflow总体介绍-学习笔记

1 什么是 Kubeflow Kubeflow 是一个开源项目社区和生态系统&#xff0c;支持机器学习 (ML) 生命周期中的每个阶段 &#xff0c;并支持相关的开源 工具和框架&#xff0c;Kubeflow 使 Kubernetes 上的 AI/ML 变得简单、可移植且可扩展。 Kubeflow 都能提供模块化、可扩展的工具…