Wall Follower : https://goo.gl/6bTBz6
Recursive Algorithm : https://goo.gl/5l44Eh
Arduino MEGA 2560 : https://goo.gl/8Oi7Jv
Arduino UNO : https://goo.gl/jtYgEb
Bluetooth HC-05 : https://goo.gl/vmX4iM
Pixy CMUcam5 : https://goo.gl/Z9gpf9
Adafruit Motor Shield : https://goo.gl/3hnqFt
OpenCM9.04-C : https://goo.gl/e4OYSn
Dynamixel XL-320 : https://goo.gl/Uc3e7D
Programming Implementation
ในส่วนต่อไปคือโปรแกรมในการเคลื่อนที่นั้นนอกจากจะใช้ encoder ในการวัดระยะในการเคลื่อนที่แล้ว ยังสามารถนำมาควบคุมความเร็วในการเคลื่อนที่ได้อีกด้วย โดยจะใช้หลักการนับสัญญาณพัลส์ได้กี่พัลส์ใน 100 มิลลิเซค (ms)
ยกตัวอย่าง :: ถ้ามอเตอร์หมุนและส่งสัญญาณพัลส์ได้ 10 พัลส์ต่อ 100 มิลลิเซค ถ้ารับสัญญาณพัลส์ได้ น้อยกว่า 10 พัลส์จะทำการเพิ่มความเร็วมอเตอร์ แต่ถ้ารับสัญญาณพัลส์ได้มากกว่า 10 จะลดความเร็วมอเตอร์
ถ้าในระหว่างการสำรวจเส้นทางถ้าหุ่นยนต์เกิดการเอียงจะใช้ Ultrasonic ในการช่วยตรวจสอบและปรับความเร็วมอเตอร์ได้ โดยการที่เราแบ่งช่วงเซ็นเซอร์ของ Ultrasonic เป็นระยะใกล้ กลาง ไกล จะเทียบกับกำแพง ถ้าวัดได้ระยะใกล้จะทำการเพิ่มความเร็วมอเตอร์ข้างซ้าย
ถ้าวัดได้ระยะกลางจะทำการปรับความเร็วมอเตอร์ทั้ง 2 ข้างเท่ากัน
ถ้าวัดได้ระยะไกลจะทำการเพิ่มความเร็วมอเตอร์ข้างขวา
การติดต่อสื่อสารระหว่างหุ่นยนต์กับคอมพิวเตอร์
การสื่อสารระหว่างหุ่นยนต์และคอมพิวเตอร์ จะอาศัย การสื่อสารแบบไร้สายระยะใกล้ คือ การใช้
บลูทูท (Bluetooth) ส่งข้อความหรือ ตัวเลขเพื่อสื่อสารกัน
1. การส่งข้อมูลของคอมพิวเตอร์ถึง หุ่นยนต์
ใน หุ่นยนต์ สามารถเคลื่อนที่ได้ 4 ลักษณะ คือ การเดินไปข้างหน้า การหันไปทางซ้าย การหันไปทางขวา และการหันกลับหลัง
ดังนั้นเมื่อถึงเวลาที่ต้องสำรวจ (Exploration) จะต้องส่งคำสั่งให้กับหุ่น รหัสการสั่งมีดังนี้
รหัสเลข 0 จะเป็นคำสั่งให้หุ่นเคลื่อนไปข้างหน้า
รหัสเลข 1 จะเป็นคำสั่งให้หุ่นเคลื่อนถอยหลัง
รหัสเลข 2 เป็นคำสั่งให้เลี้ยวซ้าย
รหัสเลข 3 เป็นคำสั่งให้เลี้ยวขวา
รหัสเลข 4 เป็นคำสั่งให้หันกลับหลัง
รหัส 0 1 2 3 4 นั้นมีประโยชน์ต่อการโปรแกรม ในภาษา ซี อันเนื่องจาก คุณสมบัติของ enumeration ซึ่งสามารถใช้ตัวเลข เพื่อเรียก ข้อความที่อยู่ในฟังก์ชัน enum ทำให้สะดวกต่อการเรียกใช้และประมวลผลของโปรแกรมจากคอมพิวเตอร์
รหัส 0 1 2 3 4 นั้นมีประโยชน์ต่อการโปรแกรม ในภาษา ซี อันเนื่องจาก คุณสมบัติของ enumeration ซึ่งสามารถใช้ตัวเลข เพื่อเรียก ข้อความที่อยู่ในฟังก์ชัน enum ทำให้สะดวกต่อการเรียกใช้และประมวลผลของโปรแกรมจากคอมพิวเตอร์
2. การส่งข้อมูลของหุ่นยนต์ไปยังคอมพิวเตอร์
เนื่องจากว่า สิ่งกีดขวางของหุ่นประกอบไปตามเซ็นเซอร์อัลตราโซนิก ซึ่งติดตั้งในหุ่น ทำให้หุ่นสามารถตรวจจับสิ่งกีดขวาง รวมถึงมี กล้องจับสีภาพ PIXY ช่วยในการจับสีที่กล้องตรวจจับได้
สิ่งกีดขวางที่เป็นไปได้คือ กำแพง ด้านต่างๆ ซึ่งประกอบไปด้วย กำแพงด้านซ้าย ด้านขวา และข้างหน้า
ดังรูปที่ 1 สิ่งกีดขวาง หรือ พื้นที่ว่าง ดังรูปที่่ 2
ดังรูปที่ 1 สิ่งกีดขวาง หรือ พื้นที่ว่าง ดังรูปที่่ 2
รูปที่ 1 รูปแสดงตำแหน่งการติดตั้งของอุปกรณ์ตรวจจับ หรือ เซนเซอร์อัลตราโซนิก
ค่าที่ตรวจจับ หากเป็นกำแพง จะส่งค่าที่เป็น 1 มาทำให้ทราบว่ามีสิ่งกีดขวาง
ค่าที่ถูกตรวจจับ หากเป็นช่องว่าง จะส่งค่าที่เป็น 0 มาทำให้ทราบว่าบริเวณนั้นเป็นทางที่สามารถเดินได้
ตามตัวอย่าง ดังรูปที่ 3
ตามตัวอย่าง ดังรูปที่ 3
ค่าเหล่านี้ก็จะถูกส่งกลับไปยัง คอมพิวเตอร์เพื่อสร้างแผนที่ที่ สำรวจ
การจับสีภาพของกล้อง pixy นั้นจะสามารถทำได้โดยการกำหนด หาก สีเป็นดังต่อไปนี้
รหัสสี 0 หมายถึง สีขาว
รหัสสี R หมายถึง สีแดง
รหัสสี G หมายถึง สีเขียว
รหัสสี B หมายถึง สีน้ำเงิน
รูปที่ 4 รูปแสดง รหัสของการตรวจจับสี โดยกล้อง pixy
Source Code #
#include <AFMotor.h>
float dir = 0.0, error = 0.0, no_wall = 40;
int sl = 0, sr = 0, spd_l = 200, spd_r = 250, sl_c = 0, sr_c = 0;
enum Wallcolor {white, red, green, blue, orange};
enum direct {forw, left, right, retn, right2, left2};
enum command {fow, lef, rgt, rtn, none, rtn2};
enum ck_sen {clr, ct_sen, sd_data, ct_rtn, ct_fw};
int en1 = 21, en2 = 20; //180//180
int n = 0, clr_n = 0, start2 = 0;
char p1 = ' ', p = ' ';
char test[128];
int n_reval = 0, kk = 0;
unsigned de = 0;
unsigned int ti, ti_pre = 0;
int mode1 = 0, mode = 0, mode2 = 0 , mode3 = 0 , n_sf = 0;
char p3;
String data;
String sm[3];
int sen[3], sd[3];
int nn = 0;
int Scut = 0;
ck_sen ck_sn;
direct dirt;
command cmd;
AF_DCMotor m1(1);
AF_DCMotor m2(2);
void setup()
{
pinMode(en1, INPUT);
pinMode(en2, INPUT);
pinMode(32, OUTPUT);
attachInterrupt(digitalPinToInterrupt(en1), en_l, RISING );
attachInterrupt(digitalPinToInterrupt(en2), en_r, RISING );
Serial.begin(9600);
Serial1.begin(9600); // For uno
Serial2.begin(9600); //For Bluetooth
m1.setSpeed(199);
m2.setSpeed(245);
mode = 0; clr_en();
m_stp();
delay(2000); p1 = ' '; p = ' '; cmd = none; ck_sn = clr;
digitalWrite(32, HIGH);
delay(20);
digitalWrite(32, LOW); //m_for();
// Serial2.println("R");
}
void loop()
{
//Serial.println(test);
if (sen[0] <= 13 )
{
if ((sen[0] > 0 && sen[0] < 8) && (sen[2] < 38 && sen[2] > 16))
{
dirt = right;
}
else if ((sen[0] > 0 && sen[0] <= 13) && sen[2] > 38)
{
dirt = forw;
}
else if ((sen[0] > 0 && sen[0] < 8) && sen[2] < 16 && sen[2] > 9)
{
dirt = right2;
}
else
{
dirt = forw;
}
}
else if (sen[0] > 16)
{
if ((sen[2] > 0 && sen[2] < 8 ) && (sen[0] < 38 && sen[0] > 16))
{
dirt = left;
}
else if ((sen[2] > 0 && sen[2] < 8 ) && sen[0] > 38)
{
dirt = forw;
}
else if ((sen[2] > 0 && sen[2] < 8 ) && sen[0] < 16 && sen[0] > 9)
{
dirt = left2;
}
else
{
dirt = forw;
}
}
////////////////////////////
if (ck_sn == ct_sen)
{ if (sd[0] == 1 && sd[1] == 1 && sd[2] == 1)
{
digitalWrite(32, HIGH);
delay(50);
digitalWrite(32, LOW);
}
else
{
digitalWrite(32, LOW); delay(50);
}/*
Serial.print(sd[0]); Serial.print(","); Serial.print(sd[1]); Serial.print(","); Serial.print(sd[2]);
Serial.print(","); Serial.println(white);*/
de++;
if (de > 28)
{
de = 0;
ck_sn = sd_data;
}
}
else if (ck_sn == sd_data)
{ de++; delay(50);
if (de > 15)
{ de = 0;
clr_en();
if (sen[1] < 14)
{
ck_sn = ct_rtn; de = 0;
}
else if (sen[1] < 37 && sen[1] > 20)
{
ck_sn = ct_fw; de = 0;
}
else
{
Serial2.print(sd[0]); Serial2.print(","); Serial2.print(sd[1]); Serial2.print(","); Serial2.print(sd[2]);
Serial2.print(","); Serial2.println(white);
digitalWrite(32, HIGH);
delay(50);
digitalWrite(32, LOW);
clr_en(); ck_sn = clr;
de = 0;
}
}
}
else if (ck_sn == ct_rtn)
{ // Serial2.println(sen[1]);
m_re(); de = 0;
if (sl > 12 && sr > 12)
{ Serial.print("yyy "); Serial.println(ck_sn);
de = 0;
m_stp();
delay(800);
clr_en(); ck_sn = ct_sen;
}
}
else if (ck_sn == ct_fw)
{
m_for();
de = 0;
if (sl > 12 && sr > 12)
{ Serial.print("yyy "); Serial.println(ck_sn);
de = 0;
m_stp();
delay(800);
clr_en(); ck_sn = ct_sen;
}
}
if (p1 == '0' || cmd == fow )//
{ m_for(); ck_spd(); de++;
/* sprintf(test, " S1 =%d S2=%d spd_l %d spd_r %d %s|", sen[0], sen[2], spd_l, spd_r, dirt);
Serial2.println(test);*/
// Serial2.print(test); Serial2.print(" error" ); Serial2.print(error); Serial2.print(" dir "); Serial2.println(dir);
if (sl > 116 && sr > 116 )
{
m_stp();
clr_en(); p1 = ' '; de = 0;
cmd = none;
ck_sn = ct_sen;
}
ck_local();
}
else if (p1 == '2' || cmd == rgt) //2
{ m_rr();
if (sl >= 73)
{
n = 1;
m_stp();
delay(800);
// Serial2.println(sl);
//delay(1200);
if (sl > 80 && sl <= 100)
{
mode2 = 1;
n_sf = sl - 80;
}
else if ( sl <= 75 && sl > 63)
{
n_sf = 78 - sl;
mode2 = 2;
}
else
{ mode2 = 0; clr_en();
m_stp();
delay(800);
}
clr_en();
if (mode2 != 0)
{
while (1)
{
if (mode2 == 1)
{
m_rr_re();
if (sl >= n_sf)
{
m_stp();
delay(800);
break;
}
}
else if (mode2 == 2)
{
m_rr();
if (sl >= n_sf)
{
m_stp();
delay(800);
break;
}
}
}
p1 = ' ';
mode2 = 0;
}
if (mode2 == 0)
{
cmd = none; p1 = ' '; Serial2.println("R"); Serial.println("R"); digitalWrite(32, HIGH);
delay(50);
digitalWrite(32, LOW);
clr_en();
ck_local();
}
}
}
else if (p1 == '1' || cmd == lef)
{
m_ll();
if (sr >= 70)
{
n = 1;
m_stp();
delay(800);
// Serial2.println(sr);
if (sr > 82 && sr <= 100)
{
mode3 = 1;
n_sf = sr - 82;
}
else if ( sr >= 64 && sr < 70)
{
n_sf = 70 - sr;
mode3 = 2;
}
else
{
mode3 = 0; clr_en();
m_stp();
delay(800);
}
clr_en();
if (mode3 != 0)
{
while (1)
{
if (mode3 == 1)
{
m_ll_re();
if (sr >= n_sf)
{
m_stp();
delay(800);
break;
}
}
else if (mode3 == 2)
{
m_ll();
if (sr >= n_sf)
{
m_stp();
delay(800);
break;
}
}
}
mode3 = 0; clr_en(); p1 = ' ';
}
if (mode3 == 0)
{
cmd = none; Serial2.println("R"); Serial.println("R"); digitalWrite(32, HIGH);
delay(50);
digitalWrite(32, LOW);
clr_en(); p1 = ' '; //spd_l = 165; spd_r = 230;
n_reval = 1;
ck_local();
}
}
}
else if (p1 == '3' || cmd == rtn)
{
m_r();// return
if (sl > 68)
{
m1.setSpeed(0);
}
if (sr > 72)
{
m2.setSpeed(0);
}
if (sl > 70 && sr > 71)
{
m_stp();
delay(800);
if (sr > 90)
{ n_sf = sr - 84; clr_en();
m_ll();
while (sr < n_sf)
{
m_ll();
} m_stp(); delay(500);
}
// Serial2.print(sl); Serial.print(" "); Serial2.println(sr);
Serial2.println("R"); Serial.println("R"); digitalWrite(32, HIGH);
delay(50);
digitalWrite(32, LOW);
cmd = none; clr_en(); p1 = ' ';
// spd_l = 214; spd_r = 230;
n_reval = 1;ck_local();
}
}
else if ( cmd == rtn2)
{
m_l();// return
if (sl > 68)
{
m1.setSpeed(0);
}
if (sr > 72)
{
m2.setSpeed(0);
}
if (sl > 66 && sr > 69)
{
m_stp();
delay(800);
if (sl > 90)
{ n_sf = sl - 84;
clr_en();
while (sl < n_sf)
{ //Serial2.println("ee");
m_rr();
} m_stp(); delay(500);
}
// Serial2.print(sl); Serial.print(" "); Serial2.println(sr);
Serial2.println("R"); Serial.println("R"); digitalWrite(32, HIGH);
delay(50);
digitalWrite(32, LOW);
cmd = none; clr_en(); p1 = ' ';
spd_l = 220; spd_r = 230; n_reval = 1;
}
}
else if (p1 == '5' )
{
m_re(); Serial.println(sen[1]);
if (sl > 15 && sr > 15)
{
m_stp();
delay(800);
clr_en(); p1 = ' ';
}
}
}
void m_for()
{
m1.run(FORWARD);
m2.run(FORWARD);
}
void m_re()
{ m1.setSpeed(199);
m2.setSpeed(245);
m1.run(BACKWARD);
m2.run(BACKWARD);
}
void m_rr()
{
m1.setSpeed(199);
m2.setSpeed(245);
m1.run(FORWARD);
m2.run(RELEASE);
}
void m_rr_re()
{
m1.run(BACKWARD);
m2.run(RELEASE);
}
void m_r()
{
m1.setSpeed(240);
m2.setSpeed(255);
m1.run(FORWARD);
m2.run(BACKWARD);
}
void m_l()
{
m1.setSpeed(230);
m2.setSpeed(255);
m2.run(FORWARD);
m1.run(BACKWARD);
}
void m_ll()
{
m1.setSpeed(199);
m2.setSpeed(245);
m2.run(FORWARD);
m1.run(RELEASE);
}
void m_ll_re()
{
m2.run(BACKWARD);
m1.run(RELEASE);
}
void m_stp()
{
m1.run(RELEASE);
m2.run(RELEASE);
}
void en_l()
{
sl++; sl_c++;
}
void en_r()
{ sr++; sr_c++;
}
void clr_en()
{
sl = 0; sr = 0;
}
void clr_en_spd()
{
sl_c = 0; sr_c = 0;
}
void ck_local()
{
if (sen[0] > 0 && sen[0] < 8)
{
spd_l = 230; spd_r = 200;
}
else if (sen[2] > 0 && sen[2] < 8)
{
spd_l = 175; spd_r = 255;
}
else
{
spd_l = 193; spd_r = 245;
}
}
void ck_spd()
{
ti = millis() - ti_pre;
if (ti >= 100)
{ ti_pre = millis();
if (dirt == forw)
{ /*
spd_l = 199;
spd_r = 245;*/
// Serial.print("fowww ");
//Serial.println(test);
//spd_r = 245;
if (n_reval == 0)
{ spd_r = 245;
spd_l = 194;
}
else if (n_reval >= 3)
{
n_reval = 0;
}
else
{
n_reval++;
}
if (sl_c > 12)
{
spd_l -= 4;
}
else if (sl_c < 10)
{
spd_l += 4;
}
limit_spd();
}
else if (dirt == right)
{ //Serial.print("rigt ");
n_reval = 0;
spd_l += 6;
spd_r = 210;
limit_spd();
}
else if (dirt == right2)
{
n_reval = 0;
spd_l += 3;
spd_r = 228;
limit_spd();
}
else if (dirt == left)
{ //Serial.print("left ");
n_reval = 0;
spd_l = 170;
spd_r += 6;
limit_spd();
}
else if (dirt == left2)
{ //Serial.print("left ");
n_reval = 0;
spd_l = 185;
spd_r += 3;
limit_spd();
}
m1.setSpeed(spd_l);
m2.setSpeed(spd_r);
clr_en_spd();
}
}
void limit_spd()
{
if (spd_l > 255)
{
spd_l = 255;
}
else if (spd_l < 140)
{
spd_l = 140;
}
if (spd_r > 255)
{
spd_r = 255;
}
else if (spd_r < 200)
{
spd_r = 200;
}
}
void serialEvent2()
{
while (Serial2.available())
{
p1 = Serial2.read();
}
if (p1 == 'S')
{
Serial.println("CMP!! ");
Serial2.print("1,0,1,"); Serial2.println(white);
p1 = ' ';
}
if (p1 == '3')
{
p1 = ' ';
if (sen[0] > sen[2])
{
// Serial2.println("111");
cmd = rtn;
}
else
{ //Serial2.println("222");
cmd = rtn2;
}
}
digitalWrite(32, HIGH);
delay(300);
digitalWrite(32, LOW);
}
void serialEvent1()
{
while (Serial1.available())
{ p = Serial1.read();
if (p == ']')
{
Scut = 1;
} else
{
data += p;
}
}
if (Scut == 1)
{
Strcut();
Scut = 2;
con_step();
Scut = 0; data = "";
}
}
void Strcut()
{
while (1)
{ int check = data.indexOf(',');
if (check != -1)
{
sm[nn] = data.substring(0, check);
data = data.substring(check + 1, data.length());
nn++;
}
else
{
sm[nn] = data;
mode = 1;
nn = 0;
break;
}
}
}
void con_step()
{
sen[0] = sm[0].toInt();
sen[1] = sm[1].toInt();
sen[2] = sm[2].toInt();
if (sen[0] > 45)
{
sd[0] = 0;
}
else
{
sd[0] = 1;
}
if (sen[1] > 37)
{
sd[1] = 0;
}
else
{
sd[1] = 1;
}
if (sen[2] >= 45)
{
sd[2] = 0;
}
else
{
sd[2] = 1;
}
}
void serialEvent()
{
while (Serial.available())
{
p = Serial.read();
}
if (p == '0')
{
//p1 = '0';
p = ' ';
cmd = fow;
}
if (p == '1')
{
//p1 = '1';
p = ' ';
cmd = lef;
}
if (p == '2')
{
//p1 = '2';
p = ' ';
cmd = rgt;
}
if (p == '3')
{
// p1 = '3';
p = ' ';
cmd = rtn;
}
if (p == '4')
{
cmd = rtn2;
}
if (p == '5')
{
p = ' ';
p1 = '5';
}
}
Programming Design

