# 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)