โปรเจกต์ บันทึกแห่งความล้มเหลว
Humanoid arm + hand prototype ที่สร้างจากไม้ Balsa
Humanoid arm + hand prototype ที่สร้างจากไม้ Balsa
แม้ว่าทุกคนมักจะเลือกแชร์ผลงานที่ประสบความสำเร็จและ Prototype ที่น่าทึ่ง แต่ที่นี่ผมจะเลือกทำในทางตรงกันข้าม: ในขณะที่นักสร้าง Robotic arm ระดับ Hobby เกือบทุกคนจะโฟกัสไปที่แขนหุ่นยนต์ประเภทอุตสาหกรรม ผมกลับตั้งใจที่จะสร้างชุดมือและแขนแบบ Humanoid ที่มี Mechanicals และฟังก์ชันการทำงานที่เกือบจะเหมือนกับร่างกายมนุษย์ โดยใช้วิธีที่ประหยัดค่าใช้จ่ายที่สุดเท่าที่จะเป็นไปได้ แต่ด้วยขีดจำกัดของวัสดุและ Components ต่างๆ มันจึงกลายเป็นเรื่องราวของความล้มเหลว อย่างไรก็ตาม เรื่องราวความล้มเหลวก็คือเรื่องราวของบทเรียนที่ได้เรียนรู้เช่นกัน
ในตอนแรก ผมเริ่มจากการสังเกตและศึกษาการเคลื่อนไหวของมือตัวเอง รวมถึงขนาดของมัน แม้กระทั่งตอนเดินไปไหนมาไหน ผมยอมเสี่ยงอย่างมากที่คนแถวบ้านอาจจะคิดว่าผมเป็นโรคทางจิตขั้นรุนแรงได้ :)
การออกแบบครั้งแรกเสร็จสิ้นในเวลา 2-3 เดือน จากนั้นจึงสร้าง Prototype โดยใช้กระดาษลังและไม้จิ้มฟัน ซึ่งมันดีมากที่ได้เห็นว่าแนวทางด้าน Mechanical นั้นน่าพอใจและสามารถตอบสนองความต้องการพื้นฐานได้หรือไม่






จากนั้นผมก็หยุดพักไปนานเพื่อคิดหาไอเดียที่ดีกว่าเดิม
การออกแบบครั้งแรกมีชิ้นส่วนด้าน Mechanical มากเกินไปและควรได้รับการปรับแต่งให้เหมาะสม ดังนั้นผมจึงตัดสินใจเริ่มใหม่ตั้งแต่ต้น และเลือกวัสดุตั้งแต่เริ่มคือ: Balsa Wood แต่ทางเลือกนั้นกลับทำให้ผมต้องจบโปรเจกต์เร็วกว่าที่คิด แต่อย่างไรก็ตาม ผมบอกได้เลยว่ามันทำงานได้ดีทีเดียว
หลังจากตัดสินใจเลือกวัสดุได้แล้ว ผมเริ่มออกแบบ Mechanical ใหม่โดยใช้ชิ้นส่วนแบบ Mono-block ให้มากที่สุด "กระดูก" ส่วน Metacarpal / Proximal / Medial / Distal ทั้งหมด รวมถึงส่วน Trapezium และ Wrist ได้รับการออกแบบ ประกอบแบบเสมือนจริง และจำลองการทำงานร่วมกันบนคอมพิวเตอร์ ในช่วงเวลานั้น ผมยังสังเกตเห็นว่าการออกแบบปัจจุบันของผมไม่สามารถหยิบจับอะไรได้เลย เพราะอะไรน่ะหรือ?
เพราะในระยะเริ่มต้น ฝ่ามือถูกออกแบบให้แบนราบสนิท เหมือนกับมือหุ่นยนต์อื่นๆ ส่วนใหญ่ที่มีขายทั่วไป ซึ่งนั่นทำให้วัตถุลื่นหลุดมือได้ง่าย แม้จะกำนิ้วมือแล้วก็ตาม

อย่างไรก็ตาม ฝ่ามือของมนุษย์ไม่ได้เป็นแบบนั้น หากคุณยืดนิ้วตรงและมองจากด้านข้าง คุณอาจคิดว่ามันแบน แต่เมื่อคุณมองจากด้านปลายนิ้วโดยตรง (หรือลองสัมผัสหลังมือดู) จะเห็นได้ชัดว่าฝ่ามือของคุณมีโครงสร้างเป็นแอ่งเว้า (Concave) ซึ่งเกิดจากกระดูก Metacarpal ที่เรียงตัวตามกระดูก Trapezium ที่มีความเว้า และเมื่อคุณกำมือ นิ้วมือจะโอบล้อมพื้นที่ทรงกลมจากทุกด้านไปพร้อมๆ กับฝ่ามือของคุณ
ดังนั้นผมจึงนำสิ่งนี้มาใส่ในดีไซน์ของผม: Trapezium ถูกปรับรูปทรงให้มีความเว้า ส่งผลให้ฝ่ามือมีความเว้าตามไปด้วยเนื่องจากกระดูก Metacarpal เรียงตัวตามแนวของ Trapezium หลังจากทดลองปรับมุมอยู่หลายครั้ง ในที่สุดมันก็ใช้งานได้ในทางทฤษฎี :)

ในช่วงนั้น ผมยังต้องการก้าวข้ามขีดจำกัดของการเป็นแค่ Mechanical เพียงอย่างเดียว และเริ่มคิดเรื่องการขับเคลื่อน (Actuation) แต่จะทำอย่างไรล่ะ? ผมต้องใช้ Servo motors แต่ถึงแม้จะใช้รุ่นที่เล็กที่สุด ผมก็ไม่มีพื้นที่สำหรับติดตั้งพวกมันในส่วนของ Hand และ Wrist ผมพบว่าผมต้องการ Servos ถึง 12 ตัวเฉพาะสำหรับส่วน Hand และ Wrist เพื่อเลียนแบบการเคลื่อนไหวทั้งหมด ในขณะเดียวกัน ผมยังตัดสินใจว่าจะเลียนแบบ "กล้ามเนื้อ" และ "เอ็น" อย่างไร: หลังจากทดลองกับวัสดุที่แตกต่างกันสองสามอย่าง ผมพบว่าสายเอ็นตกปลาคือ "กล้ามเนื้อ" ที่ดีที่สุดเนื่องจากความแข็งแรงและความยืดหยุ่น และใช้ยางรัดของเป็น "เอ็น" (ซึ่งเราจะพูดถึงพวกมันกันต่อ) ในระหว่างการทดลองเหล่านั้น ผมยังได้ติดตั้ง "Muscle guides" โดยใช้ท่อ PVC ขนาด 6mm อีกด้วย


