top of page
IMG_0959.heic

หุ่นยนต์บังคับมือ The Cube Transporter: ภารกิจปล่อยลูกบาศก์ จุดประกายนักประดิษฐ์จิ๋ว

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


ภาพรวมของหุ่นยนต์ The Cube Transporter
ภาพรวมของหุ่นยนต์ The Cube Transporter

1. แนวคิดและแรงบันดาลใจ: สื่อการเรียนรู้ที่สนุกได้ทันที

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


วิดีโอตัวอย่าง การควบคุมโดยนักเรียน friend robot academy

2. โครงสร้างและอุปกรณ์หลัก (Hardware)

โครงสร้างหลัก (Chassis) ของหุ่นยนต์ถูกออกแบบและผลิตขึ้นด้วยแผ่นอะคริลิคใส และฐานปล่อยลูกบาศก์ใช้ 3D Printing โดยใช้เส้นพลาสติกสีเหลืองสดใส ข้อดีคือสามารถปรับแต่งขนาดและช่องว่างต่างๆ ให้พอดีกับอุปกรณ์อิเล็กทรอนิกส์แต่ละชิ้นได้อย่างลงตัว โดยมีส่วนประกอบสำคัญดังนี้:


The Cube Transporter
The Cube Transporter

  • 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 และ ตัวรับสัญญาณสีดำ
    JoyStick playstation PS2 wireless และ ตัวรับสัญญาณสีดำ

3. กลไกการปล่อยลูกบาศก์ (The Dispensing Mechanism)

ช่องเก็บลูกบาศก์แนวดิ่ง ลูกบาศก์(20 x 20 mm) จะถูกวางซ้อนกันในฐานปล่อย เมื่อเรากดปุ่มJoyStick Servo Motor จะหมุนเพื่อดันให้ลูกบาศก์ก้อนล่างสุดไหลออกมา


Tower ปล่อยลูกบาศก์ 20x20 mm
Tower ปล่อยลูกบาศก์ 20x20 mm

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 เพื่อการทำงานในรู้แบบอื่นๆ

ลูกบาศก์ ขนาด 20x20 mm
ลูกบาศก์ ขนาด 20x20 mm

สินค้าในบทความ


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();
}

ความคิดเห็น


bottom of page