ภาพสนามแข่งขันรอบคัดเลือก ปี 2547

 

แนวคิดในการสร้างโปรแกรมเพื่อแก้ปัญหา และคำอธิบายโปรแกรม

                สำหรับการทำงานของโปรแกรมนี้จะอาศัยหลักการแบบพื้นฐานที่สุดคือการกำหนดเส้นทางเดินของตัวหุ่นยนต์ในรูปแบบที่ค่อนข้างตายตัว โดยจะกำหนดการขับเคลื่อนของหุ่นยนต์เมื่อเคลื่อนที่ผ่านทางแยกในรูปแบบต่างๆ โดยการตั้งรูปแบบการขับเคลื่อนผ่านแยกต่างๆจากเส้นทางที่จะเคลื่อนที่ผ่าน ซึ่งจะอาศัยการตรวจจับจากเซนเซอร์ชนิด reflex จำนวน 3 ตัว โดยที่เมื่อมองภาพจากด้านบนลงไปยังตัวหุ่นยนต์

·        เซนเซอร์ด้านซ้ายต่อเข้ากับช่อง AI 29

·        เซนเซอร์ตำแหน่งกลางต่อเข้ากับช่อง AI 25

·        เซนเซอร์ด้านขวาต่อเข้ากับช่อง AI 21


 

สำหรับเส้นทางเดินของหุ่นยนต์ภายในสนามนั้นได้มีการกำหนดตำแหน่งเป้าหมายตั้งแต่เป้าหมายที่ 1 ถึง 9  และระบุตำแหน่งต่างๆที่อยู่บนตารางด้วยตัวอักษรต่างๆ ตามรูปที่ 2 และ3 ซึ่งเป้าหมายการทำงานของตัวอย่างโปรแกรมtable.ic นี้จะมีขั้นตอนแบบคร่าวๆดังนี้

1.       รับค่าจากการกำหนดว่าจะเริ่มต้นออกจากฐาน Aหรือ B จากการตั้งค่าโปรแกรมด้วยเมนู knob ซึ่งการทำงานส่วนนี้เกิดจากการทำงานของฟังก์ชัน select_base()

2.       กำหนดตำแหน่งเป้าหมาย(ตั้งแต่ 1 ถึง 9) จากการตั้งค่าโปรแกรมด้วยเมนู knob โดยมีความสัมพันธ์กับฐานตามรูปที่ 2 และ3 ซึ่งการทำงานส่วนนี้เกิดจากการทำงานของฟังก์ชัน select_target() โดยคืนค่าออกมาเป็นหมายเลขของตำแหน่งเป้าหมายนั่นเอง

3.       หุ่นยนต์เคลื่อนออกจากฐาน(Aหรือ B)ไปยังจุด a ซึ่งการทำงานส่วนนี้เกิดจากการทำงานของฟังก์ชัน goto_start() เพื่อเตรียมตัวไปยังตำแหน่งเป้าหมายที่กำหนดจากโปรแกรม

4.       หุ่นยนต์เคลื่อนออกจากฐาน(Aหรือ B)ไปยังเป้าหมายเพื่อเก็บวัตถุเป้าหมายแล้วเดินวนกลับมายังหน้าฐานตามเส้นทางที่ถูกกำหนดไว้ในแต่ละเป้าหมาย ซึ่งการทำงานส่วนนี้เกิดจากการทำงานของการเรียกคำสั่งฟังก์ชัน goto_target(target); โดยก่อนหน้านี้ตัวแปร target ได้เก็บค่าตำแหน่งเป้าหมายจากการกำหนดจากเมนูตามข้อที่

 


สำหรับเส้นทางที่หุ่นยนต์จะเดินทางไปสู่ตำแหน่งเป้าหมายต่างๆจะมีเส้นทางเดินที่ถูกกำหนดไว้ตามตารางดังนี้

 

ตารางที่1 แสดงลักษณะเส้นทางเดินของหุ่นยนต์เมื่อเริ่มต้นที่ฐาน B

เป้าหมายที่

ขาไป

ขากลับ

1

อยู่ที่ตำแหน่งเป้าหมายที่ 1 แล้ว

1èeèdèbèB

2

aècèg

2èfèdèbèB

3

aècègèj

3èièfèdèbèB

4

aèc

4èmèlèaèB

