portaldacalheta.pt
  • หลัก
  • การเพิ่มขึ้นของระยะไกล
  • ผู้คนและทีมงาน
  • การวางแผนและการพยากรณ์
  • การออกแบบ Ux
เทคโนโลยี

บทแนะนำการเขียนโปรแกรมหุ่นยนต์เบื้องต้น



หมายเหตุบรรณาธิการ: เมื่อวันที่ 16 ตุลาคม 2018 บทความนี้ได้รับการปรับปรุงให้ทำงานร่วมกับเทคโนโลยีล่าสุด

มาดูกันว่าหุ่นยนต์เจ๋งมาก พวกเขากำลังจะวิ่งไปทั่วโลกในสักวันหนึ่งและหวังว่าในเวลานั้นพวกเขาจะรู้สึกสงสารผู้สร้างที่มีรูปร่างไม่ดี (a.k.a. นักพัฒนาหุ่นยนต์ ) และช่วยเราสร้างพื้นที่ยูโทเปียที่เต็มไปด้วยความอุดมสมบูรณ์ แน่นอนฉันล้อเล่น แต่ เรียงลำดับเท่านั้น .

ในความทะเยอทะยานของฉันที่จะมีอิทธิพลเล็กน้อยในเรื่องนี้ฉันจึงใช้ หลักสูตรทฤษฎีการควบคุมหุ่นยนต์อิสระ เมื่อปีที่แล้วซึ่งเป็นจุดสิ้นสุดในการสร้างหุ่นยนต์จำลองที่ใช้ Python ซึ่งทำให้ฉันสามารถฝึกฝนทฤษฎีการควบคุมบนหุ่นยนต์ที่เรียบง่ายเคลื่อนที่และตั้งโปรแกรมได้



ในบทความนี้ฉันจะแสดงวิธีใช้เฟรมเวิร์กหุ่นยนต์ Python ในการพัฒนาซอฟต์แวร์ควบคุมอธิบายรูปแบบการควบคุมที่ฉันพัฒนาขึ้นสำหรับหุ่นยนต์จำลองของฉันแสดงให้เห็นว่ามันมีปฏิสัมพันธ์กับสภาพแวดล้อมอย่างไรและบรรลุเป้าหมายและพูดคุยเกี่ยวกับ ความท้าทายพื้นฐานของการเขียนโปรแกรมหุ่นยนต์ที่ฉันพบระหว่างทาง



ในการทำตามบทช่วยสอนเกี่ยวกับการเขียนโปรแกรมหุ่นยนต์สำหรับผู้เริ่มต้นคุณควรมีความรู้พื้นฐานสองประการ:



  • คณิตศาสตร์ - เราจะใช้ฟังก์ชันตรีโกณมิติและเวกเตอร์
  • Python - เนื่องจาก Python เป็นหนึ่งในภาษาการเขียนโปรแกรมหุ่นยนต์ขั้นพื้นฐานที่ได้รับความนิยมมากขึ้นเราจะใช้ประโยชน์จากไลบรารีและฟังก์ชันพื้นฐานของ Python

ตัวอย่างโค้ดที่แสดงที่นี่เป็นเพียงส่วนหนึ่งของโปรแกรมจำลองทั้งหมดซึ่งอาศัยคลาสและอินเทอร์เฟซดังนั้นในการอ่านโค้ดโดยตรงคุณอาจต้องมีประสบการณ์ใน Python และ การเขียนโปรแกรมเชิงวัตถุ .

สุดท้ายหัวข้อที่เป็นทางเลือกที่จะช่วยให้คุณทำตามบทช่วยสอนนี้ได้ดีขึ้นคือการรู้ว่าเครื่องของรัฐคืออะไรและเซ็นเซอร์และตัวเข้ารหัสช่วงทำงานอย่างไร



ความท้าทายของหุ่นยนต์ที่ตั้งโปรแกรมได้: การรับรู้เทียบกับความเป็นจริงและความเปราะบางของการควบคุม

ความท้าทายพื้นฐานของหุ่นยนต์ทั้งหมดคือสิ่งนี้เป็นไปไม่ได้เลยที่จะรู้สภาพที่แท้จริงของสิ่งแวดล้อม ซอฟต์แวร์ควบคุมหุ่นยนต์สามารถคาดเดาสถานะของโลกแห่งความเป็นจริงตามการวัดที่เซ็นเซอร์ส่งกลับมาเท่านั้น สามารถพยายามเปลี่ยนสถานะของโลกแห่งความเป็นจริงผ่านการสร้างสัญญาณควบคุมเท่านั้น

วิกฤตการณ์ทางการเงินในกรีซอธิบาย

ภาพนี้แสดงให้เห็นถึงปฏิสัมพันธ์ระหว่างหุ่นยนต์ทางกายภาพและการควบคุมคอมพิวเตอร์เมื่อฝึกเขียนโปรแกรมหุ่นยนต์ Python



ซอฟต์แวร์ควบคุมหุ่นยนต์สามารถคาดเดาสถานะของโลกแห่งความเป็นจริงตามการวัดที่เซ็นเซอร์ส่งกลับมาเท่านั้น

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

นี่เป็นหนึ่งในเหตุผลสำคัญที่ทำให้การเขียนโปรแกรมหุ่นยนต์เป็นเรื่องยาก เรามักจะเห็นวิดีโอเกี่ยวกับหุ่นยนต์วิจัยล่าสุดในห้องทดลองการแสดงความคล่องแคล่วการนำทางหรือการทำงานเป็นทีมที่ยอดเยี่ยมและเรามักจะถามว่า“ ทำไมสิ่งนี้จึงไม่ถูกใช้ในโลกแห่งความเป็นจริง” ครั้งต่อไปที่คุณเห็นวิดีโอดังกล่าวลองดูว่าสภาพแวดล้อมในห้องปฏิบัติการมีการควบคุมสูงเพียงใด ในกรณีส่วนใหญ่หุ่นยนต์เหล่านี้สามารถทำงานที่น่าประทับใจเหล่านี้ได้ตราบเท่าที่สภาพแวดล้อมยังคงอยู่ในขอบเขตที่แคบของแบบจำลองภายใน ดังนั้นกุญแจสำคัญอย่างหนึ่งในการพัฒนาหุ่นยนต์คือการพัฒนาแบบจำลองที่ซับซ้อนยืดหยุ่นและแข็งแกร่งมากขึ้นและกล่าวว่าความก้าวหน้าขึ้นอยู่กับขีด จำกัด ของทรัพยากรการคำนวณที่มีอยู่



กุญแจสำคัญประการหนึ่งในการพัฒนาหุ่นยนต์คือการพัฒนาโมเดลที่ซับซ้อนยืดหยุ่นและแข็งแกร่งมากขึ้น