การออกแบบโปรแกรมคอมพิวเตอร์ สำหรับควบคุมหุ่นยนต์ จะแบ่งออกเป็น 3 ส่วนหลัก คือ
- Explore Maze ส่วนการรับข้อมูลจากหุ่นยนต์ว่ามีกำแพงที่ด้านซ้าย หน้า ขวา และสีกำแพง
- Recursive AI ส่วนการเลือกเส้นทางที่ใกล้ที่สุดและไม่มีทางตันเพื่อหาทางออก แบบการเรียก Function ตัวเองซ้ำ
- Solution Maze ส่วนการสั่งงานหุ่นยนต์ให้เดินไปตามเส้นทางที่ได้ solve
โดยโปรแกรมสามารถสั่งงานและ Simulation เพื่อจำลองการติดต่อสื่อสารระหว่างหุ่นยนต์กับคอมพิวเตอร์ที่ลงโปรแกรม Solve Maze ไว้
ดูได้ที่ https://www.youtube.com/watch?v=dkjcQIngzuA
โปรแกรมจะมีการสื่อสารกับคอมพิวเตอร์ผ่าน Bluetooth โดย ชุดคำสั่งที่คอมพิวเตอร์
ส่งมามีดังนี้ ถ้าส่ง "S" Robo Maze จะส่งค่าเซ็นเซอร์ที่ Ultrasonic อ่านค่ามาได้ไปยังคอมพิวเตอร์
ส่ง "0,1,2,3" เป็นชุดคำสั่งในการควบคุมการเคลื่อนที่ของหุ่นยนต์ เช่น เดินหน้า ถอย
หลัง เลี้ยวซ้าย เลี้ยวขวา แต่ถ้าหุ่ยนต์มีการเคลื่อนที่ เลี้ยวซ้าย เลี้ยวขวา และถอยหลัง เรียบร้อยแล้วจะส่ง "R" กลับไปยังคอมพิวเตอร์
Flowchart ของโปรแกรมควบคุมการทำงานของ Robo Maze
โปรแกรมจะมีการสื่อสารกับคอมพิวเตอร์ผ่าน Bluetooth โดย ชุดคำสั่งที่คอมพิวเตอร์
ส่งมามีดังนี้ ถ้าส่ง "S" Robo Maze จะส่งค่าเซ็นเซอร์ที่ Ultrasonic อ่านค่ามาได้ไปยังคอมพิวเตอร์
ส่ง "0,1,2,3" เป็นชุดคำสั่งในการควบคุมการเคลื่อนที่ของหุ่นยนต์ เช่น เดินหน้า ถอย
หลัง เลี้ยวซ้าย เลี้ยวขวา แต่ถ้าหุ่ยนต์มีการเคลื่อนที่ เลี้ยวซ้าย เลี้ยวขวา และถอยหลัง เรียบร้อยแล้วจะส่ง "R" กลับไปยังคอมพิวเตอร์
Mechanics Implementation
- ใช้มอเตอร์ที่มีขนาดเล็กและวางแผงจงจรซ้อนกันในแนวตั้ง
- ใช้สองล้อในการขับเคลื่อนตัวหุ่ยนต์
- ใช้ Arduino ในการพัฒนาโปรแกรมของหุ่นยนต์
- ติด Bluetooth เพื่อสื่อสารกับ PC
- ออกแบบ Protocol ในการสื่อสารระหว่างหุ่นยนต์และ PC
- ใช้ถ่านชาร์จขนาด AA และ Power Bank เป็นแหล่งจ่ายพลังงานให้หุ่นยนต์
- ติด Ultrasonic 3 ตัวเพื่อตรวจจัดระยะของหุ่นยนต์กับ Maze
- ติดกล้อง 1 ตัวไว้ด้านหน้าหุ่นยนต์เพื่อตรวจวัดสี
สมัครสมาชิก:
บทความ (Atom)