10. QoS — กับดักที่เจอกันมากที่สุด
Important
หัวข้อนี้ไม่ใช่เรื่องขั้นสูง แต่เป็นสาเหตุอันดับต้น ๆ ที่ทำให้คนเพิ่งเริ่ม เสียเวลาเป็นวันทั้งที่โค้ดถูกหมด
อาการคลาสสิก: ros2 topic list เห็นชื่อ topic · ros2 node list เห็น node ครบ ·
ไม่มี error สักบรรทัด · แต่ ไม่มีข้อมูลไหลถึงกันเลย
QoS คืออะไร
ข้อตกลงว่าจะส่งข้อมูลกัน "แบบไหน" — publisher เสนอเงื่อนไข subscriber ขอเงื่อนไข ถ้าเงื่อนไขไม่เข้ากัน ทั้งคู่จะไม่เชื่อมต่อกันเลย และไม่มีใครแจ้งอะไร
3 ข้อที่ทำให้พังบ่อยที่สุด
ข้อ |
ตัวเลือก |
ความหมาย |
|---|---|---|
Reliability |
|
ส่งซ้ำจนกว่าจะถึง — ห้ามหาย |
|
ส่งแล้วแล้วกัน หายได้ — เร็วกว่า |
|
Durability |
|
ใครมาทีหลังไม่ได้ของเก่า |
|
เก็บค่าล่าสุดไว้ให้คนมาทีหลัง |
|
History |
|
เก็บล่าสุดกี่ชิ้น |
กฎความเข้ากัน — จำข้อเดียวนี้พอ
Warning
🔴 ผู้ส่งต้องให้ "ไม่น้อยกว่า" ที่ผู้รับขอ
publisher ให้ |
subscriber ขอ |
ผล |
|---|---|---|
|
|
✅ ต่อได้ |
|
|
✅ ต่อได้ (ให้มากกว่าที่ขอ) |
|
|
🔴 ไม่ต่อ — เงียบสนิท |
|
|
✅ ต่อได้ |
แถวสีแดงคือกรณีที่เจอจริงเกือบทุกครั้ง เพราะ
เซ็นเซอร์ส่วนใหญ่ใช้
BEST_EFFORT(LiDAR · กล้อง · IMU) เพราะข้อมูลมาถี่ ตกไปบ้างไม่เป็นไร ค่าใหม่มาแทนใน 0.1 วินาทีค่าเริ่มต้นของ
create_subscriptionคือRELIABLE
เขียน subscriber แบบตรงไปตรงมาแล้วไปฟัง /scan จึงไม่ได้อะไรเลย
วิธีตรวจ — คำสั่งเดียวจบ
ros2 topic info -v /scan
Type: sensor_msgs/msg/LaserScan
Publisher count: 1
Node name: lidar_node
QoS profile:
Reliability: BEST_EFFORT ← ผู้ส่งให้แค่นี้
Durability: VOLATILE
Subscription count: 1
Node name: my_node
QoS profile:
Reliability: RELIABLE ← ผู้รับขอมากกว่า → ไม่ต่อกัน
Durability: VOLATILE
Tip
-v คือหัวใจ — ถ้าไม่ใส่จะเห็นแค่จำนวน publisher/subscriber
ซึ่งขึ้นเป็น 1 ทั้งคู่ ดูเหมือนทุกอย่างปกติ ทั้งที่ไม่ได้เชื่อมกัน
จำคำสั่งนี้ให้ขึ้นใจ มันคือคำสั่งที่ประหยัดเวลาได้มากที่สุดในคู่มือทั้งเล่ม
วิธีแก้
ใช้โปรไฟล์สำเร็จรูปที่ตรงกับชนิดข้อมูล
from rclpy.qos import qos_profile_sensor_data
self.sub = self.create_subscription(
LaserScan, '/scan', self.on_scan,
qos_profile_sensor_data) # แทนที่จะใส่เลข 10
qos_profile_sensor_data คือ BEST_EFFORT + depth 5 ซึ่งตรงกับที่เซ็นเซอร์ส่ง
ถ้าต้องตั้งเอง
from rclpy.qos import QoSProfile, ReliabilityPolicy, DurabilityPolicy
qos = QoSProfile(
depth=5,
reliability=ReliabilityPolicy.BEST_EFFORT,
durability=DurabilityPolicy.VOLATILE,
)
กับดักซ้อน — ros2 topic echo ก็ติดกฎเดียวกัน
ros2 topic echo ก็เป็น subscriber ตัวหนึ่ง จึงเจอปัญหาเดียวกันได้
ถ้า echo เงียบแต่ RViz เห็นข้อมูล ให้ลอง
ros2 topic echo /scan --qos-profile sensor_data
Danger
🔴 อย่าสรุปว่า "ไม่มีข้อมูล" จากการที่ echo เงียบเพียงอย่างเดียว
เคยมีคนไล่หาสาเหตุถึงขั้นถอดสาย LiDAR มาเปลี่ยน ทั้งที่ข้อมูลไหลปกติมาตลอด แค่เครื่องมือที่ใช้ดูมันเข้ากันไม่ได้
ตรวจด้วย ros2 topic info -v เสมอ ก่อนจะสรุปอะไรเกี่ยวกับฮาร์ดแวร์
TRANSIENT_LOCAL — ของที่ส่งครั้งเดียว
บาง topic ส่งข้อมูลครั้งเดียวแล้วเงียบ เช่น
topic |
ทำไม |
|---|---|
|
แผนที่ส่งครั้งเดียวตอนโหลด |
|
ตำแหน่งเซ็นเซอร์ไม่เคยเปลี่ยน |
ถ้าใช้ VOLATILE node ที่เปิดทีหลังจะไม่มีวันได้แผนที่ เพราะพลาดตอนที่ส่งไปแล้ว
from rclpy.qos import QoSProfile, DurabilityPolicy
qos = QoSProfile(depth=1, durability=DurabilityPolicy.TRANSIENT_LOCAL)
self.sub = self.create_subscription(OccupancyGrid, '/map', self.on_map, qos)
Note
อาการของข้อนี้คือ "เปิดตามลำดับหนึ่งแล้วใช้ได้ อีกลำดับหนึ่งใช้ไม่ได้" ซึ่งดูเหมือนปัญหาจังหวะเวลา แต่จริง ๆ เป็นเรื่อง QoS
สรุปที่ควรจำ
อาการ "ดูปกติทุกอย่างแต่ไม่มีข้อมูล" → ros2 topic info -v ก่อนเสมอ
เซ็นเซอร์ (LiDAR · กล้อง · IMU) → qos_profile_sensor_data
แผนที่ · tf_static → TRANSIENT_LOCAL
คำสั่งควบคุม (/cmd_vel) → ค่าเริ่มต้น (RELIABLE) ถูกแล้ว
Important
จบส่วนที่ 1 แล้ว ถึงตรงนี้ควรทำได้ 3 อย่าง
อ่านออกว่าระบบมี node อะไร คุยกันทาง topic ไหน
เขียน node ของตัวเองที่ส่งและรับข้อมูลได้
หาสาเหตุเป็นเมื่อข้อมูลไม่ถึงกัน ซึ่งสำคัญที่สุดตอนต่ออุปกรณ์จริง
ส่วนที่ 2 จะเริ่มต่อของจริง เริ่มจากทำให้ล้อหมุนก่อน
ก่อนหน้า: 9. RViz · กลับหน้าแรก: ROS 2 สำหรับหุ่นยนต์เคลื่อนที่