[หมายเหตุด้านข้าง: นักปรัชญาและนักจิตวิทยาจะสังเกตว่าสิ่งมีชีวิตต่างก็ต้องทนทุกข์ทรมานจากการพึ่งพาการรับรู้ภายในของตัวเองว่าประสาทสัมผัสของมันกำลังบอกอะไรพวกเขา ความก้าวหน้ามากมายในวิทยาการหุ่นยนต์มาจากการสังเกตสิ่งมีชีวิตและดูว่าพวกมันตอบสนองต่อสิ่งเร้าที่ไม่คาดคิดอย่างไร ลองคิดดูสิ แบบจำลองภายในของโลกของคุณคืออะไร? มันต่างจากมดกับปลา? (หวังว่านะ) อย่างไรก็ตามเช่นเดียวกับมดและปลาก็มีแนวโน้มที่จะทำให้ความเป็นจริงบางอย่างของโลกลดลง เมื่อสมมติฐานของคุณเกี่ยวกับโลกไม่ถูกต้องอาจทำให้คุณเสี่ยงต่อการสูญเสียการควบคุมสิ่งต่างๆ บางครั้งเราเรียกสิ่งนี้ว่า“ อันตราย” เช่นเดียวกับที่หุ่นยนต์ตัวน้อยของเราดิ้นรนเอาชีวิตรอดจากจักรวาลที่ไม่รู้จักเราทุกคนก็เช่นกัน นี่เป็นข้อมูลเชิงลึกที่มีประสิทธิภาพสำหรับนักหุ่นยนต์]

โปรแกรมจำลองหุ่นยนต์ที่ตั้งโปรแกรมได้

ตัวจำลองที่ฉันสร้างขึ้นถูกเขียนขึ้น Python และขนานนามอย่างชาญฉลาด วันเสาร์ Rimulator . คุณสามารถค้นหา v1.0.0 บน GitHub . ไม่มีระฆังและนกหวีดมากนัก แต่ถูกสร้างขึ้นเพื่อทำสิ่งหนึ่งได้เป็นอย่างดี: ให้การจำลองหุ่นยนต์เคลื่อนที่ได้อย่างแม่นยำและมอบกรอบการทำงานที่เรียบง่ายให้กับนักหุ่นยนต์ที่ต้องการเพื่อฝึกการเขียนโปรแกรมซอฟต์แวร์หุ่นยนต์ แม้ว่าจะดีกว่าเสมอที่จะมีหุ่นยนต์ตัวจริงให้เล่น แต่หุ่นยนต์จำลอง Python ที่ดีนั้นสามารถเข้าถึงได้มากกว่าและเป็นจุดเริ่มต้นที่ดี



ในหุ่นยนต์ในโลกแห่งความเป็นจริงซอฟต์แวร์ที่สร้างสัญญาณควบคุม ('คอนโทรลเลอร์') จำเป็นต้องทำงานด้วยความเร็วสูงมากและทำการคำนวณที่ซับซ้อน สิ่งนี้มีผลต่อการเลือกภาษาการเขียนโปรแกรมหุ่นยนต์ที่ดีที่สุดที่จะใช้: โดยปกติแล้ว C ++ จะใช้สำหรับสถานการณ์ประเภทนี้ แต่ในแอปพลิเคชันหุ่นยนต์ที่เรียบง่าย Python เป็นการประนีประนอมที่ดีมากระหว่างความเร็วในการดำเนินการและความง่ายในการพัฒนาและการทดสอบ

ซอฟต์แวร์ที่ฉันเขียนจำลองหุ่นยนต์วิจัยในชีวิตจริงที่เรียกว่า Khepera แต่สามารถปรับให้เข้ากับหุ่นยนต์เคลื่อนที่ได้หลายรุ่นที่มีขนาดและเซ็นเซอร์ต่างกัน เนื่องจากฉันพยายามตั้งโปรแกรมจำลองให้ใกล้เคียงกับความสามารถของหุ่นยนต์จริงมากที่สุดลอจิกการควบคุมจึงสามารถโหลดลงในหุ่นยนต์ Khepera จริงโดยมีการปรับโครงสร้างน้อยที่สุดและจะทำหน้าที่เหมือนกับหุ่นยนต์จำลอง คุณลักษณะเฉพาะที่นำมาใช้อ้างถึง Khepera III แต่สามารถปรับให้เข้ากับ Khepera IV ใหม่ได้อย่างง่ายดาย



กล่าวอีกนัยหนึ่งการเขียนโปรแกรมหุ่นยนต์จำลองนั้นคล้ายคลึงกับการเขียนโปรแกรมหุ่นยนต์จริง นี่เป็นสิ่งสำคัญหากต้องใช้โปรแกรมจำลองเพื่อพัฒนาและประเมินแนวทางต่างๆของซอฟต์แวร์ควบคุม

ในบทช่วยสอนนี้ฉันจะอธิบายสถาปัตยกรรมซอฟต์แวร์ควบคุมหุ่นยนต์ที่มาพร้อมกับ v1.0.0 ของ วันเสาร์ Rimulator และให้ข้อมูลโค้ดจากซอร์ส Python (มีการปรับเปลี่ยนเล็กน้อยเพื่อความชัดเจน) อย่างไรก็ตามฉันขอแนะนำให้คุณดำดิ่งสู่แหล่งที่มาและทำความเข้าใจ เครื่องจำลองได้รับการแยกและใช้เพื่อควบคุมหุ่นยนต์เคลื่อนที่ต่างๆรวมถึง Roomba2 จาก iRobot . ในทำนองเดียวกันโปรดอย่าลังเลที่จะแยกโครงการและปรับปรุง

ตรรกะการควบคุมของหุ่นยนต์ถูก จำกัด ไว้ที่คลาส / ไฟล์ Python เหล่านี้:

  • models/supervisor.py - คลาสนี้มีหน้าที่ในการปฏิสัมพันธ์ระหว่างโลกจำลองรอบหุ่นยนต์และตัวหุ่นยนต์เอง มันพัฒนาเครื่องแสดงสถานะหุ่นยนต์ของเราและทริกเกอร์คอนโทรลเลอร์เพื่อคำนวณพฤติกรรมที่ต้องการ
  • models/supervisor_state_machine.py - คลาสนี้แสดงถึงความแตกต่าง รัฐ ซึ่งหุ่นยนต์สามารถเป็นได้ขึ้นอยู่กับการตีความของเซ็นเซอร์
  • ไฟล์ใน models/controllers ไดเร็กทอรี - คลาสเหล่านี้ใช้พฤติกรรมที่แตกต่างกันของหุ่นยนต์ตามสถานะที่ทราบของสภาพแวดล้อม โดยเฉพาะอย่างยิ่งตัวควบคุมเฉพาะจะถูกเลือกขึ้นอยู่กับเครื่องของรัฐ

เป้าหมาย

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

อย่างไรก็ตามเพื่อให้เรื่องยุ่งยากสภาพแวดล้อมของหุ่นยนต์อาจเต็มไปด้วยอุปสรรค หุ่นยนต์อาจไม่ชนกับสิ่งกีดขวางระหว่างทางไปสู่เป้าหมาย ดังนั้นหากหุ่นยนต์พบสิ่งกีดขวางก็จะต้องหาทางไปรอบ ๆ เพื่อให้หุ่นยนต์ไปสู่เป้าหมายได้

หุ่นยนต์ที่ตั้งโปรแกรมได้

หุ่นยนต์ทุกตัวมาพร้อมกับความสามารถและการควบคุมที่แตกต่างกัน มาทำความคุ้นเคยกับหุ่นยนต์จำลองที่ตั้งโปรแกรมได้ของเรา

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

อินพุตควบคุม: เซนเซอร์

มีหลายวิธีที่หุ่นยนต์อาจติดตั้งเพื่อตรวจสอบสภาพแวดล้อมของมัน ซึ่งอาจรวมถึงอะไรก็ได้ตั้งแต่พร็อกซิมิตีเซ็นเซอร์เซ็นเซอร์แสงกันชนกล้องและอื่น ๆ นอกจากนี้หุ่นยนต์อาจสื่อสารกับเซ็นเซอร์ภายนอกที่ให้ข้อมูลที่พวกเขาไม่สามารถสังเกตได้โดยตรง

