ROS2 小白学习入门 话题发布和订阅(C++实现)

ROS2 入门应用 发布和订阅(C++实现)

目录

1.创建工作空间

2.创建功能包

3.创建cpp源文件

	3.1 话题发布
		
	3.2 话题订阅

4.添加依赖关系

5.添加编译信息

	5.1 添加搜索库
	
	5.2 增加可执行文件
	
	5.3 增加可执行文件的位置

6.编译和运行

1. 创建工作空间

假设新的工作空间名称为ros2_ws,创建文件夹ros2_ws及其子文件夹src,然后进入其中:

1 | mkdir -p ~/ros2_ws/src
2 | cd ~/ros2_ws/src

后续将工作空间中的功能包都统一放入src目录中

2. 创建功能包

假设我们创建的功能包叫做cpp_pubsub,通过以下命令来创建:

1 | ros2 pkg create --build-type ament_cmake cpp_pubsub

创建完成之后,在cpp_pubsub功能包存在以下四个文件:

1 | CMakeLists.txt
2 | include  
3 | package.xml  
4 | src

简单解释下这四个文件:

  1. CMakeLists.txt: 文件CMakeLists.txt是用于构建软件包的CMake构建系统的输入。任何兼容 CMake 的包都包含一个或多个 CMakeLists.txt 文件,这些文件描述了如何构建代码以及将其安装到何处。简单理解就是告诉cmake命令我们对这个目录下的文件做什么事情.
  2. include: 文件夹include用来存放项目中所需要的头文件(包括自定义的头文件)
  3. package.xml: 文件package.xml用于描述 pacakge 的基本信息,包含了 package 的名称、版本号、内容描述、维护人员、软件许可、编译构建工具、编译依赖、运行依赖等信息。实际上 rospack find 、rosdep 等命令之所以能快速定位和分析出 package 的依赖项信息,就是直接读取了每一个 pacakge 中的 package.xml 文件。它为用户提供了快速了解一个 pacakge 的渠道。
  4. src: 文件夹src用来存放功能源代码文件,也就是用来存放.cpp文件

3. 创建cpp源文件

进入cpp_pubsub功能包的src目录中:

1 | cd ~/ros2_ws/src/cpp_pubsub/src

3.1 话题发布

新建publisher_member_function.cpp文件,该cpp文件的功能是话题发布:

1 | vim publisher_member_function.cpp

复制以下代码到publisher_member_function.cpp中

#include 
#include 
#include 
#include 
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"

/* 方便表示时间 */
using namespace std::chrono_literals;

/* This example creates a subclass of Node and uses std::bind() to register a
* member function as a callback from the timer. */

/* 继承rclcpp:: node创建节点类MinimalPublisher */
class MinimalPublisher : public rclcpp::Node
{
  public:
    /* 公共构造函数将节点命名为minimal_publisher,并将count_初始化为0 */
    MinimalPublisher()
    : Node("minimal_publisher"), count_(0)
    {
      /* 初始化发布者publisher_ ,使用String消息类型、主题名称topic和在发生备份时限制消息所需的队列大小10 */
      publisher_ = this->create_publisher<std_msgs::msg::String>("topic", 10);

      /* 初始化timer_,设置timer_callback函数每500ms执行一次 */
      timer_ = this->create_wall_timer(500ms, std::bind(&MinimalPublisher::timer_callback, this));
    }

  private:
    /* 定义定时器回调函数 */
    void timer_callback()
    {
      /* 打印并发布字符串信息 */
      auto message = std_msgs::msg::String();
      message.data = "publisher send Hello, world: " + std::to_string(count_++);
      RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", message.data.c_str());
      publisher_->publish(message);
    }

    /* 计时器、发布者和计数器字段的声明 */
    rclcpp::TimerBase::SharedPtr timer_;
    rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
    size_t count_;
};

int main(int argc, char * argv[])
{
  /* 初始化ROS2 */
  rclcpp::init(argc, argv);

  /* 运行节点MinimalPublisher */
  rclcpp::spin(std::make_shared<MinimalPublisher>());

  /* 退出ROS2 */
  rclcpp::shutdown();
  return 0;
}

3.2 话题订阅

新建subscriber_member_function.cpp文件,该文件的功能是话题订阅:

1 | vim subscriber_memeber_function.cpp

复制以下代码到subscriber_memeber_function.cpp文件中

#include 
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"

/* 占位符,代替回调函数中的第一个参数 */
using std::placeholders::_1;


/* 继承rclcpp:: node创建节点类MinimalSubscriber */
class MinimalSubscriber : public rclcpp::Node
{
  public:
    /* 公共构造函数将节点命名为minimal_subscriber */
    MinimalSubscriber()
    : Node("minimal_subscriber")
    {
      /* 初始化订阅者subscription_  ,使用String消息类型、主题名称topic和在发生备份时限制消息所需的队列大小10,
         订阅话题回调函数topic_callback */
      subscription_ = this->create_subscription<std_msgs::msg::String>(
      "topic", 10, std::bind(&MinimalSubscriber::topic_callback, this, _1));
    }

