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

OOP ก็คือ “ธรรมชาติของวัตถุ” หมายความว่า OOP จะมองสิ่งแต่ละสิ่งถือเป็น “วัตถุชิ้นหนึ่ง” (Object) มันจะมีสีแดงหรือสีเขียว ยาวหรือสั้น มันก็คือวัตถุชิ้นหนึ่งเหมือนกัน และเราสามารถกำหนดประเภทหรือคลาสให้กับวัตถุเหล่านั้นได้ นอกจากนี้ เมื่อ OOP มองทุกสิ่งถือเป็นวัตถุชิ้นหนึ่งแล้ว ยังสามารถคิดต่อไปอีกว่า “วัตถุแต่ละอย่างนั้น ต่างก็มีลักษณะและวิธีการใช้งานเป็นของตัวเอง” หมายความว่า วัตถุแต่ละชนิดหรือแต่ละชิ้นต่างก็มีรูปร่าง ลักษณะ และการใช้งาน (การกระทำ) ที่แตกต่างกันออกไป เราจะเรียกคุณลักษณะของวัตถุว่า แอตทริบิวต์ (Attribute) และจะเรียกวิธีการใช้งานวัตถุว่า เมธทอด (Method)

1 : เขียน Node ภาษา C++ เบื้องต้น


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


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


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


 #include "rclcpp/rclcpp.hpp"
  
 class MyNode : public rclcpp::Node
 {
 public:
     MyNode() : Node("cpp_test"), counter_(0)
     {
         RCLCPP_INFO(this->get_logger(), "Hello Cpp Node");
  
         timer_ = this->create_wall_timer(std::chrono::milliseconds(1),
                                          std::bind(&MyNode::timerCallback, this));
     }
  
 private:
     void timerCallback()
     {
         counter_++;
         RCLCPP_INFO(this->get_logger(), "Hello %d", counter_);
     }
     
     rclcpp::TimerBase::SharedPtr timer_;
     int counter_;
 };
  
 int main(int argc, char **argv)
 {
     rclcpp::init(argc, argv);
     auto node = std::make_shared<MyNode>();
     RCLCPP_INFO(node->get_logger(), "Hello Cpp Node");
     rclcpp::spin(node);
     rclcpp::shutdown();
     return 0;
 }

   

เข้าไปใน โฟลเดอร์ ros2_ws

cd ros2_ws/

Build แพ็คเกจ my_cpp_pkg

colcon build --packages-select my_cpp_pkg


เปิด Terminator หน้าต่างที่ 2

เรียกใช้งานด้วยคำสั่ง ros2 run ชื่อPackag ชื่อNode

ros2 run my_cpp_pkg cpp_node


จะแสดงผลลัพธ์ การทำงาน


3 : อธิบายโค้ด


ก่อนอื่นเรานำเข้าไลบรารี rclcpp จาก rclcpp เราจะสามารถดึงฟังก์ชันหลักของ ROS2 ได้มากมายเช่น nodes, topics, services, ฯลฯ

 #include "rclcpp/rclcpp.hpp"


ในโปรแกรมหลักของเรา สิ่งแรกที่เราทำคือเริ่มต้นการสื่อสารระหว่าง ROS2 กับ rclcpp:: init (). คุณต้องผ่าน 2 อาร์กิวเมนต์ ซึ่งเป็นพารามิเตอร์ที่คุณได้รับจาก ฟังก์ชัน main() ส่วนนี้สำคัญมาก ต้องเรียก rclcpp:: init () ก่อนที่คุณจะสร้างโหนดใดๆ

int main(int argc, char **argv)
 {
     rclcpp::init(argc, argv);


มาเขียนโค้ดขั้นต่ำที่เหมือนเดิมกันใหม่ แต่คราวนี้ด้วย OOP เราจะเพิ่มฟังก์ชันการทำงานบางอย่างหลังจากนั้น

 
 int main(int argc, char **argv)
  {
      rclcpp::init(argc, argv);
      auto node = std::make_shared<MyNode>();
      RCLCPP_INFO(node->get_logger(), "Hello Cpp Node");
      rclcpp::spin(node);
      rclcpp::shutdown();
      return 0;
  } 


เราประกาศวัตถุ ROS2 Timer เป็นแอตทริบิวต์ส่วนตัวของคลาส โปรดทราบว่า ROS2 นำเสนอประเภทที่มีประโยชน์บางอย่างแก่คุณ: คุณสามารถใช้ SharedPtr ซึ่งหมายความว่าในความเป็นจริงแล้ววัตถุจะอยู่ภายใน std::shared_ptr.

rclcpp::TimerBase::SharedPtr timer_;


เราเพิ่มการโทรกลับสำหรับตัวจับเวลานี้ เราทำให้วิธีนี้เป็นแบบส่วนตัว เนื่องจากจะเรียกจากภายในโหนดคลาสเท่านั้น

ภายในวิธีนี้เราเพียงแค่พิมพ์บางอย่างบนหน้าจอด้วย RCLCPP_INFO (). เราใช้วิธีสืบทอดมา get_logger () เพื่อรับตัวบันทึกของโหนดและการตั้งค่าทั้งหมดที่เข้ากันได้

 
 private:
      void timerCallback()
      {
          counter_++;
          RCLCPP_INFO(this->get_logger(), "Hello %d", counter_);
      } 

ภายใน Constructor เราเริ่มต้นตัวจับเวลาด้วยวิธีอื่นที่สืบทอดมา: create_wall_timer (). เราจำเป็นต้องให้ 2 อาร์กิวเมนต์: ระยะเวลาระหว่างการเรียกกลับ 2 ครั้ง และฟังก์ชันที่จะเรียก ที่นี่เพื่อผ่านวิธีการเรียนที่เราต้องใช้ std::bind() การเรียกกลับจะเริ่มทำงานเมื่อโหนดเริ่มทำงาน

และอย่างที่คุณเห็น ไม่จำเป็นต้องเปลี่ยนแปลงอะไรในฟังก์ชันหลักของโปรแกรม ใช้คลาสที่สืบทอดมาจาก rclcpp::Node ทำให้แนวทางค่อนข้างเป็นแบบแยกส่วน

 
      MyNode() : Node("cpp_test"), counter_(0)
      {
          RCLCPP_INFO(this->get_logger(), "Hello Cpp Node");
   
          timer_ = this->create_wall_timer(std::chrono::milliseconds(1),
                                           std::bind(&MyNode::timerCallback, this));
      }