เขียน Publisher และ Subscriber ด้วยภาษา C++

Topics : ข้อความจะถูกส่งผ่านระบบการขนส่งที่มีการ Publisher / Subscriber โหนดที่ส่งข้อความจะเรียกว่า Publisher ชื่อที่ระบุใน Topic จะใช้เพื่อระบุเนื้อหาของข้อความ โหนดที่สนใจในข้อมูลบางประเภทจะทำการ Subscriber หัวข้อที่ต้องการ อาจมีการ Publisher / Subscribe หลายรายพร้อมกันสำหรับหัวข้อเดียวและโหนดเดียวอาจ Publisher / Subscriber ได้หลาย Topics โดยทั่วไป Publisher / Subscriber จะไม่ได้ตระหนักถึงการมีอยู่ของกันและกัน กล่าวอีกนัยคือการแยกกันผลิตข้อมูลตามหน้าที่ของตนเอง

Topic ก็เหมือนกับหัวเรื่องที่เราไว้คุยกัน พูดง่ายๆคือ Publisher Node จะ Publish Message ของตัวเองขึ้นไปที่ Topic แล้วจะมี Subscriber Node มารอรับ Message ที่ส่งมาทาง Topic ที่มีชื่อเดียวกัน

เวลาพูดว่า Publisher หมายถึงการที่ Node จะส่ง Message ออกไปที่ Topic โดยไม่สนใจว่าจะมี Node รอรับอยู่รึเปล่า Node 1 ตัวสามารถ Publisher Message ออกไปได้หลายๆ Topics

Message : ข้อความ ค่ามาตรฐาน (จำนวนเต็ม, จุดลอย, บูลีน, ฯลฯ ) ข้อมูลอาร์เรย์ทั่วๆไป รวมไปถึงข้อมูลอาร์เรย์ที่มีความซับซ้อน

1 : เขียน Node ภาษา C++ แบบ OOP


เขียน Node ภาษา C++ แบบ OOP ตามขั้นตอนลิงค์ด้านล่าง


2 : เปิด Terminator 4 หน้าต่าง


3 : เขียน Publisher ด้วยภาษา C++


เขียนโหนดส่งข้อความ Publisher ด้วยภาษา C++

โดยไปที่ หน้าต่างที่ 1 ใช้คำสั่งเข้าไปใน โฟลเดอร์ที่ต้องการสร้างไฟล์

cd ros2_ws/scr/my_cpp_pkg/src/

ls


สร้างไฟล์ robot_news_station.cpp

touch robot_news_station.cpp


ใช้ Visual Studio Code เปิดไฟล์ robot_news_station.cpp เขียนโค้ด + Save ตามโค้ดด้านล่าง

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

class RobotNewsStationNode : public rclcpp::Node
{
public:
    RobotNewsStationNode() : Node("robot_news_station"), robot_name_("R2D2")
    {
        publisher_ = this->create_publisher<example_interfaces::msg::String>("robot_news", 10);
        timer_ = this->create_wall_timer(std::chrono::milliseconds(500),
                                         std::bind(&RobotNewsStationNode::publishNews, this));
        RCLCPP_INFO(this->get_logger(), "Robot News Station has been started.");
    }

private:
    void publishNews()
    {
        auto msg = example_interfaces::msg::String();
        msg.data = std::string("Hi, this is ") + robot_name_ + std::string(" from the Robot News Station");
        publisher_->publish(msg);
    }

    std::string robot_name_;
    rclcpp::Publisher<example_interfaces::msg::String>::SharedPtr publisher_;
    rclcpp::TimerBase::SharedPtr timer_;
};

int main(int argc, char **argv)
{
    rclcpp::init(argc, argv);
    auto node = std::make_shared<RobotNewsStationNode>();
    rclcpp::spin(node);
    rclcpp::shutdown();
    return 0;
}


เปิดไฟล์ package.xml เพิ่มโค้ดบรรทัดที่ 13 ด้วยโค้ดด้านล่าง

<depend>example_interfaces</depend>


เปิดไฟล์ CMakeLists.txt เพิ่มโค้ดบรรทัดที่ 21 ด้วยโค้ดด้านล่าง

find_package(example_interfaces REQUIRED)


และ เพิ่มโค้ดบรรทัดที่ 26 และ 27 ด้วยโค้ดด้านล่าง

add_executable(robot_news_station src/robot_news_station.cpp)
ament_target_dependencies(robot_news_station rclcpp example_interfaces)


