ตัวอย่างนี้จะเป็นการสร้างหุ่นยนต์เคลื่อนที่ ที่สามารถควบคุมผ่าน wifi ได้ ซึ่งจะเป็นระบบพื้นฐานของหุ่นยนต์เคลื่อนที่แบบอัตโนมัติในพื้นที่ปิด (indoor) ที่มีความสามารถในการสร้างแผนที่และเคลื่อนที่ไปยังตำแหน่งที่ต้องการพร้อมหลบหลีกสิ่งกีดขวางได้โดยอัตโนมัติ
การทำงานจะแยกเป็น 2 ส่วนคือ
- PC ซึ่งทำหน้าที่เป็นตัวรับข้อมูลจากเซนเซอร์ต่างๆบนหุ่นยนต์มาสร้างและแสดงผลแผนที่ รวมทั้งระบุตำแหน่งปัจจุบันของหุ่นยนต์ที่สอดคล้องกับตำแหน่งในแผนที่ โดยคอมพิวเตอร์จะติดตั้ง Ubuntu 22.04 พร้อมกับ ROS2 foxy
- หุ่นยนต์เคลื่อนที่แบบขับเคลื่อนด้วยล้อแยก (Differential Drive) ที่ติดตั้งมอเตอร์ DC จำนวน 2 ตัว และล้อเอนกประสงค์ (Caster Wheel) ที่ด้านหลัง มอเตอร์ DC ทั้งสองตัวติดตั้งตัวเข้ารหัส (Encoder) เพื่อช่วยในการติดตามความเร็วและระยะทางที่หุ่นยนต์เคลื่อนที่ มอเตอร์ถูกควบคุมด้วยโมดูลควบคุมมอเตอร์ L298N ซึ่งรับพลังงานจากแบตเตอรี่ลิเธียม 12 โวลต์ เซนเซอร์สำคัญอีกตัวหนึ่งคือ LiDAR ที่ติดตั้งอยู่ด้านบนของหุ่นยนต์ ซึ่งใช้สำหรับตรวจจับระยะทางและสภาพแวดล้อมรอบตัว ส่วนที่เป็น “สมอง” ของหุ่นยนต์ คือ Raspberry Pi 4 Model B ที่ใช้ในการประมวลผลและควบคุมการทำงานทั้งหมดของระบบ ที่จะทำงานร่วมกับ Arduino ที่ทำหน้าที่อ่านค่าตำแหน่งของมอเตอร์และควบคุมการหมุนของมอเตอร์ โดย Raspberry Pi จะติดตั้ง Ubuntu 22.04 พร้อมกับ ROS2 foxy ไว้เช่นกัน
เราจะเริ่มจากการสร้าง Node อย่างง่าย 2 node บน PC เพื่อทดสอบการรับส่งข้อมูลระหว่าง node คือ Command_node และ Drive_node
ขั้นตอนที่ 1: การตั้งค่า ROS2 Workspace
- เปิด terminal และสร้าง Workspace Directory ด้วยคำสั่งดังนี้
mkdir -p ~/ros2_ws/src cd ~/ros2_ws |
เริ่มต้น Workspace ด้วยคำสั่ง colcon build
- Source Workspace หลังจาก build แล้ว ให้ source workspace: source install/setup.bash
ขั้นตอนที่ 2: การสร้าง ROS2 Package
- ไปที่โฟลเดอร์ src : cd ~/ros2_ws/src
- สร้าง Package : ros2 pkg create MobileRobot_py –build-type ament_python –dependencies rclpy std_msgs
- เข้าไปในโฟลเดอร์ Package: cd MobileRobot_py
ขั้นตอนที่ 3: การสร้าง Node
Command Node
- สร้างไฟล์ command_node.py และ drive_node.py
mkdir -p MobileRobot_py touch MobileRobot_py/command_node.py touch MobileRobot_py/drive_node.py chmod +x MobileRobot_py/command_node.py chmod +x MobileRobot_py/drive_node.py |
เขียนโค้ดใน command_node.py
import rclpy from rclpy.node import Node from std_msgs.msg import String from pynput import keyboard import threading class CommandNode(Node): def __init__(self): super().__init__(‘command_node’) self.publisher_ = self.create_publisher(String, ‘/drive_commands’, 10) self.get_logger().info(‘CommandNode is running. Press keys to send commands.’) self.listener = keyboard.Listener(on_press=self.on_press) self.listener.start() def on_press(self, key): try: if key.char == ‘i’: self.publish_command(‘Forward’) elif key.char == ‘j’: self.publish_command(‘TurnLeft’) elif key.char == ‘l’: self.publish_command(‘TurnRight’) elif key.char == ‘m’: self.publish_command(‘Backward’) elif key.char == ‘k’: self.publish_command(‘Stop’) except AttributeError: pass def publish_command(self, command): msg = String() msg.data = command self.publisher_.publish(msg) self.get_logger().info(f’Publishing: “{msg.data}”‘) def main(args=None): rclpy.init(args=args) node = CommandNode() try: rclpy.spin(node) except KeyboardInterrupt: pass finally: node.destroy_node() rclpy.shutdown() if __name__ == ‘__main__’: main() |
2. Drive Node
- เขียนโค้ดใน drive_node.py
import rclpy from rclpy.node import Node from std_msgs.msg import String class DriveNode(Node): def __init__(self): super().__init__(‘drive_node’) self.subscription = self.create_subscription( String, ‘/drive_commands’, self.listener_callback, 10 ) self.get_logger().info(‘DriveNode is ready to receive commands.’) def listener_callback(self, msg): command = msg.data self.get_logger().info(f’Received command: “{command}”‘) if command == ‘Forward’: self.forward() elif command == ‘TurnLeft’: self.turn_left() elif command == ‘TurnRight’: self.turn_right() elif command == ‘Backward’: self.backward() elif command == ‘Stop’: self.stop() else: self.get_logger().warn(f’Unknown command: “{command}”‘) def forward(self): self.get_logger().info(‘Executing forward operation.’) def turn_left(self): self.get_logger().info(‘Executing turn left operation.’) def turn_right(self): self.get_logger().info(‘Executing turn right operation.’) def backward(self): self.get_logger().info(‘Executing backward operation.’) def stop(self): self.get_logger().info(‘Executing stop operation.’) def main(args=None): rclpy.init(args=args) drive_node = DriveNode() try: rclpy.spin(drive_node) except KeyboardInterrupt: pass finally: drive_node.destroy_node() rclpy.shutdown() if __name__ == ‘__main__’: main() |
ขั้นตอนที่ 4: แก้ไขไฟล์ setup.py
- เปิดไฟล์ setup.py : nano setup.py
- แก้ไขเนื้อหาให้เป็นดังนี้:
from setuptools import setup package_name = ‘MobileRobot_py’ setup( name=package_name, version=’0.0.0′, packages=[package_name], data_files=[ (‘share/ament_index/resource_index/packages’, [‘resource/’ + package_name]), (‘share/’ + package_name, [‘package.xml’]), ], install_requires=[‘setuptools’], zip_safe=True, maintainer=’Your Name’, maintainer_email=’your.email@example.com’, description=’MobileRobot_py project with Command_node and Drive_node.’, license=’Apache License 2.0′, tests_require=[‘pytest’], entry_points={ ‘console_scripts’: [ ‘command_node = MobileRobot_py.command_node:main’, ‘drive_node = MobileRobot_py.drive_node:main’, ], }, ) |
ขั้นตอนที่ 5: Build Package
- กลับไปยัง Workspace Directory: cd ~/ros2_ws
- Build Package: colcon build
- Source Workspace : source install/setup.bash
ขั้นตอนที่ 6: รัน Node ใช้ 2 Terminal ในการรัน Node:
Terminal 1: รัน drive_node: ros2 run MobileRobot_py drive_node
Terminal 2: รัน command_node: ros2 run MobileRobot_py command_node
Note:
- สามารถตรวจสอบการรับส่ง message ระหว่าง node ได้จากการใช้คำสั่ง ros2 topic list จะแสดงรายชื่อของ topic ที่มีการ publish ระหว่างแต่ละ node
- ถ้าต้องการดูรายละเอียดของ topic (เช่น ชนิดของข้อความที่ใช้) ทำได้โดยใช้คำสั่ง ros2 topic info <topic_name>
- ถ้าต้องการตรวจสอบข้อมูลในแต่ละ topic สามารถทำได้โดยใช้คำสั่ง ros2 topic echo <topic_name>
เมื่อรันเรียบร้อย คุณจะสามารถส่งคำสั่งผ่านแป้นพิมพ์เพื่อควบคุมหุ่นยนต์ได้ ดังรูป
สรุป
บทความนี้แสดงให้เห็นขั้นตอนการสร้าง ROS2 Package และ Node อย่างง่าย พร้อมตัวอย่างโค้ดที่ช่วยให้คุณเริ่มต้นใช้งาน ROS2 สำหรับการควบคุมหุ่นยนต์เคลื่อนที่ได้ทันที! ในบทความถัดไป จะทดลองควบคุมหุ่นยนต์โดยการย้าย/สร้าง drive_node บนหุ่นยนต์และรอรับคำสั่งจาก command_node บน PC เพื่อควบคุมหุ่นยนต์ผ่านการกด keyboard