หุ่นยนต์บังคับมือ The Cube Transporter: ภารกิจปล่อยลูกบาศก์ จุดประกายนักประดิษฐ์จิ๋ว
- Siwadon Heebkaew
- 15 ก.พ.
- ยาว 3 นาที
ในบทความนี้ ผมจะมาแชร์โปรเจกต์หุ่นยนต์ที่ผมเพิ่งสร้างเสร็จ ซึ่งเป็น หุ่นยนต์บังคับมือ (Manual Remote Control) ที่มีฟังก์ชันพิเศษคือ กลไกการปล่อยลูกบาศก์ (Cube Dispenser) สำหรับภารกิจส่งของหรือทำภารกิจจำลองต่างๆ

1. แนวคิดและแรงบันดาลใจ: สื่อการเรียนรู้ที่สนุกได้ทันที
จุดเริ่มต้นของโปรเจกต์นี้มาจากความต้องการสร้างหุ่นยนต์เคลื่อนที่ (Mobile Robot) เพื่อนำไปใช้สอนเด็กๆ ในระดับชั้นอนุบาล โจทย์ที่ผมตั้งไว้คือ หุ่นยนต์ต้องไม่ใช่แค่เครื่องจักรที่เดินไปมาอย่างไร้จุดหมาย แต่ต้องสามารถ "มีปฏิสัมพันธ์กับวัตถุได้" ที่สำคัญที่สุดคือ เด็กๆ ต้องสามารถเล่นและเรียนรู้ได้ทันทีโดยไม่ต้องกังวลเรื่องการเขียนโค้ดที่ซับซ้อน ผมจึงออกแบบให้มันสามารถบรรทุกและปล่อย "ลูกบาศก์สีเหลือง" ในจุดที่ต้องการ เพื่อให้เด็กๆ สนุกไปกับการแก้ปัญหาและการกะระยะผ่านการเล่น (Learning through Play)
2. โครงสร้างและอุปกรณ์หลัก (Hardware)
โครงสร้างหลัก (Chassis) ของหุ่นยนต์ถูกออกแบบและผลิตขึ้นด้วยแผ่นอะคริลิคใส และฐานปล่อยลูกบาศก์ใช้ 3D Printing โดยใช้เส้นพลาสติกสีเหลืองสดใส ข้อดีคือสามารถปรับแต่งขนาดและช่องว่างต่างๆ ให้พอดีกับอุปกรณ์อิเล็กทรอนิกส์แต่ละชิ้นได้อย่างลงตัว โดยมีส่วนประกอบสำคัญดังนี้:

Controller: ใช้บอร์ด FriendRobot Model Nano Plus
Driving System: ระบบขับเคลื่อนแบบ 2 ล้อหลัง (Rear-Wheel Drive) โดยใช้มอเตอร์กระแสตรง (DC gear motor 3 - 6 V 1:48) ร่วมกับล้อยางแบบมีฟองน้ำซับ ขนาด 65 mm. และใช้ ล้ออิสระติดตั้งไว้ใต้ฐานด้านหน้า เพื่อให้หุ่นยนต์สามารถเลี้ยวและหมุนตัวได้คล่องแคล่วแบบ 360 องศา
Cube Tower Mechanism: ส่วนจัดเก็บลูกบาศก์ถูกออกแบบเป็นทรงสูง (Tower) เพื่อให้ลูกบาศก์ซ้อนกันในแนวดิ่ง โดยใช้ MG90S Micro Servo เป็นตัวควบคุมการปิด-เปิดช่องปล่อยลูกด้านล่าง
Remote Control: ใช้ระบบควบคุมผ่าน JoyStick playstation PS2 wireless (ตัวรับสัญญาณสีดำที่ติดตั้งอยู่ด้านหน้า)และเซนเซอร์รับสัญญาณ ในการบังคับทำได้จากระยะไกลโดยไม่มีสายรบกวน

JoyStick playstation PS2 wireless และ ตัวรับสัญญาณสีดำ
3. กลไกการปล่อยลูกบาศก์ (The Dispensing Mechanism)
ช่องเก็บลูกบาศก์แนวดิ่ง ลูกบาศก์(20 x 20 mm) จะถูกวางซ้อนกันในฐานปล่อย เมื่อเรากดปุ่มJoyStick Servo Motor จะหมุนเพื่อดันให้ลูกบาศก์ก้อนล่างสุดไหลออกมา

