【ROS2】概念:中级-组件

 目录

  • ROS 1 - 节点 vs. 节点组

  • ROS 2 - 统一 API

  •  编写组件

  •  使用组件

  •  实际应用

ROS 1 - 节点 vs. 节点组 

在 ROS 1 中,您可以将代码编写为 ROS node 或 ROS nodelet 。ROS 1 节点被编译成可执行文件。另一方面,ROS 1 nodelet 被编译成共享库,然后由容器进程在运行时加载。

ROS 2 - 统一 API 

在 ROS 2 中,推荐的编写代码方式类似于 nodelet - 我们称之为 Component 。这使得将常见概念添加到现有代码中变得容易,比如生命周期 https://design.ros2.org/articles/node_lifecycle.html 。在 ROS 1 中最大的缺点是有不同的 API,而在 ROS 2 中避免了这一点,因为两种方法使用相同的 API。

 注意

仍然可以使用“编写自己的主程序”的节点样式,但对于常见情况,不推荐这样做。

通过将流程布局设为部署时的决策,用户可以选择:

  • 在单独的进程中运行多个节点,具有进程/故障隔离的好处以及更容易调试单个节点

  • 在单个进程中运行多个节点,具有较低的开销,并且可选择更高效的通信(参见进程内通信 https://docs.ros.org/en/jazzy/Tutorials/Demos/Intra-Process-Communication.html )

此外, ros2 launch 可以通过专门的启动操作来自动化这些操作。

编写组件 

由于组件仅内置于共享库中,因此它没有 main 函数(请参阅 Talker 源代码 https://github.com/ros2/demos/blob/jazzy/composition/src/talker_component.cpp )。组件通常是 rclcpp::Node 的子类。由于它不控制线程,因此不应在其构造函数中执行任何长时间运行或阻塞的任务。相反,它可以使用计时器来获取定期通知。此外,它可以创建发布者、订阅者、服务器和客户端

#include "composition/talker_component.hpp" // 包含"composition/talker_component.hpp"头文件


#include <chrono>    // 包含时间库
#include <iostream>  // 包含输入输出流库
#include <memory>    // 包含智能指针库
#include <utility>   // 包含工具库


#include "rclcpp/rclcpp.hpp"         // 包含rclcpp库
#include "std_msgs/msg/string.hpp"   // 包含标准消息库


using namespace std::chrono_literals;  // 使用chrono字面量命名空间


namespace composition  // 定义composition命名空间
{


// 创建一个Talker "组件",它继承了通用的rclcpp::Node基类。
// 组件被构建成共享库,因此不编写自己的main函数。
// 使用组件的共享库的进程将实例化该类作为ROS节点。
Talker::Talker(const rclcpp::NodeOptions & options)
: Node("talker", options), count_(0)  // 构造函数初始化列表,设置节点名称为"talker",并初始化count_为0
{
  // 创建一个在"chatter"主题上发布"std_msgs/String"消息的发布者。
  pub_ = create_publisher<std_msgs::msg::String>("chatter", 10);  // 创建发布者,队列大小为10


  // 使用定时器安排周期性消息发布。
  timer_ = create_wall_timer(1s, [this]() {return this->on_timer();});  // 创建定时器,每1秒调用一次on_timer函数
}


void Talker::on_timer()  // 定时器回调函数
{
  auto msg = std::make_unique<std_msgs::msg::String>();  // 创建一个唯一指针,指向std_msgs::msg::String类型的消息
  msg->data = "Hello World: " + std::to_string(++count_);  // 设置消息数据为"Hello World: "加上当前计数
  RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", msg->data.c_str());  // 打印日志信息
  std::flush(std::cout);  // 刷新标准输出流


  // 将消息放入队列中由中间件处理。
  // 这个调用是非阻塞的。
  pub_->publish(std::move(msg));  // 发布消息
}


}  // namespace composition


#include "rclcpp_components/register_node_macro.hpp"  // 包含rclcpp组件注册宏头文件


// 使用class_loader注册组件。
// 这作为一种入口点,使组件在其库加载到运行进程中时可被发现。
RCLCPP_COMPONENTS_REGISTER_NODE(composition::Talker)  // 注册composition::Talker组件

将这样的类制作成组件的一个重要方面是,该类使用包 rclcpp_components 中的宏注册自身(请参见源代码的最后一行)。这使得在将其库加载到正在运行的进程中时可以发现该组件 - 它充当了一种入口点。

此外,一旦组件创建完成,必须将其注册到索引中,以便工具能够发现它。

add_library(talker_component SHARED src/talker_component.cpp)
rclcpp_components_register_nodes(talker_component "composition::Talker")
# To register multiple components in the same shared library, use multiple calls
# rclcpp_components_register_nodes(talker_component "composition::Talker2")

例如,请查看本教程 

https://docs.ros.org/en/jazzy/Tutorials/Intermediate/Writing-a-Composable-Node.html

 注意

为了使 component_container 能够找到所需的组件,必须从已获取相应工作区的 shell 中执行或启动它。

 使用组件 

组合包https://github.com/ros2/demos/tree/jazzy/composition 包含几种不同的方法来使用组件。最常见的三种方法是:

  1. 启动一个(通用容器进程 https://github.com/ros2/rclcpp/blob/jazzy/rclcpp_components/src/component_container.cpp )并调用容器提供的 ROS 服务 load_node。然后,ROS 服务将加载由传递的包名和库名指定的组件,并在正在运行的进程中开始执行它。除了以编程方式调用 ROS 服务外,您还可以使用命令行工具通过传递的命令行参数来调用 ROS 服务

  2. 创建一个包含多个在编译时已知节点的自定义可执行 https://github.com/ros2/demos/blob/jazzy/composition/src/manual_composition.cpp 文件。这种方法要求每个组件都有一个头文件(对于第一种情况,这不是严格需要的)。

  3. 创建一个启动文件,并使用 ros2 launch 创建一个加载了多个组件的容器进程。

 实际应用 

试用 Composition 演示。https://docs.ros.org/en/jazzy/Tutorials/Intermediate/Composition.html

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值