หุ่นยนต์อ้างอิงของเราติดตั้ง เซ็นเซอร์อินฟราเรดเก้าตัว - รุ่นใหม่มีอินฟราเรดแปดตัวและอัลตราโซนิกพร็อกซิมิตีเซนเซอร์ 5 ตัวจัดเรียงเป็น 'กระโปรง' ในทุกทิศทาง มีเซ็นเซอร์ที่หันไปทางด้านหน้าของหุ่นยนต์มากกว่าด้านหลังเพราะโดยปกติหุ่นยนต์จะต้องรู้ว่าอะไรอยู่ข้างหน้ามากกว่าสิ่งที่อยู่ข้างหลัง

นอกจากพร็อกซิมิตีเซ็นเซอร์แล้วหุ่นยนต์ยังมี คู่ของสัญลักษณ์ล้อ ติดตามการเคลื่อนไหวของล้อ สิ่งเหล่านี้ช่วยให้คุณสามารถติดตามจำนวนการหมุนของแต่ละล้อโดยการหมุนไปข้างหน้าเต็มรูปแบบหนึ่งวงคือ 2,765 เห็บ หมุนไปในทิศทางตรงกันข้ามนับย้อนกลับลดจำนวนเห็บแทนที่จะเพิ่ม คุณไม่ต้องกังวลเกี่ยวกับตัวเลขเฉพาะในบทช่วยสอนนี้เนื่องจากซอฟต์แวร์ที่เราจะเขียนใช้ระยะทางที่เดินทางซึ่งแสดงเป็นเมตร ต่อมาฉันจะแสดงวิธีคำนวณจากเห็บด้วยฟังก์ชัน Python อย่างง่าย

เอาต์พุตควบคุม: ความคล่องตัว

หุ่นยนต์บางตัวขยับขา บางม้วนเหมือนลูกบอล บางคนก็เลื้อยเหมือนงู

หุ่นยนต์ของเราคือ ไดรฟ์ที่แตกต่างกัน หุ่นยนต์ซึ่งหมายความว่ามันหมุนไปรอบ ๆ ด้วยสองล้อ เมื่อล้อทั้งสองหมุนด้วยความเร็วเท่ากันหุ่นยนต์จะเคลื่อนที่เป็นเส้นตรง เมื่อล้อเคลื่อนที่ด้วยความเร็วที่ต่างกันหุ่นยนต์จะหมุน ดังนั้นการควบคุมการเคลื่อนไหวของหุ่นยนต์ตัวนี้จึงต้องควบคุมอัตราการหมุนของล้อทั้งสองนี้ให้เหมาะสม

ไฟ

ใน Sobot Rimulator การแยกระหว่างหุ่นยนต์“ คอมพิวเตอร์” และโลกทางกายภาพ (จำลอง) นั้นรวมอยู่ในไฟล์ robot_supervisor_interface.py ซึ่งกำหนด API ทั้งหมดสำหรับการโต้ตอบกับเซ็นเซอร์และมอเตอร์“ หุ่นยนต์จริง”:

  • read_proximity_sensors() ส่งคืนอาร์เรย์ของค่าเก้าค่าในรูปแบบดั้งเดิมของเซ็นเซอร์
  • read_wheel_encoders() ส่งคืนอาร์เรย์ของสองค่าที่ระบุจำนวนเห็บทั้งหมดตั้งแต่เริ่มต้น
  • set_wheel_drive_rates( v_l, v_r ) รับค่าสองค่า (เป็นเรเดียนต่อวินาที) และตั้งค่าความเร็วซ้ายและขวาของล้อเป็นสองค่านั้น

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

เครื่องจำลอง

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

ไฟล์ world.py เป็นคลาส Python ที่แสดงถึงโลกจำลองโดยมีหุ่นยนต์และสิ่งกีดขวางอยู่ภายใน ฟังก์ชันขั้นตอนในคลาสนี้ดูแลการพัฒนาโลกที่เรียบง่ายของเราโดย:

  • ใช้กฎฟิสิกส์กับการเคลื่อนไหวของหุ่นยนต์
  • พิจารณาการชนกับสิ่งกีดขวาง
  • ให้ค่าใหม่สำหรับเซ็นเซอร์หุ่นยนต์

ในท้ายที่สุดมันจะเรียกผู้บังคับบัญชาหุ่นยนต์ที่รับผิดชอบในการสั่งงานซอฟต์แวร์สมองของหุ่นยนต์

ฟังก์ชันขั้นตอนจะดำเนินการแบบวนซ้ำเพื่อให้ robot.step_motion() เคลื่อนย้ายหุ่นยนต์โดยใช้ความเร็วล้อที่คำนวณโดยหัวหน้างานในขั้นตอนการจำลองก่อนหน้านี้

# step the simulation through one time interval def step( self ): dt = self.dt # step all the robots for robot in self.robots: # step robot motion robot.step_motion( dt ) # apply physics interactions self.physics.apply_physics() # NOTE: The supervisors must run last to ensure they are observing the 'current' world # step all of the supervisors for supervisor in self.supervisors: supervisor.step( dt ) # increment world time self.world_time += dt

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

แบบจำลองที่เรียบง่าย

อันดับแรกหุ่นยนต์ของเราจะมีแบบจำลองที่เรียบง่ายมาก มันจะทำให้เกิดข้อสันนิษฐานมากมายเกี่ยวกับโลก สิ่งที่สำคัญบางอย่าง ได้แก่ :

  • ภูมิประเทศเป็นที่ราบและสม่ำเสมอ
  • อุปสรรคไม่เคยกลม
  • ล้อไม่เคยลื่น
  • ไม่มีอะไรที่จะผลักดันหุ่นยนต์ไปรอบ ๆ
  • เซ็นเซอร์ไม่เคยทำงานผิดพลาดหรืออ่านค่าผิดพลาด
  • ล้อมักจะหมุนเมื่อมีการบอกกล่าว

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

ห่วงควบคุม

ตอนนี้เราจะเข้าสู่แกนหลักของซอฟต์แวร์ควบคุมของเราและอธิบายพฤติกรรมที่เราต้องการตั้งโปรแกรมภายในหุ่นยนต์ คุณสามารถเพิ่มพฤติกรรมเพิ่มเติมในกรอบนี้ได้และคุณควรลองใช้แนวคิดของคุณเองหลังจากอ่านจบ! หุ่นยนต์ตามพฤติกรรม ซอฟต์แวร์ได้รับการเสนอมากว่า 20 ปีแล้วและยังคงเป็นเครื่องมือที่มีประสิทธิภาพสำหรับหุ่นยนต์เคลื่อนที่ ดังตัวอย่างในปี 2550 ชุดของพฤติกรรม ถูกนำมาใช้ในการแข่งขัน DARPA Urban Challenge ซึ่งเป็นการแข่งขันรถยนต์ขับเคลื่อนอัตโนมัติครั้งแรก!

หุ่นยนต์เป็นระบบไดนามิก สถานะของหุ่นยนต์การอ่านเซ็นเซอร์และผลกระทบของสัญญาณควบคุมอยู่ในฟลักซ์คงที่ การควบคุมวิธีการเล่นเหตุการณ์เกี่ยวข้องกับสามขั้นตอนต่อไปนี้:

  1. ใช้สัญญาณควบคุม
  2. วัดผล.
  3. สร้างสัญญาณควบคุมใหม่ที่คำนวณเพื่อให้เราเข้าใกล้เป้าหมายมากขึ้น