เป็นที่ชัดเจนว่าส่วนของ Arm ควรได้รับการออกแบบด้วยเช่นกัน แต่เนื่องจาก Front Arm มีการเคลื่อนไหวแบบบิดและยก จึงเหลือที่ว่างเพียงแห่งเดียวสำหรับติดตั้ง Servos นั่นคือ Upper Arm
และมันคงจะดีถ้าสามารถขับเคลื่อนส่วน Upper Arm ได้ด้วย แต่การสร้าง Universal joint ตรงจุดเชื่อมต่อระหว่าง Upper Arm และ Shoulder นั้นซับซ้อนและยากมาก อย่างไรก็ตาม Upper Arm อาจจะทำแค่การยก (Lever) และโครงสร้างทั้งหมดอาจถูกหมุนโดย Shoulder ผมจึงตัดสินใจจำกัดโครงสร้าง Mechanical ไว้ที่ Shoulder
หลังจากประกอบส่วน Hand และ Wrist เสร็จ ผมเริ่มศึกษาเรื่อง Front Arm และทันใดนั้นก็เกิดคำถามขึ้นมาว่า: ผมจะบิดมือของผมได้อย่างไร?
บางทีจนถึงตอนนี้คุณอาจจะไม่เคยคิดถึงเรื่องนี้เลย ในทาง Mechanical การบิดต้องใช้การเชื่อมต่อแบบจุดหมุน (Pivot) แต่มันถูกประกอบในร่างกายมนุษย์อย่างไร? คำตอบที่ผมตามหาอยู่ในหน้าเว็บของ Stan Prokopenko: https://www.proko.com/types-of-joints/
(ต้องขอบคุณเขามากสำหรับแนวทางที่ยอดเยี่ยมนี้ ในระหว่างนั้นผมยังได้เรียนรู้ว่าการเคลื่อนไหวแบบบิดนี้เรียกว่า “pronation” และ “supination” ในศัพท์ทางการแพทย์)
เห็นได้ชัดว่ามีการเชื่อมต่อแบบ Pivot ระหว่างกระดูก Ulna และ Radius แต่ทำไมจุดหมุนนั้นถึงอยู่ที่ส่วนท้ายตรงข้อศอกล่ะ? มันทำยากไม่ใช่เหรอ? และผมจะเลียนแบบกลไกนั้นได้อย่างไร?
ดังที่เห็นในแอนิเมชันของ Prokopenko กระดูก Ulna และ Radius พันอยู่รอบจุดหมุนด้วยกัน เมื่อกล้ามเนื้อที่ทำหน้าที่เฉพาะหดตัว Radius จะหมุนรอบ Ulna ทำให้ข้อมือบิดได้
ตอนแรกผมทำตามนั้นเป๊ะๆ: ผมเตรียม Prototype โดยวางจุดหมุนไว้ที่ปลายข้อศอก ยึดทั้ง Ulna และ Radius เข้ากับ Wrist และพันพวกมันไว้ที่ปลายข้อศอกโดยใช้ยางรัดของ อย่างไรก็ตาม โครงสร้างนี้ไม่สามารถเคลื่อนไหวได้อย่างราบรื่นแม้จะใช้มือหมุนเอง หลังจากพิจารณากลไกอย่างละเอียดอีกครั้ง ผมจึงเปลี่ยนวิธีใหม่โดยการออกแบบรูปทรงของ Ulna และ Radius ใหม่ทั้งหมด: Ulna ถูกออกแบบมาเพื่อการยกเท่านั้นและแยกจาก Wrist อย่างสิ้นเชิง จุดหมุน Ulna-Radius ถูกย้ายมาอยู่ส่วนกลางของ Front Arm และสุดท้าย Wrist จะถูกยึดไว้กับ Radius เท่านั้น ซึ่งในที่สุดรูปทรงของ Ulna และ Radius ก็กลายเป็นคล้ายกับกระดูกจริงมากขึ้น

(สำหรับ Front Arm ของคุณ จุดหมุนนั้นอยู่ที่ปลายข้อศอก เพราะเป็นตำแหน่งที่ดีที่สุดในการ ป้องกันจุดหมุนจากความเสียหาย หากกระดูก Front Arm หักจากการ กระแทก คุณยังสามารถใช้งานมันได้ตามปกติเมื่อหายดีแล้ว อย่างไรก็ตาม หาก จุดหมุนถูกวางไว้ตรงกลาง มันจะได้รับความเสียหายได้ง่ายหากเกิดการ กระแทก และคุณอาจจะไม่สามารถบิดมือได้อีกเลยแม้กระดูกจะ สมานแล้วก็ตาม)
หลังจากแก้ปัญหานี้ได้แล้ว ตอนนี้ผมต้องตัดสินใจเรื่องการวางแนวของ "กล้ามเนื้อ" การวางแนวไม่ควรได้รับผลกระทบจากการเคลื่อนไหวของ Ulna และ Radius ดังนั้นจึงชัดเจนว่าเชือกกล้ามเนื้อทั้งหมดควรไปถึง Actuators ที่ Upper Arm โดยผ่านจุดหมุน Ulna-Radius ผมจึงยอมสละปากกาหนึ่งด้ามเพื่อใช้ตัวด้ามของมันเป็นข้อต่อแบบกลวง
ในที่สุด Mechanical ของ Humanoid Arm + Hand ทั้งหมดก็สามารถเคลื่อนไหวได้เกือบเหมือนของจริง: การเปิดและปิด Fingers รวมถึงความสามารถในการหยิบจับสิ่งของ, การหมุนซ้าย-ขวาและขึ้น-ลงที่ Wrist, การบิด Front Arm ได้เกือบ 270 องศา รวมถึงการยกมันขึ้น, การยก Upper Arm และการหมุนโครงสร้างแขนทั้งหมดได้เกือบ 180 องศาที่ Shoulder
โปรดทราบว่าถ้าผมพิกัดไม่ผิด แขนมนุษย์จริงๆ มีแกนการเคลื่อนไหวถึง 27 แกนจาก Shoulder ถึง Hand:
ดังนั้น Mechanical prototype ที่เสร็จสมบูรณ์จึงสามารถทำสิ่งเหล่านี้ได้บน 22 แกน ยกเว้นการเหวี่ยงของ Upper Arm และการขยับนิ้วสำหรับท่า Vulkan Salute


เมื่อสร้างกลไก Front Arm เสร็จ ความจำเป็นในการใช้ Servo ตัวที่ 13 ก็เกิดขึ้นสำหรับการทำ “pronation” และ “supination” และตัวที่ 14 สำหรับการยก Front Arm ทั้งหมด
การออกแบบส่วน Upper Arm และ Shoulder นั้นง่ายกว่าเมื่อเทียบกับกลไกของ Front Arm แต่เนื่องจากความอ่อนของไม้ Balsa ส่วนปลายไหล่ของ Upper Arm ควรได้รับการเสริมความแข็งแรงจากด้านภายใน และท่อ PVC ขนาดเส้นผ่านศูนย์กลาง 40 mm ก็ทำหน้าที่นั้นได้อย่างสมบูรณ์ และท่อ PVC ขนาด 25 mm ถูกสวมผ่านเข้าไป ทำให้แกนการยกของ Upper Arm เสร็จสมบูรณ์