5

aècèg

5èoèpèhèfèdèbèB

6

aècègèj

6èqèrèkèièfèdèbèB

7

aèlèm

7ètèsèlèaèB

8

aèlèmèo

8èvètèsèlèaèB

9

aèlèmèoèg

9èxèvètèsèlèaèB

 

หมายเหตุ               เมื่อหุ่นยนต์เคลื่อนที่ไปถึงเป้าหมายจะทำการกลับตัวหันหน้าเข้าหาวัตถุภายในกรอบเป้าหมาย ดังนั้นในตอนขากลับ เช่นขากกลับจากเป้าหมายที่ 5 เป็น

5èoèpèhèfèdèbèB


หมายถึง เดินตัดเข้าไปในกรอบเป้าหมายที่ 5 แล้วเดินเข้าในแนวตั้งฉากกับเส้น o หลังจากนั้นจะเลี้ยวขวาเดินตามเส้น o เมื่อพบ 4 แยกแรกจะเลี้ยวขวาเดินไปตามเส้น p และ h และจะเลี้ยวขวาเดินไปตามเส้น f และ d จากนั้นเลี้ยวขวาไปตามเส้น b และเลี้ยวซ้ายอีกครั้งเพื่อกลับไปยังฐาน B

 

 

                จะสังเกตว่าลักษณะเส้นทางเดินเมื่อพิจารณาเมื่อเริ่มต้นที่ฐาน A ตามตารางที่ 1 ถ้าหากมีการกำหนดตำแหน่งหมายเลขและเส้นทางเดินในลักษณะเดียวกับฐาน B ที่ได้แสดงไว้ในรูปที่ 2 และกำหนดให้เส้นทางเดินของแต่ละตำแหน่งเป้าหมายเป็นแบบเดียวกันก็จะทำให้การเขียนโปรแกรมแกรมง่ายขึ้น(เส้นทางเดินเป็นไปตามตารางที่ 1 เช่นเดียวกับเริ่มต้นที่ฐาน B) แต่ปัญหาที่เกิดขึ้นก็คือลักษณะของเส้นทางเมื่อเทียบกับจุดเริ่มต้นจะมีลักษณะกลับกัน ซึ่งจุดนี้ทำให้ผู้พัฒนาจะต้องเขียนโปรแกรมในลักษณะกลับเส้นทางให้ตรงกันข้ามเมื่อเทียบกับการเดินทางที่เริ่มต้นจากฐาน B เพราะในการแข่งขันจริงผู้แข่งขันอาจถูกกำหนดให้เริ่มต้นที่ฐาน Aหรือ B ซึ่งไม่อาจคาดเดาได้ 

เช่น สมมุติว่าเมื่อมีการเริ่มต้นที่ฐาน B และเป้าหมายที่จะไปคือเป้าหมายที่ 2 โดยจากรูปที่ 2 จะมีเส้นทางขาไปคือ  aècèg                 หมายถึงหุ่นยนต์จะเดินตรงตามเส้น a เมื่อพบแยกขวาแรกจะต้องเลี้ยวขวาแล้วเดินตรงตามเส้น c และผ่าน 4 แยกเดินตรงไปหยุดที่ประมาณกึ่งกลางเส้น g

                สำหรับการการเริ่มต้นที่ฐาน A การที่จะไปเป้าหมายที่ 2 ในโปรแกรมจะกำหนดเส้นทางเดินในแบบเดียวกันคือ aècèg แต่จะสังเกตว่าการที่จะเปลี่ยนเส้นทางจาก a ไป c นั่นคือเมื่อพบแยกซ้ายแรกจะต้องเลี้ยวซ้าย(ไม่ใช่เลี้ยวขวาเมื่อเทียบกับฐาน B)ซึ่งจะกระทำตรงข้ามเมื่อเทียบกับฐาน B และนอกจากนี้พบว่าเส้นทางเดินจะเป็นในลักษณะกลับกันระหว่างการเริ่มต้นด้วย 2 ฐานนี้ ดังนั้นในการเขียนโปรแกรมควบคุมในแต่ละฐานจึงเป็นส่วนกลับซึ่งกันและกันในขั้นตอนการเลี้ยว กล่าวคือถ้าฐาน A เลี้ยวซ้าย ฐาน B จะเป็นเลี้ยวขวา 

 

