ROS2 中节点通过发布和订阅话题来传递信息,一个节点可以作为发布者发布信息,也可以同时作为订阅者订阅其它节点发布的话题。

话题由主题和消息类型组成:

  • 主题是一个字符串
  • 消息类型是传输数据的格式

话题查看

  1. 启动小海龟节点

    1
    
    ros2 run turtlesim turtlesim_node
  2. 查看小海龟节点信息

    1
    
    ros2 node info /turtlesim

    可以看到 node 的相关信息

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    /turtlesim
      Subscribers:
        /parameter_events: rcl_interfaces/msg/ParameterEvent
        /turtle1/cmd_vel: geometry_msgs/msg/Twist
      Publishers:
        /parameter_events: rcl_interfaces/msg/ParameterEvent
        /rosout: rcl_interfaces/msg/Log
        /turtle1/color_sensor: turtlesim/msg/Color
        /turtle1/pose: turtlesim/msg/Pose

    Subscribers 列出节点订阅了哪些话题,前面为主题,后面为话题消息类型,Publishers 列出发布哪些话题

  3. 查看一个话题信息

    1
    
    ros2 topic info /turtle1/cmd_vel
    1
    2
    3
    
    Type: geometry_msgs/msg/Twist
    Publisher count: 0
    Subscription count: 1

    显示出数据类型,发布者数量,被订阅的数量

  4. 查看消息类型的详细定义

    1
    
    ros2 interface show geometry_msgs/msg/Twist
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    # This expresses velocity in free space broken into its linear and angular parts.
    
    Vector3  linear
            float64 x
            float64 y
            float64 z
    Vector3  angular
            float64 x
            float64 y
            float64 z

    可以看到消息类型为两个数组,第一个为线速度,第二个为角速度

  5. 使用 ros2 命令发布话题消息

    1
    
    ros2 topic pub /turtle1/cmd_vel geometry_msgs/msg/Twist "{linear: {x: 1.0, y: 0.0}, angular: {z: 1.0}}"

C++ 发布话题

用 C++ 实现每秒发布一个字符串

  1. 创建功能包

    1
    
    ros2 pkg create  cpp_topic_publish_demo --destination-directory src --build-type ament_cmake --dependencies rclcpp std_msgs --license Apache-2.0
  2. 新建节点,每秒发布字符串

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    
    #include "rclcpp/node.hpp"
    #include "rclcpp/executors.hpp"
    #include "std_msgs/msg/string.hpp"
    
    using namespace std::chrono_literals;
    
    class TopicPublishDemo : public rclcpp::Node {
    public:
        TopicPublishDemo(): Node("topic_publish_demo_node"), count_(0) {
            publisher_ = create_publisher<std_msgs::msg::String>("topic_string", 10);
            auto timer_callback = [this]() {
                auto msg = std_msgs::msg::String();
                msg.data = "Hello, Now is " + std::to_string(count_++);
                RCLCPP_INFO(get_logger(), "Publishing: %s", msg.data.c_str());
                publisher_->publish(msg);
            };
            timer_ = create_wall_timer(1s, timer_callback);
    
        }
    
    
    private:
        rclcpp::TimerBase::SharedPtr timer_;
        rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
        size_t count_;
    };
    
    
    int main(int argc, char* argv[]) {
        rclcpp::init(argc, argv);
    
        rclcpp::spin(std::make_shared<TopicPublishDemo>());
    
        rclcpp::shutdown();
        return 0;
    }

C++ 订阅话题

  1. 创建功能包

    1
    2
    
    ros2 pkg create  cpp_topic_subscribe_demo --destination-directory src --build-type ament_cmake --dependencies rc
    lcpp std_msgs --license Apache-2.0
  2. 节点订阅代码

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    
    #include "rclcpp/node.hpp"
    #include "rclcpp/executors.hpp"
    #include "std_msgs/msg/string.hpp"
    
    using namespace std::chrono_literals;
    
    class TopicSubscribeDemo : public rclcpp::Node {
    public:
        TopicSubscribeDemo(): Node("topic_subscribe_demo_node") {
            auto topic_callback = [this](std_msgs::msg::String::UniquePtr msg) {
                RCLCPP_INFO(get_logger(), "I received: %s", msg->data.c_str());
            };
            subscriber_ = create_subscription<std_msgs::msg::String>("topic_string", 10, topic_callback);
        }
    
    private:
        rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscriber_;
    };
    
    
    int main(int argc, char* argv[]) {
        rclcpp::init(argc, argv);
    
        rclcpp::spin(std::make_shared<TopicSubscribeDemo>());
    
        rclcpp::shutdown();
        return 0;
    }

自定义消息类型

  1. 创建一个功能包,用来定义消息类型

    1
    
    ros2 pkg create --destination-directory=src --build-type=ament_cmake --license Apache-2.0 --dependencies std_msgs demo_interfaces
  2. 在包根目录下新建一个 msg 目录,里面创建一个 Demo.msg 文件

    1
    
    std_msgs/String name
  3. 在 CMakeLists.txt 中添加以下代码

    1
    2
    3
    4
    5
    6
    
    find_package(rosidl_default_generators REQUIRED)
    
    rosidl_generate_interfaces(${PROJECT_NAME}
      "msg/Demo.msg"
      DEPENDENCIES std_msgs
    )
  4. 在 package.xml 中添加以下代码

    1
    2
    3
    
    <buildtool_depend>rosidl_default_generators</buildtool_depend>
    <exec_depend>rosidl_default_runtime</exec_depend>
    <member_of_group>rosidl_interface_packages</member_of_group>
  5. 使用 colcon build 编译

使用自定义消息

将前面的 publishsubscribes 改成使用自定义消息

  1. 在 package.xml 中添加依赖

    1
    
    <depend>demo_interfaces</depend>
  2. CMakeLists.txt 中添加依赖

    1
    2
    
    find_package(demo_interfaces)
    ament_target_dependencies(${PROJECT_NAME} rclcpp std_msgs demo_interfaces)
  1. 将原来的 std_msgs::msg::String 改为 demo_interface::msg::Demo
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    
    #include "rclcpp/node.hpp"
    #include "rclcpp/executors.hpp"
    #include "demo_interfaces/msg/demo.hpp"
    
    using namespace std::chrono_literals;
    
    class TopicSubscribeDemo : public rclcpp::Node {
    public:
        TopicSubscribeDemo(): Node("topic_subscribe_demo_node") {
            auto topic_callback = [this](demo_interfaces::msg::Demo::UniquePtr msg) {
                RCLCPP_INFO(get_logger(), "I received: %s", msg->name.data.c_str());
            };
            subscriber_ = create_subscription<demo_interfaces::msg::Demo>("topic_string", 10, topic_callback);
        }
    
    private:
        rclcpp::Subscription<demo_interfaces::msg::Demo>::SharedPtr subscriber_;
    };
    
    
    int main(int argc, char* argv[]) {
        rclcpp::init(argc, argv);
    
        rclcpp::spin(std::make_shared<TopicSubscribeDemo>());
    
        rclcpp::shutdown();
        return 0;
    }