Final Report
.1פרק - 1מבוא .1.1ניסויים התנהגותיים ניסויים נוירופיזיולוגיים התנהגותיים דורשים רישום אותות חשמליים מבעל חיים חי ובמקרים מסוימים גם מתנהג ,כשהתנהגות מוגדרת כתגובה של בעל החיים לגירויים חיצוניים. קיים מספר רב של פרוטוקולים לניסויים התנהגותיים .ניתן לחלק אותם לקבוצות לפי סוגי גירוי ודרגות התנהגות ובהתאם לכך גם דרישות ממערכת הניסויית ומתוכנה התנהגותית. .1קבוצה של ניסויים בהם הנבדק רדום .הגירוי החשמלי מוכל על נוירון מסוים או קבוצת נוירונים והתגובה ה"התנהגותית" נמדדת באזור אחר מתא בודד או קבוצה של תאים. בניסויים מסוג זה אין צורך במערכת התנהגותית ,כל הדרוש הן אלקטרודות לגירוי ואלקטרודות לרישום (בד"כ .)Patch-clamp 1. Henrik Jo¨rntell, Carl-Fredrik Ekerot (2006) Properties of Somatosensory Synaptic Integration in Cerebellar Granule Cells In Vivo. J Physiol
.2קבוצה של ניסויים בהם הנבדק ער ונדרש לבצע פעולה התנהגותית פשוטה יחסית ,כגון להסתכל על נקודה מסוימת במרחב או לחיצה על כפתור .בניסויים מסוג זה בנוסף למערכת הרישום ישנה גם מערכת התנהגותית ,כגון תוכנה התנהגותית אשר מציגה את התמונות לנבדק או בודקת האם הכפתור נלחץ וכדומה. 1. AK Kreiter, W Singer (1996) Stimulus-dependent synchronization of neuronal responses in the visual cortex of the awake macaque monkey. Journal of Neuroscience )2. Thomas Wachtler, Terrence J. Sejnowski, Thomas D. Albright (2003 Representation of Color Stimuli in Awake Macaque Primary Visual Cortex, Neuron
.3קבוצה של ניסויים בהם הנבדק ער ונדרש לבצע פעולות מוטוריות מסובכת יחסית .בעל החיים מתנהג במלא מובן המילה .בנוסף למערת הרישום נדרשת חומרה אשר מייצרת גירויים (ויזואליים, קוליים ,הָפטיים וכדומה) ומודדת את התנועה (כוון ,מהירות, כוח). 1. Kris M. Horn, Milton Pong and Alan R. Gibson (2003) Discharge of inferior olive cells during reaching errors and perturbations, Brain Research
.4קבוצה של ניסויים בהם הנבדק מקבל גירויים משולבים ממערכת .)VR (Virtual Realityמערכות כאלה בד"כ כוללות רובוטים הָפטיים ומחשב בעל יכולת הצגה של תמונה תלת-ממדית. מטרת המחקר של ד"ר עופר דונחין היא לחקור את תפקידם של קורטקס המוטורי והצרבלום בלמידה מוטורית ,ולכן הנבדקים יהיו ערים ויבצעו משימות התנהגותיות
שונות .למידה מוטורית תושג ע"י הפעלת כוחות הפרעה ( )Perturbation forcesעל הגפה שתבצע את המשימה וחזרה על המשימה עד שהתבנית של התנועה עם ההפרעה תהיה דומה לתבנית של התנועה בלי ההפרעה .הניסויים לא יכללו (לפחות לא בשלב הראשון) הדמיה וירטואלית (.)VR .1.2מבנה כללי של המערכת מערכת הניסוי מורכבת ממרכיבי חומרה שונים ותוכנה התנהגותית.
Perturbation Phantom Reward Phantom Food Pump Sound cue Force Plates Behavioral Software
1. 2. 3. 4. 5. 6.
מערכת הניסוי כוללת את כל המרכיבים הדרושים לביצוע ניסוי התנהגותי ולבצע את כל המשימות הדרושות. המערכת כוללת שני רובוטים הָפטיים – פאנטומים ,כאשר לכל אחד מהם תפקיד שונה .תפקידו של פאנטום ( )1פרמיום ( )Premiumהוא להפעיל כוחות מפריעים ( )Perturbation forcesעל הגפה של החתול .הכוחות יכולים להיות מסוגים שונים ודרגות שונות של מורכבות :כוח קבוע בכוון קבוע ,כוח קבוע המאונך לכוון התנועה ,כוח משתנה בהתאם למהירות וכוון התנועה וכו'. תפקידו של פאנטום ( )2אומני ( )Omniהוא לשמש מטרה (גירוי ויזואלי) שאותה החתול ירצה לתפוס כדי לקבל תגמול .פאנטום זה מספק את המסגרת של מהלך הניסוי ומבחינת החתול זהו הממשק הוויזואלי של המערכת .הוא לא מפעיל כוחות על החתול ,אלא מבצע פעולות כגון :הגעה לנקודה שבה הוא נגיש לחתול, המתנה בגובה מסוים ,הדמיה של משטח חסר חיכוך וכולי ,בהתאם לדרישות מהלך הניסוי.
תפקידה של המשאבה הוא לספק את האוכל (התגמול) לחתול .האוכל יכול להינתן בצורה אוטומטית לפי ההגדרות של הניסוי או בצורה ידנית ע"י לחוצה על כפתור בתוכנה. החתול יקבל סימנים קוליים בשלבים שונים של הניסוי ,למשל ,צליל שיסמן את תחילת הניסוי. תפקידם של force platesהוא לספק מידע נוסף למערכת על מיקומו והתנהגותו של החתול בזמן הניסוי .לפי המשקל ניתן לדעת האם החתול עומד עם שני הגפיים ,מושיט גפה וכו'. כל המערכת מנוהלת ע"י תוכנה התנהגותית .היא זו שיודעת לאסוף את כל המידע מכל החומרה ולנהל את הניסוי לפי תוצאות העיבוד של מידע זה. .1.3תאור התוכנה ההתנהגותית תוכנת Catsהיא תוכנה התנהגותית אשר מנהלת את כל ההיבטים של הניסוי ע"י כך שמספקת לחוקר כלים פשוטים ,אך גמישים ,להרכבת מהלך הניסוי ,שליטה על ביצועו ,ע"י שליטה על כל מרכיבי החומרה והסנכרון ביניהם ,ושליטה על התנהגות בעל החיים (באמצאות החומרה) כאשר מבצע תנועות ההושטה במהלך הניסוי.
.1פרק – 2רקע תכני – Object Oriented Programming .1.4תכנות מונחה עצמים תכנות מונחה עצמים הוא פרדיגמה תכנותית שמשתמשת ב"אובייקטים" ( )Objectsליצירת תוכנות מחשב ואפליקציות .שפת ,++Cבניגוד לשפת , Cהיא שפה מונחת עצמים. מושגים שמתקשרים עם תכנות מונחה עצמים הם הורשה (,)Inheritance מודולאריות ( ,)Modularityפולימורפיזם – ריבוי צורות ( )Polymorphismו אנקפסולציה (.)Encapsulation אובייקט בתכנות הוא כמו אובייקט בחיים – יש לו תכונות ויש פעולות שאפשר לעשות איתו (עליו) .רכב הוא אובייקט .צבע הרכב ,מספר גלגלים ,יצרן וכו' הן התכונות של הרכב .הרכב יכול לנסוע ,לעצור ,לחנות וכו' – אלא הן הפעולות שהוא יכול לבצע. הדוגמא הכי פשוטה לאובייקט בתכנות היא משתנה ,שהוא סוג של אובייקט. משתנה מסוג Integerהוא משתנה פשוט .המאפיין היחיד שלו (או בשפה יותר מקצועית )Data memberהוא הערך שהוא יכול להחזיק .הפעולות שאפשר לבצע עליו הן – חיבור ,חיסור וכו' .כאשר המשתנה מוגדר ;int iVariable
אנחנו בעצם יוצרים אובייקט מסוג ,intההגדרה של סוג האובייקט נקראת מחלקה ( .)classהמחלקה היא זו שמגדירה מה הם ה data members-של האובייקט ומה הם ה Member functions-שלו (הפעולות שאפשר לבצע בהקשר לאובייקט). הורשה ( )Inheritanceהיא יכולת להשתמש במחלקה קיימת כדי ליצור סוג אובייקט חדש (כמו ההורשה בהקשר הגנטי) .למחלקת הבת יהיו אותן תכונות ויכולות כמו למחלקת האם עם אפשרות להוסיף אפשרויות חדשות או לשנות את הישנות. לדוגמא אם יש לנו מחלקה "בעל חיים" (שתכלול הגדרות הכי כלליות עבור יצור חי) ונרצה להוריש ממנה מחלקה "בן-אדם" אז נרצה להוסיף למחלקה החדשה תכונה של "דיבור" ,וכך "בן-אדם" מקבל את כל התכונות של "בעל חיים" ובנוסף יודע "לדבר"( .לא השתמשתי בדוגמא ב"חושב" מכוון שיכולת זו שנויה במחלוקת). מודולאריות ( )Modularityמתארת את היכולת של השפה לקבץ פעולות ליחידות עצמאיות ולהשתמש ביחידות אלה .חשיבה מודולארית מאפשרת "למחזר" את הקוד – להשתמש במודול מסוים מספר פעמיים במקום לשכתב את הקוד. קבוצה של מתכנתים יכולה לחלק את העבודה וכך כל אחד יעובד על המודולים שלו ובסוף רק יחברו אותם ביחד (הגישה הזו מקלה מאוד בעבודה על פרויקטים משוטפים). פולימורפיזם ( - )Polymorphismבמילים פשוטות זוהי היכולת להתייחס למחלקת בת כאילו היא מחלקת אם .אשתמש שוב בדוגמא של מחלקת "בעל חיים" ומחלקה מורשת ממנה "בן-אדם" .השפה מאפשרת להגדיר מצביע מסוג "בעל חיים" ולהצביע איתו על "בן-אדם"( .תכונה זו מאוד הגיונית ,כי הרי בן- אדם הוא רק מקרה פרטי של בעל החיים). אנקפסולציה ( – )Encapsulationהמשמעות של המושג היא "הסתרת מידע". השאיפה היא שהמשתמש של המחלקה שהוגדרה על-ידי (משתמש הוא מתכנת
אחר שרוצה ליצור אובייקט מסוג המחלקה או רוצה להוריש מחלקה מהמחלקה שלי) לא יצטרך לדעת שום דבר על מבנה הפנימי של המחלקה שלי, הוא ידע רק איך להשתמש בה .דוגמא מהחיים – טלוויזיה – רובינו יודעים להשתמש בה ,אבל לא יודעים איך היא בנויה. תכנות מונחה עצמים מאפשר צורת תיכונות שונה מהגישה הקלאסית של שפת ,Cונותן למתכנת כלים רבים ומגוונים ליצירת אפליקציות טובות יותר ,אך לגישה זו ישנם גם חסרונות מכוון שלא כל רעיון בתכנות ניתן להפוך לאובייקט וגם שימוש מוגזם בחלוקה של משימות לחלקים קטנים יותר ויותר הופך את הקוד ללא ברור ולא קריא. MFC - Microsoft Foundation Class Library.1.5 זוהי ספריית קוד ( )code libraryשעוטפת חלק מ Windows API-בתוך מחלקות של ,++Cבמטרה ליצור מסגרת לפיתוח אפליקציות .היא כוללת מחלקות רבות המוגדרות כדי לטפל בפקודות מערכת ,חלונות ואובייקטים גרפים (common )controlsשונים .בנוסף ,מיקרוסופט הרחיבה את הסינטקס של ++Cעם סדרה של מאקרו ( )macrosלניהול של Windows messages, Exceptionsוכו'. MFCמספקת מסגרת של Document/Viewבמטרה להפריד את המידע ()Data מממשק משתמש ( ,)User interfaceוכך כאשר עובדים עם המידע שבתוכנה ניגשים ל Document-וכאשר עובדים עם ממשק משתמש נגשים ל( View-אשף הפרויקטים של Ms Visual Studioיוצר שתי מחלקות לפי התבנית AppNameView וAppNameDoc -ולכן אם התוכנה נקראת Catsמתקבלות שתי מחלקות: CatsViewו.)CatsDoc- .1.6ריבוי משימותMultithreading , ישנם שני סוגים של ריבוי משימות :מבוססות processומבוססות .thread processהוא בעצם תוכנה שרצה במחשב וריבוי משימות מבוסס processמאפשר למספר תוכנות לרוץ במקביל .בסוג זה של ריבוי משימות ה"תוכנה" היא היחידה הכי קטנה ובסיסית שהמערכת יכולה להריץ. threadהוא יחידה של קוד שניתן להרצה .בריבוי משימות מבוסס threadלכל processישנו לפחות threadאחד .המשמעות היא שכל תוכנה יכולה לבצע מספר משימות בו-זמנית. ניתן לסכם את ההבדלים בין שני הסוגים של ריבוי משימות בכך שריבוי משימות מבוסס processאחראי על הרצה בו זמנית של תוכנות וריבוי משימות מבוסס threadאחראי על הרצה בו זמנית של חלקים שונים של אותה התוכנה. כאן חשוב לציין שריבוי משימות בו-זמני אמיתי ניתן ליישם רק במערכות מבוססות מספר מעבדים ( ,)multiple-CPUכאשר לכל processאו threadישנה גישה בלתי מוגבלת למעבד. כאשר המערכת כוללת רק מעבד אחד ,ריבוי המשימות הוא מדומה ,זמן המעבד מחולק בין ה threads-השונים,וגודלו נקבע לפי מספר פקטורים ,כולל הpriority- שלהם. ריבוי משימות ( )threadמשונה בצורה פונדמנטלית את הארכיטקטורה של התוכנה .בניגוד לתוכנה בעלת threadאחד שמתבצעת בצורה ליניארית ,תוכנה בעלת מספר threadsכוללת אלמנטים של פרלליות ,ולכן האתגר הוא לנהל את התקשורת בין ה –.threads
.1.7זמן-אמת מערכת נקראת מערכת זמן-אמת אם הצלחת הפעולה תלויה לא רק בנכונות לוגית של התוצאה ,אלא גם בזמן ביצוע הפעולה. ישנם שני סוגים של מערכות זמן-אמת :קשיחות ( )hardומתונות (.)soft במערכת קשיחה אי ביצוע הפעולה הדרושה בזמן שהוקצב נחשבת לכישלון המערכת .מערכות מסוג זה מתבססות על אינטראקציה מאוד קרובה עם חומרה .קוצב לב הוא דוגמא טובה למערכת כזאת ,תגובה מאוחרת תגרום למוות של המטופל. במערכות מתונות אי ביצוע הפעולה הנדרשת בזמן שהוקצב לא נחשבת לכישלון אלא רק מורידה את הביצועים של המערכת .נגן מדיה שמפספס את ההצגה של חלק מהפריימים הוא דוגמא טובה. מערכת הפעלה "חלונות" אינה מערכת זמן-אמת ולכן גם כל אפליקציה שרצה במערכת זו אינה יכולה להיות אפליקציית זמן-אמת. תוכנת " "Catsשואפת ,במסגרת המגבלות של מערכת ההפעלה ,להיות מערכת זמן-אמת ,כאשר שימוש בריבוי המשימות הוא אחד הכלים המשמשים למטרה זו. .1.8רובוטים הָפֵטיים – Haptic Devices – Haptic Deviceהתקן שמטרתו היא לדמות תחושה עורית ,לדמות אפקטים של חיכוך ,צמיגות ,קשיחות וכו' .ההתקן מפעיל כוחות מתאימים על האדם (או בעל חיים) שמשתמש בו וע"י כך נותן תחושה של האפקט הרצוי. המערכת כוללת שני רובוטים הָפֵטיים ( )hapticשל חברת .Sensable מטרתם של הרובוטים האלה במערכת הניסוי שונה ברוב המקרים מייעודם המקורי ,במקום לדמות תחושה הרובוט מופעל ע"י התוכנה ומבצע משימות שונות ללא הספקת גירוי לחתול. לכל רובוט תפקיד משלו במערכת: – Phantom Omniמתפקד כמטרה שאותה החתול צריך לתפוס .מבצע פעולות כגון תזוזה למקום מסוים ,המתנה בנקודה מסוימת ,הדמיה של משטח ללא חיכוך וכו'. – Phantom Premiumמתפקד כמפריעה לתנועה ( .)Perturbatoionמפעיל כוחות על הגפה של החתול בהתאם להגדרות הניסוי ,כגון כוח קבוע בכוון מסוים ,כוח אנכי לתנועה וכו'.
.1.9משאבה המשאבה משמשת להזרמת אוכל ( )rewardלחתול בזמן ביצוע הניסוי. מפרט של המשאבה מצורף לדו"ח.
המשאבה כוללת אפשרות של שליטה מרחוק.
ניתן לראות שני טרמינלים ( )Remote Start/Stop Inputבצידה האחורי של המשאבה שמאפשרים לשלוט על הפעלה או הפסקה של המשאבה (אין אפשרות לשלוט על כוון או הכוח מרחוק). שני הטרמינלים חוברו לכרטיס A/Dוכך התקבלה האפשרות לשלוט על עבודת המשאבה מתוך התוכנה ההתנהגותית. החיבור נעשה לא ישירות ,אלא דרך מעגל אלקטרו-אופטי. למעגל שתי מטרות: .1התמרת האות של כרטיס ה A/D-להפעלת המשאבה (.)5V-on, 0V-off .2בידוד המערכת (ע"י שתי דיודות אופטיות). Pump Control Opto Pulse TTL #4 Pump Rear Panel
1k
Manual #3
2
8 7 6
Computer Control SW1 SWKEY-SPDT
3
5
6N139
U1
R1
1 6 2 7 3 8 4 9 5
DB9 Female
P1
Switch C.K. 7101
.1.10כרטיס D/A NI PCI-6229
מפרט של הכרטיס מצורף לדו"ח .כמה מילים על A/Dולהזכיר שגם לכרטיס יש .API
כבל שמתחבר לכרטיס A/Dמתחבר בצידו השני לקופסת חיבורים (SCB-68 )Shielded I/O connector blockמוגנת ומותאמת להקטנת רעש ,בעלת 68ערוצים.
Force Plates.1.11 תפקידם של Force Platesהוא לספק מידע על מידת הלחץ שאותו מפעיל החתול על המשטח שעליו הוא עומד ,וע"י כך לקבוע את מצבו של החתול ברגע נתון (האם נשען עם שתי הגפיים הקדמיות ,גפה אחת ,זז למקום לא רצוי וכו'). בשלב זה אין במעבדה את החומרה המתאימה ולכן גם לא קיים קוד בתוכנה שמטפל באירועים של החומרה הזו .כאשר יבנו/יקנו את החומרה המתאימה היא תעבוד מול כרטיס ה.A/D- .1.12רמקולים חתולים הם בעלי טווח שמיעה רחב יותר מאשר בני אדם (חתולים , Hz 45-64,000 :בני אדם )Hz 64-23,000ולכן אין צורך במכשור מיוחד להשמעת צלילים.
אנחנו משתמשים ברמקול מחשב סטנדרטי של חברת .Creative
.2פרק – 3מבנה התוכנה ממבט של משתמש .1.13מבוא תוכנת CATSמיודעת לניהול וביצוע ניסויים נוירו פיזיולוגיים התנהגותיים בחתולים. התוכנה נותנת אפשרות להרכיב מהלך ניסוי ,מבצעת את הניסוי ע"י שליטה בחומרה חיצונית ורושמת את תוצאות הניסוי. בפרק הזה אני אתאר את התיאוריה הכללית של בנית הניסוי ואז אתן סקירה של ממשק המשתמש בהקשר של בניית ניסוי. כפי שנאמר קודם ,התוכנה נבנתה על בסיס תוכנת Monkeyוחלק מהשמות של מחלקות "הורשו" גם הם מהתוכנה הקודמת. במשך בניית התוכנה הגענו למסכנה שיש צורך בשמות שמתארים טוב יותר את התפקיד של המחלקות אשר מייצגות בלוקים אשר מרכיבים את הניסוי. מכיוון שההחלטה הסופית בקשר לשמות התקבלה ברגע האחרון ,החלטנו לשמור על שמות המחלקות כמו שהם ,אך מצד השני להוסיף לכל החלונות של ממשק המשתמש את השמות החדשים .בשלבים הבאים של פיתוח התוכנה ישתנו גם השמות של המחלקות עצמן. .1.14תאור כללי של מבנה הניסוי ()Session היחידה הביצועית הכי קטנה של התוכנה היא ניסיון ( .)Trialישנם שני שלבים בהגדרת ניסיון – סוג ניסיון ( )Trial Typeו -הגדרת ניסיון (.)Trial Definition סוגי ניסיונות מוגדרים ע"י המתכנת בהתאם לניסיונות שנכתבו עבור התוכנה – המשתמש אינו משנה הגדרות ברמה זו. המשתמש יוצר הגדרות של ניסיונות ( )Trial Definitionsמתוך הסוגי הניסיונות שהוגדרו ע"י המתכנת .ניתן ליצור מספר רב של ניסיונות שונים מכל סוג ניסיונות ,כאשר מהלך הניסיון יהיה קבוע אבל פרמטרים שונים (זמנים ,מיקום הפנטומים ,צלילים וכו'). ניסיונות מאוגדים לבלוקים ( .)Blocksבלוק יכול להכיל בתוכו סוג אחד או יותר של ניסיונות מסוג אחד או יותר .בלוקים הם יחידות נוחות מאוד לבניית הניסוי – ניתן לשכפל אותם ולשנות את הסדר שלהם וכך להקל על שינויים במהלך הניסוי. הניסוי ( )Sessionמורכב מאחד או יותר בלוקים ,המסודרים בסדר מסוים שנקבע מראש ע"י המשתמש.
Session Block 1 Repeat L Times
Trial Definition A
Repeat M Times
Trial Definition B
Repeat N Times
Trial Definition A
Repeat O Times
Trial Definition C
Repeat P Times
Trial Definition B
Block 2
Block 3
Block 4
Block 5
פרמטרים הם הערכים שהמשתמש יכול לשנות וע"י כך להשפיע על התנהגות התוכנה .דוגמא לפרמטרים היא נקודה שעליה הפאנטום צריך להגיע בתחילת הניסיון -המשתמש יכול לספק את הקואורדינאטות וכך לשנות את המיקום (הדרך הנכונה לשינוי פרמטרים מפורטת בהמשך). כל ניסיון מכיל בתוכו הגדרה של מהלך הביצוע ותנאים להצלחה או לכישלון שלו .מהלך הניסיון זהו אוסף של פקודות הניתנות לחומרה ,כאשר הפקודות ניתנות בהתאם לשלב בניסיון ובהתאם לתגובת הנבדק באותו שלב .ב fig-ניתן לראות מספר דוגמאות למהלכי הביצוע של ניסיונות פשוטים יחסית.
Start
Reward Phantom - Start position
“Go ”cue to the cat
Wait for limb movement No
Yes
RewardPhantom - Move Away
Give Reward
Reward Phantom -Move Away
Falure
Success
Start
Reward Phantom - Start position
“Go ”cue to the cat
Wait for limb movement No
Yes
RewardPhantom - Move Away
Perturbation Phantom - Force On
Falure
Wait for Target reach No
Perturbation Phantom - Force Off Reward Phantom - Move Away
Falure
Yes Perturbation Phantom - Force Off Give Reward
RewardPhantom -Move Away
Success
.1.15ממשק משתמש האינפורמציה מהסעיף הקודם תעזור לנו להבין טוב יותר את החלקים השונים בממשק המשתמש .סעיף זה יתאר איך מתבצעות למעשה הפעולות שתוארו בסעיף הקודם. ממשק המשתמש של התוכנה תוכנן כך שכל המידע יהיה זמין וכל פעולה שהמשתמש ירצה לבצע תהיה במרחק של לחיצת עכבר.
חלקי הממשק הדומיננטיים הם שלושת החלונות ( )Documentsשמטרתם היא להציג מידע למשתמש (לחלון העליון ישנם גם תפקידים אחרים) והסרגל כלים מצידו השמאלי של המסך. החלון העליון חלון הפאנטומים .החלון מציג את מיקום הפאנטומים וגם מאפשר לכייל אותם .המשתמש יכול להפעיל או להפסיק את עדכון המיקום של הפאנטומים (כל פאנטום בנפרד) ובנוסף יכול לספק את תדירות העדכון (במילישניות).
חלון אמצעי חלון סטטיסטיקת הצלחות בניסוי .החלון מציג 4ערכים :סה"כ ניסיונות שנעשו ,הצלחות ,כישלונות וכמה פעמים הניסיון הופסק באמצע ע"י המשתמש. החלון התחתון חלון המשתנים .חלון זה מציג את כל המשתנים שהוגדרו עבור הניסוי ע"י המשתמש. סרגל כלים סרגל הכלים הוא מרכז השליטה העיקרי של התוכנה .בעזרת הכפתורים שעליו ניתן לבנות את מהלך הניסוי ,להריץ את הניסוי ,להפסיק אותו ולבצע פעולות נוספות אחרות כגון הפעלה ידנית של צליל או הפעלת משאבת התגמול בצורה ידנית בכל שלב של הניסוי. Start Session Run Section Stop Session Parameters
Blocks Build Section Trial Definitions
Trial Types
Manual Pump
Manual Control Section
Manual Sound
.i
ממשק בניית מהלך הניסוי
תהליך בניית מהלך הניסוי ( )Sessionמורכב ממספר שלבים: .1הגדרת סוג הניסיון ( )Trial Typeבקוד ע"י המתכנת (בהמשך) והוספתו לרשימת הניסיונות. .2הרכבת הגדרות הניסיונות (.)Trial Definitions .3הרכבת בלוקים ( )Blocksמהגדרות של ניסיונות. .4הוספת הפרמטרים ( )Parametersלניסוי. עבור כל שלב ברשימה לעיל ישנו כפתור על סרגל הכלילים הראשי ,אשר פותח תיבת דו-שיח אשר מאפשרת לבצע את הפעולות הרצויות. לבדוק האם לשינוי של פרמטרים וכו' בזמן הרצה יש משמעות ולציין את זה איפשהו
.1כפתור ה( Trials Types-
) פותח את תיבת הדו-שיח הבאה:
כאן ניתן לראות את רשימת ה Trial Types-הקיימים כרגע בתוכנה (בדוגמא זו רואים את ה .)Taming Trial-שלושת הכפתורים בחלון זה נותנים את האפשרות להוסיף ,לערוך או למחוק את הניסיון (כפתורים Editו Delete -הופכים להיות פעילים רק כאשר נבחר את הניסיון שאותו נרצה לערוך או למחוק). לחיצה על כפתור ה Add-תפתח חלון דו-שיח של הוספת ניסיון חדש:
בחלון זה ניתן להוסיף את שם הניסיון בלבד ,את שאר הפרטים מוסיפים ע"י תיבת דו-שיח :Edit
בחלון זה מוסיפים את שם הפונקציה שמפעילה את הניסיון ואת כל הפרמטרים שלה. הוספת הניסיון תעשה ע"י המתכנת שכותב את הפונקציה ורוצה להפוך אותה לזמינה לשימוש.
.2כפתור ה( Trial Definitions - את הניסיונות:
) פותח את תיבת הדו-שיח שבה מגדירים
בחלון זה יש לנו אפשרות להוסיף ,לערוך ,למחוק או לשכפל את אחד מהניסיונות (במקום ליצור חדש). לחיצה על כפתור ה Add-תפתח את תיבת דו-שיח הבאה:
בחלון זה יש לנו אפשרות להגדיר סוג חדש .הניסיון מבוסס על סוג ניסיון כלשהו בתוספת פרמטרים מיוחדים .בתיבה זו ניתן לבחור את סוג הניסיון שעליו נתבסס ,להוסיף פרמטרים ולתת שם ו ID -לניסיון חדש. .3כפתור ה( Blocks-
) פותח את תיבת הדו-שיח שבה מוגדרים הבלוקים.
בתיבה זו ניתן לראות אילו בלוקים מוגדרים עבור ה Session-הנוכחי ,מהו סדר הביצוע שלהם בזמן ההרצה והאם הם פעילים (אם בלוק מסוים מוגדר כ Not Active-הוא לא יבוצע בזמן הרצת ה.)Session- לחיצה על כפתור ה Add-תפתח את תיבת דו-שיח שבה ניתן להוסיף בלוק חדש ע"י הוספת השם שלו ולחיצה על כפתור .Ok
לחיצה על כפתור ה Edit-תפתח את תיבת דו-שיח שבה ניתן לערוך את הבלוק.
ברשימה מופיעים כל ה Trials-שהוספנו לבלוק הזה ,מספר הפעמים שנבצע את הניסיון בזמן ההרצה והאם לבצע אותו או לא (האם הוא .)Active בחירה של אחד הניסיונות מהרשימה ולחיצה על Editתפתח תיבת דו-שיח שבה ניתן לשנות את מספר הפעמים שהניסיון יבוצע:
לחיצה על כפתור Addתיפתח תיבת דו-שיח שבה ניתן לבחור את הניסיון שנרצה להוסיף לבלוק ,מתוך רשימה של ניסיונות מוגדרים:
.4כפתור ה ) ( Parameters-פותח את תיבת הדו-שיח שבה ניתן להוסיף פרמטרים ולשנות את ערכם:
כמו בכל שאר החלונות ,לחיצה על כפתור ה Add-תפתח תיבת דו-שיח שבה ניתן להוסיף פרמטר חדש .לחיצה על כפתור Editתיתן אפשרות לשנות פרמטר קיים.
.3פרק – 4הקוד .1.16מבוא תוכנת Catsהתבססה בראשית על קוד של תוכנה שמשמשת לניהול ניסויים בקופים – .Monkeyהתכנון הראשוני היה לבצע שינויים קלים בתוכנה זו ולהכניס אותה לשימוש ,אך אחרי בחינה של יכולות התוכנה התברר שהיא עובדת עם כלים שונים ולמרות הדמיון הרעיוני הרב האימפלמנטציה של רוב חלקי התוכנה לא התאימה לצרכים שלנו. להמשיך..... .1.17מבט כללי על הקוד כאמור התוכנה מחלקת את המשימות שלה למספר ,threadsשרצים במקביל וע"י כך מושגת היעילות המרבית בניצול משאבי המערכת. להוסיף הסבר בהתחלה של OOושל ( MFCעבור כל מושג שאני משתמש בו) להוסיף גם תאור של UINT CatsDocהיא המחלקה ( )classהראשית של התוכנה ,היא זו שמגיבה לפעולות של המשתמש (כגון לחיצה על כפתור) והיא זו שמפעילה את ההליכים (.)threads ישנם 3הליכים בתוכנה: ;)1. UINT TrialThread(LPVOID param ;)2. UINT PhantomsThread(LPVOID param ;)3. UINT UtilityThread(LPVOID param
* .UINT- Unsigned Integer * – LPVOIDמצביע 32ביט לסוג ( )typeלא מוגדר (ולכן יכול להצביע לכל סוג של אובייקט). שלושת ההליכים מוגדרים בקובץ נפרד הנקרא .Threads.cpp TrialThread.1
הליך זה אחראי על קומפילציה של כל הפרמטרים ומהלך הניסוי והרצתם בסדר הרצוי. PhantomsThread.2 הליך זה משמש להפעלת הפאנטום (כאשר יהיו שני פאנטומים יהיה צורך בהליך נוסף זהה). UtilityThread.3 תפקידו היחיד של הליך זה הוא "לחכות" לאות סיום של הניסוי ולעצור את ה.TrialThread-
CatsDoc
UtilityThread
PhantomsThread
TrialThread
שלושת ההליכים מקבלים כפרמטר מצביע ( )pointerל CatsDoc-ולכן יש להם גישה לכל המשאבים של המחלקה הזו. תקשורת בין שני ההליכים PhatomsThreadו TrialThread-מתבצעת ע"י מחלקה שנקראת .PhantomMessengerאובייקט של מחלקה זו מוגדר ב.CatsDoc- CatsDoc Communication
Communication
PhantomsThread
PhantomMessenger
TrialThread
PhantomsThreadו UtilityThread -מופעלים ב InitializeDocument -של המחלקה ,CatsDocז"א הם קיימים במשך כל זמן פעילות התוכנה ,מרגע ההרצה ועד הסיום (הם לא מופסקים בשום מקום). TrialThreadמופעל רק כאשר המשתמש לוחץ על כפתור ההרצה של הניסוי ומופסק כאשר הניסוי נגמר או כאשר המשתמש לוחץ על כפתור העצירה. המשך הפרק נכנס לפרטי הקוד ,ומכוון שישנם חלקי קוד שמופעלים במקביל עומדת בפני משימה לא קלה בלתאר אותו .לסדר שבו יתוארו המחלקות בהמשך יש משמעות והוא מנסה להעביר צורת מחשבה מסוימת ,שלדעתי תסייע בהבנת המבנה של הקוד בפרט והתוכנה בכלל. כאמור threadsמחלקים את המשימות של התוכנה למספר חלקים שרצים במקביל .בשלב הראשון אתאר את המחלקות הקשורות לבניית מהלך הניסוי. תהליך בניית מהלך הניסוי יוצר תבנית מסוימת (לפי הגדרות המשתמש) ולפיה מריצה את הניסיונות השונים ,ולכן בשלב השני אתאר את המחלקה Trial שממנה מורשים כל המחלקות שמגדירות ניסיונות וגם TamingTrialשהיא מחלקה שמורשת מ.Trial- מכוון שמהלך הניסיון מורכב בעיקרו מהפעלה של חומרה שונה ,בשלב השלישי אתחיל לתאר את המחלקות שקשורות לחלקי חומרה שונים ואיך מתבצעת התקשורת בין ה Trial-והחומרה.
.1.18תהליך בניית מהלך הניסוי תהליך זה מורכב מאסיפת המידע מהמשתמש ,קומפילציה והרצת הניסיונות ( )trialsלפי הסדר הנתון .במהלך ההרצה נאסף הקלט מהפאנטומים ומקורות אחרים ומתקבלת ההחלטה על הצלחתו של הניסיון ועל המשך ה.session- המחלקה Trials קובץ Trials.cppמגדיר חמש מחלקות שתפקידן להיות מיכלים ()containers
למידע על מהלך הניסוי .המחלקות הן: class CTrialParam:public CObject class CTrialList:public CObject class CTrialDef:public CObject class CTrialOrder:public CObject class CTrialGroup:public CObject
)1 )2 )3 )4 )5
חמשת המחלקות מורשות מ CObject-שהיא מחלקה בסיסית לרוב המחלקות של MFCומורישה מספר תכונות בסיסיות שימושיות למחלקות כגון סריאליזציה ( .)Serializationלפי שמות המחלקות ניתן לראות שכל אחת מהן אחראית על מידע מסוג שונה ,כגון פרמטרים של הניסיון ,הגדרת הניסיון וכו'. המחלקה הראשית שמוגדרת בקובץ זה היא: להזכיר שוב את השמות החדשים class CTrials:public CObject
היא מחזיקה את כל המידע של הניסוי ( )sessionהנוכחי ,כאשר היא מחזיקה אובייקטים מחמשת המחלקות כ( .data members-למעשה כל הdata members- מוגדרים מסוג CObjectוה casting-מתבצע בכל פעם שנגשים למידע) .מחלקה זו שומרת בנוסף data membersשמחזיקים פלט ביניים של המהדר (.)compiler
class CTrials:public CObject { DECLARE_SERIAL(CTrials); public: virtual void Serialize(CArchive& ar); CObArray m_TrialParam; //info from Param dialog box CObArray m_TrialOrder; //info from Order dialog box CObArray m_TrialList; //info from List dialog box CObArray m_TrialDef; //info from TrialDef dialog box CObArray m_code_seg; //array of objects returned from compiler - code CObArray m_data_seg; //array of objects returned from compiler - data CObArray m_fun_tab; //holds constant function list from functions.cpp CObArray m_event_tab; // holds constant events declared at CCatsDoc.cpp CUIntArray m_seq_tab; //holds sequence of all trials - returned from gen_seq() CUIntArray m_startGroups; //also returned from gen_seq() CTrials(); ~CTrials(); //calls clear_all() CObArray & GetDef(); CObArray & GetList(); CObArray & GetOrder(); CObArray & GetParam(); CObArray & GetEvents(); CTextFile *txtTrialsData; void WriteToText(CTextFile &txtFile); // writes all class data to text file void ReadFromText(CTextFile &txtFile); //reads all data from text file void clear_all(); void clear(); void compile(); void gen_seq(); void run(int n); };
שמטרתן היא להכין את המידע,למחלקה מוגדרות גם מספר פונקציות . תפקידן יובהר בהמשך.שבמחלקה ולהעביר אותו לעיבוד ע"י המהדר .CatsDoc-אובייקט מסוג זה נמצא ב Compile המחלקה
שתפקידה להדרcompile מכיל בתוכו הגדרה של מחלקהcompile.cpp קובץ .ולהריץ את הניסוי ולכן לא אכנס לפרטי הקומפילציה ואפרטMonkey מחלקה זו מיובאת מהתוכנה .רק מספר פונקציות שאיתן אני מתממשק compile(); gen_seq(); run();
() מבצעת את הקומפילציה הבסיסית הכוללת בדיקת תאימותcompile פונקציה פונקציה זו קוראת לשלוש פונקציות אחרות.המידע שהוזן ע"י המשתמש :המוגדרות באותה המחלקה
;)(lexical ;)(syntax ;)(semantic
שמות הפונקציות מתארות את הבדיקות שהן מבצעות. )(compileמחזירה שתי מחרוזות ( )listsמסוג CObjectלאובייקט מסוג .CTrials פונקציה )(gen_seqמקבלת כקלט את הפלט של )(compileומחזירה שתי מחרוזות של מספרים ( )integersהמקודדים את סדר הביצוע של הניסוי. פונקציה )(runקוראת למצביעים ( )pointersלפונקציות המתאימות וכך מריצה את הניסוי. מהלך האירועים בהפעלת הניסוי שתי המחלקות שתוארו לעיל הן השחקניות העיקריות של הפעלת הניסוי (אך לא היחידות). TrialThreadמופעל בפונקציה )(CCatsDoc::OnStartשנקראת כאשר המשתמש לוחץ על לחצן ה.Start- ההליך TrialThreadמבצע מספר פעולות לפי הסדר: .1מאפס ומנקה את כל המשתנים הזמניים שיכולים להכיל מידע מהרצה קודמת של הניסוי. .2קורא לפונקציה )(compileשל אובייקט מסוג ( CTrialsוהיא קוראת ל )(compile -של המהדר). .3קורא לפונקציה )(gen_seqשל אובייקט מסוג ( CTrialsוהיא קוראת ל )(gen_seq -של המהדר). .4נכנס ללולאה ועבור כל איבר במחרוזת m_seq_tab (member variableשל אובייקט מסוג CTrialsשמאוכלס ע"י הרצת פונקצית )(gen_seqשל המהדר) קורא לפונקצית )(runשל אובייקט מסוג ( CTrialsשיקרא לפונקצית )(runשל המהדר). .5בכל סבב של הלולאה אחרי הקריאה ל )(run-נאסף כל המידע שהתקבל בזמן ההרצה ובהתאם להצלחה או כשלון של הניסיון מתקבלת ההחלטה האם להמשיך או להפסיק את ביצוע הניסוי. .6מתבצע חישוב של זמן )ITI (Inter Trial Intervalונקראת פונקצית )Sleep(tש"מרדימה" את ההליך לזמן ה.ITI-
.1.19הגדרת הניסיון ()Trial תפקידה של מחלקה זו ,והמחלקות שמורשות ממנה ,לתאר את מהלך הניסיון ע"י שליחת אירועים לחומרה וקבלת פידבק ממנה. כל Trialשיכול להיות מוגדר בתוכנה יהיה מורש ממחלקת האם Trialשמוגדרת בקובץ .Trial.cpp מחלקת האם Trialמגדירה את הפרמטרים האוניברסאליים עבור כל ניסיון שיכול להיות מורש ממנה .פרמטרים מיוחדים יכולים להיות מוגדרים במחלקת הבת. הפונקציה שאחראית על קריאת הערכים של הפרמטרים שהוזנו ע"י המשתמש נקראת .)(getParams :מחלקת הבת שתגדיר פרמטרים מיוחדים תצטרך להגדיר פונקציה זו מחדש ולקרא לפונקציה של מחלקת האם כדי לקרא לכל הפרמטרים. )(void Trial::getParams { ;)CParam* param=(CParam*)in_param.GetAt(0 ;)if (param->type==CO_NUMBER) m_TargetStartX=atof(param->name ;)param=(CParam*)in_param.GetAt(1 ;)if (param->type==CO_NUMBER) m_TargetStartY=atof(param->name ;)param=(CParam*)in_param.GetAt(2 ;)if (param->type==CO_NUMBER) m_TargetStartZ=atof(param->name ;)param=(CParam*)in_param.GetAt(3 ;)if (param->type==CO_NUMBER) m_ForceStart=atof(param->name )………..(more parameters }
* המחלקה CParamמוגדרת ב.compile.cpp- מחלקת האם Trialכוללת את הפונקציה )(runTrialשתכלול את מהלך הביצוע של הניסיון .פונקציה זו היא וירטואלית טהורה ( )pure virtualמכוון שאין הגדרה ברירת מחדל לביצוע הניסיון וכל מחלקה שתהיה מורשת ממחלקת האם תהיה חייבת להגדיר את מהלך הביצוע של הניסיון. ;virtual void runTrial(){}; // = 0
בנוסף Trial ,מגדיר מספר פונקציות שמעדכנות משתנים שמקפיצות אירועם הקשורים להצלחה או כשלון של הניסיון. protected: virtual void successfulTrial(); //set Trial Successful virtual void trialFail(int catsEventID); //set Trial Failed )…void sendCatsEvent(int catsEventID); //Send Current Event (like: cat started eating
TamingTrial
מחלקת הבת TamingTrialהיא מחלקה שמורשת מ Trial-ומגדירה מהלך ניסיון של אילוף החתול .בניסיון זה משתתף רק פאנטום אחד ,מכוון שרוצים לאלף את החתול להושיט את הגפה ולא להפריע לו בתנועה.
לניסיון זה אין משתנים שמיוחדים רק לו ,ולכן אין צורך עקרוני בהגדרה מחודשת של פונקצית ,)(getParamsאבל היא מוגדרת כדי לשמור על המבנה הבסיסי של המחלקות מסוג .Trialפונקציה זו רק קוראת לפונקצית )(getParams של .Trial )(void TamingTrial::getParams } ;)(Trial::getParams {
המחלקה צריכה להגדיר מחדש את הפונקציה הוירטואלית הטהורה )(runTrial של מחלקת האם .Trial הקוד בנוי על ראיון של שליחת הוראה לפעולה (הפעלת פאנטום ,צליל ,כרטיס A/Dאו חומרה אחרת) והמתנה עד לקבלת אות של סיום הפעולה. //Phantom moves to starting position _ p_msgr->MoveToPoint(m_TargetStartX, ;)m_TargetStartY,m_TargetStartY,m_ForceStart,m_TimeInitialPosition )while(true { )if(WaitForSingleObject(p_msgr->ph_finished_event, 0) == WAIT_OBJECT_0 { ;break } ;)Sleep(1 }
הפעלת הפאנטום.1.20 Phantoms)( שמשווק עם הפאנטומיםAPI- משמשת כממשק לPhantom המחלקה ., OpenHaptics 2.0 toolkit המחלקה נבנתה כך שתהיה אפשרות להתייחס לפאנטום כאל אובייקט שיכול לבצע פעולות שונות (בניגוד לגישה של פונקציה אחת שיודעת לבצע את כל במקרה של צורך. ובכך ניתן לבנות בקלות מהלכי ביצוע שונים,)הפעולות .בהוספת "יכולת" כלשהי כל מה שנדרש הוא להוסיף פונקציה נוספת למחלקה class Phantom } public: Phantom(int p_num); virtual ~Phantom(); void StartFriction(HDdouble gain, HDdouble magnitude); //friction effect void StartSpring(HDdouble gain, HDdouble magnitude, _ hduVector3Dd anchor ); //spring effect void StartViscosity(HDdouble gain, HDdouble magnitude); //viscosity effect void StartConstForce(HDdouble gain, HDdouble magnitude, _ hduVector3Dd direction); //constant force effect void StartPointMass(); //point mass effect void StartFrictionlessPlane(hduVector3Dd point, double stiff); //Frictionless plane effect void StartAntiGrav(); //Antigravity effect void EndFriction(); void EndSpring(); void EndViscosity(); void EndConstForce(); void EndPointMass(); void EndFrictionlessPlane(); void EndAntiGrav(); hduVector3Dd GetPosition(); //return position of the current phantom hduVector3Dd GetForce(); //return force of the current phantom HDboolean Calibrate(); private: void initHL(HHD hHD, HHLRC &hHLRC);
// Initialize an HL rendering context for a //particular device instance void initHD(HDstring pConfigName, HHD &m_hHD); //Initialize an HD device instance bool b_calibrated; void SetCalibration(bool val); bool GetCalibration(); void MakeCurrent(); HDstring p_name; HLuint friction_force; HLuint spring_force; HLuint viscous_force; HLuint constant_force; HLuint pointmass_force; HLuint frictionless_plane_force; PointMass *pointMass; FrictionlessPlane * frictionlessPlane; };
) מקבלconstructor( הבונה של המחלקה,המחלקה אינה אוניברסאלית לגמרי ) והם אלה שקובעים את הזהות של הפאנטום (ז"א שכרגע2 או1 פרמטר (ערכים .)התוכנה יכולה להשתמש רק בשני פאנטומים Phantom::Phantom(int p_num) { switch(p_num){ case 1: initHD("PHANToM 1", hHD1); initHL(hHD1, hHLRC1); break; case 2: initHD("PHANToM 2", hHD2); initHL(hHD2, hHLRC2); break; default: this.~Phantom(); } //hlGenEffects - Generates unique identifiers for effects that //may be used with the hlStartEffect function friction_force = hlGenEffects(1); spring_force = hlGenEffects(1); viscous_force = hlGenEffects(1); constant_force = hlGenEffects(1); pointmass_force = hlGenEffects(1); frictionless_plane_force = hlGenEffects(1); //create an object of custom effect frictionlessPlane = new FrictionlessPlane(); }
API-כוחות מובנים ב ולכן אין צורך, של הפאנטומים מספק מספר כוחות מובנים סטנדרטייםAPI-ה : צורת השימוש היא. אלא ניתן להשתמש בהם,להגדירם מחדש void Phantom::StartFriction(HDdouble gain, HDdouble magnitude) { MakeCurrent(); //make the phantom current hlBeginFrame(); //enter the rendering block hlEffectd(HL_EFFECT_PROPERTY_GAIN, gain); //set force gain hlEffectd(HL_EFFECT_PROPERTY_MAGNITUDE, magnitude); //set force magnitude hlStartEffect(HL_EFFECT_FRICTION, friction_force); //set force type hlEndFrame(); //exit rendering block (send to phantom) } void Phantom:: EndFriction () { MakeCurrent(); hlBeginFrame(); hlStopEffect(friction_force); //stop effect hlEndFrame(); }
)Custom Forces( כוחות מוגדרים בצורה מיוחד
של הפאנטומים נותן אפשרות להגדיר כוחות מיוחדים ומתאר צעדיםAPI-ה פונקציות שמגדירות את3 לצורך הגדרת כוח מיוחד מגדירים.לכתיבתם . המהלך והסיום של אפקט הכוח המיוחד,ההתחלה אפקט הכוח המיוחד שמוגדר בתוכנה הוא משטח ללא חיכוך (הכוח יתואר .)בהמשך :Phantom הפונקציה להפעלת אפקט הכוח המיוחד במחלקה void Phantom::StartFrictionlessPlane(hduVector3Dd point, double stiff) { frictionlessPlane->SetParameters(point,stiff); MakeCurrent(); hlBeginFrame(); hlCallback(HL_EFFECT_COMPUTE_FORCE, (HLcallbackProc) _ FrictionlessPlane::computeForceCB, frictionlessPlane); hlCallback(HL_EFFECT_START, (HLcallbackProc) _ FrictionlessPlane::startEffectCB, frictionlessPlane); hlCallback(HL_EFFECT_STOP, (HLcallbackProc) _ FrictionlessPlane::stopEffectCB, frictionlessPlane); hlStartEffect(HL_EFFECT_CALLBACK, frictionless_plane_force); hlEndFrame(); }
שלושת הפונקציות הדרושות מוגדרות במחלקה נפרדת הנקראת אבל, אינו מחייב את הכוח להיות מוגדר כמחלקהAPI- ה.FrictionlessPlane .)הגדרה כזו מאפשרת טיפול יותר נוח באפקט (כגון אתחול FrictionlessPlane המחלקה
המחלקה מגדירה את שלושת הפונקציות הדרושות להפעלת האפקט הרצוי .ובנוסף את כל המשתנים הפנימיים הדרושים ליצירת האפקט class FrictionlessPlane { public: FrictionlessPlane(); //default constructor FrictionlessPlane(hduVector3Dd point, double stiff);//with parameters SetParameters(hduVector3Dd point, double stiff); //set parameters virtual ~FrictionlessPlane(); //destructor //functions required for effect rendering static void HLCALLBACK computeForceCB(HDdouble force[3], _ HLcache *cache, void *userdata); static void HLCALLBACK startEffectCB(HLcache *cache, void *userdata); static void HLCALLBACK stopEffectCB(HLcache *cache, void *userdata); private: hduVector3Dd m_position; hduVector3Dd m_velocity; HDdouble m_kStiffness; // Stiffnes, i.e. k value, of the plane. //Higher stiffness results in a harder surface. hduVector3Dd m_point_in_space; //point defines 3 perpendicular surfaces (by axis) hduVector3Dd m_direction_flag; //defines which of 3 surfaces to use and the direction //only one value is not zero, possible values -1,0,1 };
:computeForce יצירת אפקט הכוח מתרחשת בפונקצית
void FrictionlessPlane::computeForceCB(HDdouble force[3], HLcache *cache, void *userdata) } FrictionlessPlane *pFrictionlessPlane = static_cast
(userdata); // Get the current proxy position from the state cache. // Note that the effect state cache is maintained in workspace coordinates, // so we don't need to do any transformations in using the proxy // position for computing forces hduVector3Dd PhantomPosition; hlCacheGetDoublev(cache, HL_PROXY_POSITION, PhantomPosition); hduVector3Dd x; //F=kx hduVector3Dd f(0,0,0); //returned force double PenetrationDistance; for(int i=0; i<3; i++) //try each possible plane (3 possibilities) { if(pFrictionlessPlane->m_direction_flag[i] == 0) continue; //no plane if ((PhantomPosition[i] <= pFrictionlessPlane->m_point_in_space[i] && pFrictionlessPlane->m_direction_flag[i] > 0) ||(PhantomPosition[i] > pFrictionlessPlane->m_point_in_space[i]) &&(pFrictionlessPlane->m_direction_flag[i] < 0)) { // Create a force vector repelling the user from the plane proportional // to the penetration distance, using F=kx where k is the plane // stiffness and x is the penetration vector. Since the plane is // oriented at the Y=0, the force direction is always either directly // upward or downward, i.e. either (0,1,0) or (0,-1,0); PenetrationDistance =fabs(fabs(PhantomPosition[i]) – _ fabs(pFrictionlessPlane->m_point_in_space[i])); //relative to plane // Hooke's law explicitly: x[i] = PenetrationDistance*(pFrictionlessPlane->m_direction_flag[i]); f[i] = (pFrictionlessPlane->m_kStiffness)*x[i]; }//end if }//end for // Send the force to the device force[0] = f[0]; force[1] = f[1]; force[2] = f[2]; {
ניתן לראות שהפונקציה מאוד גמישה ומאפשרת (ע"י שינוי הפרמטרים) להגדיר ) ולכל סוג ניתן לספק אחד משניz-x ציר,y-z ציר,x-y סוגי משטחים (ציר3 .) סוגים של משטחים6 כוונים (סה"כ ולכן הבונה של,הגמישות הזו לא מנוצלת כרגע בתוכנה כתוצאה מחוסר צורך המחלקה מגדיר את הכוון ולא מקבל את הפרמטר מהמשתמש (כמובן שכאשר .) השינוי יהיה כל יחסית,יהיה הצורך
PhantomThread
זהו ההליך שמפעיל את הפאנטום (כאשר יתווסף פאנטום נוסף לתוכנה ,יהיה צורך בהליך זהה נוסף). הליך זה מופעל ,בניגוד לשני ההליכים האחרים ,מרגע שהתוכנה מתחילה לרוץ ומופסק ביחד עם סיום התוכנה. ההליך נכנס ללולאה ומחקה לאירועים שמגיעים מהליך אחר ()TrialThread ומבצע משימות בהתאם לאירועים אלה. case(WAIT_OBJECT_0+1): //Frictionless Plane _ ph_ptr1->StartFrictionlessPlane(p_msgr->m_vSurfacePoint, ;)p_msgr->m_dSurfaceStiffness )while (EventCurT-EventStartTm_dTimeToReachTarget { //get phantoms position ;)(CSec.Lock //update the current location ;)(p_msgr->m_vPhCurLocation=ph_ptr1->GetPosition ;)(CSec.Unlock )if(WaitForSingleObject(p_msgr->ph_stop_event, 0)==WAIT_OBJECT_0 { break;//leave if get stop event } Sleep(1); //less processor usage ;)(EventCurT = timeGetTime } ;)(ph_ptr1->EndFrictionlessPlane ;)(p_msgr->SignalPhFinishedTask ;ph_todo_ev_occured= WAIT_TIMEOUT ;break
.1.21תקשורת בין PhantomוTrial - המחלקה PhantomMessangerמאפשרת העברת מידע בין שני ההליכים וכך הפקודות שנשלחות מההליך של Trialמגיעות להליך של .Phantom ממבט ראשון יצירת מחלקה זו נראה מיותר מכוון שאפשר להפעיל אירועים ( )eventsוכך Trialיוכל להפעיל את ה ,Phantom-אבל אירוע לא יכול להעביר משתנים ולכן יצירתה של המחלקה היה נחוץ .בנוסף תפקידה של מחלקה זו לבודד בין שני ההליכים ,כך שכשאחד מהם ישתנה לא יהיה צורך בשינוי בשני. מחלקה זו מוגדרת ב CatsDoc-ומכוון שמצביע ל CatsDoc-מועבר לשני ההליכים שניהם מקבלים גישה לאובייקט מסוג .PhantomMessanger כאשר Trialצירך להפעיל את הפאנטום הוא קורא לפונקציה מתאימה ב- PhantomMessangerומעביר אליה את כל הפרמטרים הנחוצים כארגומנטים. PhantomMessangerשומר את הפרמטרים במשתנים הפנימיים שלו (data )membersומקפיץ אירוע ( .)eventההליך של פאנטום מקשיב כל הזמן לאירועים ולכן קולט את האירוע וניגש למשתנים של PhantomMessangerכדי לקבל את הפרמטרים המתאימים.
class PhantomMessenger { public: PhantomMessenger(); virtual ~PhantomMessenger(); void MoveToPoint(double x, double y, double z, double force, double time); void RunAwayToPoint(double x, double y, double z, double force, double time); void SetSurface(double x, double y, double z, double stiff, double time); void StopMoving(); void SignalPhFinishedTask(); void SignalEventOccured(); bool CheckArea(double x,double y, double z, double r); bool CheckArea(hduVector3Dd v, double r); void SetReward(double t_before_reward, double t_reward); void SetPhToDoEventStatus(DWORD ev); DWORD GetPhToDoEventStatus(); //Events which signal the phantom to do something HANDLE ph_todo_events[NUM_OF_PHANTOM_TODO_EVENTS]; CEvent ev_todo_surface; CEvent ev_todo_move; CEvent ev_todo_runaway; CEvent ev_todo_reward; HANDLE ph_stop_event; CEvent ev_todo_stop_move; HANDLE ph_finished_event; CEvent ev_finished_move; HANDLE ph_event_status; CEvent ev_event_status; //Phantom Parameters hduVector3Dd m_vPointToMoveTo; double m_dMoveForce; hduVector3Dd m_vSurfacePoint; double m_dSurfaceStiffness; hduVector3Dd m_vFeedCenter; double m_dFeedRadius; hduVector3Dd m_vPhCurLocation; //Timing Parameters double m_dTimeToReachTarget; //time given to Cat to grab the target double m_dTimeMoveToPosition; //time given the target to get to point double m_dTimeForReward; double m_dTimeEscape; private: DWORD dw_ph_event_status; //holds the current phantom todo event };
.1.22כרטיס A/Dוהמשאבה כאמור האות להפעלת ועצירת המשאבה מועבר למשאבה דרך כרטיס .A/D המעגל שמתפקד כמתאם בין הכרטיס והמשאבה עובד כמו מפסק ,הוא מפעיל את המשאבה כאשר הכרטיס מספק לו מתח של 5Vומפסיק אותה כאשר אין מתח .למטרה הזו השתמשתי ב line-אחד של הכרטיס ו.ground- בכרטיס A/Dכל lineיכול לקבל ערך )on( 1או .)off( 0כאשר הוא במצב on מסופק דרכו מתח של .5V האימפלמנטציה של התקשורת עם הכרטיס מוגדרת במחלקה .DIO class DIO { public: ;)(DIO ;)(virtual ~DIO void setLine(int line, int value); //set the value of a line int getLine(int line); //get the value of a line private: int32 ;error ;TaskHandle taskHandle uInt8 ;]data[8 char ;]errBuff[2048 ;}
אובייקט ממחלקה זו מסוגל לרשום או לקרא ערך מערוץ אחד של הכרטיס. בגרסאות הבאות כרטיס A/Dיאסוף בנוסף מידע מה Force Plates-ויתקשר עם המערכת של Alpha-Omegaולכן יהיה צורך להוסיף יכולות נוספות למחלקה זו, כגון באפר. .1.23צלילים התוכנה נותנת אפשרות להשמיעה קבצי *wav.כדי לאפשר לתת אותות קוליים ( )audio cuesלביצוע משימה לחתול. הגדרות ההשמעה נמצאות במחלקה .Sound מחלקה זו כוללת פונקציה אחת בלבד ,)play(int iSound ,אשר נקראת כאשר יש צורך בהשמעה של צליל .הפרמטר יכול לקבל ערך מ 1-עד ( 8עם אופציה להרחבה) וכך ניתן לבחור בין 8צלילים שונים. אובייקט מסוג Soundמוגדר במחלקה CatsDocולכן הוא נגיש לכל ההליכים. class Sound } public: ;)(Sound ;)(virtual ~Sound ;)void Play(int iSound ;}
.4פרק – 5תוצאות
.5פרק – 6סיכום ומסקנות