(ตรงนี้ผมเขียนเหมือนมันทำเสร็จอย่างรวดเร็ว แต่มันไม่ได้เป็นแบบนั้น ก่อนที่จะเริ่มลงมือทำจริง ชิ้นส่วนทั้งหมดต้องถูกจำลอง สร้างโมเดล ตรวจสอบการประกอบและการทำงานร่วมกันบนคอมพิวเตอร์ก่อน การนำไปใช้งานจริงจะเกิดขึ้นหลังจากการออกแบบทางวิศวกรรม (Engineering) เสมอ)
อย่างไรก็ตาม ตั้งแต่เริ่มมีความคิดเรื่องการขับเคลื่อน ก็มีอีกคำถามหนึ่งอยู่ในใจผม…
คำถามเรื่องการ Support ไม่ได้มีไว้สำหรับโครงสร้าง Mechanical เท่านั้น มันยังจำเป็นสำหรับการยึด Controllers ไว้ที่ไหนสักแห่งที่ใกล้กับโครงสร้าง Mechanical เนื่องจากความยาวที่จำกัดของ Jumper wires หลายวันมานี้ผมจินตนาการถึงโครงสร้าง Support ที่ทำจากไม้, ท่อ หรือวัสดุอื่นๆ… แต่ผมไม่ชอบไอเดียเหล่านั้นเลย
และคืนหนึ่ง ผมก็พบคำตอบที่สมบูรณ์แบบที่แขวนอยู่ข้างโต๊ะผมมาตั้งนานแล้ว: ขาตั้งกล้อง (Tripod) ของผมเอง เพียงแค่เปลี่ยนสกรูของมันเป็นน็อตเพื่อยึดแกนหมุนของ Shoulder มันก็สามารถใช้งานได้อย่างสมบูรณ์แบบ และมันก็ได้ผล
ในที่สุดผมจึงออกแบบแค่แกนและแผ่นฐานสำหรับ Shoulder โดยใช้ชิ้นส่วนเฟอร์นิเจอร์ที่เหลือ ฝาปิดท่อน้ำทิ้งพลาสติก และไม้ Balsa จากนั้นทุกอย่างก็ถูกวางลงบน Tripod
ตอนนี้ผมสามารถเริ่มติดตั้ง Muscles, Actuators และส่วนอื่นๆ ได้แล้ว แต่ก่อนหน้านั้น อีกเรื่องหนึ่งก็เกิดขึ้น…
ทันทีที่ตัดสินใจจะขับเคลื่อนแขนทั้งหมด ผมก็นับจำนวนสัญญาณที่ต้องใช้และพบกับข่าวร้ายที่เลี่ยงไม่ได้: ผมมี Arduino Due เพียงตัวเดียว และแม้ว่ามันจะมี Input/Outputs จำนวนมากเมื่อเทียบกับรุ่นอื่นๆ แต่มันก็ยังไม่พอ ในตอนแรกผมต้องกำหนดความต้องการเพิ่มเติมก่อน จากนั้นผมก็พบว่า Arduino Leonardo เป็นทางเลือกที่เหมาะสมที่สุดสำหรับใช้เป็น Secondary controller หลังจากเตรียมลิสต์ Input/Output และศึกษามันอยู่พักหนึ่ง ส่วน Hand และ Wrist จะอยู่ภายใต้การดูแลของ Due; ในขณะที่การควบคุม Front Arm, Upper Arm และ Shoulder จะจัดการโดย Leonardo
จากนั้น อีกคำถามหนึ่งก็เกิดขึ้น…
เนื่องจากมีความจุสูงกว่าและความเร็วที่เร็วกว่า จึงควรให้ Due เป็น Master อย่างไรก็ตาม มีปัญหาเรื่องการ “คุย” กัน: Due ทำงานที่ 3.3V ในขณะที่ Leonardo ทำงานที่ 5V เนื่องจากการสื่อสาร I2C ของพวกมันใช้ระดับแรงดันไฟฟ้าเดียวกับ Controller การเชื่อมต่อโดยตรงจะทำให้ Due ไหม้ได้
ผมต้องทำให้ Interface นั้นง่ายที่สุดเท่าที่จะเป็นไปได้ และวิธีที่ดีที่สุดคือการใช้สัญญาณ Analog (แรงดันไฟฟ้า) พูดง่ายๆ คือ Leonardo ควรจะ “เข้าใจ” ว่า;
ผมจึงตัดสินใจใช้ Output Analog (แรงดันไฟฟ้า) DAC0 และ DAC1 บน Due และใช้ A0 และ A1 บน Leonardo ในการรับค่าเพื่อกำหนดสิ่งเหล่านั้น
อย่างไรก็ตาม การทำให้แรงดัน Output คงที่นั้นต้องใช้เวลาเล็กน้อย ดังนั้น Leonardo อาจเข้าใจคำขอของ Due ผิดพลาดได้ มันจึงควรต้องรู้ด้วยว่าคำสั่งนั้นพร้อมให้อ่านแล้วหรือยัง สัญญาณที่สามของการสื่อสารจึงเกิดขึ้น: “Execute” ข่าวดีก็คือ มันสามารถเดินสายตรงจาก Ch.50 (Digital output) ของ Due ไปยัง Ch.8 (Digital input) ของ Leonardo ได้เลย
สุดท้าย Due จะรู้ได้อย่างไรว่าคำสั่งของมันถูกดำเนินการแล้ว? สัญญาณที่สี่ควรจะถูกส่งจาก Leonardo กลับไปยัง Due: Feedback “Command Executed” แต่มีปัญหาเนื่องจากระดับแรงดันไฟฟ้าที่กล่าวถึงข้างต้น หากสายสัญญาณยืนยันนั้นเชื่อมต่อโดยตรงจาก Ch.12 (Digital output) ของ Leonardo ไปยัง Ch.51 (Digital input) ของ Due ผมคงต้องเตรียมจัดงานศพให้เจ้า Due ผู้โชคร้ายได้เลย :)
แต่อย่างไรก็ตาม ปัญหานี้ง่ายที่สุดในโปรเจกต์นี้: Output 5V ของ Leonardo สามารถแบ่งแรงดันได้ด้วย Resistors ขนาดกิโลโอห์มเพียงสองตัว จากนั้นจึงส่งไปยัง Due ในระดับ ~3.3V ในขั้นตอนการทดลองจริง การใช้ 270k และ 470k ก็ใช้งานได้ดีมาก
คุณสั่งงาน Muscles เพื่อให้เกิดการเคลื่อนไหว แต่ส่วนของร่างกายต้องสามารถกลับคืนสู่ตำแหน่งเดิมได้เมื่อกล้ามเนื้อที่เกี่ยวข้องคลายตัว ดังนั้นจึงมี Tendons อยู่ในร่างกายของคุณ ซึ่งวางอยู่ในทิศทางตรงกันข้ามกับกล้ามเนื้อ แต่ Tendons ไม่ได้แข็งแรงเท่ากับกล้ามเนื้อเนื่องจากหน้าที่การใช้งานที่ต่างกัน
ผมได้เขียนถึงวัสดุที่ตัดสินใจใช้เป็น Muscles และ Tendons ไว้ด้านบนแล้ว อย่างไรก็ตาม ในระหว่างการทดลอง ผมสังเกตเห็นว่าสายเอ็นตกปลาอาจติดขัดระหว่างข้อต่อ Mechanical ได้ง่าย ดังนั้นจึงควรใช้ “Guides” (ตัวประคอง) และท่อ PVC ขนาดเส้นผ่านศูนย์กลาง 6mm ก็ตอบโจทย์นี้ได้พอดี
ไม่มีการใช้ Tendons สำหรับ Ulna และ Upper Arm เนื่องจากแรงโน้มถ่วงทำงานได้ดีอยู่แล้ว


เมื่อผมตัดสินใจจะขับเคลื่อนแขน ความจำเป็นของ Limit Switches ก็ตามมาทันที หากไม่มีพวกมัน Servos ก็อาจจะทำความเสียหายให้กับชิ้นส่วน Mechanical ได้ง่ายมาก แต่น่าเสียดายที่ Servos ไม่ได้ทรงพลังอย่างที่ผมคิด
ในตอนแรกผมคิดว่า Limit switches จะเป็นส่วนที่ง่ายที่สุดอย่างหนึ่ง แต่มันกลับไม่เป็นเช่นนั้น โดยเฉพาะที่นิ้วมือ ปัญหานั้นเกิดจากสาเหตุหลัก 2 ประการ: อย่างแรกคือไม้ Balsa อีกครั้งเนื่องจากความอ่อนตัวของมัน และอย่างที่สองคือมาตราส่วนของดีไซน์ของผม อย่างที่เห็นในรูปด้านบน ความตั้งใจของผมคือการสร้างมือและแขน Humanoid ในมาตราส่วน 1:1
อย่างไรก็ตาม ผมสร้างจุดสัมผัสด้วยสายไฟและเทปอลูมิเนียม แม้ว่าพวกมันจะจำเป็นต่อการปกป้องชิ้นส่วน Mechanical แต่พวกมันก็สูญเสียฟังก์ชันการทำงานไปตามกาลเวลา โดยเฉพาะที่นิ้วมือ เนื่องจากการสึกหรอของชิ้นส่วนไม้ Balsa
Position Sensors ถูกติดตั้งโดยใช้ Potentiometers ในบางจุดเท่านั้นและพวกมันทำงานได้ค่อนข้างดี