เพิ่มโค้ดบรรทัดที่ 31 ด้วยโค้ดด้านล่าง

robot_news_station


กลับสู่ Home เพื่อจะ Build แพ็กเกจ my_cpp_pkg

cd

cd ros2_ws/


คำสั่ง Build เฉพาะ แพ็กเกจ my_cpp_pkg

colcon build --packages-select my_cpp_pkg --symlink-install


ไปที่ หน้าต่างที่ 2 โดยเราจะต้องทําให้ Terminator รู้จักคําสั่งของ ROS2 ด้วย คําสั่ง

source ~/.bashrc


เรียกใช้งาน robot_news_station ด้วย ROS2

ros2 run my_cpp_pkg robot_news_station


ไปที่ หน้าต่างที่ 4 ให้แสดง Node ที่กำลังทำงานอยู่

ros2 node list


แสดง Topic ที่กำลังทำงานอยู่

ros2 topic list


แสดงข้อมูลที่ส่งออกมาจาก topic robot_news

ros2 topic echo /robot_news


ไปที่ หน้าต่างที่ 3 เรียกใช้งาน smartphone ที่เขียนด้วยภาษา Python ก่อนหน้านี้

จะแสดงการรับ Message จากที่ส่งออกมาจาก topic robot_news ของภาษา C++ ซึ่งแสดงให้เห็นว่า ROS2 สามารถทำงานข้ามภาษาได้

ros2 run my_py_pkg smartphone

กด Ctrl + c เพื่อหยุดการทำงาน


4 : เขียน Subscriber ด้วยภาษา C++


Subscriber หมายถึงการที่ Node จะรับ Message จาก Topic ที่ตัวเองรอรับอยู่ Node 1 ตัวสามารถ Subscriber Message ที่มาจากหลายๆ Topic ได้

เขียน โหนด รับข้อความ Subscriber ด้วยภาษา C++ โดยกลับไปที่ หน้าต่างที่ 1  ใช้คำสั่งเข้าไปใน โฟลเดอร์ที่ต้องการสร้างไฟล์

cd scr/my_cpp_pkg/src

ls


สร้างไฟล์ smartphone.cpp

touch smartphone.cpp


ใช้ Visual Studio Code เปิดไฟล์ smartphone.cpp เขียนโค้ด + Save ตามโค้ดด้านล่าง

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

class SmartphoneNode : public rclcpp::Node
{
public:
    SmartphoneNode() : Node("smartphone")
    {
        subscriber_ = this->create_subscription<example_interfaces::msg::String>(
            "robot_news", 10,
            std::bind(&SmartphoneNode::callbackRobotNews, this, std::placeholders::_1));
        RCLCPP_INFO(this->get_logger(), "Smartphone has been started.");
    }

private:
    void callbackRobotNews(const example_interfaces::msg::String::SharedPtr msg)
    {
        RCLCPP_INFO(this->get_logger(), "%s", msg->data.c_str());
    }

    rclcpp::Subscription<example_interfaces::msg::String>::SharedPtr subscriber_;
};

int main(int argc, char **argv)
{
    rclcpp::init(argc, argv);
    auto node = std::make_shared<SmartphoneNode>();
    rclcpp::spin(node);
    rclcpp::shutdown();
    return 0;
}


เปิดไฟล์ CMakeLists.txt เพิ่มโค้ดบรรทัดที่ 29 และ 30 ด้วยโค้ดด้านล่าง

add_executable(smartphone src/smartphone.cpp)
ament_target_dependencies(smartphone rclcpp example_interfaces)


เพิ่มโค้ดบรรทัดที่ 35 ด้วยโค้ดด้านล่าง

 smartphone


ไปที่ หน้าต่างที่ 1 กลับสู่ Home เพื่อจะ Build แพ็กเกจ my_cpp_pkg

cd

cd ros2_ws/


คำสั่ง Build เฉพาะ แพ็กเกจ my_cpp_pkg

colcon build --packages-select my_cpp_pkg --symlink-install


ไปที่ หน้าต่างที่ 3 โดยเราจะต้องทําให้ Terminator รู้จักคําสั่งของ ROS2 ด้วย คําสั่ง

source ~/.bashrc


เรียกใช้งาน smartphone ด้วย ROS2 จะแสดงการรับ Message จากที่ส่งออกมาจาก topic robot_news ภาษา C++

ros2 run my_cpp_pkg smartphone