ขั้นตอนเหล่านี้จะเกิดขึ้นซ้ำแล้วซ้ำเล่าจนกว่าเราจะบรรลุเป้าหมาย ยิ่งเราทำสิ่งนี้ได้หลายครั้งต่อวินาทีการควบคุมที่ละเอียดกว่าเราก็จะมีเหนือระบบ หุ่นยนต์ Sobot Rimulator ทำซ้ำขั้นตอนเหล่านี้ 20 ครั้งต่อวินาที (20 Hz) แต่หุ่นยนต์จำนวนมากต้องทำสิ่งนี้หลายพันหรือล้านครั้งต่อวินาทีเพื่อให้มีการควบคุมที่เพียงพอ จำคำแนะนำก่อนหน้านี้ของเราเกี่ยวกับภาษาการเขียนโปรแกรมหุ่นยนต์ที่แตกต่างกันสำหรับระบบหุ่นยนต์และข้อกำหนดความเร็วที่แตกต่างกัน

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

เคล็ดลับที่ดี: การทำให้โมเดลง่ายขึ้น

ในการควบคุมหุ่นยนต์ที่เราต้องการตั้งโปรแกรมเราต้องส่งสัญญาณไปที่ล้อด้านซ้ายเพื่อบอกว่าต้องเลี้ยวเร็วแค่ไหนและสัญญาณแยกไปที่ล้อด้านขวาจะบอก มัน เลี้ยวเร็วแค่ไหน เรียกสัญญาณเหล่านี้ว่า v ล และ v ร . อย่างไรก็ตามการคิดอย่างต่อเนื่องในแง่ของ v ล และ v ร ยุ่งยากมาก แทนที่จะถามว่า“ เราต้องการให้ล้อซ้ายหมุนเร็วแค่ไหนและเราต้องการให้ล้อขวาหมุนเร็วแค่ไหน?” เป็นเรื่องธรรมดากว่าที่จะถามว่า“ เราต้องการให้หุ่นยนต์เคลื่อนที่ไปข้างหน้าเร็วแค่ไหนและเราต้องการให้หุ่นยนต์หมุนเร็วแค่ไหน?” เราจะเรียกพารามิเตอร์เหล่านี้ว่าความเร็ว v และความเร็วเชิงมุม (การหมุน) ω (อ่าน“ โอเมก้า”) ปรากฎว่าเราสามารถสร้างโมเดลทั้งหมดของเราได้ v และ ω แทน v ล และ v ร และเมื่อเราพิจารณาได้แล้วว่าเราต้องการให้หุ่นยนต์ที่ตั้งโปรแกรมไว้เคลื่อนที่อย่างไรการแปลงค่าทั้งสองนี้ทางคณิตศาสตร์เป็นค่า v ล และ v ร เราจำเป็นต้องควบคุมล้อหุ่นยนต์จริงๆ สิ่งนี้เรียกว่าไฟล์ รุ่น unicycle ของการควบคุม

ในการเขียนโปรแกรมหุ่นยนต์นั้น

นี่คือรหัส Python ที่ใช้การแปลงขั้นสุดท้ายใน supervisor.py โปรดทราบว่าถ้า ω คือ 0 ล้อทั้งสองจะหมุนด้วยความเร็วเท่ากัน:

# generate and send the correct commands to the robot def _send_robot_commands( self ): # ... v_l, v_r = self._uni_to_diff( v, omega ) self.robot.set_wheel_drive_rates( v_l, v_r ) def _uni_to_diff( self, v, omega ): # v = translational velocity (m/s) # omega = angular velocity (rad/s) R = self.robot_wheel_radius L = self.robot_wheel_base_length v_l = ( (2.0 * v) - (omega*L) ) / (2.0 * R) v_r = ( (2.0 * v) + (omega*L) ) / (2.0 * R) return v_l, v_r

สถานะการประมาณ: หุ่นยนต์รู้จักตัวเอง

เมื่อใช้เซ็นเซอร์หุ่นยนต์จะต้องพยายามประมาณสภาพแวดล้อมรวมทั้งสถานะของตัวเอง ค่าประมาณเหล่านี้จะไม่สมบูรณ์แบบ แต่ต้องดีพอสมควรเพราะหุ่นยนต์จะพิจารณาจากการตัดสินใจทั้งหมดเกี่ยวกับการประมาณเหล่านี้ การใช้พร็อกซิมิตีเซ็นเซอร์และสัญลักษณ์วงล้อเพียงอย่างเดียวต้องพยายามเดาสิ่งต่อไปนี้:

  • ทิศทางไปสู่อุปสรรค
  • ระยะห่างจากอุปสรรค
  • ตำแหน่งของหุ่นยนต์
  • ส่วนหัวของหุ่นยนต์

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

ดังนั้นหากค่านี้แสดงการอ่านที่สอดคล้องกับระยะทาง 0.1 เมตรเราจะรู้ว่ามีสิ่งกีดขวางอยู่ห่างออกไป 0.1 เมตรและไปทางซ้าย 75 องศา หากไม่มีสิ่งกีดขวางเซ็นเซอร์จะส่งกลับการอ่านค่าช่วงสูงสุด 0.2 เมตร ดังนั้นหากเราอ่าน 0.2 เมตรบนเซ็นเซอร์เจ็ดเราจะถือว่าไม่มีสิ่งกีดขวางในทิศทางนั้นจริงๆ

เนื่องจากวิธีการทำงานของเซ็นเซอร์อินฟราเรด (การวัดการสะท้อนของอินฟราเรด) ตัวเลขที่ส่งกลับจึงเป็นการแปลงที่ไม่ใช่เชิงเส้นของระยะทางจริงที่ตรวจพบ ดังนั้นฟังก์ชัน Python สำหรับกำหนดระยะทางที่ระบุจะต้องแปลงการอ่านเหล่านี้เป็นเมตร เสร็จสิ้นใน supervisor.py ดังต่อไปนี้:

# update the distances indicated by the proximity sensors def _update_proximity_sensor_distances( self ): self.proximity_sensor_distances = [ 0.02-( log(readval/3960.0) )/30.0 for readval in self.robot.read_proximity_sensors() ]

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

การกำหนดตำแหน่งและส่วนหัวของหุ่นยนต์ (เรียกรวมกันว่า ท่าทาง ในการเขียนโปรแกรมหุ่นยนต์) ค่อนข้างท้าทายกว่า หุ่นยนต์ของเราใช้ odometry เพื่อประเมินท่าทางของมัน นี่คือจุดที่สัญลักษณ์ล้อเข้ามาจากการวัดว่าล้อแต่ละล้อหมุนไปมากแค่ไหนนับตั้งแต่การวนซ้ำครั้งสุดท้ายของวงควบคุมทำให้สามารถประมาณได้อย่างดีว่าท่าทางของหุ่นยนต์เปลี่ยนแปลงไปอย่างไร แต่ เฉพาะในกรณีที่การเปลี่ยนแปลงมีขนาดเล็ก .

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

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

ด้านล่างนี้คือฟังก์ชัน odometry แบบเต็มใน supervisor.py ที่ปรับปรุงหุ่นยนต์ก่อให้เกิดการประมาณค่า โปรดทราบว่าท่าทางของหุ่นยนต์ประกอบด้วยพิกัด x และ y และส่วนหัว theta ซึ่งวัดเป็นเรเดียนจากแกน X ที่เป็นบวก บวก x อยู่ทางทิศตะวันออกและบวก y อยู่ทางทิศเหนือ ดังนั้นส่วนหัวของ 0 แสดงว่าหุ่นยนต์หันหน้าไปทางทิศตะวันออกโดยตรง หุ่นยนต์จะถือว่าท่าทางเริ่มต้นของมันคือ (0, 0), 0