การทำงานภายในโปรแกรมหลัก

void main(void)

{

   ao();                     // เริ่มต้นปิดการขับมอเตอร์ทุกช่อง

    select_base();  // แสดงเมนูการตั้งค่าว่าให้เริ่มต้นที่ฐาน Aหรือ B โดยค่าที่รับได้จะเก็บลงในตัวแปร base เพื่อใช้

// เปรียบเทียบในการขับเคลื่อนหุ่นยนต์

    goto_start();      // ไปยังจุดเริ่มอ้างอิง

    target = select_target(); // แสดงเมนูรับค่าการกำหนดเป้าหมายซึ่งจะคืนค่าออกมาเก็บไว้ที่ตัวแปร target  

    goto_target(target);         // สั่งขับเคลื่อนหุ่นยนต์ไปยังเป้าหมายที่ถูกกำหนดจนกระทั่งกลับมาถึงยังฐาน

    run_fd(1.0);       // เด

    ao();

    while(1);

}

 

หน้าที่การทำงานของฟังก์ชันต่างๆภายในโปรแกรม

 

ฟังก์ชัน run_fd     ทำหน้าที่ขับเคลื่อนให้หุ่นยนต์เดินตรงไปข้างหน้าโดยสามารถกำหนดเวลาในการเดินได้

 

ฟังก์ชัน run_bk    ทำหน้าที่ขับเคลื่อนให้หุ่นยนต์ถอยหลังตรงไปข้างหน้าโดยสามารถกำหนดเวลาในการเดินได้

 

ฟังก์ชัน turn_left  ทำหน้าที่ขับเคลื่อนให้หุ่นยนต์เลี้ยวซ้ายโดยสามารถกำหนดเวลาในการเลี้ยวได้

 

ฟังก์ชัน turn_right  ทำหน้าที่ขับเคลื่อนให้หุ่นยนต์เลี้ยวขวาโดยสามารถกำหนดเวลาในการเลี้ยวได้

 

ฟังก์ชัน read_sensor          ทำหน้าที่อ่านค่าอะนาลอกที่ช่อง AI29, AI25 และ AI21 เข้ามาเก็บยังตัวแปรเปรียบเทียบ left,mid และright ตามลำดับ

 

ฟังก์ชัน spin_find_line      ทำหน้าที่พยายามปรับตำแหน่งทิศทางของเซนเซอร์ให้อยู่ในลักษณะคร่อมเส้นสนาม ซึ่งจะถูกเรียกใช้งานภายในฟังก์ชัน cross_all,cross_left และcross_right อีกครั้งหนึ่ง

 

ฟังก์ชัน cross_all             ทำหน้าที่ตรวจจับแยกและเส้นซึ่งจะเป็นจริงเมื่อเกิดเหตุการณ์ที่เซนเซอร์ทั้ง 3 ตัวตรวจพบเส้นสนาม(สีขาว)พร้อมกันทั้งหมด ซึ่งจะสามารถกำหนดได้ว่าจะให้หุ่นยนต์เดินผ่านไปโดยไม่มีการเลี้ยวใดๆ หรือจะให้เลี้ยวซ้ายหรือขวาทำได้ดังนี้

                เรียกคำสั่งฟังก์ชัน(อ้างอิงคำสั่งจากตำแหน่งเริ่มต้นที่ฐาน B)                          

เช่น

cross_all(PASS);  // เมื่อต้องการให้หุ่นยนต์เดินผ่านแยกในลำดับที่ใกล้ตัวที่สุดของตัวหุ่นยนต์ 

                cross_all(LEFT);  // เมื่อต้องการให้หุ่นยนต์เลี้ยวซ้ายเมื่อผ่านแยกในลำดับที่ใกล้ตัวที่สุดของตัวหุ่นยนต์                   cross_all(RIGHT); // เมื่อต้องการให้หุ่นยนต์เลี้ยวขวาเมื่อผ่านแยกในลำดับที่ใกล้ตัวที่สุดของตัวหุ่นยนต์     

 