เนื่องจากพื้นที่ที่จำกัด ผมจึงมีทางเลือกเพียงทางเดียวในการขับเคลื่อน (หรืออาจจะเรียกว่าการดึง “stretch” หรือ "flexion/extension" ตามศัพท์การแพทย์) “กล้ามเนื้อ” สำหรับส่วน Front Arm, Wrist และ Hand รวมถึงนิ้วมือด้วย นั่นคือ: Multi-turn Micro Servo Motors ซึ่งทั้งหมดควรจะติดตั้งอยู่ที่ Upper Arm ดังนั้นการออกแบบ Upper Arm จึงต้องคำนึงถึงการวางแนวของเชือกด้วย
ในตอนแรก ผมสร้างพูลเลย์ (Pulleys) ขนาดเส้นผ่านศูนย์กลาง ~10 mm จากไม้ Balsa อย่างไรก็ตาม Output torque ของ Micro Servos นั้นต่ำมาก ผมจึงปรับแต่ง Wheel horns ของมันใหม่: ลดเส้นผ่านศูนย์กลางลงด้วยการเจียร และใช้ Washer ระหว่าง Horn และ Servo พร้อมเจาะรูใกล้จุดศูนย์กลางเพื่อเชื่อมต่อเชือก ด้วยวิธีนี้ Servos จะสามารถรับโหลดที่สูงขึ้นได้เล็กน้อย



ในขั้นตอนต่อมา จำเป็นต้องมีชุดพูลเลย์เพิ่มเติมเพื่อยก Front Arm และ Upper Arm ด้วย ซึ่งก็ได้ทำขึ้นมา แม้ว่า Micro Servo จะรับน้ำหนักการยกของ Front Arm ไหว แต่ก็จำเป็นต้องใช้ Standard servo สำหรับ Upper Arm เนื่องจากมันต้องรับน้ำหนักของ Upper Arm, Front Arm, Hand และสายไฟทั้งหมดที่อยู่บนนั้นด้วย
Standard servo อีกตัวถูกนำมาใช้สำหรับการหมุน Shoulder


และวิธีที่ดีที่สุดในการทำเช่นนั้นคือการทำสิ่งที่เรียกว่า “Terminal assemblies” หลังจากศึกษาอยู่สองสามวัน ผมพบว่าสามารถสร้าง Terminal Assemblies 3 ชุดได้จากแผ่น PCB เอนกประสงค์ขนาด 8x2 cm เพียง 2 แผ่น
แน่นอนว่า Terminal Assemblies ทั้งหมดต้องได้รับการทดสอบหลังจากการ “ผลิต” ครั้งแรกและในระหว่างการบัดกรีสายไฟ



หลังจากติดตั้ง -เกือบจะ- ทุกอย่างแล้ว ผมใช้แค่ Arduino Leonardo และเริ่มทดสอบ Servo แต่ละตัวด้วย Routine ง่ายๆ ทีละตัวเพื่อตรวจสอบว่ามันทำงานได้ตามต้องการหรือไม่ Routine นั้นพื้นฐานมาก เขียนขึ้นเพื่อใช้งาน Servo เพียงตัวเดียวด้วยความเร็วต่ำที่สุดและทิศทางถูกเลือกโดย Digital inputs ในเวลานั้นสามารถใช้งานทุกอย่างได้ทีละตัว แต่เมื่อจ่ายไฟให้ Servos ทั้งหมดพร้อมกัน เซอร์ไพรส์ที่ไม่คาดคิดก็เกิดขึ้น ซึ่งเราจะพูดถึงปัญหานั้นในภายหลัง

แม้ว่าผมจะดัดแปลง Servo arms เพื่อใช้เป็นพูลเลย์ที่มีเส้นผ่านศูนย์กลางต่ำมากแล้ว แต่มันก็ยังไม่เพียงพอต่อการทำ “pronation” และ “supination” ของ Radius ผมจึงสร้างชุด Micro pulley ขึ้นมา

อันแรกทำงานได้ดีแต่มันใหญ่เกินไป ผมจึงลดขนาดมันลงในขั้นตอนถัดมา