4. การพัฒนาซอฟต์แวร์ (Software)
ส่วนที่ทำให้หุ่นยนต์ตัวนี้ทำงานได้ คือชุดคำสั่งที่เขียนขึ้นด้วยภาษา C++ ผ่าน Arduino IDE โดยผมได้ออกแบบระบบซอฟต์แวร์ให้มีความยืดหยุ่นสูง ดังนี้:
ระบบเมนูและการตั้งค่า (Interactive Menu): ผมเขียนฟังก์ชัน manu() เพื่อให้สามารถตั้งค่าหุ่นยนต์ได้ ผ่านหน้าจอ OLED โดยใช้ปุ่ม Knob บนตัวหุ่นยนต์เพื่อเลือกเมนู ไม่ว่าจะเป็นการปรับความเร็ว (Speed), การปรับสมดุลทิศทาง (Straight), หรือการตั้งองศาของ Servo
การจดจำค่าด้วย EEPROM: ระบบจะทำการบันทึกค่าความเร็วและองศาของ Servo ที่เราตั้งไว้ลงในหน่วยความจำ EEPROM ทำให้เมื่อปิดแล้วเปิดเครื่องใหม่ หุ่นยนต์จะยังคงจำค่าล่าสุดได้โดยไม่ต้องตั้งค่าใหม่ทุกครั้ง
การควบคุมผ่านจอย PS2 ไร้สาย: ใช้ไลบรารี PS2X ในการรับสัญญาณจากจอยสติ๊ก โดยออกแบบให้:
D-Pad (ปุ่มทิศทาง): ใช้ควบคุมการเดินหน้า-ถอยหลัง และการเลี้ยวแบบ "ระบบขับเคลื่อน 2 ล้อแยกอิสระ(Differential Drive)"
ปุ่มสามเหลี่ยม (PSB_TRIANGLE): ทำหน้าที่เป็นสวิตช์สั่งงานให้ Servo (Servo P) ขยับเพื่อปล่อยลูกบาศก์ และเมื่อปล่อยปุ่ม Servo จะกลับไปที่ตำแหน่งล็อค (Servo K) ทันที
ระบบป้องกันการติดขัด: ในโค้ดมีการหน่วงเวลา (Delay) และการคืนค่าตำแหน่ง Servo เพื่อให้กลไกการปล่อยลูกบาศก์ทำงานได้ต่อเนื่องและไม่เกิดการ Jamming ของตัวลูกบาศก์ใน Tower
(สามารถดูรายละเอียดโค้ดการทำงานทั้งหมดได้ที่ส่วนท้ายของบทความ)
บทสรุป
หุ่นยนต์ตัวนี้ไม่ได้เป็นแค่โครงงานชิ้นหนึ่ง แต่คือบทสรุปความตั้งใจของผมที่อยากรวบรวมทักษะทั้งด้านการออกแบบกลไก (Mechanical Design), ระบบไฟฟ้า (Electronics) และการเขียนโปรแกรม (Programming) มาสร้างเป็นสื่อการสอนที่ทำให้เด็กปฐมวัยเข้าถึงเทคโนโลยีได้ผ่านความสนุก
สำหรับก้าวต่อไป ผมตั้งใจจะพัฒนาให้หุ่นยนต์ตัวนี้ทำงานได้หลากหลายมากขึ้น ทั้งการเปลี่ยนจากJoyStick มาใช้การควบคุมผ่านipad เพื่อการทำงานในรู้แบบอื่นๆ