ฟังก์ชัน cross_left            ทำหน้าที่ตรวจจับแยกซ้ายซึ่งจะเป็นจริงเมื่อเกิดเหตุการณ์ที่เซนเซอร์ด้านซ้ายและกลางตรวจพบเส้นสนาม(สีขาว)ส่วนด้านขวาพบพื้นสนาม ซึ่งจะสามารถกำหนดได้ว่าจะให้หุ่นยนต์เดินผ่านไปโดยไม่มีการเลี้ยวใดๆ หรือจะให้เลี้ยวซ้ายหรือขวาทำได้ดังนี้

                เรียกคำสั่งฟังก์ชัน(อ้างอิงคำสั่งจากตำแหน่งเริ่มต้นที่ฐาน B)

เช่น                         

cross_left(PASS); // เมื่อต้องการให้หุ่นยนต์เดินผ่านแยกซ้ายในลำดับที่ใกล้ตัวที่สุดของตัวหุ่นยนต์         

                cross_left(LEFT); // เมื่อต้องการให้หุ่นยนต์เลี้ยวซ้ายเมื่อผ่านแยกซ้ายในลำดับที่ใกล้ตัวที่สุดของตัวหุ่นยนต์           cross_left(RIGHT); // เมื่อต้องการให้หุ่นยนต์เลี้ยวขวาเมื่อผ่านแยกซ้ายในลำดับที่ใกล้ตัวที่สุดของตัวหุ่นยนต์              

ฟังก์ชัน cross_right          ทำหน้าที่ตรวจจับแยกซ้ายซึ่งจะเป็นจริงเมื่อเกิดเหตุการณ์ที่เซนเซอร์ด้านซ้ายและกลางตรวจพบเส้นสนาม(สีขาว)ส่วนด้านขวาพบพื้นสนาม ซึ่งจะสามารถกำหนดได้ว่าจะให้หุ่นยนต์เดินผ่านไปโดยไม่มีการเลี้ยวใดๆ หรือจะให้เลี้ยวซ้ายหรือขวาทำได้ดังนี้

                เรียกคำสั่งฟังก์ชัน(อ้างอิงคำสั่งจากตำแหน่งเริ่มต้นที่ฐาน B)                          

cross_right(PASS); // เมื่อต้องการให้หุ่นยนต์เดินผ่านแยกซ้ายในลำดับที่ใกล้ตัวที่สุดของตัวหุ่นยนต์       

                cross_right(LEFT);// เมื่อต้องการให้หุ่นยนต์เลี้ยวซ้ายเมื่อผ่านแยกซ้ายในลำดับที่ใกล้ตัวที่สุดของตัวหุ่นยนต์          cross_right(RIGHT); // เมื่อต้องการให้หุ่นยนต์เลี้ยวขวาเมื่อผ่านแยกซ้ายในลำดับที่ใกล้ตัวที่สุดของตัวหุ่นยนต์

 

ฟังก์ชัน goto_target         ทำหน้าที่ขับเคลื่อนหุ่นยนต์ไปยังตำแหน่งเป้าหมายที่กำหนดแล้วเดินทางกลับมายังฐานตามเส้นทางที่กำหนดไว้ในแต่ละเป้าหมาย

                เรียกคำสั่งฟังก์ชัน

เช่น

                goto_target(6);     // เมื่อต้องการสั่งการให้หุ่นยนต์ไปทำกิจกรรมที่เป้าหมายที่ 6

 

ฟังก์ชัน global_part1        เป็นฟังก์ชันที่จัดเก็บเส้นทางเดินบางส่วนของการเดินทางขากลับจากเป้าหมายที่ 1 ซึ่งจะเป็นส่วนที่ซ้ำกับเส้นทางเดินขากลับของเป้าหมายที่ 2 และ 4

 

ฟังก์ชัน global_part2        เป็นฟังก์ชันที่จัดเก็บเส้นทางเดินบางส่วนของการเดินทางขากลับจากเป้าหมายที่ 2 ซึ่งจะเป็นส่วนที่ซ้ำกับเส้นทางเดินขากลับของเป้าหมายที่ 3 และ 5

 

ฟังก์ชัน global_part3        เป็นฟังก์ชันที่จัดเก็บเส้นทางเดินบางส่วนของการเดินทางขากลับจากเป้าหมายที่ 3 ซึ่งจะเป็นส่วนที่ซ้ำกับเส้นทางเดินขากลับของเป้าหมายที่ 6

 