น่าเสียดายที่ยังมีแรงบิดที่ Servo มากเกินไป ผมพยายามปรับแต่ง แต่ในขณะที่ Front Arm อยู่ในตำแหน่งแนวนอนและ Servo ดึง Radius มาวางบน Ulna จนสุดแล้วพยายามจะปล่อย ยาง “tendon” ที่เกี่ยวข้องไม่สามารถดึงมันกลับมาได้เนื่องจากแรงเสียดทานและแรงต้านบน Micro pulley ผมจึงดึง Tendon ให้ตึงขึ้น แต่คราวนี้ Servo ก็ดึงไม่ไหวอีกต่อไป… หลังจากทดลองไปสองสามครั้ง Servo ก็เริ่มไม่เสถียรและในไม่ช้า Motor controller ของมันก็ไหม้ไป นั่นคือเหยื่อรายแรกของโปรเจกต์นี้ โปรดทำความเคารพและสวดภาวนาให้ฮีโร่ไร้นามผู้นี้ด้วย - อ้อ ไม่สิ มันมีชื่อว่า “11FR” ครับ :)
(แน่นอนว่าทุกจุดและทุก Component มี Tag เฉพาะของมันเอง ไม่เช่นนั้นคงเป็นไปไม่ได้ที่จะสร้างโครงสร้าง เดินสายไฟ และเขียนโปรแกรมทั้งหมด อดทนหน่อยนะครับ เดี๋ยวเราจะได้พูดถึงความสำคัญของ Engineering กันด้วย)
ดังนั้นผมจึงตัดสินใจหยุดการทดลองบิด Front Arm ไว้แค่นี้ มันต้องใช้แรงบิดที่มหาศาลมาก และในขณะเดียวกันการทดลองต่อไปอาจนำไปสู่การพังของ Front Arm ทั้งหมดได้
ไม่มีอะไรเป็นพิเศษที่จะพูดครับ ในตอนแรกทุกอย่างดูโอเค… จนกระทั่ง Servos พังและชิ้นส่วนไม้ Balsa สึกหรอ
ผมได้กล่าวถึงหลักการไปแล้ว และในขั้นตอนการเขียนโปรแกรม นี่คือส่วนแรก ในช่อง Analog นั้น Due/DAC0 และ Leonardo/A0 ถูกกำหนดให้เป็น Set Value Register เพื่อส่งและรับค่าที่ตั้งไว้เป็นเปอร์เซ็นต์; Due/DAC1 และ Leonardo/A1 ถูกกำหนดให้เป็น Section Selector
แต่มันจำเป็นต้องทำ Ranging (การกำหนดช่วง) ด้วย: Due สามารถใช้ช่วงค่าได้เต็มที่ แต่เนื่องจาก Output DACx จะสร้างแรงดันไฟฟ้าในช่วง 0.75-2.55V จึงต้องปรับ Matching scales บน Leonardo ดังนั้น Leonardo จึงถูกตั้งค่าให้อ่าน Set Value ระหว่าง (Integer) 168-846 ซึ่งสอดคล้องกับ 0-100% สำหรับ Section selector ช่วงค่าทั้งหมดจะถูกแบ่งออกเป็น 5 ส่วนทางฝั่ง Due และค่าที่สอดคล้องกันจะถูกคำนวณสำหรับ Leonardo แต่ในการใช้งานจริงจำเป็นต้องใช้ Deadband ด้วย ดังนั้นแม้ค่าที่ใช้บน Due จะแน่นอน แต่พวกมันจะถูกตีความบน Leonardo เป็น (ค่า +/- Deadband) เพื่อให้แน่ใจว่าเลือก Section ได้ถูกต้อง
ในที่สุด Routine การสื่อสารก็เสร็จสมบูรณ์ดังนี้บน Controller ทั้งสองตัวและทำงานได้ดี
บน Due:
const int _FA_Radius = 840; // Front Arm Radius Section
const int _FA_Ulna = 1530; // Front Arm Ulna Section
const int _UpperArm = 2220; // Upper Arm Section
const int _Shoulder = 2910; // Front Arm Radius Section
void CommandToLeonardo(String Section, int SetValue) {
/* SetValue is given as Percentage */
int Section_Num=0;
if (Section=="_11FR") Section_Num = _FA_Radius;
else if (Section=="_11FU") Section_Num = _FA_Ulna;
else if (Section=="_21UA") Section_Num = _UpperArm;
else if (Section=="_31SH") Section_Num = _Shoulder;
if (Section_Num==0)
Serial.println("Section cannot be recognized.");
if ((SetValue<0) || (SetValue>100))
Serial.println("Set Value should be between 0-100).");
if ((Section_Num!=0) && (SetValue>=0) && (SetValue<=100) )
{
Serial.print(String(SetValue) + "% -> ");
switch(Section_Num){
case _FA_Radius: Serial.println("Front Arm-Radius");
break;
case _FA_Ulna : Serial.println("Front Arm-Ulna");
break;
case _UpperArm : Serial.println("Upper Arm"); break;
case _Shoulder : Serial.println("Shoulder"); break;
}
analogWrite (_00XXCM, Section_Num);
analogWrite (_00XXSP, map(SetValue, 0, 100, 0, 4095));
delay(50); // Delay for stabilization of outputs
digitalWrite(_00XXXC, HIGH); // Execute Command
do { // Wait until getting OK from Leonardo
} while(!digitalRead(_00XXOK));
Serial.println("ARD LNRD = OK.");
analogWrite (_00XXCM, 0);
analogWrite (_00XXSP, 0);
// Resetting Section and Set Value outputs
delay(50); // Delay for stabilization of outputs
digitalWrite(_00XXXC, LOW); // Resetting Command
} /* End of "if (Section_Num!=0)" */
} /* End of "void CommandToLeonardo()" */
void loop() {
if (Serial.available())
{
String Command=ReadUserInputOverSerial();
Serial.println(Command);
if (Command.substring(0,1)=="?") /* Help */
{
Serial.println("Commands:");
Serial.println(" CTL [Section] [SetValue (0-100)]");
Serial.println(" CommandToLeonardo - Section: First 5 characters of PWM Tag (e.g. _10WH for _10WHCM).");
Serial.end(); Serial.begin(_BAUDRATE);
while(!Serial) { };
}
if (Command.substring(0,3)=="CTL")
CommandToLeonardo(Command.substring(4,9),
Command.substring(10).toInt());
CommandPromptOverSerial("[ARD DUE/] > ");
} /* End of "if (Serial.available())..." */
} /* End of "void loop()": ARDUINO DUE */
บน Leonardo:
const int _FA_Radius = 310; // Front Arm Radius Section
const int _FA_Ulna = 425; // Front Arm Ulna Section
const int _UpperArm = 540; // Upper Arm Section
const int _Shoulder = 655; // Front Arm Radius Section
const int _ICDB = 40; // Deadband
/* Since the signals cannot generate exact integer values,
* Section shall be identified by [ (SectionValue) +/- _ICDB ]
* reading.
*/
void setup() {
/* Initial Feedback to Due */
digitalWrite(_00XXOK, LOW);
}
void CommandFromDue() {
CommandFromDue_Section = analogRead(_00XXCM);
if ( ((_FA_Radius-_ICDB)<=CommandFromDue_Section) &&
(CommandFromDue_Section<=(_FA_Radius+_ICDB)) )
CommandFromDue_Section=_FA_Radius;
else if ( ((_FA_Ulna-_ICDB)<=CommandFromDue_Section) &&
(CommandFromDue_Section<=(_FA_Ulna+_ICDB)) )
CommandFromDue_Section=_FA_Ulna;
else if ( ((_UpperArm-_ICDB)<=CommandFromDue_Section) &&
(CommandFromDue_Section<=(_UpperArm+_ICDB)) )
CommandFromDue_Section=_UpperArm;
else if ( ((_Shoulder-_ICDB)<=CommandFromDue_Section) &&
(CommandFromDue_Section<=(_Shoulder+_ICDB)) )
CommandFromDue_Section=_Shoulder;
else
CommandFromDue_Section=0;
CommandFromDue_SetValue =
map(analogRead(_00XXSP),168,846,0,100);
/* Due generates DAC outputs between 0.55-2.75 V and it
* corresponds (int) 168-846 on Arduino Leonardo.
* Here analog reading is converted to Percentage.
*/
boolean CommandFromDue_Execute = digitalRead(_00XXXC);
String Section = "";
int Speed = 0;
if ((CommandFromDue_Section!=0) && CommandFromDue_Execute)
{
Serial.println();
Serial.print("Command by ARD DUE: "+
String(CommandFromDue_SetValue) + "% -> ");
switch(CommandFromDue_Section){
case _FA_Radius: Serial.println("Front Arm-Radius");
Section = "_11FR";
Speed = 20;
break;
case _FA_Ulna : Serial.println("Front Arm-Ulna");
Section = "_11FU";
Speed = 20;
break;
case _UpperArm : Serial.println("Upper Arm");
Section = "_21UA";
Speed = 90;
break;
case _Shoulder : Serial.println("Shoulder");
Section = "_31SH";
Speed = 30;
break;
}
Serial.println(" -action-");
MoveToPosition(Section, Speed, CommandFromDue_SetValue);
Serial.println("OK -> ARD DUE.");
CommandPromptOverSerial("[ARD LNRD/] > ");
digitalWrite(_00XXOK, HIGH); delay(100);
digitalWrite(_00XXOK, LOW); // OK Feedback to Due
} /* End of "if (Section_Num!=0)" */
} /* End of "void CommandFromDue()" */


นอกเหนือจากการสื่อสารระหว่างกันแล้ว ผมจะไม่ขออธิบายรายละเอียดเชิงลึกของโค้ดส่วนอื่นๆ เพื่อไม่ให้ผู้อ่านเบื่อ เพราะในท้ายที่สุดคุณสามารถดูโปรแกรมฉบับเต็มที่มีความยาวกว่า 800+ บรรทัดสำหรับ Due และ 500+ บรรทัดสำหรับ Leonardo พร้อมทั้งบันทึกอธิบายได้ ดังที่คุณจะเห็นในโปรแกรม มีฟังก์ชันที่มีชื่อเดียวกันอยู่แล้ว โดยหลักการแล้วฟังก์ชันเหล่านั้นเหมือนกันในแง่ของอัลกอริทึม แต่มีการเปลี่ยนแปลงบางอย่างตามขอบเขตของการควบคุม
void CommandPromptOverSerial(Prompt)
สร้างขึ้นเพื่อให้มี Interface ของ Command line ที่ดูสวยงาม นอกจากนั้นมันยังเริ่มการสื่อสาร Serial ใหม่เพื่อให้แน่ใจว่าได้ทำการ Flush ข้อมูลแล้ว
String ReadUserInputOverSerial()
ทำหน้าที่อ่าน Input ของผู้ใช้ที่ Serial Monitor และส่งค่ากลับมาเป็น String
void Read(Tag)
void LoopTest()
ทั้งสองฟังก์ชันนี้สร้างขึ้นเพื่อทดสอบลูปแต่ละส่วน (Input/Output channels)
LoopTest() จะมีส่วนย่อยที่ใช้สำหรับ Analog Input, Analog Output, Digital Input, Digital Output และประเภท PWM-Output; แต่ Read(Tag) มีไว้เพื่ออ่านค่า Input โดยตรงจากระดับบนสุดโดยไม่ต้องเข้าไปใน LoopTest()