# update the estimated position of the robot using it's wheel encoder readings def _update_odometry( self ): R = self.robot_wheel_radius N = float( self.wheel_encoder_ticks_per_revolution ) # read the wheel encoder values ticks_left, ticks_right = self.robot.read_wheel_encoders() # get the difference in ticks since the last iteration d_ticks_left = ticks_left - self.prev_ticks_left d_ticks_right = ticks_right - self.prev_ticks_right # estimate the wheel movements d_left_wheel = 2*pi*R*( d_ticks_left / N ) d_right_wheel = 2*pi*R*( d_ticks_right / N ) d_center = 0.5 * ( d_left_wheel + d_right_wheel ) # calculate the new pose prev_x, prev_y, prev_theta = self.estimated_pose.scalar_unpack() new_x = prev_x + ( d_center * cos( prev_theta ) ) new_y = prev_y + ( d_center * sin( prev_theta ) ) new_theta = prev_theta + ( ( d_right_wheel - d_left_wheel ) / self.robot_wheel_base_length ) # update the pose estimate with the new values self.estimated_pose.scalar_update( new_x, new_y, new_theta ) # save the current tick count for the next iteration self.prev_ticks_left = ticks_left self.prev_ticks_right = ticks_right

เมื่อหุ่นยนต์ของเราสามารถสร้างค่าประมาณของโลกแห่งความเป็นจริงได้ดีแล้วเรามาใช้ข้อมูลนี้เพื่อบรรลุเป้าหมายของเรา

ที่เกี่ยวข้อง: วิดีโอสอนฟิสิกส์เกม - การตรวจจับการชนกันสำหรับวัตถุที่เป็นของแข็ง

วิธีการเขียนโปรแกรม Python Robot: Go-to-Goal Behavior

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

สิ่งนี้จะกลายเป็นงานง่ายๆและสามารถตั้งโปรแกรมใน Python ได้อย่างง่ายดาย หากเราก้าวไปข้างหน้าในขณะที่เผชิญหน้ากับเป้าหมายเราจะไปที่นั่น ต้องขอบคุณ odometry ของเราทำให้เรารู้ว่าพิกัดและส่วนหัวปัจจุบันของเราคืออะไร เรายังรู้ว่าพิกัดของเป้าหมายคืออะไรเนื่องจากถูกตั้งโปรแกรมไว้ล่วงหน้า ดังนั้นการใช้พีชคณิตเชิงเส้นเพียงเล็กน้อยเราสามารถกำหนดเวกเตอร์จากตำแหน่งของเราไปยังเป้าหมายได้ดังใน go_to_goal_controller.py:

# return a go-to-goal heading vector in the robot's reference frame def calculate_gtg_heading_vector( self ): # get the inverse of the robot's pose robot_inv_pos, robot_inv_theta = self.supervisor.estimated_pose().inverse().vector_unpack() # calculate the goal vector in the robot's reference frame goal = self.supervisor.goal() goal = linalg.rotate_and_translate_vector( goal, robot_inv_theta, robot_inv_pos ) return goal

สังเกตว่าเรากำลังนำเวกเตอร์ไปสู่เป้าหมาย ในกรอบอ้างอิงของหุ่นยนต์ และไม่อยู่ในพิกัดโลก หากเป้าหมายอยู่บนแกน X ในกรอบอ้างอิงของหุ่นยนต์นั่นหมายความว่าเป้าหมายนั้นอยู่ตรงหน้าหุ่นยนต์ ดังนั้นมุมของเวกเตอร์นี้จากแกน X คือความแตกต่างระหว่างหัวเรื่องและหัวเรื่องที่เราต้องการให้อยู่ กล่าวอีกนัยหนึ่งก็คือไฟล์ ข้อผิดพลาด ระหว่างสถานะปัจจุบันของเรากับสิ่งที่เราต้องการให้เป็นสถานะปัจจุบัน เราจึงต้องการที่จะ ปรับอัตราการหมุนของเรา ω เพื่อให้มุมระหว่างหัวเรื่องกับเป้าหมายเปลี่ยนไปเป็น 0 เราต้องการลดข้อผิดพลาด:

# calculate the error terms theta_d = atan2( self.gtg_heading_vector[1], self.gtg_heading_vector[0] ) # calculate angular velocity omega = self.kP * theta_d

self.kP ในส่วนย่อยด้านบนของการใช้งาน Python ของคอนโทรลเลอร์คือการเพิ่มการควบคุม มันเป็นค่าสัมประสิทธิ์ที่กำหนดความเร็วในการส่งของเรา สัดส่วน ว่าห่างจากเป้าหมายที่เราเผชิญอยู่แค่ไหน หากข้อผิดพลาดในหัวเรื่องของเราคือ 0 อัตราการหมุนก็จะเป็น 0 ในฟังก์ชัน Python จริงภายในไฟล์ go_to_goal_controller.py คุณจะเห็นกำไรที่คล้ายกันมากขึ้นเนื่องจากเราใช้ไฟล์ ตัวควบคุม PID แทนค่าสัมประสิทธิ์สัดส่วนอย่างง่าย

ตอนนี้เรามีความเร็วเชิงมุมแล้ว ω เราจะกำหนดความเร็วไปข้างหน้าได้อย่างไร v เหรอ? หลักการทั่วไปที่ดีคือสิ่งที่คุณอาจจะรู้โดยสัญชาตญาณ: ถ้าเราไม่ได้เลี้ยวเราสามารถไปข้างหน้าด้วยความเร็วเต็มที่จากนั้นยิ่งเราเลี้ยวเร็วเท่าไหร่เราก็จะยิ่งช้าลงเท่านั้น โดยทั่วไปแล้วสิ่งนี้จะช่วยให้ระบบของเรามีเสถียรภาพและดำเนินการภายในขอบเขตของโมเดลของเรา ด้วยประการฉะนี้ v เป็นฟังก์ชันของ ω . ใน go_to_goal_controller.py สมการคือ:

กวดวิชาการทดสอบหน่วยสตูดิโอภาพ
# calculate translational velocity # velocity is v_max when omega is 0, # drops rapidly to zero as |omega| rises v = self.supervisor.v_max() / ( abs( omega ) + 1 )**0.5

ข้อเสนอแนะในการอธิบายอย่างละเอียดเกี่ยวกับสูตรนี้คือการพิจารณาว่าโดยปกติเราจะชะลอตัวลงเมื่ออยู่ใกล้เป้าหมายเพื่อที่จะไปให้ถึงด้วยความเร็วเป็นศูนย์ สูตรนี้จะเปลี่ยนไปอย่างไร? จะต้องมีการแทนที่ v_max() ด้วย โดยมีบางอย่างเป็นสัดส่วนกับระยะทาง ตกลงเราเกือบจะเสร็จสิ้นการวนรอบการควบคุมเดียว สิ่งเดียวที่ต้องทำคือเปลี่ยนพารามิเตอร์ unicycle-model ทั้งสองนี้เป็นความเร็วของล้อที่แตกต่างกันและส่งสัญญาณไปยังล้อ ต่อไปนี้คือตัวอย่างวิถีของหุ่นยนต์ภายใต้ตัวควบคุมการไปสู่เป้าหมายโดยไม่มีสิ่งกีดขวาง:

นี่คือตัวอย่างของหุ่นยนต์ที่ตั้งโปรแกรมไว้

อย่างที่เราเห็นเวกเตอร์ไปยังเป้าหมายเป็นข้อมูลอ้างอิงที่มีประสิทธิภาพสำหรับเราในการคำนวณการควบคุมของเรา เป็นการแสดงถึง“ ที่ที่เราต้องการไป” ดังที่เราจะเห็นความแตกต่างที่สำคัญเพียงอย่างเดียวระหว่างการไปสู่เป้าหมายและพฤติกรรมอื่น ๆ คือบางครั้งการไปสู่เป้าหมายเป็นความคิดที่ไม่ดีดังนั้นเราจึงต้องคำนวณเวกเตอร์อ้างอิงที่แตกต่างกัน

วิธีการเขียนโปรแกรม Python Robot: พฤติกรรมหลีกเลี่ยงอุปสรรค

การไปสู่เป้าหมายเมื่อมีสิ่งกีดขวางในทิศทางนั้นเป็นประเด็น แทนที่จะวิ่งเข้าหาสิ่งต่างๆที่ขวางทางเรามาลองตั้งโปรแกรมกฎหมายควบคุมที่ทำให้หุ่นยนต์หลีกเลี่ยงสิ่งเหล่านี้

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

ดังนั้นเมื่อไม่มีสิ่งกีดขวางอยู่ข้างหน้าเราจึงต้องการให้เวกเตอร์อ้างอิงของเราชี้ไปข้างหน้า แล้ว ω จะเป็นศูนย์และ v จะเป็นความเร็วสูงสุด อย่างไรก็ตามทันทีที่เราตรวจพบสิ่งกีดขวางด้วยพร็อกซิมิตีเซ็นเซอร์เราต้องการให้เวกเตอร์อ้างอิงชี้ไปในทิศทางใดก็ตามที่อยู่ห่างจากสิ่งกีดขวาง ซึ่งจะทำให้เกิด ω ยิงขึ้นเพื่อทำให้เราห่างจากอุปสรรคและทำให้เกิด v เพื่อให้แน่ใจว่าเราจะไม่บังเอิญเจออุปสรรคในกระบวนการ

วิธีที่เป็นระเบียบในการสร้างเวกเตอร์อ้างอิงที่เราต้องการคือเปลี่ยนการอ่านค่าความใกล้เคียงเก้าค่าให้เป็นเวกเตอร์และหาผลรวมถ่วงน้ำหนัก เมื่อตรวจไม่พบสิ่งกีดขวางเวกเตอร์จะรวมกันแบบสมมาตรทำให้ได้เวกเตอร์อ้างอิงที่ชี้ไปข้างหน้าตามที่ต้องการ แต่ถ้าเซ็นเซอร์เปิดอยู่บอกว่าด้านขวาหยิบสิ่งกีดขวางมันจะทำให้เวกเตอร์ที่มีขนาดเล็กลงในผลรวมและผลลัพธ์จะเป็นเวกเตอร์อ้างอิงที่เลื่อนไปทางซ้าย

สำหรับหุ่นยนต์ทั่วไปที่มีตำแหน่งเซนเซอร์ต่างกันสามารถใช้แนวคิดเดียวกันนี้ได้ แต่อาจต้องมีการเปลี่ยนแปลงน้ำหนักและ / หรือการดูแลเพิ่มเติมเมื่อเซ็นเซอร์มีความสมมาตรทั้งด้านหน้าและด้านหลังของหุ่นยนต์เนื่องจากผลรวมถ่วงน้ำหนักอาจกลายเป็นศูนย์ได้ .

เมื่อตั้งโปรแกรมอย่างถูกต้องหุ่นยนต์จะสามารถหลีกเลี่ยงอุปสรรคที่ซับซ้อนเหล่านี้ได้

นี่คือรหัสที่ทำใน avoid_obstacles_controller.py:

# sensor gains (weights) self.sensor_gains = [ 1.0+( (0.4*abs(p.theta)) / pi ) for p in supervisor.proximity_sensor_placements() ] # ... # return an obstacle avoidance vector in the robot's reference frame # also returns vectors to detected obstacles in the robot's reference frame def calculate_ao_heading_vector( self ): # initialize vector obstacle_vectors = [ [ 0.0, 0.0 ] ] * len( self.proximity_sensor_placements ) ao_heading_vector = [ 0.0, 0.0 ] # get the distances indicated by the robot's sensor readings sensor_distances = self.supervisor.proximity_sensor_distances() # calculate the position of detected obstacles and find an avoidance vector robot_pos, robot_theta = self.supervisor.estimated_pose().vector_unpack() for i in range( len( sensor_distances ) ): # calculate the position of the obstacle sensor_pos, sensor_theta = self.proximity_sensor_placements[i].vector_unpack() vector = [ sensor_distances[i], 0.0 ] vector = linalg.rotate_and_translate_vector( vector, sensor_theta, sensor_pos ) obstacle_vectors[i] = vector # store the obstacle vectors in the robot's reference frame # accumulate the heading vector within the robot's reference frame ao_heading_vector = linalg.add( ao_heading_vector, linalg.scale( vector, self.sensor_gains[i] ) ) return ao_heading_vector, obstacle_vectors

ใช้ผลลัพธ์ ao_heading_vector ตามที่เราอ้างอิงสำหรับหุ่นยนต์เพื่อพยายามจับคู่นี่คือผลลัพธ์ของการเรียกใช้ซอฟต์แวร์หุ่นยนต์ในการจำลองโดยใช้เพียงตัวควบคุมหลีกเลี่ยงสิ่งกีดขวางโดยไม่สนใจจุดเป้าหมายโดยสิ้นเชิง หุ่นยนต์จะกระเด้งไปรอบ ๆ อย่างไร้จุดหมาย แต่มันไม่เคยชนกับสิ่งกีดขวางและยังสามารถนำทางในพื้นที่แคบ ๆ ได้:

หุ่นยนต์ตัวนี้สามารถหลีกเลี่ยงสิ่งกีดขวางภายในโปรแกรมจำลองหุ่นยนต์ Python ได้สำเร็จ

วิธีการเขียนโปรแกรม Python Robot: Hybrid Automata (Behavior State Machine)

จนถึงตอนนี้เราได้อธิบายพฤติกรรม 2 อย่างคือการไปสู่เป้าหมายและการหลีกเลี่ยงอุปสรรค - แยกกัน ทั้งสองทำหน้าที่ได้อย่างน่าชื่นชม แต่เพื่อให้บรรลุเป้าหมายในสภาพแวดล้อมที่เต็มไปด้วยอุปสรรคเราจำเป็นต้องรวมเข้าด้วยกัน

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

เฟรมเวิร์กหุ่นยนต์ Python ของเราใช้เครื่องสถานะในไฟล์ supervisor_state_machine.py

ด้วยพฤติกรรมที่มีประโยชน์สองประการของเราตรรกะง่ายๆแนะนำตัวเอง: เมื่อตรวจไม่พบสิ่งกีดขวางให้ใช้พฤติกรรมมุ่งสู่เป้าหมาย เมื่อตรวจพบสิ่งกีดขวางให้เปลี่ยนไปใช้พฤติกรรมหลีกเลี่ยงสิ่งกีดขวางจนกว่าจะตรวจไม่พบสิ่งกีดขวางอีกต่อไป

