เขียน 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