מימוש Fuzzy Logic ב-HC12 - מאת
אלי קולברג
מאמר זה מבוסס על סדרת הרצאות שהעברתי
במועדון הרובוטיקה, בנושא בקרת Fuzzy Logic
ומימושה באמצעות ההוראות המיוחדות לכך במיקרו קונטרלר 68HC12
של מוטורולה. מאמר זה עוסק בפן הישומי של מערכות פאזיות על ארבעת
שלביו, ולא בפיתוח המתמטי והרעיוני של לוגיקה פאזית.
כאשר עוסקים במערכות פאזיות, ניתן להבחין בארבעה צעדים בתהליך:
1.
פאזיפיקציה (fuzzification).
2.
היסק (inference).
3.
דה פאזיפיקציה (defuzzification).
4.
צירוף (combination).
שני הראשונים מתיחסים לחלק ה-"אם" (premise) ושני האחרונים קשורים לחלק ה-"אז" (conclusion).
1.
תהליך הפאזיפיקציה הוא הצעד הראשון המבוצע על ידי כל
מערכת פאזית. כאן מתבצעת העברה של המידע לדרגות השתייכות מתאימות של כל אחד
מהסעיפים (clause) של חלק האם (premise) של כל חוק פאזי. למשל טמפ' של 50 מעלות תקבל את דרגת ההשתייכות
0.7 ב term טמפ' חמה. זה מודגם באיור 1 (כאשר x הוא
נתון כניסה ו- m הוא דרגת השתתיכות):
בסיום התהליך, כל סעיף של כל חלק אם
של כל חוק, יוחלף בדרגת השתייכות. ואז רשימת החוקים תראה כך:
if m1,1 and … and m1,m then C is z1 and … and D is w1
if m2,1 and … and m2,m then C is z2 and … and D is w2
•
…..
…..
if mn,1 and … and mn,m then C is zn and … and D is wn
2.
תהליך ההיסק עוסק בחיבור כל המשתנים הפאזיים למשתנה אחד
שמיוצג על ידי פונקציית השתייכות אחת. בדרך זו, צירוף של פונקציות השתייכות לאחת,
מייצרת דרגת השתייכות אחת. ההיסק על חלק האם מתבצע לפי הכללים הבאים (בחלק האם שבו
יש and נשתמש ב- min):
בסיום התהליך, נוצרת
התוצאה הסופית שמסומנת ב- mp:
if mp1then C is z1 and … and D is w1
if mp2 then C is z2 and … and D is w2
…….
…….
if mpn then C is zn and … and D is wn
3.
תהליך הדהפאזיפיקציה עוסק בהמרת דרגת ההשתייכות mp שחושבה על ידי
תהליך ההיסק בחזרה לערך מהתחום הנתון. תהליך זה מבוצע על חלק האז (conclusion) של החוק. היות ורוב פונקציות ההשתייכות בנויות בצורה שיוצרת טרנספורמציות
של אחד להרבה בין דרגת ההשתייכות והתחום, קיים חוסר ודאות הקשור לבחירת הערך
הנכון. ישנן שיטות רבות לבצע דהפאזיפיקציה. בכל חוק נקבל מערך מינימום של משתנה
הכניסה, שטח ב term של משתנה מוצא. (איור 2)
בכדי לפתור את הבעיה נשתמש בשיטת מרכז
הכובד. הסיבות לכך הן: השיטה פשוטה, קלה להבנה, קלה ליישום והכללית ביותר.
בסיום תהליך הדה פאזיפיקציה נקבל:
if mp1then
A1,1 and … and B1,m
if mp2
then A2,1 and … and B2,m
……..
……..
if mpn
then An1 and … and Bnm
כאשר A ו- B הם שטחים מחושבים. (כדוגמת השטח האפור באיור 2).
4.
תפקיד חוק הצירוף הוא למצוא פתרון אחד כולל. אנו נחבר
את התוצאות שחושבו בתהליך הדה פאזיפיקציה ונפעיל נוסחה שתניב ערך יחיד. ישנן שיטות
רבות לחישוב הפתרון. אם השטח הכולל הוא A, אז:
A=A1,1+A1,2+…+A1,n
אין מוסיפים שטחים משותפים יותר מפעם אחת. מתוך השטח הכולל מוצאים את מרכז
הכובד וזה יהיה נתון המוצא הבודד (איור 3).
למערכת בקרה פאזית מבוססת מיקרו קונטרולר יש שני חלקים. החלק הראשון הוא
גרעין היסק פאזי (fuzzy inference kernel) שמתבצע באופן מחזורי לקביעת מוצאי המערכת בהתבסס על
המבואות הנוכחיים של המערכת. החלק השני של המערכת הוא בסיס הידע (knowledge base) שמכיל את פונקציות ההשתייכות והחוקים. איור 4 הוא דיאגרמת בלוקים
של סוג זה של מערכת לוגיקת פאזי.
את בסיס הידע יכול לפתח מומחה יישום
בלי שום ניסיון תכנות מיקרו קונטרולר. פונקציות השתייכות הן פשוט הביטוי של הבנת
המומחה את המונחים הלשוניים המתארים את המערכת שצריך לבקר. החוקים הם משפטים רגילים
בשפה טבעית, המתארים את הפעולות שאדם מומחה היה עושה בכדי לפתור את הבעייה
היישומית.
אפשר להוריד את החוקים ופונקציות
ההשתייכות למבני נתונים פשוטים יחסית (בסיס הידע) שאפשר לשמור בזיכרון הלא מחיק.
את גרעין ההיסק הפאזי יכול לכתוב מתכנת שאינו יודע כיצד עובדת מערכת היישום. הדבר
היחיד שעל המתכנת לדעת בקשר למידע בסיס הידע הוא לשמור אותו במקומות בזיכרון
שמשמשים את גרעין ההיסק.
כל מעבר דרך גרעין ההיסק הפאזי, מייצר
אותות מוצא מהמערכת בתגובה לתנאי המבוא הנוכחיים. הגרעין מבוצע כמה פעמים שצריך
בכדי לקבל בקרה ראויה. אם הגרעין יבוצע יותר פעמים ממה שצריך, אז יבוזבזו זמן
עיבוד והספק של המעבד. לעומת זאת השהייה ארוכה מידי בין המעברים יכולה לגרום
למערכת לצאת מבקרה.
הוראת MEM
במהלך שלב הפאזיפיקציה, משווים את
ערכי מבואות המערכת הנוכחיים כנגד פונקציות השתייכות של המבואות, שאוחסנו קודם
לכן, בכדי לקבוע את הדרגה שבה כל תווית (term) של כל
מבוא מערכת הוא אמת (דרגת השתייכות).
(בספרות מוטורולה משתמשים במונח תווית (label) ולא term כיוון שבאופן רגיל, לterm דרגת השתייכות בין 0 ל- 1 ולא בין 0 ל- 255 כמו
כאן). את זה משיגים על ידי מציאת ערך ה- Y (ציר Y הוא דרגת ההשתייכות או דרגת האמת), עבור ערך המבוא הנוכחי על
פונקציית השתייכות טרפזית לכל תווית של כל מבוא מערכת. ב- HC12 הוראת MEM מבצעת חישוב זה עבור תווית אחת של מבוא מערכת אחד. בכדי לבצע
משימת פאזיפיקציה מלאה למערכת, חייבים לבצע מספר הוראות MEM, בדרך
כלל בתוך לולאה.
ב HC12, דרגת ההשתייכות היא לא בין 0 ל- 1 אלא
בין $00 לבין $FF היות ודרגת ההשתייכות היא מילה בת 8 ביט. כאן
היא נקראת דרגת האמת (Degree of Truth).
בשלב הראשון, שלב הפאזיפיקציה,
משתמשים בפונקציה MEM ליצירת התוויות של כל משתנה. כל מה
שהכרנו קודם כ- term, נקרא כאן פונקציית השתייכות טרפזואידית
לתווית. תווית יכולה להיות למשל אחד מתוך: קר, קריר, פושר, חמים, חם או חם מאוד.
דוגמא נוספת: איטי מאוד, איטי, בינוני, מהיר, או מהיר מאוד.
באיור 5, בכתובת ADDR יהיה המספר 40$ (מיקום על ציר ה-X של
נקודה 1).
בכתובת ADDR+1 יהיה המספר $D0 (מיקום על ציר ה-X של נקודה 2).
בכתובת ADDR+2 יהיה המספר $08
[ שיפוע 1 :
$FF/(X OF SATURATION- X OF
POINT 1)
]
בכתובת ADDR+3 יהיה המספר $04
[
שיפוע 2 : $FF/(
X OF POINT 2-X OF SATURATION) ]
איור 5: מבנה פונקצית
השתייכות של תווית (term) בודדת
במעבד CPU12, הנמצא
בתוך מיקרו הבקר HC12, אפשר
להגדיר פונקציית השתייכות "קריספית".
במקרה זה אחד או שני הצדדים מאונך (שיפוע אינסופי). היות ולא משתמשים בערך שיפוע
של 00$, הוא משמש להגדרת שיפוע אינסופי בהוראת MEM.
באיור 6 מופיעה מערכת עם שלוש
פונקציות השתייכות מבוא, אחת לכל תווית של מבוא מערכת. ציר ה- X של כל שלושת פונקציות ההשתייכות מייצג את התחום של ערכים אפשריים
של מבוא מערכת. הקו האנכי דרך כל שלושת פונקציות ההשתייכות מייצג ערך מבוא מערכת
מסוים. ציר ה- Y מייצג את דרגת האמת (השתייכות) ומשתנה
משקר מוחלט ($00 או 0%)
לאמת מוחלטת ($FF או
100%). ערך ה- Y היכן שהקו האנכי חותך חותך כל אחת
מפונקציות ההשתייכות, הוא הדרגה שבה ערך המבוא הנוכחי מתאים לתווית הקשורה אלי ועבור
מבוא המערכת. לדוגמא, הביטוי "הטמפרטורה חמימה" הוא 25% נכון ($40). הערך $40 יאוחסן בכתובת בזיכרון ה- RAM, והוא
נקרא מבוא פאזי (במקרה זה, המבוא הפאזי עבור "הטמפרטורה חמימה"). קיימת
כתובת ב- RAM לכל מבוא פאזי (לכל תווית של כל מבוא
מערכת). ההבדל בין term לתווית מתבטא בשני
אופנים: 1. דרגת השתייכות של term משתנה בין 0 ל-
1, ואילו של תווית משתנה בין 0 ל- 255 (מספר מקסימלי בן 8 ביטים). 2. ערך הכניסה
לטרם הוא מספר התלוי בגודל פיסיקלי או אחר של המשתנה בעוד שתחום התוויות מוגבל
למספרים בין 0 ל- 255, ולכן נדרש נירמול ערכי המשתנה. כאשר זה ידוע, ניתן להחליף
את המושגים ביניהם.
כאשר מתחילים את צעד הפאזיפיקציה,
הערך הנוכחי של מבוא המערכת נמצא בצובר של המעבד, אוגר מציין אחד מצביע על הגדרת פונקציית
ההשתייכות הראשונה בבסיס הידע, ואוגר מציין שני מצביע על המבוא הפאזי הראשון ב- RAM. בכל
פעם שמחושב מבוא פאזי באמצעות ביצוע הוראת MEM, התוצאה
מאוחסנת למבוא פאזי ושני המצביעים מעודכנים באופן אוטומטי כך שהם יצביעו על
הכתובות הקשורות למבוא הפאזי הבא. הוראת MEM דואגת
לכל מלבד ספירת מספר התוויות למבוא מערכת והטענת הערך הנוכחי של כל מבואות המערכת
העוקבים.
התוצאה הסופית של צעד הפאזיפיקציה היא
טבלה של מבואות פאזיים המייצגים תנאי מערכת נוכחיים.
האלגוריתם של הוראת MEM:
1. קרא מילה מ- (0,X) – נקודה 1 ונקודה 2.
2. X:=X+4.
3. קרא מילה מ- (2 ,X) - שיפוע 1 ושיפוע 2.
4. delta
1:= ACCA-point 1 (ערך הכניסה
צריך להיות ב- ACCA)
5. delta
2 := point 2-ACCA
6. אם אחד ההפרשים
הוא שלילי, אז סמן טעות, ובצע: 0=שיפוע 1 וגם 0= שיפוע 2
7. אם אין טעות, grade 1 := slope 1
* delta1
וגם grade 2 := slope 2 *delta 2
8. אם שיפוע 2 = 0 או דרגה
2 גדולה מ- $FF, ואין טעות, אז דרגת השתייכות := $FF, אחרת דרגת השתייכות := דרגה 2
9. אם שיפוע 1
= 0 או דרגה 1 גדולה מ- $FF, ואין טעות, אז דרגת השתייכות := דרגת
השתייכות, אחרת דרגת השתייכות := דרגה 1
10. כתיבת byte בכתובת 1,Y- עם תוצאת כניסת פאזי (דרגת השתייכות).
הוראת REV
הערכת חוקים היא המרכיב המרכזי
בתוכנית היסק של לוגיקה עמומה. בצעד זה מתקיים עיבוד של רשימת חוקים מבסיס הידע
תוך שימוש בערכי מבוא פאזיים נוכחיים מה- RAM, בכדי לייצר
רשימה של מוצאים פאזיים ב- RAM. ניתן להתייחס
למוצאים פאזיים אלו כהצעות גולמיות למה צריכים להיות מוצאי המערכת בתגובה לתנאי
המבוא הנוכחיים. לפני שניתן ליישם תוצאות אלו, צריכים לעבד עוד יותר את המוצאים
הפאזיים, או במילים אחרות לבצע דה-פאזיפיקציה, בכדי לייצר ערך מוצא יחיד שמייצג את
ההשפעה המשולבת של כל המוצאים הפאזיים.
כאן נתאר את הוראות הערכת החוקים.
הוראת REV אינה כוללת אפשרות למשקל שונה לכל חוק. כאן כל החוקים הם בעלי
חשיבות זהה. הוראת REVW עושה זאת אך לא נדון בכך כאן. בהוראת REV אנו מממשים הערכת min-max של החוקים.
אוגרי המעבד משמשים כמצביעים ולתוצאות חישובי ביניים.
לדוגמא, חוק אופייני יכול להיות:
אם הטמפרטורה היא
חמימה והלחץ הוא גבוה, אז החימום (צריך להיות) כבוי.
במבט ראשון, נראה שקידוד חוק זה בצורה
קומפקטית המובנת למעבד יהיה קשה, אולם בעצם זה פשוט להביא את החוק לרשימה קטנה של
מצביעי זיכרון. החלק השמאלי של החוק הוא הצהרה של מצבי מבוא והחלק הימני של החוק
הוא הצהרה של פעולות מוצא.
החלק השמאלי של חוק עשוי מאחד או יותר
(בדוגמא שנתנו, 2) של תנאי קדם, המחוברים על ידי
פאזי ואופרטור. כל ביטוי תנאי קדם מבוסס על השם של מבוא מערכת, אחריו
מופיע "הוא" או "היא"
ואחריו שם תווית. התווית חייבת להיות מוגדרת על ידי פונקציית השתייכות בבסיס הידע.
כל ביטוי של תנאי קדם מתאים לאחד מהמבואות הפאזיים שב- RAM. היות
ופעולת and היא האופרטור היחיד שאפשר לחבר איתו ביטויי תנאי קדם, אין צורך
לכלול זאת בחוק המקודד. תנאי הקדם יכולים להיות מקודדים כרשימה פשוטה של מצביעים ל
(או כתובת של) המבואות הפאזיים שאליהם הם מתייחסים.
החוקים המלאים מאוחסנים בבסיס הידע
כרשימה של מצביעים או כתובות של מבואות פאזיים ומוצאים פאזיים. בכדי שלוגיקת הערכת
החוק תעבוד, חייב להיות אמצעי של ידע אילו מצביעים מתייחסים למבואות הפאזיים ואילו
מתייחסים למוצאים הפאזיים. חייבת גם להיות דרך לדעת מתי הגענו לחוק האחרון במערכת.
שיטה אחת לארגון יכולה להיות קביעת
מספר קבוע של של חוקים עם מספר מסוים של תנאי קדם ופעולות. שיטה שניה, שמיושמת ב- HC11 היא לסמן את סיום החוק בערך שמור, ולהשתמש בביט במצביעים בכדי
להבדיל בין תנאים לפעולות. שיטה שלישית של
ארגון המשמשת ב- HC12, היא לסמן את סוף רשימת החוק עם ערך
שמור, ולהפריד תנאים מפעולות עם ערך שמור נוסף. זה מאפשר מספר כלשהוא של חוקים,
ומאפשר לכל חוק להיות בעל מספר כלשהוא של תנאים ופעולות, בכפוף למגבלות שנכפות על
ידי זמינות זכרון המערכת.
כל חוק מוערך בצורה רציפה, אולם
החוקים כקבוצה מטופלים כאילו כולם היו מוערכים בו זמנית. שני אופרטורים מתמטיים
מתבצעים במהלך הערכת החוק. האופרטור הפאזי and המתאים
לפעולה המתמטית מינימום, והאופרטור הפאזי or המתאים לפעולה
המתמטית מקסימום. ה and הפאזי משמש לחיבור תנאים באותו חוק. ה or הפאזי מיושם בין חוקים עוקבים. לפני שמעריכים איזה שהם חוקים, כל
המוצאים הפאזיים מאותחלים לאפס (כלומר לא נכונים כלל).כאשר כל חוק מוערך, התנאי
הקטן ביותר (מינימום) נלקח להיות דרגת האמת הכוללת של החוק. ערך אמת זה של החוק מיושם לכל פעולה של החוק
(באמצעות איחסון של ערך זה למוצא הפאזי המתאים) אלא אם המוצא הפאזי הוא כבר גדול
יותר (מקסימום). אם שני כללים משפיעים על אותו מוצא פאזי, החוק שהוא יותר אמת גובר
שולט על הערך שבמוצא הפאזי היות והחוקים קשורים על ידי or פאזי
מרומז.
במקרה של שיקלול חוקים, ערך האמת עבור
חוק נקבע כרגיל על ידי מציאת תנאי החוק הקטן ביותר. לפני יישום ערך אמת זה לפעולות
עבור החוק, הערך מוכפל בשבר שבין אפס (החוק לא מאופשר) לאחד (החוק מאופשר במלואו).
ערך האמת המותאם, מיושם אז למוצאים הפאזיים.
התוצאה הסופית של צעד הערכת החוק היא
טבלה של מוצאים פאזיים מומלצים או גולמיים ב- RAM. ערכים
אילו התקבלו מהכנסת התנאים הנוכחיים (ערכי מבוא פאזיים) לחוקי המערכת בבסיס הידע. לא
ניתן לספק את התוצאות הגולמיות ישירות למוצאי המערכת היות והם יכולים להיות שנויים
במחלוקת או מעורפלים. למשל, מוצא גולמי אחד יכול להצביע על כך שמוצא המערכת צריך
להיות בינוני עם דרגת אמת של 50%, ובאותו זמן, מוצא גולמי אחר מצביע על כך שמוצא
המערכת צריך להיות נמוך עם דרגת אמת של 25%.
היות והוראת REV היא
ביסודה הוראת עיבוד רשימה, זמן הביצוע תלוי במספר האלמנטים שברשימת החוקים. הוראת REV מאפשרת פסיקות במהלכה (בדרך כלל בתוך שלושה מחזורים), כך שאינה
גורמת ל-worst
case בהשהיית פסיקות. היות וכל תוצאות
הביניים ומצב ההוראות מוחזקים במחסנית, שגרת הפסיקה יכולה לכלול אפילו הוראות REV בלתי תלויות.
הכנות קודמות לביצוע הוראת REV
יש צורך לבצע הכנות לחלק מאוגרי המעבד
ולמספר מיקומים בזיכרון, לפני הפעלת הוראת REV.
האוגרים X ו- Y משמשים כמצביעים לרשימת החוקים ולמבואות
ולמוצאים הפאזיים. צובר A משמש לאיחסון תוצאות חישובי ביניים
וצריך להיות מאותחל ל- $FF. ביט V (גלישה) באוגר המצב (CCR) משמש לקביעת מצב ההוראה ומראה האם מבוצע כעת חלק התנאי או חלק ה-
then. בתחילה ערך V מאותחל
לאפס, בכדי לסמן עיבוד של החלק הראשון.
צריך לאפס את המוצאים הפאזיים (כתובות בזיכרון RAM). אם
לא נאפס אותם קודם לביצוע ההוראה, אנו עלולים לקבל תוצאות שגויות.
אוגר מציין X מאותחל
לכתובת של האלמנט הראשון ברשימת החוקים (של בסיס הידע). הוראת REV מעדכנת באופן אוטומטי מצביע זה כך שההוראות יחודשו כראוי אם תקרה
פסיקה. לאחר שמסתיימת הוראת REV, X יצביע על הכתובת הבאה אחרי תווי המפריד $FF שמסמן את סיום רשימת החוקים.
אוגר מציין Y מאותחל
להצביע על כתובת הבסיס של המבואות והמוצאים הפאזיים (בזיכרון ה- RAM). כל חלק תנאי של חוק הוא היסט של 8 ביט unsigned מכתובת בסיס זו למבוא הפאזי המיוחס. כל חלק then של חוק
הוא היסט של 8 ביט unsigned מכתובת בסיס זו למוצא הפאזי המיוחס. ערך
אוגר מצביע Y נשאר קבוע לכל אורך ביצוע הוראת REV.
צובר A בן 8
ביט, משמש לאיחסון תוצאות חישובי ביניים במהלך ביצוע הוראת REV. במהלך
עיבוד חלק התנאי, A מאותחל ל- $FF, והוא מוחלף בכל ערך קטן יותר שמאוזכר בחלק התנאי של החוק (MIN). במהלך עיבוד חלק ה- then, A מחזיק את ערך האמת של החוק. ערך אמת זה מאוחסן לכל מוצא פאזי
שמאוזכר על ידי חלק ה- then, אלא אם מוצא פאזי זה כבר גדול יותר (MAX).
לפני תחילת ביצוע הוראת REV, חייבים לאתחל את A ל- $FF (הערך הגדול ביותר של 8 ביט)
היות והערכת החוק מתחילה תמיד עם עיבוד של חלקי התנאי של החוק הראשון. עבור החוקים
העוקבים, הערך $FF מוצב
באופן אוטומטי לתוך A, כאשר ההוראה מגלה את תו הסימן $FE בין חלק ה- then
האחרון של החוק הקודם, והאיבר הראשון בחלק התנאי של חוק חדש.
ההוראה LDAA #$FF מאפסת את ביט V יחד עם איתחול A ל- $FF. זה מתאים לדרישות האיתחול של הוראת REV לאפס
את ביט V כמו גם לאתחל את A ל- $FF. ברגע
שמתחילה הוראת REV, הערך של ביט V מנוהל
אוטומטית כאשר מתגלים התווים המפרידים $FE.
הדרישה האחרונה לאיפוס כל המוצאים
הפאזיים ל- $00, היא
חלק מהאלגוריתם MAX. בכל פעם שאיבר בחלק then של חוק מתייחס למוצא פאזי, מוצא פאזי זה מושווה לערך האמת של החוק
הנוכחי. אם ערך האמת הנוכחי גדול יותר, הוא נכתב במקום (על) הערך הקודם של המוצא
הפאזי. אחרי שהוערכו כל החוקים, המוצא הפאזי מכיל את ערך האמת עבור חוק הכי אמת (most-true) שאיזכר מוצא פאזי זה.
אחרי שמסתיים ביצוע הוראת REV, A יחזיק את ערך האמת עבור החוק האחרון
שברשימת החוקים. ביט V צריך להיות 1, היות והאלמנט האחרון
שלפני מציין הסיום $FF, היה צריך להיות איבר בחלק ה- then. אם
ביט V הוא אפס לאחר ביצוע REV, זה יכול
להצביע על כך שרשימת החוקים נבנתה לא כהלכה.
האלגוריתם של הוראת REV:
1.
קרא בית ב (0,X) – האלמנט הראשון ברשימת החוק. בדרך כלל זה יהיה
איבר התנאי הראשון של החוק הראשון.
2.
X:=X+1. מצביע
על האיבר הבא ברשימת החוק.
3. השהייה פנימית.
4. כאן נקרא מבוא
פאזי או מוצא פאזי, תלוי באיזה חלק של החוק
נמצאים, מלבד אם מגיעים לתווי ההפרדה $FE ו- $FF. תהליך הקריאה משתמש באוגר מצביע Y לכתובת
הבסיסית ובבית הקודם שנקרא בחוק זה (Rx) כהיסט ללא סימן
(חיובי) מ- Y. המבוא הפאזי או המוצא הפאזי שנקרא כאן,
ישמש אותנו בצעד 6.0 או 6.1. ה- Rx שנקרא קודם לכן
משמש כהיסט מ- Y עבור
קריאה זו, וגם ערכו נבדק לראות האם הוא תו הפרדה ($FE). אם הוא אכן $FE, וביט V היה 1,
זה מצביע על מעבר מעיבוד חלק התוצאה (then) של חוק אחד
לתחילת העיבוד של חלק התנאי של החוק הבא. במעבר זה, צובר A מאותחל
ל- $FF בכדי
להיות מוכן לפעולת min למציאת הערך המבוא הפאזי הקטן ביותר.
בנוסף לכך, אם Rx ערכו $FE, ביט V הופך את ערכו בכדי להצביע על השינוי
מתנאי לתוצאה או מתוצאה לתנאי.
5.
כאן נקרא בית חדש של החוק ממיקום (0,X), ואז יקודם X (X=X+1),
אלא אם הגענו לסיום ערכו של Rx הוא $FF (מה שמסמן את סיום רשימת החוק). לא נשתמש בבית חדש זה עד ביצוע
צעד 4 של המעבר הבא דרך הלולאה.
בין צעד 5 לבין 6.x משתמשים בערכו של V בכדי לקבוע באיזה
משני המסלולים ללכת. אם V הוא 0, מטפלים במסלול של התנאי והמעבד
מתקדם לצעד 6.0. אם V הוא 1, מטופל חלק התוצאה והמעבד ממשיך לצעד 6.1.
6.
ערך צובר A מושווה
עם המבוא הפאזי שנקרא בצעד 4 קודם לכן, והערך
הנמוך מבין שניהם מוצב בצובר A (פעולת min). אם Rx הוא $FE, זהו מעבר בין תנאי החוק לתוצאות
החוק, ומדלגים על פעולת ה- min.
6.1 כאן גם ערך
צובר A מושווה עם המבוא הפאזי שנקרא בצעד 4
קודם לכן, והערך הגבוה מבין שניהם מוצב בכתובת Rx,Y (פעולת
max). אם Rx הוא $FF, זהו סיום החוקים, אחרת חוזרים לצעד 4.
הוראת WAV
הצעד האחרון בתוכנית לוגיקה עמומה
מאחדת את המוצאים הפאזיים הגולמיים למוצא מערכת מאוחד. שלא כמו הצורות הטרפזיות
המשמשים למבואות, ה CPU12
משתמש בערכים בודדים לפונציות השתייכות המוצא. כמו עם המבואות, ציר ה- X מייצג את תחום הערכים האפשריים עבור מוצא המערכת. פונציות השתייכות
של מספרים בדידים מבוססות על מיקום בציר ה- x עבור
תווית של מוצא המערכת. מוצאים פאזיים מתאימים לגובה בציר ה- y של
פונקציית השתייכות המוצא המתאימה.
הוראת WAV מחשבת
את סכומי המונה והמכנה ומבצעת מבצעת חישובי ממוצע משוקלל המשמשים לביצוע
דהפאזיפיקציה. פסאודו פקודה wavr משמשת לחזרה
מפעולת ממוצע משוקלל בפסיקה. WAV מחשבת סכומי
המונה והמכנה הבאים:
(1)
כאשר n הוא
מספר התוויות (terms), של מוצא המערכת, Si הם המיקומים (מרחקים) הבודדים המתקבלים מבסיס הידע, ו Fi הם המוצאים הפאזיים שמתקבלים מה- RAM. Si ו- Fi הם ערכים בני שמונה ביט. צובר B מחזיק את מונה האיטרציות n. בתוכנית רגילה
של לוגיקה עמומה ב- HC12, n הוא
שמונה או פחות (למרות שההוראה יכולה לטפל בעד 255). אוגרים זמניים פנימיים מחזיקים
את ערכי סכומי הביניים (24 ביט למונה ו- 16 ביט למכנה. החלוקה האחרונה מבוצעת
בעזרת הוראת EDIV נפרדת מייד אחרי הוראת WAV. חלוקה זו היא: Y=(Y:D)/X כלומר מחברים
אוגרים Y ו- D ומחלקים את מה שמתקבל בתוכן אוגר X. התוצאה נמצאת ב- Y והשארית ב- D. אם
למערכת יש יותר ממוצא מערכת אחד, הוראת WAV מתבצעת
פעם אחת עבור כל מוצא מערכת.
הכנות מוקדמות לביצוע הוראת WAV
לפני ביצוע הוראת WAV צריך להכין את האוגרים X ו- Y וצובר B. אוגר X חייב
להכיל את הכתובת של המיקום (singleton) הראשון מרשימת
Si מבסיס הידע. אוגר Y חייב להכיל את
הכתובת של המוצא הפאזי הראשון של מערכת מוצאים זאת Fi. אוגר B
חייב להכיל את מספר התוויות של מוצאי מערכת זו.
בתוכנית הבאה מופיע גרעין היסק פאזי
שלם שנכתב בשפת אסמבלי של ה- HC12. המספרים בסוגריים המרובעות הם מספר המחזורים.
הגרעין משתמש בשני מבואות פאזיים עם שבע תוויות לכל אחד, ומוצא מערכת אחד עם שבע
תוויות. התוכנית מקומפלת ל- 57 בתים. היא מתבצעת במשך לערך בתדירות אפיק של 8MHz. אפשר בקלות להרחיב את המבנה הבסיסי למערכת לשימוש כללי עם מספר
גדול יותר של מבואות ומוצאים.
תוכנית 1: גרעין היסק פאזי למעבד 12HC68
* |
|
|
|
|
|
01 |
[2] |
FUZZIFY: |
LDX |
#INPUT_MFS |
;point at MF definitions |
02 |
[2] |
|
LDY |
#FUZ_INS |
;point at fuzzy input table |
03 |
[3] |
|
LDAA |
CURRENT_INS |
;get first input value |
04 |
[1] |
|
LDAB |
#7 |
;7 labels per input |
05 |
[5] |
GRAND_LOOP: |
MEM |
|
;evaluate one MF |
06 |
[3] |
|
DBNE |
B,GRAD_LOOP |
;for 7 labels of 1 input |
07 |
[3] |
|
LDAA |
CURRENT_INS+1 |
;get second input value |
08 |
[1] |
|
LDAB |
#7 |
;7 labels per input |
09 |
[5] |
GRAND_LOOP1: |
MEM |
|
;evaluate one MF |
10 |
[3] |
|
DBNE |
B,GRAD_LOOP1 |
;for 7 labels of 1 input |
|
|
|
|
|
|
11 |
[1] |
|
LDAB |
#7 |
;loop count |
12 |
[2] |
RULE_EVAL: |
CLR |
1,Y+ |
;clr a fuzzy out & inc
ptr |
13 |
[3] |
|
DBNE |
b,RULE_EVAL |
;loop to clr all fuzzy outs |
14 |
[2] |
|
LDX |
#RULE_START |
;point at first rule element |
15 |
[2] |
|
LDY |
#FUZ_INS |
;point at fuzzy ins and outs |
16 |
[1] |
|
LDAA |
#$FF |
;init A (and clears V-bit) |
17 |
[3n+4] |
|
REV |
|
;process the rule list |
|
|
|
|
|
|
18 |
[2] |
DEFUZ: |
LDY |
#FUZ_OUT |
;point at fuzzy outputs |
19 |
[1] |
|
LDX |
#SGLTN_POS |
;point at singleton positions |
20 |
[1] |
|
LDAB |
#7 |
;7 fuzzy outs per COG output |
21 |
[8b+9] |
|
WAV |
|
;calculate sums for wtd av |
22 |
[11] |
|
EDIV |
|
;final divide for wtd av |
23 |
[1] |
|
TFR |
Y,D |
;move result to A:B |
24 |
[3] |
|
STAB |
COG_OUT |
;store system output |
|
* |
|
|
|
|
|
***** |
End |
|
|
|
שורות 1-3 מאתחלות מצביעים ומטעינות את ערך
מבוא המערכת לתוך צובר A.
שורה 4 מציבה את מניית הלולאה עבור הלולאה
בשורות 5 ו- 6.
שורות 5 ו-6 מבצעות את לולאת הפאזיפיקציה עבור
שבע התוויות של מבוא מערכת אחד. הוראת MEM מוצאת
את ערכי y
על פונקציית השתייכות טרפזית עבור ערך המבוא הנוכחי, לתווית אחת של
המבוא הנוכחי, ואז מאחסנת את התוצאה למבוא הפאזי המתאים. המצביעים ב- X ו- ט מתעדכנים אוטומטית בארבע ואחד, כך שהם מצביעים לפונקציית
ההשתייכות הבאה ולמבוא הפאזי הבא בהתאמה.
שורה 7 מטעינה את הערך הנוכחי של מבוא המערכת
הבא. המצביעים ב- X ו- Y כבר
מצביעים למקומות הנכונים כתוצאה מהעדכון האוטומטי של הוראת MEM בשורה
5.
שורה 8 מטעינה מחדש את מונה הלולאה.
שורות 9 ו- 10 יוצרות לולאה לביצוע
פאזיפיקציה של שבע תוויות של מבוא המערכת השני. כאשר התוכנית מגיעה לשורה 11, אוגר
מציין Y מצביע על הכתובת הבאה אחרי הכניסה הפאזית האחרונה, שהיא המוצא
הפאזי הראשון במערכת זו.
שורה 11 מאתחלת את מונה הלולאה לאיפוס שבע
המוצאים הפאזיים.
שורות 12 ו- 13 יוצרות לולאה לאיפוס כל
המוצאים הפאזיים לפני שמתחילה הערכת החוק.
שורה 14 מאתחלת את אוגר מציין X להצביע על האלמנט הראשון ברשימת החוק עבור הוראת REV.
שורה 15 מאתחלת את אוגר מציין Y להצביע על המבואות והמוצאים הפאזיים במערכת. רשימת החוק (עבור REV) מבוססת על היסט של 8 ביט מכתובת הבסיס למבואות פאזיים מסויימים
או מוצאים פאזיים מסויימים. הערך המיוחד $FE מתפרש על ידי REV כסמן בין תנאי
החוק ופעולות החוק.
שורה 16 מאתחלת את צובר A לערך הגבוה ביותר בן 8 ביט בהכנות למציאת המבוא הפאזי הקטן ביותר
שמיוחס על ידי תנאי של חוק. ההוראה LDAA #$FF גם מאפסת את
ביט V באוגר המצב של המעבד, כך שהוראת REV יודעת
שהיא מעבדת תנאים. במהלך עיבוד רשימת חוק, ביט V ממותג
בכל פעם שמתגלה $FE
ברשימה. ביט V מציין האם REV מעבד
תנאים או פעולות.
שורה 17 מכילה הוראת REV, שהיא
לולאה עצמאית לעיבוד אלמנטים עוקבים ברשימת החוק עד שמתגלה התו $FF. עבור מערכת עם 17 חוקים עם שני
תנאים ותוצאה אחת, הוראת REV לוקחת 259
מחזורים, אבל היא ניתנת לפסיקה כך שהיא לא גורמת להשהיית פסיקה ארוכה.
שורות 18 עד 20 מאתחלות את המצביעים ומונה
האיטרציות עבור הוראת WAV.
שורה 21 היא תחילת הדה-פאזיפיקציה. הוראת WAV מחשבת סכום מכפלות וסכום משקלים.
שורה 22 משלימה את הדה פאזיפיקציה. הוראת EDIV מבצעת חלוקה של 32 ביטים ב- 16 ביטים על תוצאות הביניים מ- WAV לקבלת ממוצע משוקלל.
שורה 23 מעבירה את תוצאות EDIV לצובר כפול D.
שורה 24 מאחסנת את 8 הביטים הנמוכים של תוצאת
הדה פאזיפיקציה.
דוגמא לבקרת PID
למהירות מנוע DC
(מיועדת לבעלי רקע בבקרת מנועים ע"י PWM )
המטרה היא לתכנן בקר מנוע מבוסס מיקרוקונטרולר בלוגיקה עמומה (איור 7).
המפעיל הוא אות ספרתי PWM. ההספק למנוע מבוקר על ידי שינוי ה- duty cycle (DC) של גל ריבועי בתדר של 15KHz לערך. DC מוגדר
כאחוז שבו האות נמצא במצב ‘on’ מתוך זמן
המחזור כולו. הערך הנקוב של התדר אינו משנה ללוגיקה או לחישוב ה-DC כיוון שמדובר באחוז מזמן המחזור. לעומת זאת קיים לכל מנוע תדר
עבודה אופטימלי המאפשר למנוע להוציא הספק מירבי ל- DC נתון.
לכן לכל מנוע יש למצוא את תדר העבודה האופטימלי. תדר זה מושפע בעיקר מהתנגדות
והשראות סלילי המנוע.
ה- FET-ים
שנמצאים ברכיב LMD18201 מספקים זרם למנוע רק כאשר המוצא הספרתי
של אות ה- PWM הוא גבוה. קיימות דיודות הגנה ששומרות
על האלקטרוניקה מהכא"מ המושרה שנוצר כאשר הזרם ממותג (dI/dt גבוה)
לסליל המנוע.
היות ותדירות הגל הריבועי מהירה יותר
מתגובת המנוע, הרי שהמנוע מגיב רק ל- DC ולא לחלקים
הגבוהים או הנמוכים המסויימים של הגל הריבועי.
מוצא האנקודר הוא תדירות (ביחידות של Hz) היחסית למהירות המנוע (בסל"ד). ניתן למדוד התדירות של אות
זה בעזרת מערכת ה- Input Capture. מהירות המנוע היא פונקציה לא
לינארית של ההספק המוזן. עקב החיכוך והאינרציה, ישנו תחום הספק שמתחתיו המנוע לא
יסתובב.
המערכת שלנו תספור את כמות הפולסים של
האנקודר הקיימים בין גלישת שעון (TCNT) אחת לבאה
אחריה. משך הזמן שעובר בין שתי גלישות עוקבות הוא במקרה שלנו 8.125ms. במהירות רצויה
של 100 סל"ד, מספר הפולסים יהיה 80. הסבר החישוב: אנו מדברים על מנועים שבהם
יחס התמסורת הוא 1:30, ועל גלגל האנקודר 200cpr (חורים לסיבוב). היות והאנקודר ממוקם לפני התמסורת הרי שסה"כ
יהיו לנו 200 x 30 = 6000 חורים לסיבוב. מספר סיבובי המנוע בשניה אחת יהיה: 100rpm/60spm כלומר 100
סיבובים לדקה חלקי 60 שניות לדקה. ומכאו נקבל rps
סיבובים לשניה. מספר הסיבובים ב 8.125ms יהיה rps * 8.125/1000. אם נכפיל נתון
זה ב- 6000 (מספר חורי האנקודר לסיבוב אחד), נקבל כ- 80 חורים בזמן של 8.125ms.
נבנה מערכת טיפול בפסיקות לצרכי בקרת
מהירות. נבנה זאת עבור מעבד 68HC812D60A של חברת מוטורולה. מערכת זו תהיה נכונה גם למעבדים אחרים של
מוטורולה ממשפחת HC12 שיש בהם יכולת ביצוע PWM כלומר כל המשפחה למעט סיומת A4.
נניח שאנקודר גלגל ימני מחובר ל- PT0 ואנקודר גלגל שמאלי מחובר ל- PT1. בסיס
וקטור הפסיקות הוא הכתובת $0600 ($ מסמן מספר בבסיס 16 – הקסאדצימלי). כתובת פסיקת PT0 רחוקה $2E (46) כתובות מהבסיס. כתובת פסיקת PT1 רחוקה $2C (44) כתובות מהבסיס. כתובת פסיקת
גלישת השעון רחוקה $1E (30) כתובות מהבסיס.
מכאן, נתחיל בסימון כתובות שלושת
הפסיקות הללו:
תוכנית 2: סימון כתובות הפסיקה
int_ base: |
Equ |
$0600 |
tch0: |
Equ |
int_base +
$2e |
tch1: |
Equ |
int_base +
$2c |
tovf: |
Equ |
int_base +
$1e |
שגרת הפסיקה לספירת פולסי אנקודר ימין
תקרא enc_rt, ספירת פולסי אנקודר שמאל תתבצע בשגרת פסיקה בשם enc_lft, ואילו שגרת הפסיקה לביצוע הבקרה בכל פעם שקורית גלישת השעון תקרא
ovf_fuz.
נכתוב כעת את שגרת איתחול הפסיקות
בצורה הבאה:
01 |
int_init: |
|
|
|
02 |
|
movb |
#$05, tctl4 |
* looking
for rising edge on port t bits 0 and 1 |
03 |
|
movb |
#$03, tmsk1 |
* enable
local switches interrupts of pt0 and pt1 |
04 |
|
movb |
#$80, tmsk2 |
* enable
timer overflow interrupt |
05 |
|
movb |
#$00, tios |
* make sure that port t is in input capture mode |
06 |
|
ldx |
#enc_rt |
* load right
encoder routine address into x register |
07 |
|
stx |
tch0 |
* update channel
0 interrupt vector to point towards enc_rt interrupt routine |
08 |
|
ldx |
#enc_lft |
* load left
encoder routine address into x register |
09 |
|
stx |
tch1 |
* update
channel 1 interrupt vector to point towards enc_lft interrupt routine |
10 |
|
ldx |
ovf_fuz |
* load timer
overflow routine address into x register |
11 |
|
stx |
tovf |
* update
timer overflow interrupt vector to point towards tovf interrupt routine |
12 |
|
movb |
#$03, tflg1 |
* clear flag
bits 0 and 1 and wait for rising edges |
13 |
|
movb |
#$80, tflg2 |
* clear
overflow flaf bit (msb) in tflg2 |
14 |
|
movb |
#$00,l_count |
* clear
counter of right encoder pulses |
15 |
|
movb |
#$00,r_count |
* clear
counter of left encoder pulses |
16 |
|
movb |
#$00,old_lerr |
* clear
value of old left speed error |
17 |
|
movb |
#$00,old_rerr |
* clear
value of old right speed error |
18 |
|
movb |
#$00, l_sum |
* clear
counter of the sum of left speed errors |
19 |
|
movb |
#$00, r_sum |
* clear
counter of the sum of right speed errors |
20 |
|
movb |
#80, l_speed |
* determine
80 tics as left motor desired speed |
21 |
|
movb |
#80, r_speed |
* determine
80 tics as right motor desired speed |
22 |
|
cli |
|
* activate interrupts
global switch |
23 |
|
rts |
|
|
שורה 1 מכילה את תווית או את שם שגרת איתחול
הפסיקה. בשורה שניה אנו מציבים ערך של 01 בביטים 0 ו- 1 ב- tctl4 כלומר המעבד יחכה לקצה עולה (01) בביט 0 של port T (שני
הביטים הימניים ב tctl4 מצביעים על כך), באותה
צורה הערכים 01 בביטים 2 ו- 3 של tctl4
מצביעים על כך שהמעבד יחכה לקצה עולה בביט 1 של port T.
שורה 3 מציבה ערך 1 בביטים 0 ו- 1 של tmsk1 ובכך מאפשרת פסיקה בביטים המקבילים (0 ו- 1) של portT. אילו מתגים מקומיים. אם נציב ערך אפס באחד מביטים אלו של tmsk1, לא יהיו יותר פסיקות בביט המקביל ב port T, אך
בשאר הביטים במידה ופסיקות מאופשרות הן תמשכנה להתקיים.
שורה 4 מאפשרת קיום פסיקה בכל פעם שתקרה
גלישה בשעון הכללי TCNT.
שורה 5 מוודאת שכל הביטים ב- port T יהיו במצב של לכידת מבוא (Input Capture ).
למרות שזוהי ברירת המחדל, נוודא בצורה זו שגם אם בטעות ביטים מסוימים הוגדרו
כהשוואת מוצא (Output Compare), הם יחזרו
להיות מכוונים ללכידת מבוא. יש לשים לב שאם ברצוננו להשתמש בביטים מסויימים
כהשוואת מוצא, יש לשנות את הפרמטר המספרי שבפקודה זו.
בשורה 6 אנו מטעינים את כתובת שגרת הפסיקה של
אנקודר ימין לתוך אוגר מציין X.
בשורה 7 אנו שומרים כתובת זו במקום המתאים
לביט 0 של port T בוקטור כתובות הפסיקה.
בשורות 8 עד 11, אנו שומרים באופן דומה את
כתובות שגרות הפסיקה של אנקודר שמאל (pt1) ושל גלישת
השעון הכללי (tovf) במקומות המתאימים בוקטור הפסיקה.
בשורה 12 אנו מאפסים את הדגלים שקשורים לפסיקה
בביטים 0 ו- 1 של port T על ידי איפוס ביטים 0 ו- 1 ב- tflg1. יש לשים לב שביטים שהם דגלים, כתיבת 1 בהם, תגרום לאיפוס (0) שלהם.
לכאורה דבר והיפוכו. יש לכך סיבה שמוסברת במדריכים של חברת מוטורולה. ביט שאינו
דגל, הכתיבה אליו רגילה ואם נרשום בו 1, הוא יהיה 1.
בשורה 13 אנו מאפסים את הדגל הקשור לפסיקת
גלישת השעון. גם כאן כתיבת 1 לביט תגרום לו להפוך ל- 0.
בשורות 14 ו- 15 אנו מאפסים את המונים של
אנקודר ימין ואנקודר שמאל בהתאמה, בכדי שהספירה תתחיל מאפס. יש לשים לב לכך שאנו
מניחים כי כתובות המונים הוגדרו קודם לכן באיתחול התוכנית הראשית (כמו לגבי שאר
התוויות בתוכנית).
בשורות 16 ו- 17 אנו מאפסים את ערך השגיאה
הקודמת של מהירות שמאל ומהירות ימין.
בשורות 18 ו- 19 אנו מאפסים את מוני ערכי
השגיאה המצטברת של מהירויות שמאל וימין בהתאמה.
בשורות 20 ו- 21 אנו קובעים את המהירות
הרצויה למנוע שמאל ומנוע ימין בהתאמה. בנסיעה ישרה זה יהיה אותו מספר, אולם בסיבוב
בקשת נחליף ערך זה באחד או בשני המנועים.
בשורה 22 אנו מפעילים את המתג הכללי שמאפשר את
קיום כל הפסיקות שהגדרנו.
שורה 23 מסמנת את סיום שגרת הפסיקה.
כעת נכתוב את שגרות הפסיקה שהוזכרו קודם לכן.
תוכנית 4: שגרת פסיקה לאנקודר ימין
01 |
enc_rt: |
|
|
|
02 |
|
inc |
r_count |
* increment
r_count value for each rising edge (pulse) |
03 |
|
movb |
#$01, tflg1 |
* clear
tflg1 bit 0 and be prepared to next pulse |
04 |
|
rti |
|
* return to
main program |
השורה הראשונה מכילה את תווית (שם) שגרת הפסיקה לאנקודר ימין.
שורה 2
מגדילה ב- 1 את ערך מונה הפולסים המגיעים מאנקודר ימין.
שורה 3
מאפסת את הדגל בביט 0 של tflg1 המסמן קצה עולה
ב- pt0 בכדי להיות מוכן לקצה עולה של הפולס הבא.
שורה 4
מחזירה את השליטה לתוכנית הראשית. יש לשים לב ששגרת פסיקה מסתיימת בפקודה rti ולא rts כמו
בשגרה רגילה. ההבדל נובע מכך ששגרת פסיקה, אין לדעת מתי תתבצע במהלך התוכנית
בניגוד לשגרה רגילה, ולכן גם פקודת החזרה שלה מסמנת כי זו חזרה מפסיקה.
שגרת הפסיקה לאנקודר
שמאל דומה מאוד ואין צורך להסבירה שוב.
תוכנית 5: שגרת פסיקה לאנקודר שמאל
01 |
enc_lft: |
|
|
|
02 |
|
inc |
l_count |
* increment
l_count value for each rising edge (pulse) |
03 |
|
movb |
#$02, tflg1 |
* clear
tflg1 bit 1 and be prepared to next pulse |
04 |
|
rti |
|
* return to
main program |
כעת נעבור לביצוע החלק של לוגיקה עמומה
בזמן פסיקת גלישת השעון. תחילה נגדיר מספר פרמטרים ונבנה את פונקציות ההשתייכות.
למערכת יהיו המשתנים הבאים:
S* מהירות מנוע רצויה (בפולסי אנקודר במשך זמן של 8.125ms)
‘S מהירות מנוע נוכחית מוערכת (בפולסי אנקודר במשך זמן של 8.125ms)
V Duty cycle ערך הנכתב לאוגר pwdty0 (ימין) או pwdty1 (שמאל)
E = S* - S¢ השגיאה
במהירות המנוע
D = dS¢/dt = S¢(n) - S¢(n-1) קצב
השינוי במהירות המנוע
I = òEdt = E(n) + SE(i) i=1,...,n-1 צבירת
שגיאת מהירות המנוע בזמן
DV שינוי ב- DC (V=V+DV)
כעת נבנה את פונקציות ההשתייכות
שמגדירות את המצב העכשווי של הכניסות והמוצאים הקריספיים. החישוב של קבוצות
השתייכות המבוא נקרא פאזיפיקציה. לבקר פאזי זה נגדיר למען הפשטות חמש עשרה תוויות
(terms) לכניסות הקריספיות. (טבלה 1). התוויות הוגדרו בצורה הבאה: האות E מסמנת שגיאה (error), האות D מסמנת נגזרת השגיאה (derivative), האות
I מסמנת אינטגרל השגיאה (integral), האות
N מסמנת שלילי (negative), האות P מסמנת חיובי (positive), האות B מסמנת גדול (big), האות S מסמנת קטן (small), האותיות ZE מסמנות אפס (zero). כך שלמשל EPB מסמן error positive big.
ENB |
True אם המנוע מסתובב לאט במידה רבה |
ENS |
True אם המנוע מסתובב לאט במידה מועטה |
EZE |
True אם המנוע מסתובב
במהירות הנכונה |
EPS |
True אם המנוע מסתובב מהר במידה מועטה |
EPB |
True אם המנוע מסתובב מהר במידה רבה |
|
|
DNB |
True אם המנוע מאיט את מהירותו במידה רבה |
DNS |
True אם המנוע מאיט מעט את מהירותו |
DZE |
True אם מהירות המנוע נשארת
אותו דבר |
DPS |
True אם המנוע מאיץ מעט את מהירותו |
DPB |
True אם המנוע מאיץ את מהירותו במידה רבה |
|
|
INB |
True אם המנוע ממשיך לצבור מהירות יותר מדי
איטית |
INS |
True אם המנוע ממשיך לצבור מהירות איטית
במידה מועטה |
INE |
Trueאם המנוע הגיע למהירותו הנכונה |
IPS |
True אם המנוע ממשיך לצבור מהירות גבוהה
במידה מועטה |
IPB |
True אם המנוע ממשיך לצבור מהירות גבוהה
מדי |
נגדיר חמש תוויות עבור המוצא
הקריספי (טבלה 2).
טבלה 2: הגדרת תוויות למוצא הקריספי
ONB |
True אם צריך להפחית את מהירות המנוע במידה
רבה |
ONS |
True אם צריך להפחית את מהירות המנוע במידה
מעטה |
OZE |
True אם מהירות המנוע צריכה להשאר כמות
שהיא |
OPS |
True אם צריך להגדיל את מהירות המנוע במידה
מעטה |
OPB |
True אם צריך להגדיל את
מהירות המנוע במידה רבה |
כעת נכתוב את חלק התוכנית שמכיל את
המשתנים הפאזיים. נשים לב כי ההנחייה (directive) ds משמשת לשמירת שטח לא מאותחל. לא נוצר שום קוד על ידי הנחייה זו. היא
פשוט מקדמת את ערך מונה מיקום האסמבלר בגודל שמופיע בשדה האופרנד (הפרמטר). לשם
שמופיע בשדה התווית מושם הערך שעליו מצביע מונה המיקום לפני שמונה המיקום מקודם.
בתוכנית 6, למשתנה speed מושם ערך של $1000, למשתנה acceleration מושם
ערך של $1001,
למשתנה distance מושם ערך של $1002, למשתנה EPB מושם ערך של $1003 וכו'. קיימת גם אפשרות להשתמש
בהנחיית set בכדי לבצע השמה של ערך מונה המיקום למשתנה, שיכול לשמש כעזר
להקצאת מיקום והסרתה עבור המשתנים המקומיים בעזרת ההוראה LEAS. אנו
לא השתמשנו בזה כאן.
יש לשים לב שאנו מניחים
כי הקבועים והמשתנים המופיעים בתוכניות שבמאמר זה מוגדרים קודם לכן בתוכנית ראשית.
(למשל r_speed equ
80). מדובר ב:
tctl4, tmsk1, tmsk2, tios, tflg1, tflg2, l_count, r_count, l_sum, r_sum,
l_speed, r_speed.
|
|
01 |
|
org |
$1000 |
||||||||||
|
|
02 |
* crisp inputs |
|
|
||||||||||
|
|
03 |
error: |
ds |
1 |
|
|||||||||
|
|
04 |
derivative: |
ds |
1 |
|
|||||||||
|
|
05 |
integral: |
ds |
1 |
|
|||||||||
|
|
06 |
* input membership
variables |
||||||||||||
|
|
07 |
fuzvar: |
ds |
0 |
* inputs |
|||||||||
|
|
08 |
EPB: |
ds |
1 |
* speed way too
fast |
|||||||||
|
|
09 |
EPS: |
ds |
1 |
* speed too fast |
|||||||||
|
|
10 |
EZE: |
ds |
1 |
* speed ok |
|||||||||
|
|
11 |
ENS: |
ds |
1 |
* speed too slow |
|||||||||
|
|
12 |
ENB: |
ds |
1 |
* speed way too
slow |
|||||||||
|
|
13 |
DPB: |
ds |
1 |
* speed
decreasing a lot |
|||||||||
|
|
14 |
DPS: |
ds |
1 |
* speed
decreasing |
|||||||||
|
|
15 |
DZE: |
ds |
1 |
* speed constant |
|||||||||
|
|
16 |
DNS: |
ds |
1 |
* speed
increasing |
|||||||||
|
|
17 |
DNB: |
ds |
1 |
* speed
increasing a lot |
|||||||||
|
|
18 |
IPB: |
ds |
1 |
* accumulating
low speed error a lot |
|||||||||
|
|
19 |
IPS: |
ds |
1 |
* accumulating
low speed error |
|||||||||
|
|
20 |
IZE: |
ds |
1 |
* average of
zero speed error |
|||||||||
|
|
21 |
INS: |
ds |
1 |
* accumulating
high speed error |
|||||||||
|
|
22 |
INB: |
ds |
1 |
* accumulating
high speed error a lot |
|||||||||
|
|
23 |
|
|
|
|
|
||||||||
|
|
24 |
Fuzout: |
ds |
0 |
* outputs |
|||||||||
|
|
25 |
OPB: |
ds |
1 |
* add a lot of
power to the system |
|||||||||
|
|
26 |
OPS: |
ds |
1 |
* add some power
to the system |
|||||||||
|
|
27 |
OZE: |
ds |
1 |
* leave power as
is |
|||||||||
|
|
28 |
ONS: |
ds |
1 |
* subtract some
power from the system |
|||||||||
|
|
29 |
ONB: |
ds |
1 |
* subtract a lot
of power from the system |
|||||||||
|
|
30 |
BREAK: |
ds |
1 |
* apply break? |
|||||||||
|
|
31 |
* input
membership variables relative offsets |
||||||||||||
|
|
32 |
epb: |
equ |
0 |
* speed way too
fast * speed too fast * speed ok * speed too slow * speed way too
slow |
|||||||||
|
|
33 |
eps: |
equ |
1 |
||||||||||
|
|
34 |
eze: |
equ |
2 |
||||||||||
|
|
35 |
ens: |
equ |
3 |
||||||||||
|
|
36 |
enb: |
equ |
4 |
||||||||||
|
|
37 |
dpb: |
equ |
5 |
* speed
decreasing a lot |
|||||||||
|
|
38 |
dps: |
equ |
6 |
* speed
decreasing |
|
||||||||
|
|
39 |
dze: |
equ |
7 |
* speed constant |
|
||||||||
|
|
40 |
dns: |
equ |
8 |
* speed
increasing |
|
||||||||
|
|
41 |
dnb: |
equ |
9 |
* speed
increasing a lot |
|
||||||||
|
|
42 |
ipb: |
equ |
10 |
* accumulating
low speed error a lot |
|||||||||
|
|
43 |
ips: |
equ |
11 |
* accumulating
low speed error |
|||||||||
|
|
44 |
ize: |
equ |
12 |
* average of
zero speed error |
|||||||||
|
|
45 |
ins: |
equ |
13 |
* accumulating
high speed error |
|||||||||
|
|
46 |
inb: |
equ |
14 |
* accumulating high
speed error a lot |
|||||||||
|
|
47 |
* output
membership variables |
||||||||||||
|
|
48 |
opb: |
equ |
15 |
* add a lot of
power to the system |
|||||||||
|
|
49 |
ops: |
equ |
16 |
* add some power
to the system |
|||||||||
|
|
50 |
oze: |
equ |
17 |
* leave power as
is |
|||||||||
|
|
51 |
ons: |
equ |
18 |
* subtract some
power from the system |
|||||||||
|
|
52 |
onb: |
equ |
19 |
* subtract a lot
of power from the system |
|||||||||
|
|
53 |
* crisp outputs: |
|
|
|
|||||||||
|
|
54 |
dpower: |
ds |
1 |
|
|
||||||||
0 32
64 96 128 160
192 224 255
Error +128
|
1 |
|
org |
$1200 |
|
2 |
* format is point1, point2, slope1, slope2 |
|||
3 |
s_tab |
dc.b |
192, 255, 8, 0 |
* EPB |
4 |
|
dc.b |
128, 224, 5, 8 |
* EPS |
5 |
|
dc.b |
112, 144, 16, 16 |
* EZE |
6 |
|
dc.b |
32, 128, 8, 5 |
* ENS |
7 |
|
dc.b |
0, 64, 0, 8 |
* ENB |
ההנחייה dc.b היא קיצור של define constant byte שמורה
להכניס המספרים הבאים כבתים בזכרון החל מהכתובת המוגדרת בהנחיית org. חמשת התוויות (terms) מוגדרות לפי
נקודות הקצה והשיפועים שלהן. כזכור השיפועים תמיד חיוביים, שיפוע אינסופי מקבל את
הערך אפס והנקודה הראשונה שמופיעה בכל תווית היא הנקודה השמאלית.
פונקציית ההשתייכות של נגזרת השגיאה
משורטטת באיור 9.
1 |
* format is point1, point2, slope1, slope2 |
|||
2 |
a_tab |
dc.b |
224, 255, 16, 0 |
* DPB |
3 |
|
dc.b |
160, 255, 16, 4 |
* DPS |
4 |
|
dc.b |
64, 192, 8, 8 |
* DZE |
5 |
|
dc.b |
0, 96, 4, 16 |
* DNS |
6 |
|
dc.b |
0, 32, 0, 16 |
* DNB |
פונקציית ההשתייכות של
אינטגרל (סכימת) השגיאה משורטטת באיור 10.
1 |
* format is point1, point2, slope1, slope2 |
|||
2 |
i_tab |
dc.b |
224, 255, 16, 0 |
* IPB |
3 |
|
dc.b |
192, 255, 16, 8 |
* IPS |
4 |
|
dc.b |
32, 224, 4, 4 |
* IZE |
5 |
|
dc.b |
0, 64, 8, 16 |
* INS |
6 |
|
dc.b |
0, 32, 0, 16 |
* INB |
המוצאים כאן לא מופיעים בצורת פונקצית
השתייכות כמו במקרה הקלאסי של לוגיקה עמומה שבה המוצא הוא זה בעל דרגת ההשתייכות
הגבוהה ביותר (max). כאן מבצעים איחוד של מוצאים פאזיים
גולמיים למוצא מספרי מאוחד תוך שימוש במספרים בודדים (singletons). מספר singleton עבור משתנה לשוני הוא ערך יחיד
שמושם למשתנה מוצא אם הכללים מייצרים משתנה לשוני זה ברמת ביטחון של 1, וכל שאר
המשתנים מקבלים דרגת בטחון של אפס. למשל, המספר singleton עבור חם מאוד יכול להיות 100, המספר singleton עבור חם יכול להיות 95, זה שעבור פושר יכול להיות 90 וכו'. בהוראת
WAV מתבצע ממוצע משוקלל (משוואה 1) של מספרי ה- singleton (Si) ל- n מוצאים
כאלו, לפי רמת בטחון מוצא פאזי (Fi). באיור 11 מופיעות תוויות המוצא הפאזי ומימוש
מוצא קריספי.
איור 11: המוצא הקריספי
בתהליך הדה פאזיפיקציה
משתנה ההשתייכות הפאזי עבור המוצא, מגדיר
את המוצא הקריספי, המספר שיש להוסיף ל- duty cycle
כפונקציה של ערך ההשתייכות. אם למשל התווית ONS היתה
אמת (255) ושאר הארבע היו שקר (0), אז השינוי במוצא היה -60. (איור 8).
באופן כללי, מחשבים את המוצא הקריספי
כממוצע משוקלל של תוויות השתייכות פאזיות בצורה הבאה (משוואה 2):
(2) DU=(decrease a lot * (-128)
+ some decrease * (-60) + same * 0 + some increase * (60) + increase a lot *
(127))/( decrease a lot + some decrease + same + some increase + increase a
lot)
בדוגמה שלנו אם אחד ממשתני הכניסה מכיל
את האותיות NB הרי שהמוצא יהיה OPB. אם
אחד ממשתני הכניסה מכיל את האותיות PB הרי שהמוצא
יהיה ONB. אחרת, אם אחד ממשתני הכניסה מכיל את האותיות NS הרי שהמוצא יהיה OPS, ואילו אם אחד
ממשתני הכניסה מכיל את האותיות PS הרי שהמוצא
יהיה ONS. אם כל המשתנים מכילים את הערך ZE הרי
שהמוצא יהיה OZE. תוכנית 10 מכילה את ההגדרות של החוקים
הפאזיים.
1. |
rules: |
dc.b |
enb,dnb,inb,$FE,opb,$FE |
* if ENB and
DNB and INB then OPB |
2. |
|
dc.b |
enb,dnb,ins,$FE,opb,$FE |
* if ENB and
DNB and INS then OPB |
3. |
|
dc.b |
enb,dns,inb,$FE,opb,$FE |
* if ENB and
DNS and INB then OPB |
4. |
|
dc.b |
enb,dns,ins,$FE,opb,$FE |
* if ENB and
DNS and INS then OPB |
5. |
|
dc.b |
ens,dnb,inb,$FE,opb,$FE |
* if ENS and
DNB and INB then OPB |
6. |
|
dc.b |
ens,dnb,ins,$FE,opb,$FE |
* if ENS and
DNB and INS then OPB |
7. |
|
dc.b |
enb,dze,inb,$FE,opb,$FE |
* if ENB and
DZE and INB then OPB |
8. |
|
dc.b |
enb,dze,ins,$FE,opb,$FE |
* if ENB and
DZE and INS then OPB |
9. |
|
dc.b |
enb,dze,ize,$FE,opb,$FE |
* if ENB and
DZE and IZE then OPB |
10.
|
|
dc.b |
enb,dns,ize,$FE,opb,$FE |
* if ENB and
DNS and IZE then OPB |
11.
|
|
dc.b |
eze,dnb,inb,$FE,opb,$FE |
* if EZE and
DNB and INB then OPB |
12.
|
|
dc.b |
eze,dnb,ins,$FE,opb,$FE |
* if EZE and
DNB and INS then OPB |
13.
|
|
dc.b |
eze,dns,inb,$FE,opb,$FE |
* if EZE and
DNS and INB then OPB |
14.
|
|
dc.b |
epb,dpb,ipb,$FE,onb,$FE |
* if EPB and
DPB and IPB then ONB |
15.
|
|
dc.b |
epb,dpb,ips,$FE,onb,$FE |
* if EPB and
DPB and IPS then ONB |
16.
|
|
dc.b |
epb,dps,ipb,$FE,onb,$FE |
* if EPB and
DPS and IPB then ONB |
17.
|
|
dc.b |
epb,dps,ips,$FE,onb,$FE |
* if EPB and
DPS and IPS then ONB |
18.
|
|
dc.b |
eps,dpb,ipb,$FE,onb,$FE |
* if EPS and
DPB and IPB then ONB |
19.
|
|
dc.b |
eps,dpb,ips,$FE,onb,$FE |
* if EPS and
DPB and IPS then ONB |
20.
|
|
dc.b |
eps,dps,ipb,$FE,onb,$FE |
* if EPS and
DPS and IPB then ONB |
21.
|
|
dc.b |
eps,dps,ips,$FE,onb,$FE |
* if EPS and
DPS and IPS then ONB |
22.
|
|
dc.b |
epb,dze,ipb,$FE,onb,$FE |
* if EPB and
DZE and IPB then ONB |
23.
|
|
dc.b |
epb,dze,ips,$FE,onb,$FE |
* if EPB and
DZE and IPS then ONB |
24.
|
|
dc.b |
epb,dze,ize,$FE,onb,$FE |
* if EPB and
DZE and IZE then ONB |
25.
|
|
dc.b |
epb,dps,ize,$FE,onb,$FE |
* if EPB and
DPS and IZE then ONB |
26.
|
|
dc.b |
eze,dpb,ipb,$FE,onb,$FE |
* if EZE and
DPB and IPB then ONB |
27.
|
|
dc.b |
eze,dpb,ips,$FE,onb,$FE |
* if EZE and
DPB and IPS then ONB |
28.
|
|
dc.b |
eze,dps,ipb,$FE,onb,$FE |
* if EZE and
DPS and IPB then ONB |
29.
|
|
dc.b |
eze,dps,ips,$FE,onb,$FE |
* if EZE and
DPS and IPS then ONB |
30.
|
|
dc.b |
ens,dns,ins,$FE,ops,$FE |
* if ENS and
DNS and INS then OPS |
31.
|
|
dc.b |
ens,dns,ize,$FE,ops,$FE |
* if ENS and
DNS and IZE then OPS |
32.
|
|
dc.b |
ens,dze,ins,$FE,ops,$FE |
* If ENS and
DZE and INS then OPS |
33.
|
|
dc.b |
ens,dze,ize,$FE,ops,$FE |
* If ENS and
DZE and IZE then OPS |
34.
|
|
dc.b |
eze,dns,ins,$FE,ops,$FE |
* if EZE and
DNS and INS then OPS |
35.
|
|
dc.b |
eze,dns,ize,$FE,ops,$FE |
* if EZE and
DNS and IZE then OPS |
36.
|
|
dc.b |
eze,dze,ins,$FE,ops,$FE |
* if EZE and
DZE and INS then OPS |
37.
|
|
dc.b |
eze,dze,ize,$FE,oze,$FE |
* if EZE and
DZE and IZE then OZE |
38.
|
|
dc.b |
eps,dps,ips,$FE,ons,$FE |
* if EPS and
DPS and IPS then ONS |
39.
|
|
dc.b |
eps,dps,ize,$FE,ons,$FE |
* if EPS and
DPS and IZE then ONS |
40.
|
|
dc.b |
eps,dze,ips,$FE,ons,$FE |
* if EPS and
DZE and IPS then ONS |
41.
|
|
dc.b |
eps,dze,ize,$FE,ons,$FE |
* if EPS and
DZE and IZE then ONS |
42.
|
|
dc.b |
eze,dps,ips,$FE,ons,$FE |
* if EZE and
DPS and IPS then ONS |
43.
|
|
dc.b |
eze,dps,ize,$FE,ons,$FE |
* if EZE and
DPS and IZE then ONS |
44.
|
|
dc.b |
eze,dze,ips,$FE,ons,$FE |
* if EZE and
DZE and IPS then ONS |
45.
|
|
dc.b |
$FF |
|
נשים לב שבסיום רשימת תוויות הכניסה
(בין רשימת תוויות הכניסה לרשימת תווית המוצא) מפריד המספר השמור $FE. בסיום רשימת המוצא מופיע גם כן
המספר השמור $FE. בסיום
הרשימה כולה מופיע המספר השמור $FF.
נגדיר כעת את מספרי ה singleton עבור ביצוע תהליך ה- defuzzification. זה
מופיע בטבלה 3.
טבלה 3: מספרי singleton עבור תהליך הדה פאזיפיקציה שממיר את השתייכות המוצא למוצא קריספי
תויות מוצא פאזי |
singleton ערך מספר |
ONB |
-128 |
ONS |
-60 |
OZE |
0 |
OPS |
+60 |
OPB |
+128 |
כעת נכתוב התוכנית לקביעת ה- Duty cycle בכל מנוע. זה מופיע בתוכנית 11.
תוכנית 11: תוכנית פסיקת
גלישת השעון לקביעת ערך duty cycle של
מנועים שמאל וימין תוך שימוש בהוראות לוגיקה עמומה של מעבד 68hc912d60a
1.
|
ovf_fuz: |
|
|
|
2.
|
addsingleton: |
dc.b |
255,188,128,68,0 |
|
3.
|
* 128
subtructed +127 60 0
-60 -128 |
|||
4.
|
|
ldaa |
l_speed |
* load
desired speed (in tics/8.125ms) for left motor |
5.
|
|
suba |
l_count |
* subtract
the actual speed (tics) from the desired one for left motor and find left
speed error |
6.
|
|
staa |
l_error |
* save
current left motor error |
7.
|
|
ldx |
#s_tab |
|
8.
|
|
ldy |
#fuzvar |
|
9.
|
|
mem |
|
* calculate
EPB |
10. |
|
mem |
|
* calculate
EPS |
11. |
|
mem |
|
* calculate
EZE |
12. |
|
mem |
|
* calculate
ENS |
13. |
|
mem |
|
* calculate
ENB |
14. |
|
ldaa |
l_error |
* load
current left motor error |
15. |
|
suba |
old_lerr |
* subtract
the previous left speed error from the current one and find the derivative
(tics) |
16. |
|
ldx |
#a_tab |
|
17. |
|
mem |
|
* calculate
DPB |
18. |
|
mem |
|
* calculate
DPS |
19. |
|
mem |
|
* calculate
DZE |
20. |
|
mem |
|
* calculate
DNS |
21. |
|
mem |
|
* calculate
DNB |
22. |
|
ldaa |
l_error |
* load acca
with current left speed error |
23. |
|
adda |
l_sum |
* add all
errors and find left speed integral
value |
24. |
|
bvc |
l_i_calc |
* if
overflow occurred assign maximum value of 240 to acca |
25. |
|
ldaa |
#240 |
|
26. |
l_i_calc: |
ldx |
#i_tab |
|
27. |
|
mem |
|
* calculate
IPB |
28. |
|
mem |
|
* calculate
IPS |
29. |
|
mem |
|
* calculate
IZE |
30. |
|
mem |
|
* calculate
INS |
31. |
|
mem |
|
* calculate
INB |
32. |
|
ldab |
#5 |
* five
output labels |
33. |
cloop: |
clr |
1,y+ |
* clear OPB,
OPS, OZE, ONS, ONB |
34. |
|
dbne |
b,cloop |
|
35. |
|
ldx |
#rules |
|
36. |
|
ldy |
#fuzvar |
|
37. |
|
ldaa |
#$ff |
|
38. |
|
rev |
|
* prepare outputs
from rules and inputs |
39. |
|
ldy |
#fuzout |
|
40. |
|
ldx |
#addsingleton |
* prepare
for crisp output |
41. |
|
ldab |
#5 |
|
42. |
|
wav |
|
* find crisp
output |
43. |
|
ediv |
|
|
44. |
|
tfr |
y,d |
|
45. |
|
subb |
#128 |
* find final
DU for left duty |
46. |
|
stab |
dpower |
|
47. |
|
addb |
pwdty1 |
* calculate
new duty value |
48. |
|
bvc |
ldtychk |
* if
overflow occurred, assign max or min values |
49. |
|
ldab |
dpower |
|
50. |
|
bmi |
lmindty |
* overflow
of low value, so assign min duty |
51. |
|
movb |
#240, pwdty1 |
* overflow
of high value, so assign max duty |
52. |
|
jmp |
lhskping |
* update
left old error and integral values |
53. |
lmindty: |
movb |
#40, pwdty1 |
|
54. |
|
jmp |
lhskping |
|
55. |
ldtychk: |
cmpb |
#240 |
* if value
is more than 240, force 240 |
56. |
|
blo |
llowchk |
|
57. |
|
movb |
#240, pwdty1 |
|
58. |
|
jmp |
lhskping |
|
59. |
llowchk: |
cmpb |
#40 |
* if value
is less than 40, force 40 |
60. |
|
bhi |
lputdc |
|
61. |
|
movb |
#40, pwdty1 |
|
62. |
|
jmp |
lhskping |
|
63. |
lputdc: |
stab |
pwdty1 |
* otherwise
assign new duty value to pwdty1 (left motor speed) |
64. |
lhskping: |
movb |
l_error,old_lerr |
* update old
error value |
65. |
|
ldaa |
l_error |
* update
left integral value |
66. |
|
adda |
l_sum |
|
67. |
|
bvc |
l_integ |
|
68. |
|
ldaa |
l_error |
|
69. |
|
bmi |
l_minsum |
|
70. |
|
movb |
#240, l_sum |
* keep max
value at 240 |
71. |
|
jmp |
rht_chk |
|
72. |
l_minsum: |
movb |
#40, l_sum |
* keep min
value at 40 |
73. |
|
jmp |
rt_chk |
|
74. |
l_integ: |
stab |
l_sum |
|
75. |
rt_chk |
ldaa |
r_speed |
* load
desired speed (in tics/8.125ms) for right motor |
76. |
|
suba |
r_count |
* subtract
the actual speed (tics) from the desired one for right motor and find right
speed error |
77. |
|
staa |
r_error |
* save
current right motor error |
78. |
|
ldx |
#s_tab |
|
79. |
|
ldy |
#fuzvar |
|
80. |
|
mem |
|
* calculate
EPB |
81. |
|
mem |
|
* calculate
EPS |
82. |
|
mem |
|
* calculate
EZE |
83. |
|
mem |
|
* calculate
ENS |
84. |
|
mem |
|
* calculate
ENB |
85. |
|
ldaa |
r_error |
* load
current right motor error |
86. |
|
suba |
old_rerr |
* subtract
the previous right speed error from the current one and find the derivative
(tics) |
87. |
|
ldx |
#a_tab |
|
88. |
|
mem |
|
* calculate
DPB |
89. |
|
mem |
|
* calculate
DPS |
90. |
|
mem |
|
* calculate
DZE |
91. |
|
mem |
|
* calculate
DNS |
92. |
|
mem |
|
* calculate
DNB |
93. |
|
ldaa |
r_error |
* load acca
with current right speed error |
94. |
|
adda |
r_sum |
* add all
errors and find right speed integral
value |
95. |
|
bvc |
r_i_calc |
* if
overflow occurred assign maximum value of 240 to acca |
96. |
|
ldaa |
#240 |
|
97. |
r_i_calc: |
ldx |
#i_tab |
|
98. |
|
mem |
|
* calculate
IPB |
99. |
|
mem |
|
* calculate
IPS |
100.
|
|
mem |
|
* calculate
IZE |
101.
|
|
mem |
|
* calculate
INS |
102.
|
|
mem |
|
* calculate
INB |
103.
|
|
ldab |
#5 |
* five
output labels |
104.
|
cloop: |
clr |
1,y+ |
* clear OPB,
OPS, OZE, ONS, ONB |
105.
|
|
dbne |
b,cloop |
|
106.
|
|
ldx |
#rules |
|
107.
|
|
ldy |
#fuzvar |
|
108.
|
|
ldaa |
#$ff |
|
109.
|
|
rev |
|
* prepare
outputs from rules and inputs |
110.
|
|
ldy |
#fuzout |
|
111.
|
|
ldx |
#addsingleton |
* prepare
for crisp output |
112.
|
|
ldab |
#5 |
|
113.
|
|
wav |
|
* find crisp
output |
114.
|
|
ediv |
|
|
115.
|
|
tfr |
y,d |
|
116.
|
|
subb |
#128 |
* find final
DU for left duty |
117.
|
|
stab |
dpower |
|
118.
|
|
addb |
pwdty0 |
* calculate
new duty value |
119.
|
|
bvc |
rdtychk |
* if
overflow occurred, assign max or min values |
120.
|
|
ldab |
dpower |
|
121.
|
|
bmi |
rmindty |
* overflow
of low value, so assign min duty |
122.
|
|
movb |
#240, pwdty0 |
* overflow
of high value, so assign max duty |
123.
|
|
jmp |
rhskping |
* update
right old error and integral values |
124.
|
rmindty: |
movb |
#40, pwdty0 |
|
125.
|
|
jmp |
rhskping |
|
126.
|
rdtychk: |
cmpb |
#240 |
* if value
is more than 240, force 240 |
127.
|
|
blo |
rlowchk |
|
128.
|
|
movb |
#240, pwdty0 |
|
129.
|
|
jmp |
rhskping |
|
130.
|
rlowchk: |
cmpb |
#40 |
* if value
is less than 40, force 40 |
131.
|
|
bhi |
rputdc |
|
132.
|
|
movb |
#40, pwdty0 |
|
133.
|
|
jmp |
rhskping |
|
134.
|
rputdc: |
stab |
pwdty0 |
* otherwise
assign new duty value to pwdty0 (right motor speed) |
135.
|
rhskping: |
movb |
r_error,old_rerr |
* update old
right error value |
136.
|
|
ldaa |
r_error |
* update
right integral value |
137.
|
|
adda |
r_sum |
|
138.
|
|
bvc |
r_integ |
|
139.
|
|
ldaa |
r_error |
|
140.
|
|
bmi |
r_minsum |
|
141.
|
|
movb |
#240, r_sum |
* keep max
value at 240 |
142.
|
|
jmp |
sium |
|
143.
|
r_minsum: |
movb |
#40, r_sum |
* keep min
value at 40 |
144.
|
|
jmp |
sium |
|
145.
|
r_integ: |
stab |
r_sum |
|
146.
|
sium: |
movb |
#$00,
l_count |
* clear left
counter for next loop |
147.
|
|
movb |
#$00,
r_count |
* clear
right counter for next loop |
148.
|
|
movb |
#$03, tflg1 |
* clear
flags |
149.
|
|
movb |
#$80,
tflg2 |
* clear TCNT
overflow flag |
150.
|
|
rti |
|
* go out
until next TCNT overflow |
ההערות שבגוף התוכנית מסבירות את הנעשה
בה. לכן אעבור על התוכנית באופן כללי.
שורה 1 מכילה את שם שגרת הפסיקה. שגרה זו
תתבצע כל 8.125 ms שזה
הזמן בין גלישה אחת של השעון לזו שבאה אחריה.
בשורה 2, מופיעים ערכי ה singleton לפונקציית
השתייכות המוצא.
שורות 4 עד 74 מטפלות במציאת ההפרש שיש להוסיף לduty של
מנוע שמאל בעקבות נתון המהירות הנוכחית (tics/8.125ms)
וביצוע ניתוח pid בפאזי. שורות 4 ו- 5 מוצאות את
השגיאה, שורות 7-13 משמשות לחישוב דרגת ההשתייכות של מבוא פרופורצית
השגיאה, שורות 14 עד 21 משמשות לחישוב דרגת ההשתייכות של מבוא נגזרת
השגיאה, ושורות 22 עד 31 משמשות לחישוב דרגת ההשתייכות של מבוא אינטגרל
השגיאה.
שורות 32 עד 34 דואגות לאיפוס תוויות המוצא, שורות 35 עד 38 מבצעות את
החוקים הפאזיים על הכניסות הפאזיות שהתקבלו מהוראות mem בשורות
7 עד 31.
שורות 39 עד 43 דואגות למציאת המוצא הקריספי של האיטרציה הנוכחית למנוע שמאל.
בשורה 43 מתבצעת הוראת ediv בכדי לקבל הנתון ב-Y והשארית ב- D. בשורה 44
אנו מעבירים את Y ל- D בכדי
שהתוצאה תהיה ב- D. אנו מתחשבים ב- 8 ביטים הנמוכים (LSB),
הנמצאים בצובר B. בשורה 45 מפחיתים 128 מהתוצאה
בכדי לקבל גם ערכים שליליים ולהתאים התוצאה לתחום המבוקש. התוצאה נשמרת ב- dpower. את התוצאה מחברים למספר שכבר נמצא ב- pwdty1 האוגר
שמכיל את ה duty של מנוע שמאל.
שורות 48 עד 63 בודקות האם קרתה גלישה. אם כן מכניסים ל- pwdty1 ערכי קיצון 240 או 40, לפי כיוון הגלישה. אם לא קרתה גלישה נבדק
ערכו החדש של pwdty1. אם הוא בתחום שבין 240 לבין 255, ערכו
מושם למקסימום המותר, 240. אם הוא בתחום שבין 0 ל- 40, ערכו מושם למינימום המותר
40. אחרת נשמר ערכו החדש ב pwdty1. בשורות 63 עד
74 מעודכנים
ערכי old_lerr (השגיאה הקודמת), והאינטגרל (l_sum) תוך
שמירה על ערכי המינימום והמקסימום המותרים.
בצורה דומה מתעדכן ערכו של pwdty0 של מנוע ימין בשורות
75 עד 145. בשורות 146 עד 149 מאופסים המונים של אנקודר מנוע ימין ואנקודר
מנוע שמאל, וכמו כן מאופסים הדגלים של קצוות האנקודרים העולים, ושל גלישת השעון.
שורה 150 מסמנת את סיום שגרת פסיקת הגלישה והחזרת השליטה לתוכנית הראשית.
,בעלי אתר זה לא ישאו באחריות כלשהי לכל נזק, כספי או אחר
שייגרם במישרין או בעקיפין משימוש במידע המצוי באתר זה
כל הזכויות שמורות לאסף פוניס, גיא יונה ואלי קולברג ©
אין להעתיק תכנים מאתר זה ללא רשות בכתב ממנהלי האתר