ฟังก์ชัน global_part4        เป็นฟังก์ชันที่จัดเก็บเส้นทางเดินบางส่วนของการเดินทางขากลับจากเป้าหมายที่ 4 แต่ไม่ซ้ำกับเป้าหมายใด

 

ฟังก์ชัน global_part5        เป็นฟังก์ชันที่จัดเก็บเส้นทางเดินบางส่วนของการเดินทางขากลับจากเป้าหมายที่ 5 ซึ่งจะเป็นส่วนที่ซ้ำกับเส้นทางเดินขากลับของเป้าหมายที่ 8

 

ฟังก์ชัน global_part6        เป็นฟังก์ชันที่จัดเก็บเส้นทางเดินบางส่วนของการเดินทางขากลับจากเป้าหมายที่ 6 ซึ่งจะเป็นส่วนที่ซ้ำกับเส้นทางเดินขากลับของเป้าหมายที่ 9

 

ฟังก์ชัน goto_start           เป็นฟังก์ชันสำหรับขับเคลื่อนหุ่นยนต์จากฐาน(Aหรือ B) ไปยังจุดอ้างอิง

 

ฟังก์ชัน select_base           เป็นฟังก์ชันทำหน้าที่แสดงเมนูการกำหนดฐานเริ่มต้นของหุ่นยนต์โดยจะอาศัยการเลื่อนค่าตำแหน่งเป้าหมายจาก knob ซึ่งตำแหน่งฐานจะเปลี่ยนแปลงไปมาระหว่าง A กับ B เมื่อได้ตำแหน่งฐานที่ต้องการแล้วทำการกดปุ่ม START ก็จะเป็นการผ่านขั้นตอนจากการทำงานของฟังก์ชันนี้ โดยจะมีการคืนค่าจากผลลัพธ์ตรงกับค่าตำแหน่งเป้าหมายออกมาจากฟังก์ชัน

 

ฟังก์ชัน select_target          เป็นฟังก์ชันทำหน้าที่แสดงเมนูการกำหนดเป้าหมายที่ต้องการให้หุ่นยนต์ไปทำภาระกิจโดยจะอาศัยการเลื่อนค่าตำแหน่งเป้าหมายจาก knob ซึ่งค่าเป้าหมายจะเปลี่ยนแปลงตั้งแต่ 1 ถึง 9 เมื่อได้ค่าเป้าหมายที่ต้องการแล้วทำการกดปุ่ม START ก็จะเป็นการผ่านขั้นตอนจากการทำงานของฟังก์ชันนี้ โดยจะมีการคืนค่าจากผลลัพธ์ตรงกับค่าตำแหน่งเป้าหมายออกมาจากฟังก์ชัน

 

ค่าคงที่ภายในโปรแกรม

 

#define pow 40    สำหรับกำหนดให้ pow แทนด้วยตัวเลข 40 เพื่อเป็นตัวกำหนดกำลังในการขับเคลื่อนของมอเตอร์ไฟตรงภายในกลุ่มฟังก์ชันการขับเคลื่อนหุ่นยนต์

 

#define ref 70      สำหรับกำหนดให้ ref แทนด้วยตัวเลข 70 เพื่อเป็นตัวกำหนดค่าอ้างอิงสำหรับการเปรียบเทียบค่าอะนาลอกที่อ่านได้จากเซนเซอร์เพื่อแยกแยะระหว่างเส้นสนามสีขาวกลับพื้นสนามสีดำภายในโปรแกรม เพื่อใช้ในการตีความของเซนเซอร์ทั้ง 3 ตัวร่วมกัน

 

#define A 1        สำหรับกำหนดให้ A แทนด้วยตัวเลข 1 เพื่อแทนความหมายของฐาน A เริ่มต้นของหุ่นยนต์ที่จะทำการขับเคลื่อน

 

#define B 2        สำหรับกำหนดให้ A แทนด้วยตัวเลข 2 เพื่อแทนความหมายของฐาน B เริ่มต้นของหุ่นยนต์ที่จะทำการขับเคลื่อน

 

#define LEFT 1  สำหรับกำหนดให้ LEFT แทนด้วยตัวเลข 1 เพื่อแทนความหมายสั่งให้เลี้ยวซ้ายของพารามิเตอร์ dir_control ของฟังก์ชัน across_all,cross_left และcross_right เมื่อการเริ่มต้นอยู่ที่ฐาน B และจะเป็นการสั่งเลี้ยวขวาเมื่อการเริ่มต้นอยู่ที่ฐาน

 

 

