# Custom Message ในบทก่อนหน้า ผู้เรียนได้ทำความเข้าใจการสื่อสารระหว่าง Node ใน ROS 2 ผ่านกลไกของ Publisher/Subscriber, Service, และ Action ซึ่งทั้งหมดล้วนใช้ “ข้อความ” (Message) ในการแลกเปลี่ยนข้อมูลระหว่างกัน โดยข้อความเหล่านี้ส่วนใหญ่จะมาจาก ชนิดข้อมูลมาตรฐาน (Standard Message Types) เช่น std_msgs/msg/String, geometry_msgs/msg/Twist, หรือ sensor_msgs/msg/LaserScan อย่างไรก็ตาม ในการพัฒนาระบบหุ่นยนต์จริง มักมีกรณีที่ต้องส่งข้อมูลเฉพาะทาง เช่น สถานะของแขนกล, ระดับพลังงานแบตเตอรี่,หรือค่าที่มาจากเซนเซอร์หลายตัวพร้อมกัน ซึ่งข้อความมาตรฐานใน ROS 2 ไม่สามารถรองรับได้ครบทุกกรณีดังนั้นนักพัฒนาจึงต้องสร้าง Custom Message ขึ้นมาเอง เพื่อกำหนดโครงสร้างข้อมูลที่เหมาะสมกับงานของตนเอ ## สร้าง ไฟล์ .msg โดยปกติ แพ็กเกจแบบ ament_python ไม่รองรับการสร้าง .msg, .srv, .action โดยตรง เพราะขั้นตอน generate โค้ดของ ROS 2 (ผ่าน rosidl_*) ใช้ pipeline ฝั่ง CMake เป็นหลัก จึงต้องทำ แพ็กเกจอินเทอร์เฟซแยก ที่เป็น ament_cmake แล้วให้แพ็กเกจโค้ด Python (ament_python) ไปพึ่งพาใช้อีกที **เปิด Terminal ขึ้นมา เพื่อสร้างแพ็กเกจแบบ ament_cmake** ```{code-block} bash cd ~/ros2_ws/src ros2 pkg create --build-type ament_cmake --license Apache-2.0 custom_tutorials cd ~/ros2_ws colcon build ``` ```{image} images/c11/11.1.1.png :width: 80% :align: center ``` **เมื่อสร้างแพ็กเกจขึ้นมาแล้วจะได้โฟล์เดอร์ตามภาพด้านล่าง** ```{image} images/c11/11.1.2.png :width: 80% :align: center ``` **เปิด VS Code เลือก File > Open Folder** ```{image} images/c11/11.1.3.png :width: 80% :align: center ``` **และเลือก โฟลเดอร์ src จากนั้นกด Open** ```{image} images/c11/11.1.4.png :width: 80% :align: center ``` **จะปรากฎ 2 โฟลเดอร์แพ็คเกจภายใน** ```{image} images/c11/11.1.5.png :width: 80% :align: center ``` **สร้างโฟล์เดอร์ msg ภายใน custom_tutorials** ```{image} images/c11/11.1.6.png :width: 80% :align: center ``` **สร้างไฟล์ RobotStatus.msg ภายใน msg** ```{image} images/c11/11.1.7.png :width: 80% :align: center ``` **จากนั้นเขียนเนื้อหาในไฟล์ RobotStatus.msg ดังนี้** ```{code-block} bash int32 id float32 battery string status ``` ```{image} images/c11/11.1.8.png :width: 80% :align: center ``` **เปิดไฟล์ CMakeLists.txt** ```{image} images/c11/11.1.9.png :width: 80% :align: center ``` **ทำให้โค้ด CMakeLists.txt เป็นสีใน VS Code** * **ค้นหาและติดตั้ง Extension CMake Tools (จาก Microsoft)** ```{image} images/c11/11.1.10.png :width: 80% :align: center ``` ```{image} images/c11/11.1.11.png :width: 80% :align: center ``` **เพิ่มเนื้อหานี้ลงในไฟล์ CMakeLists.txt ดังนี้** ```{code-block} bash find_package(rosidl_default_generators REQUIRED) rosidl_generate_interfaces(${PROJECT_NAME} "msg/RobotStatus.msg" ) ``` ```{image} images/c11/11.1.12.png :width: 80% :align: center ``` **เปิดไฟล์ package.xml** ```{image} images/c11/11.1.13.png :width: 80% :align: center ``` **เพิ่มเนื้อหานี้ลงในไฟล์ package.xml ดังนี้** ```{code-block} bash rosidl_interface_packages ``` ```{image} images/c11/11.1.14.png :width: 80% :align: center ``` **เปิด Terminal ขึ้นมา เพื่อทำการ คอมไพล์** ```{code-block} bash cd ~/ros2_ws colcon build ``` ```{image} images/c11/11.1.15.png :width: 80% :align: center ``` **ตรวจสอบว่า message ถูกสร้างสำเร็จ** ```{code-block} bash ros2 interface show custom_tutorials/msg/RobotStatus ``` ```{image} images/c11/11.1.16.png :width: 80% :align: center ``` ## ทดลองใช้ Custom Message ที่เราสร้างไว้ (RobotStatus.msg) ในหัวข้อนี้เราจะสร้างโหนดตัวอย่างไว้ในแพ็กเกจ my_package (ชนิด ament_python) เพื่อทดสอบการสื่อสารแบบ Publisher/Subscriber โดยจะ อ้างอิงและใช้งานข้อความที่เราสร้างไว้ในแพ็กเกจ custom_tutorials (ชนิด ament_cmake) คือ custom_tutorials/msg/RobotStatus **สร้าง โหนด status_publisher.py และ status_subscriber.py ภายใน my_package** ```{image} images/c11/11.2.1.png :width: 80% :align: center ``` **ตัวอย่างโค้ด status_publisher.py** ```{code-block} python import rclpy from rclpy.node import Node from custom_tutorials.msg import RobotStatus class StatusPublisher(Node): def __init__(self): super().__init__('status_publisher') self.publisher_ = self.create_publisher(RobotStatus, 'robot_status', 10) self.timer = self.create_timer(1.0, self.publish_status) def publish_status(self): msg = RobotStatus() msg.id = 1 msg.battery = 86.5 msg.status = "Running" self.publisher_.publish(msg) self.get_logger().info(f"Publishing: id={msg.id}, battery={msg.battery},status={msg.status}") def main(): rclpy.init() node = StatusPublisher() rclpy.spin(node) node.destroy_node() rclpy.shutdown() if __name__ == '__main__': main() ``` **ตัวอย่างโค้ด status_subscriber.py** ```{code-block} python import rclpy from rclpy.node import Node from custom_tutorials.msg import RobotStatus class StatusSubscriber(Node): def __init__(self): super().__init__('status_subscriber') self.subscription = self.create_subscription( RobotStatus, 'robot_status', self.listener_callback, 10) self.subscription def listener_callback(self, msg): self.get_logger().info(f"Received: id={msg.id}, battery={msg.battery}, status={msg.status}") def main(): rclpy.init() node = StatusSubscriber() rclpy.spin(node) node.destroy_node() rclpy.shutdown() if __name__ == '__main__': main() ``` **ตั้งค่า setup.py** ```{code-block} python 'status_publisher = my_package.status_publisher:main', 'status_subscriber = my_package.status_subscriber:main', ``` ```{image} images/c11/11.2.2.png :width: 80% :align: center ``` **ตั้งค่า package.xml** ```{code-block} xml custom_tutorials ``` ```{image} images/c11/11.2.3.png :width: 80% :align: center ``` **เปิด Terminal ขึ้นมา ทำการ คอมไพล์** ```{code-block} bash cd ~/ros2_ws colcon build ``` ```{image} images/c11/11.2.4.png :width: 80% :align: center ``` **ทดสอบการทำงานของโหนด status_publisher และ status_subscriber กับ RobotStatus Message** ```{code-block} bash ros2 run my_package status_publisher ``` ```{code-block} bash ros2 run my_package status_subscriber ``` ```{image} images/c11/11.2.5.png :width: 80% :align: center ``` **สรุปสิ่งที่เพิ่มเติมในไฟล์ ให้รองรับและทำงานอย่างถูกต้อง** **1. การสร้าง Message** เมื่อสร้างไฟล์ .msg ใหม่ เช่น RobotStatus.msg เราต้องปรับสองไฟล์หลักในแพ็กเกจที่สร้าง message คือ * (1) package.xml บอก ROS ว่าแพ็กเกจนี้เป็น “interface package” ที่ต้อง generate message: ```{image} images/c11/11.2.6.png :width: 80% :align: center ``` * (2) CMakeLists.txt เพิ่มคำสั่งให้ CMake เรียกตัวสร้างโค้ด (message generator): ```{image} images/c11/11.2.7.png :width: 80% :align: center ``` บล็อกนี้คือสิ่งที่ทำให้ ROS 2 สร้างไฟล์ C++ และ Python ของ message อัตโนมัติหลัง build **2.การนำ Message ไปใช้งานใน Node (Python)** เมื่อ build เสร็จ ระบบจะสร้างโมดูล Python ขึ้นอัตโนมัติ จึงสามารถ import ได้เหมือนโมดูลทั่วไป: ```{image} images/c11/11.2.8.png :width: 80% :align: center ``` โครงสร้างคือ from <ชื่อแพ็กเกจที่สร้าง message>.msg import <ชื่อไฟล์ msg> **3. การเชื่อมโยงแพ็กเกจที่นำไปใช้** ถ้าโค้ด Node อยู่ในอีกแพ็กเกจหนึ่ง (เช่น my_package) ต้องประกาศการพึ่งพาใน package.xml ด้วย: ```{image} images/c11/11.2.9.png :width: 80% :align: center ``` custom_tutorials → เพื่อให้ระบบรู้ว่าต้องใช้ message จาก custom_tutorials rclpy → เพราะโหนดของเราใช้ ROS 2 Python API (rclpy)