void CommandToServo(Tag, Value)
อย่างที่คุณคงทราบแล้ว ผมอ้างอิงทุกอย่างด้วย Tags นั่นคือจุดประสงค์ของฟังก์ชันนี้: มันจะส่งค่าที่ตั้งไว้ (0-180) ไปยัง Servo motor ที่ถูกกำหนดให้กับ "Tag" นั้นๆ
void FullTravelDurationMeasurement(PWM_Tag, LimitSw_CCW, LimitSw_CW, int Speed)
ฟังก์ชันนี้เขียนขึ้นเพื่อตรวจสอบระยะเวลาการเคลื่อนที่เต็มช่วงสำหรับนิ้วมือ เนื่องจากพวกมันไม่สามารถติดตั้ง Position sensors จริงๆ ได้ ตามผลลัพธ์ที่ได้ พวกมันจะถูกดึงหรือปล่อยตามเวลา แม้ว่ามันจะทำงานได้ในทางทฤษฎีโดยการบังคับ Input/Outputs ด้วยมือ แต่มันไม่สามารถใช้ในระบบจริงได้เพราะสวิตช์ทำงานไม่ถูกต้องเนื่องจากการสึกหรอของ Mechanical ผลที่ตามมาคือ ระยะเวลาการเคลื่อนที่เต็มช่วงจึงทำได้แค่กำหนดจากการทดลองด้วยมือเท่านั้น แทนที่จะใช้ฟังก์ชันนี้

void MoveDuringMillis(Tag, Direction, Speed, Milliseconds)
ฟังก์ชันนี้เขียนขึ้นเพื่อขับเคลื่อนนิ้วมือที่ระบุโดย Tag ตามทิศทางและความเร็วที่กำหนดในช่วงเวลา Milliseconds ที่ระบุ
void MoveToPosition (Section, Speed, SetValue)
ฟังก์ชันนี้เขียนขึ้นเพื่อเคลื่อนย้ายส่วน (Section) ไปยังตำแหน่งที่ระบุโดย SetValue ด้วยความเร็วที่กำหนด โปรดทราบว่าส่วนนั้นควรจะมีการติดตั้ง Position sensor ด้วย
void Fingers_Hold()
void Fingers_Release()
ฟังก์ชันเหล่านี้เขียนขึ้นเพื่อให้ปลายนิ้วหุบ (เพื่อจับของ) และกางออกพร้อมๆ กัน แต่น่าเสียดายที่นิ้วมือทำงานได้เพียงครั้งเดียวและแค่บางส่วนเท่านั้น…
void Final_Demo()
ตามชื่อเลยครับ: ในท้ายที่สุดมีเพียงกลไกการยกของ Front Arm และ Upper Arm และการหมุนของ Shoulder เท่านั้นที่ใช้งานได้ ดังนั้นฟังก์ชันนี้จึงเขียนขึ้นเพื่อสาธิตการทำงานเหล่านั้น
หลังจากทดสอบสัญญาณและฟังก์ชันทั้งหมดเป็นรายตัวสำเร็จแล้ว ผมก็ได้เชื่อมต่อ Mechanical ระหว่าง Servos และส่วนต่างๆ ของแขน ตอนนี้แขนพร้อมสำหรับการทดสอบฟังก์ชันการทำงานจริงแล้ว แต่ว่า…
หลังจากทดสอบและปรับแต่ง Servos ทีละตัวแล้ว ก็ถึงเวลาที่จะจ่ายไฟให้ Servos ทั้งหมดพร้อมกัน แต่ผลลัพธ์กลับไม่เป็นอย่างที่คิด
การออกแบบเดิมของผมมี Micro Servos 14 ตัวที่ Upper Arm เพื่อรับภาระที่เบา ในขณะที่มี Standard servos 2 ตัววางอยู่ที่ Shoulder สำหรับการยก Upper Arm และการหมุน Shoulder ทั้งหมดสามารถรับระดับแรงดันไฟฟ้าเดียวกันได้คือ 6V ผมจึงทำตามนั้น
แต่พอผมสั่งงาน Standard servo ตัวหนึ่ง Micro servos บางตัวก็เริ่มทำงานเองโดยไม่มีคำสั่ง โชคดีที่ผมติดตั้งสวิตช์ไว้ที่สายจ่ายไฟ ผมจึงตัดไฟทั้งหมดได้ทันที แต่อย่างไรก็ตาม นิ้วมือบางนิ้วก็ถูกดึงแรงเกินไปเพราะการเร่งความเร็วที่รุนแรงและทันที ผมต้องคลายสกรูพูลเลย์ของ Micro servos ทั้งหมดออก แล้วลองจ่ายไฟใหม่อีกครั้ง ผลลัพธ์ก็ยังเหมือนเดิม: Micro servos ที่ไม่เสถียรนั้นไม่สามารถปรับจูนได้เลย หรือไม่สามารถรักษาความเสถียรได้แม้จะปรับจูนแล้ว และแม้ว่าจะไม่มีโหลดก็ตาม
ข้อสงสัยแรกของผมคือความเหมาะสมของ Power supply: ผมใช้ Adapter ขนาด 6V, 3Ah
ตามข้อมูลที่มี Micro servo แต่ละตัวกินไฟ 6 mA ในสภาวะ Idle, 120 mA เมื่อทำงานแบบไม่มีโหลด และ 800 mA เมื่อมอเตอร์หยุดหมุนค้าง (Stall) สำหรับ Standard servos มีพารามิเตอร์เดียวที่ให้มาคือ 100 mA เป็น Operating current ผมจึงถือว่าเป็นค่าขณะไม่มีโหลด และสมมติพารามิเตอร์ส่วนที่เหลือให้ใกล้เคียงกับ Micro servos
ดังนั้น Power supply ของผมน่าจะเพียงพอเพราะจะสั่งงาน Servo เพียงตัวเดียวในแต่ละขณะ แล้วสาเหตุของความล้มเหลวคืออะไรกันแน่?
ผมยังคงคิดเรื่อง Power supply และเริ่มตรวจสอบมันด้วย Oscilloscope ผลลัพธ์ที่ได้น่าสนใจมากในขณะที่ Standard servo เพียงตัวเดียวทำงานอยู่:

เมื่อผมเห็นการเปลี่ยนแปลงเหล่านั้น ผมจึงมีทฤษฎีว่า: เป็นไปได้ว่า Micro servos มีความอ่อนไหวต่อความผันผวนของแรงดันไฟฟ้ามากกว่า และพวกมันตีความความผันผวนนั้นเป็น “คำสั่ง” จากนั้นผมจึงตัดสินใจแยกแหล่งจ่ายไฟระหว่าง Micro servos และ Standard servos โดย Standard servos ยังคงต่ออยู่กับแหล่งจ่ายเดิมร่วมกับ Micro servo เพียงตัวเดียว (ตัวที่ใช้ยก Front Arm) ในขณะที่ Micro servos ตัวอื่นๆ ทั้งหมดถูกต่อแยกไปยังแหล่งจ่ายไฟอีกชุดหนึ่ง และการปรับเปลี่ยนนี้ก็ได้ผล
อย่างที่ผมเขียนไว้ด้านบน ในท้ายที่สุดผมไม่สามารถใช้งานได้ครบทุกส่วนเนื่องจากการสึกหรอ แต่อย่างไรก็ตาม มันยังสามารถเปิดและปิดนิ้วได้เพียงครั้งเดียว และถ้าผมยังพยายามต่อไป ผมคงทำลาย Micro servos อีก 6 ตัวที่เหลือแน่นอน ดังนั้นจึงเป็นการดีที่สุดที่จะหยุดตรงนี้ ในทางทฤษฎี คุณคงจะได้เห็นการทำงานที่ต่อเนื่องและราบรื่นกว่านี้หากชิ้นส่วนไม้ Balsa ไม่สึกหรอไปเสียก่อน
ในที่สุดหลังจากออกแบบและประกอบมานานกว่า 4.5 เดือน ก็สามารถใช้งานได้เพียงฟังก์ชันการยกของ Front- และ Upper-Arms และฟังก์ชันการหมุนของ Shoulder เท่านั้น
บางทีคุณอาจจะติดตามทุกอย่างได้ด้วยตาและมือหากโปรเจกต์ของคุณอยู่ในพื้นที่เล็กมากและประกอบด้วยอุปกรณ์เพียงไม่กี่ชิ้น
แต่ถ้าคุณต้องทำงานกับอุปกรณ์ที่มีความยาวประมาณ 1 เมตร ซึ่งประกอบด้วยชิ้นส่วนมากกว่า 500 ชิ้นใน 140 ประเภทที่แตกต่างกัน, อุปกรณ์ไฟฟ้าและอิเล็กทรอนิกส์ประมาณ 60 ชิ้น (สวิตช์, Potentiometers, Servo motors), สัญญาณมากกว่า 60 สัญญาณบน Controller สองตัวที่ต่างกันรวมถึงการเชื่อมต่อทั้งสองเข้าด้วยกัน และสายไฟหลายร้อยเส้นสำหรับการเชื่อมต่อ; การทำงานบนคอมพิวเตอร์และเอกสารจึงกลายเป็นสิ่งจำเป็นและดีที่สุดสำหรับการแก้ปัญหา (Troubleshooting)
ทุกอย่างจึงเริ่มต้นด้วยการออกแบบ Mechanical บนคอมพิวเตอร์ ในขั้นตอนต่อมา จึงมีการเพิ่ม Servo motors, สวิตช์, Position sensors และ Terminal assemblies ลงในแบบจำลอง
ดังที่ผมกล่าวไว้ในส่วนก่อนหน้านี้ Tags ที่ไม่ซ้ำกันถูกกำหนดให้กับชิ้นส่วนแต่ละชิ้น คุณจะได้เห็น Tags เหล่านั้นในโปรแกรมด้วย ซึ่งมันช่วยให้ชีวิตง่ายขึ้นมาก ในท้ายที่สุด จึงมีชุดเอกสารของโปรเจกต์ประกอบด้วย “3D Model”, “Pulley Rope Routing Sketches”, “Instrument List”, “I/O List”, “Instrument Layout”, “Terminal Board Layout” และ “Wiring Diagram”