#define RIGHT 2            สำหรับกำหนดให้                 RIGHT แทนด้วยตัวเลข 2 เพื่อแทนความหมายสั่งให้เลี้ยวขวาของพารามิเตอร์ dir_control ของฟังก์ชัน cross_all,cross_left และcross_right เมื่อการเริ่มต้นอยู่ที่ฐาน B และจะเป็นการสั่งเลี้ยวซ้ายเมื่อการเริ่มต้นอยู่ที่ฐาน A

 

#define PASS 10   สำหรับกำหนดให้ PASS แทนด้วยตัวเลข 10 เพื่อสั่งให้หุ่นยนต์เดินผ่านในแบบต่างในลำดับที่ใกล้ตัวที่สุดของตัวหุ่นยนต์เพื่อแทนความหมายสั่งให้เลี้ยวขวาของพารามิเตอร์ dir_control ของฟังก์ชัน cross_all,cross_left และcross_right

 

#define TRACK_OK ((left<ref)&&(mid>ref)&&(right<ref))         สำหรับกำหนดให้ TRACK แทนด้วยนิพจน์ ((left<ref)&&(mid>ref)&&(right<ref))  เพื่อใช้ตรวจสอบเงื่อนไขการแทร็คเส้นของหุ่นยุ่นต์ โดยจะมีการเรียกใช้ภายในฟังก์ชัน spin_find_line เพื่อพยายามปรับให้หุ่นยนต์เซนเซอร์ด้านซ้ายและขวาอยู่คร่อมเส้นสนาม

 

#define CROSS_ALL ((left>ref)&&(mid>ref)&&(right>ref))       สำหรับกำหนดให้ CROSS_ALL แทนด้วยนิพจน์ ((left>ref)&&(mid>ref)&&(right>ref)) เพื่อใช้ตรวจสอบเงื่อนไขการตรวจจับแยกและเส้นของหุ่นยุ่นต์ โดยจะมีการเรียกใช้ภายในฟังก์ชัน cross_all

 

#define OVER_RIGHT (((left<ref)&&(mid<ref)&&(right>ref))||((left<ref)&&(mid>ref)&&(right>ref)))   สำหรับกำหนดให้ CROSS_RIGHT แทนด้วยนิพจน์ (((left<ref)&&(mid<ref)&&(right>ref))||((left<ref)&&(mid>ref)&&(right>ref))) เพื่อใช้ตรวจการออกนอกเส้นไปทางขวาของหุ่นยุ่นต์ โดยจะมีการเรียกใช้ภายในฟังก์ชัน spin_find_line

 

 

#define OVER_LEFT (((left>ref)&&(mid<ref)&&(right<ref))||((left>ref)&&(mid>ref)&&(right>ref)))     สำหรับกำหนดให้ CROSS_LEFT แทนด้วยนิพจน์ (((left>ref)&&(mid<ref)&&(right<ref))||((left>ref)&&(mid>ref)&&(right>ref))) เพื่อใช้ตรวจการออกนอกเส้นไปทางซ้ายของหุ่นยุ่นต์ โดยจะมีการเรียกใช้ภายในฟังก์ชัน spin_find_line

 

#define CROSS_LEFT ((left>ref)&&(mid>ref)&&(right<ref))     สำหรับกำหนดให้ CROSS_LEFT แทนด้วยนิพจน์ ((left>ref)&&(mid>ref)&&(right<ref))เพื่อใช้ตรวจสอบเงื่อนไขการตรวจจับแยกซ้ายของหุ่นยุ่นต์ โดยจะมีการเรียกใช้ภายในฟังก์ชัน cross_left

 

#define CROSS_RIGHT ((left<ref)&&(mid>ref)&&(right>ref))  สำหรับกำหนดให้ CROSS_RIGHT แทนด้วยนิพจน์ ((left<ref)&&(mid>ref)&&(right>ref))เพื่อใช้ตรวจสอบเงื่อนไขการตรวจจับแยกขวาของหุ่นยุ่นต์ โดยจะมีการเรียกใช้ภายในฟังก์ชัน cross_right




  ชมวิดีทัศน์การแข่งขัน