สินค้าในบทความ
ดูเพิ่มเติมได้ที่ https://www.friendrobotshop.com/
สนใจสอบถามข้อมูลเพิ่มเติมเกี่ยวกับหุ่นยนต์เพื่อการศึกษา ติดต่อ LINE: [ID LINE @587tsrah]
Full Source Code (Arduino)
#include "ModelNanoPlus.h"
#include <EEPROM.h>
#define PS2_DAT 14
#define PS2_CMD 15
#define PS2_SEL 16
#define PS2_CLK 17
PS2X ps2x;
int savepin = 0; // กำหนดค่าเริ่มต้น
int motorl, motorr, straight, i, servok, servop;
void setup() {
XIO();
delay(1000);
oledClear();
oledMode(0);
setTextSize(2);
// --- อ่านค่าจาก EEPROM ---
int read_l = EEPROM.read(savepin + 0);
int read_r = EEPROM.read(savepin + 1);
int read_s = EEPROM.read(savepin + 2);
int servo_k = EEPROM.read(savepin + 3);
int servo_p = EEPROM.read(savepin + 4);
motorl = (read_l == 255) ? 50 : read_l;
motorr = (read_r == 255) ? 50 : read_r;
straight = (read_s == 255) ? 0 : map(read_s, 0, 254, -50, 50);
servok = (servo_k == 255) ? 90 : servo_k;
servop = (servo_p == 255) ? 0 : servo_p;
// คำนวณค่ามอเตอร์เริ่มต้น
motorl = motorl - straight;
motorr = motorr + straight;
oled(0, 0, "Connecting");
while (true) {
int error = ps2x.config_gamepad(PS2_CLK, PS2_CMD, PS2_SEL, PS2_DAT, false, false);
if (error == 0) {
oled(0, 0, "OK");
delay(1000);
oledClear();
break;
}
delay(500);
}
manu();
}
void loop() {
joy_stick();
}
void joy_stick() { // เติม () ตรงนี้
ps2x.read_gamepad(false, false);
servo(1, servok);
if (ps2x.Button(PSB_TRIANGLE)) {
servo(1, servop);
} else if (ps2x.Button(PSB_PAD_UP)) {
motor(1, motorl);
motor(2, motorr);
} else if (ps2x.Button(PSB_PAD_DOWN)) {
motor(1, -motorl);
motor(2, -motorr);
} else if (ps2x.Button(PSB_PAD_LEFT)) {
motor(1, -motorl / 2);
motor(2, motorr / 2);
} else if (ps2x.Button(PSB_PAD_RIGHT)) {
motor(1, motorl / 2);
motor(2, -motorr / 2);
} else {
motor(1, 0);
motor(2, 0);
}
delay(50);
}
void manu() {
while (SW_OK() == 1) {
i = knob(0, 3);
if (i == 0) oled(10, 10, "speed motor");
else if (i == 1) oled(10, 10, "straight motor ");
else if (i == 2) oled(10, 10, "servo angle ");
else if (i == 3) oled(10, 10, "play ");
}
delay(500);
oledClear();
if (i == 0) speed_motor();
else if (i == 1) straight_motor();
else if (i == 2) servo_angle();
// กด play จะหลุดออกจาก manu() ไปทำงานใน loop()
}
void speed_motor() {
oledClear();
while (SW_OK() == 1) {
int val = knob(0, 100);
oled(0, 0, "Speed: %d ", val);
motorl = val;
motorr = val;
ps2x.read_gamepad(false, false);
if(ps2x.Button(PSB_PAD_UP)){
motor(1, motorl);
motor(2, motorr);
}
else if (ps2x.Button(PSB_PAD_DOWN)) {
motor(1, -motorl);
motor(2, -motorr);
}
else {
motor(1, 0);
motor(2, 0);
}
}
EEPROM.update(savepin + 0, motorl);
EEPROM.update(savepin + 1, motorr);
oledClear();
}
void straight_motor() {
oledClear();
int base_speed = motorl;
int s_val = 0;
while (SW_OK() == 1) {
s_val = knob(-10, 10);
oled(0, 0, "Straight: %d ", s_val);
int test_l = base_speed + s_val;
int test_r = base_speed - s_val;
ps2x.read_gamepad(false, false);
if(ps2x.Button(PSB_PAD_UP)){
motor(1, test_l);
motor(2, test_r);
}
else if (ps2x.Button(PSB_PAD_DOWN)) {
motor(1, -test_l);
motor(2, -test_r);
}
else {
motor(1, 0);
motor(2, 0);
}
}
EEPROM.update(savepin + 2, map(s_val, -50, 50, 0, 254));
motorl = base_speed + s_val;
motorr = base_speed - s_val;
oledClear();
}
void servo_angle() {
oledClear();
while (SW_OK() == 1) {
servok = knob(0, 180);
oled(0, 0, "Set Servo K: %d ", servok);
servo(1, servok);
delay(10);
}
delay(500);
while (SW_OK() == 0);
oledClear();
while (SW_OK() == 1) {
servop = knob(0, 180);
oled(0, 0, "Set Servo P: %d ", servop);
servo(1, servop);
delay(10);
}
EEPROM.update(savepin + 3, servok);
EEPROM.update(savepin + 4, servop);
oledClear();
oled(0, 0, "Saved!");
delay(1000);
oledClear();
}
ความคิดเห็น