อย่างไรก็ตามเมื่อปรากฎว่าตรรกะนี้จะสร้างปัญหามากมาย สิ่งที่ระบบนี้มักจะทำเมื่อพบสิ่งกีดขวางคือการหันหน้าหนีจากนั้นทันทีที่ถอยห่างออกไปให้หันกลับมาและวิ่งเข้าไปอีกครั้ง ผลลัพธ์ที่ได้คือการวนซ้ำอย่างไม่สิ้นสุดของการสลับอย่างรวดเร็วซึ่งทำให้หุ่นยนต์ไร้ประโยชน์ ในกรณีที่เลวร้ายที่สุดหุ่นยนต์อาจสลับพฤติกรรมกับ การทำซ้ำทุกครั้ง ของลูปควบคุม - สถานะที่เรียกว่า a Zeno เงื่อนไข .

มีวิธีแก้ไขปัญหานี้หลายวิธีและผู้อ่านที่ต้องการความรู้เชิงลึกควรตรวจสอบตัวอย่างเช่น สถาปัตยกรรมซอฟต์แวร์ DAMN .

สิ่งที่เราต้องการสำหรับหุ่นยนต์จำลองอย่างง่ายของเราคือวิธีแก้ปัญหาที่ง่ายกว่านั่นคืออีกหนึ่งพฤติกรรมที่เชี่ยวชาญกับงานที่ต้องทำ รอบ ๆ อุปสรรคและไปถึงอีกด้านหนึ่ง

วิธีการเขียนโปรแกรม Python Robot: พฤติกรรมตามกำแพง

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

ด้วยข้อมูลที่ จำกัด ของเราเราไม่สามารถบอกได้แน่นอนว่าจะเร็วกว่าที่จะไปรอบ ๆ สิ่งกีดขวางทางซ้ายหรือทางขวา เพื่อตัดสินใจเลือกทิศทางที่จะทำให้เราเข้าใกล้เป้าหมายทันที เราจำเป็นต้องทราบเวกเตอร์อ้างอิงของพฤติกรรมไปสู่เป้าหมายและพฤติกรรมหลีกเลี่ยงสิ่งกีดขวางรวมทั้งเวกเตอร์อ้างอิงตามผนังที่เป็นไปได้ทั้งสองแบบด้วย นี่คือภาพประกอบวิธีการตัดสินใจขั้นสุดท้าย (ในกรณีนี้หุ่นยนต์จะเลือกไปทางซ้าย):

การใช้พฤติกรรมบางประเภทหุ่นยนต์ที่ตั้งโปรแกรมไว้จะหลีกเลี่ยงอุปสรรคและดำเนินการต่อไป

การกำหนดเวกเตอร์อ้างอิงตามผนังจะมีส่วนเกี่ยวข้องมากกว่าเวกเตอร์อ้างอิงแบบหลีกเลี่ยงอุปสรรคหรือไปสู่เป้าหมาย ดูรหัส Python ใน follow_wall_controller.py เพื่อดูวิธีการทำ

การออกแบบการควบคุมขั้นสุดท้าย

การออกแบบการควบคุมขั้นสุดท้ายใช้พฤติกรรมตามกำแพงสำหรับการเผชิญหน้ากับอุปสรรคเกือบทั้งหมด อย่างไรก็ตามหากหุ่นยนต์พบว่าตัวเองอยู่ในจุดที่คับขันและอยู่ใกล้กับการชนกันอย่างเป็นอันตรายหุ่นยนต์จะเปลี่ยนเป็นโหมดหลบหลีกสิ่งกีดขวางจนกว่าจะอยู่ในระยะที่ปลอดภัยกว่าแล้วจึงกลับไปที่กำแพงตามมา เมื่อเจรจาอุปสรรคสำเร็จแล้วหุ่นยนต์จะเปลี่ยนไปสู่เป้าหมาย นี่คือแผนภาพสถานะสุดท้ายซึ่งตั้งโปรแกรมไว้ใน supervisor_state_machine.py:

แผนภาพนี้แสดงให้เห็นการสลับระหว่างพฤติกรรมการเขียนโปรแกรมหุ่นยนต์เพื่อให้บรรลุเป้าหมายและหลีกเลี่ยงอุปสรรค

นี่คือหุ่นยนต์ที่ประสบความสำเร็จในการนำทางสภาพแวดล้อมที่แออัดโดยใช้รูปแบบการควบคุมนี้:

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

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

Tweak, Tweak, Tweak: Trial and Error

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

หุ่นยนต์มักเกี่ยวข้องกับการลองผิดลองถูกแบบเก่า ๆ

ฉันขอแนะนำให้คุณเล่นกับตัวแปรควบคุมใน Sobot Rimulator และสังเกตและพยายามตีความผลลัพธ์ การเปลี่ยนแปลงต่อไปนี้ล้วนมีผลกระทบอย่างมากต่อพฤติกรรมของหุ่นยนต์จำลอง:

  • ข้อผิดพลาดที่เพิ่มขึ้น kP ในแต่ละคอนโทรลเลอร์
  • เซ็นเซอร์ได้รับการใช้งานโดยตัวควบคุมหลีกเลี่ยงอุปสรรค
  • การคำนวณของ v เป็นหน้าที่ของ ω ในแต่ละคอนโทรลเลอร์
  • ระยะห่างของสิ่งกีดขวางที่ใช้โดยตัวควบคุมตามกำแพง
  • เงื่อนไขการสลับที่ใช้โดย supervisor_state_machine.py
  • สวยมากอย่างอื่น

เมื่อหุ่นยนต์ที่ตั้งโปรแกรมได้ล้มเหลว

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

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

ตัวอย่างสิ่งที่จะเกิดขึ้น

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

ในบทช่วยสอนนี้เราได้เรียนรู้วิธีการพัฒนาซอฟต์แวร์ควบคุมปฏิกิริยาสำหรับหุ่นยนต์โดยใช้ภาษาโปรแกรมระดับสูง Python แต่ยังมีแนวคิดขั้นสูงอีกมากมายที่สามารถเรียนรู้และทดสอบได้อย่างรวดเร็วด้วยเฟรมเวิร์กหุ่นยนต์ Python ซึ่งคล้ายกับที่เราสร้างต้นแบบไว้ที่นี่ ฉันหวังว่าคุณจะพิจารณา มีส่วนร่วม ในการสร้างสิ่งต่างๆที่จะเกิดขึ้น!


การรับทราบ: ฉันอยากจะขอบคุณ ดร. Magnus Egerstedt และ Jean-Pierre de la Croix ของ Georgia Institute of Technology ที่สอนเรื่องนี้ให้ฉันและสำหรับความกระตือรือร้นในการทำงานของฉันเกี่ยวกับ Sobot Rimulator

ที่เกี่ยวข้อง: บทช่วยสอน OpenCV: การตรวจจับวัตถุแบบเรียลไทม์โดยใช้ MSER ใน iOS

ทำความเข้าใจพื้นฐาน

หุ่นยนต์คืออะไร?

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

หุ่นยนต์ใช้ทำอะไร?

หุ่นยนต์ถูกใช้ในโรงงานและฟาร์มเพื่อทำงานหนักหรือทำซ้ำ ๆ ใช้ในการสำรวจดาวเคราะห์และมหาสมุทรทำความสะอาดบ้านและช่วยเหลือผู้สูงอายุ นักวิจัยและวิศวกรพยายามใช้หุ่นยนต์ในสถานการณ์ภัยพิบัติการวิเคราะห์ทางการแพทย์และการผ่าตัด รถขับเองก็หุ่นยนต์เหมือนกัน!

คุณสร้างหุ่นยนต์ได้อย่างไร?