  private:
    /* 定义订阅话题回调函数 */
    void topic_callback(const std_msgs::msg::String & msg) const
    {
      /* 打印话题消息的字符串信息 */
      RCLCPP_INFO(this->get_logger(), "subscriber heard: '%s'", msg.data.c_str());
    }

    /* 订阅者字段的声明 */
    rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;
};

int main(int argc, char * argv[])
{
  /* 初始化ROS2 */
  rclcpp::init(argc, argv);

  /* 运行节点MinimalSubscriber*/
  rclcpp::spin(std::make_shared<MinimalSubscriber>());

  /* 退出ROS2 */
  rclcpp::shutdown();
  return 0;
}

4. 添加依赖关系

在package.xml清单文件中
找到ament_cmake依赖项,在其下面添加源文件所需的依赖(depend):

  <buildtool_depend>ament_cmakebuildtool_depend>
  
  <depend>rclcppdepend>
  <depend>std_msgsdepend>

添加这两个依赖是为了声明功能包cpp_pubsub在执行代码时需要rclcpp和std_msgs

5. 添加编译信息

在CMakeLists.txt编译文件中

5.1 添加搜索库

首先,找到find_package(ament_cmake REQUIRED)依赖项,在其下面添加搜索源文件所需(REQUIRED)的库:

# 添加 搜索源文件所需(REQUIRED)的库
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)

5.2 增加可执行文件

然后,再增加可执行文件,并添加目标依赖关系:

# 添加publisher_member_function.cppc和csubscriber_member_function.cpp为可执行文件,并将这两个可执行文件命名为publisher和subscriber
add_executable(publisher src/publisher_memeber_function.cpp)
ament_target_dependencies(publisher rclcpp std_msgs)
add_executable(subscriber src/subscriber_member_function.cpp)
ament_target_dependencies(subscriber rclcpp std_msgs)

5.3增加可执行文件的位置

最后,增加可执行文件位置,以便ROS2可以找到可执行文件:

# 最后增加 publisher 和 subscriber 的可执行文件位置,以便ROS2找到这两个可执行文件:
install(TARGETS
  publisher
  subscriber 
  DESTINATION lib/${PROJECT_NAME})

6. 编译和运行

进入到工作空间的根目录:

1 | cd ~/ros2_ws

在编译之前,先检查缺失的依赖项(可选择跳过)

1 | rosdep install -i --from-path src --rosdistro humble -y

开始编译需要编译的功能包cpp_pubsub:

1 | colcon build --packages-select cpp_pubsub

打开一个终端,运行话题发布节点 publisher:

1 | [INFO] [1693379708.690649797] [minimal_publisher]: Publishing: 'publisher send Hello, world: 0'
2 | [INFO] [1693379709.190618679] [minimal_publisher]: Publishing: 'publisher send Hello, world: 1'
3 | [INFO] [1693379709.690624197] [minimal_publisher]: Publishing: 'publisher send Hello, world: 2'
4 | [INFO] [1693379710.190792438] [minimal_publisher]: Publishing: 'publisher send Hello, world: 3'
5 | [INFO] [1693379710.690753844] [minimal_publisher]: Publishing: 'publisher send Hello, world: 4'
6 | [INFO] [1693379711.190529707] [minimal_publisher]: Publishing: 'publisher send Hello, world: 5'
7 | [INFO] [1693379711.690732506] [minimal_publisher]: Publishing: 'publisher send Hello, world: 6'
8 | [INFO] [1693379712.190744241] [minimal_publisher]: Publishing: 'publisher send Hello, world: 7'

再打开另一个终端,运行话题订阅节点 subscriber:

1 | [INFO] [1693379708.691507770] [minimal_subscriber]: subscriber heard: 'publisher send Hello, world: 0'
2 | [INFO] [1693379709.191275055] [minimal_subscriber]: subscriber heard: 'publisher send Hello, world: 1'
3 | [INFO] [1693379709.691206499] [minimal_subscriber]: subscriber heard: 'publisher send Hello, world: 2'
4 | [INFO] [1693379710.191412097] [minimal_subscriber]: subscriber heard: 'publisher send Hello, world: 3'
5 | [INFO] [1693379710.691368655] [minimal_subscriber]: subscriber heard: 'publisher send Hello, world: 4'
6 | [INFO] [1693379711.191143146] [minimal_subscriber]: subscriber heard: 'publisher send Hello, world: 5'
7 | [INFO] [1693379711.691404893] [minimal_subscriber]: subscriber heard: 'publisher send Hello, world: 6'
8 | [INFO] [1693379712.191377454] [minimal_subscriber]: subscriber heard: 'publisher send Hello, world: 7'

至此,可以看到,消息订阅者subscriber可以收到消息发布者publisher的消息,这便是ROS2中两个节点之间通过Topic通信的应用实例

ROS2入门小白,若有不足,还请指正,欢迎学习交流,谢谢支持!

你可能感兴趣的:(c++,机器人)