תזמון של אותות בקרה למספר מנועי סרבו במקביל הוא עסק קצת יותר מסובך ממה שנראה במבט ראשון. איך פתרו את זה (כמעט לגמרי) בארדואינו, ומה צריך לקחת בחשבון אם רוצים ליצור פתרון אוניברסלי באמת?
כשהתחלתי לחפש פרויקט לא מורכב מדי לצורך אימון ב-PSoC 4, אבל כזה שלא מופיע כבר מיליון פעמים ברשת, הגעתי די מהר לעניין של שליטה במספר מנועי סרבו.
זווית הסיבוב של מנוע סרבו "אנלוגי" סטנדרטי נקבעת, כידוע, לפי אורך הפולס שהוא מקבל בקו הבקרה שלו – בדרך כלל בטווח שבין 0.5 ל-2.5 אלפיות השניה, בתוך מחזור של פלוס מינוס 20 אלפיות השניה. יצירה של פולס אחד כזה באמצעות טיימר היא עסק פשוט למדי, אבל מה עושים כשצריך לשלוט ביותר ממנוע יחיד? טיימר הוא משאב נדיר מאד, גם במיקרו-בקרים מודרניים ומשוכללים, כך שאין מצב להקדיש טיימר נפרד לכל מנוע. פתרון ראוי לשמו צריך להסתפק בטיימר אחד בלבד.
שיקול הרזולוציה
נניח שפולס של 0.5ms מסובב את הסרבו לקצה אחד של הטווח שלו, ופולס של 2.5ms מסובב אותו עד הקצה השני. לכמה תחנות ביניים אפשר, או צריך, לחלק את הטווח הזה? אפשר, למשל, לקבוע מדרגות של כ-11 מיליוניות השניה כל אחת, מה שייתן רזולוציה "טבעית" של מעלה אחת (בטווח שבין 0 ל-180). אם המנוע באיכות נמוכה, ייתכן שחוסר-הדיוק המובנה שלו יתקשה להתמודד אפילו עם זה. ביישומים מסוימים לא נזדקק בכלל לדיוק של מעלה יחידה. לעומת זאת, מנוע איכותי יוכל לתת בעת הצורך תוצאות מדויקות מאד, אם רק נחלק את הפולס ליותר מדרגות. ממידע לא-פורמלי שמצאתי ברשת, נראה שמנועי סרבו אנלוגיים מסוגלים להגיע עד רזולוציה של 4 מיליוניות השניה (כ-500 צעדים על פני טווח התנועה).
פתרון נאיבי ושגוי
הפתרון הכי פשטני, אולי, הוא קודם כל ליצור מערך של ערכי רוחב פולס, כאשר גודל המערך הוא כמספר המנועים המופעלים. לאחר מכן נגדיר טיימר שיעורר פסיקה כל 4 מיליוניות השניה. בתחילת כל מחזור (שאורכו 20ms) נתחיל פולס לכל המנועים גם יחד, ובכל פסיקה נעבור על כל המערך כדי לבדוק אם הגיע הזמן להפסיק את הפולס של מנוע מסוים.
בפתרון הזה יש בעיה חמורה מאד: בדיקה של מערך מספרים כל ארבע מיליוניות השניה היא תהליך שגוזל המון משאבים, וברור שלמשימה כזו אין באמת צורך בכל כך הרבה בדיקות. במיקרו-בקר איטי אנחנו עלולים בקלות להגיע גם למצב שבו בדיקה אחת של המערך נמשכת יותר מארבע מיליוניות השניה, ואז המערכת תקרוס לגמרי.
הדרך של ארדואינו
ספריית הסרבו של ארדואינו מאפשרת – על פי התיעוד הרשמי – שליטה נוחה בעד 12 מנועים (בלוחות מבוססי ATmega328), ובפונקציות המתקדמות שלה (כגון writeMicroseconds) אפשר לקבוע תזמונים ברזולוציה של 4 מיליוניות השניה. תוך כדי קריאה של הקוד לצורך כתיבת הערכים בוויקי, הבנתי את השיטה הפשוטה והחכמה שהמתכנתים בחרו: במקום להתחיל את כל הפולסים באותו זמן, הם שירשרו אותם.
בתחילת המחזור, התוכנה פונה למנוע הראשון, מתחילה את הפולס שלו ומגדירה שהטיימר יעורר פסיקה בסוף משך הפולס הרצוי (שמתועד במערך). בכל פסיקה כזו הפולס למנוע הפעיל מופסק, והתוכנה עוברת לטפל באותו אופן במנוע הבא בתור. כך כל מנוע מקבל בדיוק את משך הפולס הדרוש לו. כשמגיעים לסוף רשימת המנועים מתבצעת השהיה נוספת, שמשלימה את זמן המחזור למינימום הדרוש של 20ms.
זהו פתרון אלגנטי מהרבה בחינות. הפעילות של הפסיקות היא מינימלית ומרווחת, כמות הנתונים שצריך לשמור ולעבד היא זעירה, אין בעיה עם שינויי תזמונים תוך כדי פעולה (במקרה הכי גרוע יישלח עוד פולס אחד ברוחב הקודם), וגם הקוד פשוט וברור. החיסרון היחיד הוא לא בשיטה עצמה, למעשה, אלא בחמדנות. כדי להדגים זאת, הגדרתי 12 אובייקטים של מנועי סרבו בתוכנה וקבעתי לכולם משך פולס מרבי. הנה הפולסים של המנוע הראשון ושל האחרון, בלוג'יק אנלייזר:
כל פולס בפני עצמו הוא בסדר, אבל משך המחזור הכולל התארך לכמעט 30 אלפיות השניה (12 מנועים כפול 2.5ms פולס מרבי). מנועי סרבו בדרך כלל לא מתעקשים על מחזור מדויק של 20ms, אבל 30 זה באמת מוגזם, ובמקרה הקיצוני הנ"ל לא אתפלא אם כל המנועים ישתגעו לגמרי.
אילו מתכנתי ארדואינו היו מגבילים את הספריה לשמונה מנועים בלבד, היא היתה מעולה – אבל כנראה מישהו החליט שברוב המקרים "יהיה בסדר", ודחף עוד ארבעה בשביל הרושם.
פתרון אוניברסלי?
שרשור סדרתי של הפולסים מציב, אם כן, מגבלה מוחלטת על מספר המנועים. מצד שני, כל ניסיון לשלוט במספר רב יותר של מנועים באמצעות טיימר יחיד מחייב קוד הרבה יותר מורכב, עם הרבה מוקשים פוטנציאליים. את חלקם כבר הזכרתי: איך מונעים "פספוסים" של פולסים בזמן עדכון ערך? איך מוודאים שהפסיקה, או סדרה של פסיקות סמוכות, לא לוקחות יותר מדי משאבים?
עצם הרעיון של יותר משמונה מנועי סרבו בפרויקט אינו מופרך. קיימים קיטים של רובוטים למיניהם שכוללים אפילו שמונה-עשר מנועים, וזה בגבולות הסביר גם מבחינת מספר פינים במיקרו-בקר. אך אם השאיפה היא לשלוט בכל אחד ואחד מהם בנפרד, עם טיימר יחיד, כדאי להתכונן לקוד תוכנה לא טריוויאלי – כולל, למשל, איסור על עדכוני משך פולס בחלונות זמן מסוימים, מיון של מערכי תזמונים ועוד.
בשלב ראשון אנסה כנראה לממש את השיטה של ארדואינו לשמונה מנועים ב-PSoC 4, ורק לאחר מכן אעבור לפתרון מתקדם יותר. אם יש לכם רעיונות ותובנות בנושא, זה הזמן לציין אותם בתגובות!
מליוניות שניה או אלפיות?
הייתי יוצר 12 משתנים, והשהייה קצרה מאוד… רק צריך לזכור שגם כל החשבון לוקח זמן כלשהו… כמו בשעון שבת שלך..
על איזה חלק בפוסט אתה שואל?
ברזולוציות של מיליוניות שנייה הדרושות לשליטה מדויקת בסרבו, אין מצב לעבוד עם השהיות (חשוב לזכור שכל מנוע יכול לדרוש פולס של בין 0.5 ל-2.5 אלפיות שניה בסה"כ, כך שגם אם תצופף את כולם שיהיו בחפיפה מקסימלית, עם השהיות זה יכול להשבית את המיקרו-בקר ליותר מדי זמן עבור פעולות אחרות)
אחד ממנועי הסרבו של חלק מהזמן משמיע קולות כאילו הוא מאמץ לעבוד וחלק מהזמן הוא עובד מצוין יש מה לעשות?
אם זה מנוע אחד ספציפי, וכשאתה שם מנוע אחר באותה מערכת בדיוק אז אין בעיות, סימן שהמנוע פשוט מקולקל וכדאי לזרוק לפח (או לפתוח אותו לפני זה וללמוד איך הוא עובד 🙂 )
פתרון זול ולא מסובך לשימוש בעד 1000 סרבויים דרך שני חוטים על ה i2c
פתרון של שגר ושכח .
ניתן כבר למצוא את המודול באליאקספרס ב 3 ומשהו $
https://youtu.be/qW8WtNjoIcc
אני אגיד לך בדיוק איזה "שגר ושכח"… אם פעם תעבוד במפעל שמייצר מאה אלף מעגלים לאיזה צעצוע, ותציע למנהלים להוסיף $3 לכל יחידה בשביל שליטה בסרבו, הם ישגרו אותך החוצה ותוכל לשכוח מהג'וב 😉
כבר לפני הרבה זמן כשניסיתי לבנות סרבו טסטר ראיתי שסרבוים לא באמת מחוייבים למבנה הזה בדיוק, והם הרבה יותר גמישים ממה שחושבים. אתה יכול לראות לדוגמא http://www.555-timer-circuits.com/servo-tester.htmlאת המעגל הזה אם תחשב את הערכים תראה שזה שונה מאוד מהמבנה שאנחנו התרגלנו אליו. אמנם גם לשיטה הזו יש גבול, אבל אולי תגלה שמתכנתי הארדואינו לשם שינוי ידעו מה הם עושים (: לגבי אלגוריתם למספר גדול של סרבוים בכל פעם שמוגדר ערך לסרבו הייתי מסדר את כולם מהערך הגדול לקטן במערך ואז בונה מערך הפרשים. בהתחלה אני מתחיל את הפולס של כולם יחד, מחכה את ההפרש הראשון במערך ומפסיק את הפולס של המנוע… לקרוא עוד »
על זה נאמר, "שתי עוולות לא עושות צדק" – אם הסרבו לא באמת עובד לפי המפרט, ומעגל הבדיקה/הפעלה לא באמת עובד לפי המפרט, יכול להיות שבמקרה זה יצליח, ויכול להיות שהעסק ייגמר בקטסטרופה. קצת כמו לעשות overclocking למיקרו-בקר: כשזה עובד זה נפלא, אבל אם משהו נשרף פתאום, האחריות היא רק שלך ולך תמצא עכשיו פתרון אחר. לגבי השיטה להפעלת סרבואים רבים – אתה צודק גם בשיטה העקרונית וגם במגבלות שלה, אם כי יש נקודות נסתרות נוספות שאולי עוד לא ראית – למשל, מה קורה אם צריך לכבות שני מנועים בדיוק באותו זמן, או מה קורה אם כל המנועים מכובים בסמיכות… לקרוא עוד »
הייתי קובע נקודת התחלה, ו"זוכר" מה הנקודה הבאה שבה צריך לעשות שינוי. יוצאות (לכל היותר) 9 פסיקות על כל תקופה של 20 מילי שניות. את הטבלה של מצב הפינים והתזמונים אפשר לחשב בערוץ הראשי כשמשנים את ההגדרות של SERVO בודד. קצת יותר יקר בנקודה הזאת, אבל עדיין לא יקר במיוחד, תוכל לתמוך ב12 SERVOים לפי התקן עם 13 פסיקות, שזה גם לא נורא.
מה שלומך? מזמן לא שמענו ממך 🙂 מצטער, לא הבנתי מה ההבדל בין מה שהצעת לבין מה שעושים בארדואינו…
מצויין, עברתי לניו יורק לפני שנתיים בערך, מאז קצת השתתקתי.
למיטב הבנתי מהפוסט בארדואינו הם שולחים את הפולס לסרבו הראשון, מסיימים ושולחים לשני וכן הלאה. אני מציע להתחיל את כל הפולסים בבת אחת, ואז לעצור אותם אחד אחד לפי הסדר הרצוי.
אה, אוקיי – זה בעצם מה ש-avi הציע בתגובה למעלה, כולל כל המכשולים הסמויים 🙂
אכן. זה לא כיף בלי מכשולים 🙂
תוכל להפנות לקוד ארדואינו טוב שולט בכמה סרוו ?
כמו שציינתי, עד 8 מנועים הספריה Servo (יש קישור בפוסט) לארדואינו מהווה פתרון טוב, והיא נוחה מאד לשימוש.