ผมยอมรับว่าดีไซน์ของผมนั้นโบราณมาก ความตั้งใจแรกของผมคือการเลียนแบบมือและแขนของมนุษย์ในเชิง Mechanical เท่านั้น แต่ภายหลังผมตัดสินใจลองขับเคลื่อนโครงสร้างทั้งหมดดู
ในด้านการออกแบบ Mechanical ทุกอย่างเกือบจะเป็นไปตามที่คาดไว้: ชิ้นส่วน Mechanical ทั้งหมดเข้าคู่กันและทำงานร่วมกันได้อย่างสมบูรณ์แบบเมื่อขยับด้วยมือ
อย่างไรก็ตาม ปัญหาคอขวดประการแรกคือวัสดุที่เลือกใช้ นั่นคือไม้ Balsa: ผมเลือกมันเพียงเพราะมันมีชิ้นส่วนที่ตัดสำเร็จรูปขายทั่วไปสำหรับงาน Hobby, ราคาถูกมากสำหรับใช้ทำ Prototype, ขึ้นรูปง่ายด้วยเครื่องมือง่ายๆ, ซ่อมแซมง่ายแค่ใช้กาวตราช้าง และการใช้ไม้ Balsa ทำให้ผมไม่ต้องซื้อเครื่อง CNC หรือเครื่องพิมพ์ 3D ที่ราคาแพงรวมถึงอุปกรณ์เสริมและวัสดุสิ้นเปลืองของมันด้วย
แต่ไม้ Balsa ก็เป็นวัสดุที่อ่อนแอมาก และทำให้เกิดการสึกหรอเร็วกว่าที่ผมจินตนาการไว้ โดยเฉพาะที่นิ้วมือ
นอกจากนั้น มาตราส่วน 1:1 ของดีไซน์ผมยัง “ขยาย” ความอ่อนแอของไม้ Balsa เข้าไปอีก ผมต้องซ่อมแซมหลายจุดในระหว่างการสร้างและทดลองใช้งาน ตัวอย่างสุดท้ายคือ แผ่นยึดของ Servo motors บน Upper Arm ก็ทำจากไม้ Balsa และหลังจากถอดเข้าออกเพียงไม่กี่ครั้ง พวกมันก็เริ่มแตกร้าว
และเป็นไปไม่ได้เลยที่จะใช้ตลับลูกปืน (Bearings) ใดๆ โดยเฉพาะในส่วนของมือเนื่องจากข้อจำกัดด้านขนาด
ถึงอย่างนั้น ผมก็ยังแนะนำไม้ Balsa สำหรับการทำ Prototype เชิง Mechanical ในวิธีที่ประหยัดที่สุด
ปัญหาคอขวดอีกประการหนึ่งคือขนาดและกำลังของ Servos เนื่องจากมาตราส่วน ผมจึงต้องใช้เฉพาะ Micro- และ Standard servos เท่านั้น แต่พวกมันมีความสามารถที่จำกัด นั่นคือสาเหตุที่ต้องใช้ชุดพูลเลย์สำหรับการบิดของ Radius และการงอของ Ulna และ Upper Arm ทำให้แขนทั้งแขนมีโครงสร้างเหมือนปั้นจั่นมากกว่าหุ่นยนต์จริงๆ
การเลียนแบบกลไกธรรมชาติเป็นโปรเจกต์ที่ยากและซับซ้อนมาก เนื่องจากการทำงานบางอย่างของมันอาศัยการเชื่อมต่อที่ยืดหยุ่น (เกือบจะเป็น Universal) ระหว่างส่วนทางกล (=กระดูก) ที่ยึดเข้าด้วยกันด้วย Actuators (=กล้ามเนื้อ) และสปริงดึงกลับ (=เอ็น) นอกจากนี้ เอ็นยังสามารถยืดหยุ่นได้ - แม้จะเล็กน้อยและไม่เท่ากล้ามเนื้อก็ตาม
แต่นั่นไม่ใช่ทั้งหมด เมื่อคุณสั่งงาน “ส่วนใดส่วนหนึ่ง” คุณต้องรู้ตำแหน่งจริงของมันแบบ Real-time ด้วย รวมถึงต้องมีการ Interlocking การทำงานหากมันถึงขีดจำกัดของการเคลื่อนไหว นั่นหมายความว่า ควรมี Position sensor อย่างน้อย 1 ตัว และ Limit switches อย่างน้อย 2 ตัวสำหรับแต่ละส่วน
อย่างที่คุณสังเกตเห็น ผมต้องใช้ Servo motors แต่อีกทางเลือกหนึ่งคือ Stepper motors หากคุณต้องการหลีกเลี่ยง Position sensors อย่างไรก็ตาม ในกรณีนั้นจะต้องใช้ 4 Digital outputs สำหรับ Stepper แต่ละตัว ส่งผลให้ Arduino สองตัวอาจจะไม่เพียงพอ
คุณคิดว่าเราทำเสร็จแล้วหรือยัง? ไม่ครับ ยังไม่เสร็จ
ลองทำการทดลองง่ายๆ ดูนะครับ: หงายฝ่ามือขวาขึ้นตรงๆ จากนั้นงอนิ้วชี้ขวาแล้ว “เกี่ยว” ไว้กับฝ่ามือซ้าย ตอนนี้พยายามเกร็งมือขวาขึ้นข้างบนในขณะที่ใช้แค่ปลายนิ้วชี้ซ้ายกดมันไว้ เห็นอะไรไหมครับ? จนถึงระดับหนึ่ง คุณสามารถต้านทานแรงกล้ามเนื้อที่แขนได้ เพียงแค่ใช้กล้ามเนื้อที่ควบคุมกระดูกนิ้วชี้เท่านั้น
หากคุณพยายามทำแบบนั้นกับกลไกที่เลียนแบบมา มั่นใจได้เลยว่ามันจะพัง ในระหว่างการทดลองง่ายๆ ผมทำ Micro servos ไหม้ไปถึง 7 ตัว
และยังมีอีก:
ในกลไกธรรมชาติ “Actuators” จะถูกวางไว้ใกล้กับส่วนทางกลมากๆ วิธีที่ดีที่สุดที่จะทำแบบนั้นในการเลียนแบบทางกลปัจจุบัน อาจจะเป็นการใช้กระบอกไฮดรอลิก แต่ในกรณีนั้น ระบบปั๊มไฮดรอลิกที่ทรงพลัง, ถังน้ำมัน, Check valves, Solenoid valves… ก็ต้องถูกนำมาใช้ด้วย ซึ่งจะนำไปสู่ความต้องการพื้นที่ที่ใหญ่ขึ้นมากและ Controller ที่มีขีดจำกัด Input/Output สูงขึ้น
ย่อหน้าล่าสุด 2 ย่อหน้าหมายความว่า เราต้องการ Actuators ที่มีขนาดเล็กลงมากและทรงพลังมากขึ้นมหาศาล
เสร็จหรือยัง? ยังครับ มีอีกเรื่องหนึ่ง:
ลองพยายามขยับนิ้วหนึ่งนิ้วดูครับ…
คราวนี้ลองขยับ 2 หรือ 3 นิ้วพร้อมกัน… ดีครับ
ลองขยับนิ้วทั้งหมดพร้อมกับขยับข้อมือขึ้น-ลง แล้วซ้าย-ขวา… สมบูรณ์แบบ
ขยับนิ้วทั้งหมดขณะบิดข้อมือไปด้วยและยกแขนท่อนล่างไปด้วย…
[ อะไรนะ? คุณโอเคไหม? มองหน้าผมซิ ให้ผมพาไปคลินิกไหมครับ? :)) ]
ถ้าคุณไม่มีปัญหาสุขภาพร้ายแรง คุณทำได้ทั้งหมดใช่ไหมครับ? ทีนี้เราจะทำแบบนั้นกับกลไกเลียนแบบได้อย่างไร?
อย่างที่ผมบอกไป แขนและมือของมนุษย์มีการเคลื่อนไหว (แกน) ที่แตกต่างกันประมาณ 30 แกน ดังนั้นจึงต้องใช้ Actuator หนึ่งตัวสำหรับแต่ละแกน เพื่อให้การเลียนแบบที่สมจริงสามารถทำงานได้อย่างราบรื่น
หากเราต้องการทำโดยใช้ Controller ง่ายๆ อย่างตระกูล Arduino เราจำเป็นต้องมี Sub-controller หนึ่งตัวสำหรับแต่ละแกน และมี Master-controller เพื่อสั่งการพวกมันทั้งหมด Sub-controllers ควรมี Routine เฉพาะเพื่อกำหนดการเคลื่อนไหวของแต่ละส่วน สำหรับการเคลื่อนไหวโดยรวมที่กำหนดไว้ล่วงหน้าของทั้งระบบ Master ควรส่งแค่ Set points และข้อมูลการเคลื่อนไหวโดยรวมที่ร้องขอไปยัง Sub-controllers จากนั้นจึงส่งคำสั่ง “execute” ไปยังทั้งหมดพร้อมกัน และรอการยืนยัน
ตัวอย่างเช่น Sub-controller แต่ละตัวที่แยกสำหรับส่วน Proximal, Medial และ Distal ของนิ้วชี้, กลาง, นาง และก้อย (เรากำลังพูดถึง Sub-controllers 12 ตัว พร้อมด้วย Actuators, Position sensors, Limit switches และอื่นๆ ตามต้องการ) ควรจะมี -ตัวอย่างเช่น- Subroutine "Hold_a_Circular_Plate()" ที่กำหนดการเคลื่อนไหวเฉพาะสำหรับส่วนนั้นๆ เมื่อ Master controller ส่งคำสั่ง "Command_to_Hold_Circular_Plate()" มันควรจะกระตุ้น Output เพียงตัวเดียวที่ส่งไปยัง Sub-controllers ทั้งหมดพร้อมกัน แต่อีกเรื่องที่สำคัญคือการ Synchronize ของ Sub-controllers ในระดับ Microseconds ไม่เช่นนั้นการทำงานทั้งหมดจะล้มเหลว
ทางเลือกที่สองคือใช้ Controller ที่ทรงพลังกว่ามาก และ Actuators ที่มีการตอบสนองที่เร็วกว่ามาก จนการสลับการทำงานระหว่าง Actuators ต่างๆ ไม่สามารถสังเกตเห็นได้
ทางเลือกที่สามซึ่งดีที่สุดถ้าเป็นไปได้ คือการใช้ Controller ที่ทรงพลังที่สุดเท่าที่เคยมีมา ซึ่งสามารถประมวลผลแบบ Multi-processing ได้อย่างแท้จริงที่ความเร็วสูงสุด: นั่นคือสมองอินทรีย์รวมกับระบบประสาท โดยเฉพาะสมองของมนุษย์สำหรับแขนแบบ Humanoid (ในนิยายมันเคยเกิดขึ้นแล้วในซีรีส์ Robocop)
แม้ว่าในปัจจุบันจะมีบางบริษัทที่ผลิตมือเทียมแบบ Humanoid สำหรับผู้พิการ และแม้ว่าพวกมันจะเป็นดีไซน์ที่น่าทึ่งและช่วยให้ชีวิตของผู้คนง่ายขึ้น แต่เราก็ยังไม่สามารถบอกได้ว่าสิ่งเหล่านั้นเป็นการเลียนแบบที่สมบูรณ์แบบ
ผมคิดว่าเราเกือบทุกคนคงเคยเห็นหุ่นยนต์ที่น่าทึ่งที่สุดอย่าง “Atlas” ที่ออกแบบและสร้างโดย Boston Dynamics การวิจัยและพัฒนาเช่นนี้ใช้เงินมหาศาล ใช้เวลาและต้องการการศึกษา การทดลอง ความล้มเหลวนับครั้งไม่ถ้วน และอื่นๆ อีกมากมาย ตัวอย่างเช่น Boston Dynamics ก่อตั้งขึ้นในปี 1992 และตั้งแต่นั้นมาพวกเขาก็ทำงานเกี่ยวกับหุ่นยนต์มาอย่างต่อเนื่อง นั่นหมายความว่า จนถึงปัจจุบัน มีการศึกษา ทดลอง ล้มเหลว และพยายามใหม่… เกือบ 30 ปีอยู่เบื้องหลัง Atlas
ในด้านงบประมาณ “พ่อ” และ “แม่” ของ Atlas ได้รับทุนสนับสนุนจากหน่วยงานโครงการวิจัยขั้นสูงด้านการป้องกันประเทศของสหรัฐฯ (DARPA) ใช่ครับ น่าเสียดายที่หน่วยงานทางทหารเป็นผู้สนับสนุนหลักของโปรเจกต์ที่น่าทึ่งเช่นนี้ และมนุษยชาติก็ยังคงเก่งมากในการหาวิธีที่ดีที่สุดในการฆ่ากันเอง :(
และสิ่งมหัศจรรย์เช่นนี้ไม่สามารถสร้างได้ด้วยผลิตภัณฑ์สำหรับผู้บริโภคทั่วไปในตลาด ทั้งระบบ Mechanical, Electronics, Actuators, Sensors… ทั้งหมดควรได้รับการออกแบบเฉพาะเจาะจงด้วยพารามิเตอร์ที่กำหนดเองให้เหมาะสมกับโปรเจกต์แบบ Tailor-made
บางทีกล้ามเนื้อเทียมที่เพิ่งถูกคิดค้นเมื่อเร็วๆ นี้อาจช่วยพัฒนาแขนขาเทียมที่ดีขึ้นได้
ไม่ว่าในกรณีใด ผมหวังว่าคุณจะพบว่าเรื่องราวอันยาวนานนี้ รวมถึงร่างกายของคุณเองด้วย เป็นสิ่งที่น่าสนใจจากมุมมองของ Mechanical และการควบคุมนะครับ…
สนับสนุนเพื่อรับ Source Code หรือแอปพลิเคชันสำหรับโปรเจกต์นี้