การสร้างหุ่นยนต์ต้องใช้ขั้นตอนหลายขั้นตอน: รูปแบบเชิงกลของชิ้นส่วนการออกแบบเซ็นเซอร์และไดรเวอร์และการพัฒนาซอฟต์แวร์ของหุ่นยนต์ โดยปกติแล้วเนื้อดิบจะถูกสร้างขึ้นในโรงงานและซอฟต์แวร์ได้รับการพัฒนาและทดสอบกับต้นแบบการทำงานชุดแรก

คุณตั้งโปรแกรมหุ่นยนต์ได้อย่างไร?

มีสามขั้นตอนที่เกี่ยวข้อง ขั้นแรกคุณจะได้รับมอเตอร์และเซ็นเซอร์ที่ทำงานโดยใช้ไดรเวอร์นอกชั้นวาง จากนั้นคุณจะพัฒนาโครงสร้างพื้นฐานเพื่อให้คุณสามารถเคลื่อนย้ายหุ่นยนต์และอ่านเซ็นเซอร์ได้ สุดท้ายใช้สิ่งนั้นเพื่อพัฒนากิจวัตรซอฟต์แวร์ที่ซับซ้อนและชาญฉลาดเพื่อสร้างพฤติกรรมที่คุณต้องการ

ภาษาโปรแกรมที่ดีที่สุดสำหรับหุ่นยนต์คืออะไร?

ภาษาโปรแกรมหลักสองภาษาเป็นภาษาที่ดีที่สุดเมื่อใช้ในวิทยาการหุ่นยนต์: C ++ และ Python ซึ่งมักใช้ร่วมกันเนื่องจากแต่ละภาษามีข้อดีและข้อเสีย C ++ ถูกใช้ในลูปควบคุมการประมวลผลภาพและเพื่อเชื่อมต่อกับฮาร์ดแวร์ระดับต่ำ Python ใช้เพื่อจัดการกับพฤติกรรมระดับสูงและพัฒนาการทดสอบหรือการพิสูจน์แนวคิดอย่างรวดเร็ว

คุณจะเขียนโปรแกรมหุ่นยนต์โดยใช้ Java ได้อย่างไร?

สมมติว่าคุณสามารถรัน Java Virtual Machine บนหุ่นยนต์ของคุณคุณสามารถเชื่อมต่อโค้ด Java ของคุณกับมอเตอร์และไดรเวอร์เซ็นเซอร์โดยใช้ซ็อกเก็ตหรือ RPC การเขียนไดรเวอร์อุปกรณ์โดยตรงใน Java อาจยากกว่าภาษาอื่นเช่น C ++ ดังนั้นจึงควรมุ่งเน้นไปที่การพัฒนาพฤติกรรมระดับสูง!

วิศวกรรมหุ่นยนต์คืออะไร?

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

อะไรคือความแตกต่างระหว่าง Robotic Process Automation (RPA) และ Robotics Program?

ทั้งสองฟิลด์พัฒนาซอฟต์แวร์เพื่อช่วยเหลือหรือแทนที่มนุษย์ แต่ RPA กำหนดเป้าหมายงานที่มนุษย์ทำอยู่หน้าคอมพิวเตอร์เช่นส่งอีเมลยื่นใบเสร็จหรือเรียกดูเว็บไซต์ หุ่นยนต์ทำงานแทนในโลกแห่งความเป็นจริงเช่นการทำความสะอาดการขับขี่การสร้างหรือการผลิต

ใครเป็นผู้คิดค้นหุ่นยนต์ตัวแรกของโลก?

หุ่นยนต์เคลื่อนที่ตัวแรกถูกสร้างขึ้นในปี พ.ศ. 2509 ที่สถาบันวิจัยสแตนฟอร์ดโดยการนำทีมโดย Charles Rosen และ Nils Nilsson ด้วยการใช้ CPU เพียง 24 บิตและ RAM 196 KB ทำให้สามารถเคลื่อนที่ไปรอบ ๆ สำนักงานได้โดยอัตโนมัติในขณะที่หลีกเลี่ยงสิ่งกีดขวาง เนื่องจากมันสั่นขณะเคลื่อนไหวผู้สร้างจึงเรียกมันว่า Shakey

Glass-Steagall Act: การยกเลิกทำให้เกิดวิกฤตการเงินหรือไม่?

นักลงทุนและเงินทุน

Glass-Steagall Act: การยกเลิกทำให้เกิดวิกฤตการเงินหรือไม่?
Mini Tutorial - ใช้ประโยชน์จากคุณสมบัติของ Figma สำหรับกระบวนการออกแบบทั้งหมด

Mini Tutorial - ใช้ประโยชน์จากคุณสมบัติของ Figma สำหรับกระบวนการออกแบบทั้งหมด

การออกแบบ Ui

โพสต์ยอดนิยม
ความจริงเสมือนในอุตสาหกรรมยานยนต์
ความจริงเสมือนในอุตสาหกรรมยานยนต์
วิธีใช้ Bootstrap และสร้าง. NET Projects
วิธีใช้ Bootstrap และสร้าง. NET Projects
วิธีทำความเข้าใจและประเมินการลงทุนในกองทุนอสังหาริมทรัพย์ส่วนบุคคล
วิธีทำความเข้าใจและประเมินการลงทุนในกองทุนอสังหาริมทรัพย์ส่วนบุคคล
4 ไปวิจารณ์ภาษา
4 ไปวิจารณ์ภาษา
ข้อมูลเบื้องต้นเกี่ยวกับ Magento: การนำทางในระบบนิเวศอีคอมเมิร์ซยอดนิยม
ข้อมูลเบื้องต้นเกี่ยวกับ Magento: การนำทางในระบบนิเวศอีคอมเมิร์ซยอดนิยม
 
วีซ่า H-1B: การเดินทางของนักพัฒนา iOS จากฮอนดูรัสไปยัง Silicon Valley
วีซ่า H-1B: การเดินทางของนักพัฒนา iOS จากฮอนดูรัสไปยัง Silicon Valley
ข้อผิดพลาดทั่วไปในการสื่อสารกับลูกค้า: จะไม่ทำให้ลูกค้าของคุณผิดหวังได้อย่างไร
ข้อผิดพลาดทั่วไปในการสื่อสารกับลูกค้า: จะไม่ทำให้ลูกค้าของคุณผิดหวังได้อย่างไร
การออกแบบที่คาดหวัง: วิธีสร้างประสบการณ์ผู้ใช้ที่มีมนต์ขลัง
การออกแบบที่คาดหวัง: วิธีสร้างประสบการณ์ผู้ใช้ที่มีมนต์ขลัง
กราฟิก 3 มิติ: บทช่วยสอน WebGL
กราฟิก 3 มิติ: บทช่วยสอน WebGL
การออกแบบ VUI - Voice User Interface
การออกแบบ VUI - Voice User Interface
โพสต์ยอดนิยม
  • วิธีการสร้างแบบจำลองทางการเงิน
  • อุตสาหกรรมเครื่องสำอางมีมูลค่าเท่าไหร่
  • อะไรเป็นประกายดีสำหรับ
  • การให้คะแนน imdb หมายถึงอะไร
  • องค์ประกอบการออกแบบคืออะไร
หมวดหมู่
  • การเพิ่มขึ้นของระยะไกล
  • ผู้คนและทีมงาน
  • การวางแผนและการพยากรณ์
  • การออกแบบ Ux
  • © 2022 | สงวนลิขสิทธิ์

    portaldacalheta.pt