
هذه المرة ، لنلقي نظرة أعمق قليلاً على تنفيذ بعض أساليب المكتبة الرئيسية لـ ARDUINO (AVR) ، المسؤولة عن تحريك الروبوت MIRO. سيكون هذا الجزء ممتعًا لكل من تساءل عن كيفية التحكم في السرعة الخطية والزاوية للروبوت على ARDUINO ، المزودة بمحركات بأبسط برامج التشفير.
جدول المحتويات:
الجزء 1 ،
الجزء 2 ،
الجزء 3 ،
الجزء 4 ،
الجزء 5 .
لا تزال الطرق المسؤولة عن القيادة مع قياس المسافات عبارة عن ألم من حيث شرح كيف ولماذا ولماذا. أول ما تحتاج إلى معرفته عن التحكم في حركة الروبوت هو الحقيقة البسيطة والواضحة وهي أن محركات جامع الروبوت لا تدور أبدًا بنفس السرعة دون تعديل إضافي. قابض مختلف ، وخصائص خرج مختلفة لقنوات السائق ، ومحركات كهربائية مختلفة قليلاً وتزييت في علبة التروس.
الحقيقة الثانية التي يجب أن تفهمها وتعرفها هي وجود القصور الذاتي في المحرك ، حتى مع وجود نسبة ترس كبيرة بما فيه الكفاية. أي عند إزالة الجهد من أطراف المحرك ، فإن العجلة ، حتى لو لم يتم تحميلها ، تجعل الحركة بضع درجات إضافية. يعتمد حجم الدوران الإضافي هذا على قوة التحميل على العجلة ، وعلى سرعة الدوران قبل تخفيف الضغط ، وعلى نفس العوامل غير المرئية مثل نوع وكمية التشحيم في علبة التروس.
تحدد هذه الحقائق تنفيذ مجموعة من الأساليب المتعلقة بحركة الهيكل المزودة بأجهزة استشعار عداد المسافات (في حالة MIRO ، الترميز الرقمي لكل عجلة).
كما اكتشفنا في الجزء الرابع ، في طراز البرنامج ، توجد فئة
الهيكل ، والتي تطبق التحكم في دوران محركات الهيكل الفردية. أريد التأكيد - ليس التحكم في حركة الهيكل ، العربة ، ولكن التحكم في محركات العربة. يتم تطبيق التحكم المباشر في العربة في فئتي
Robot و
Miro .
لنبدأ من فوق. فيما يلي طريقة لفئة
Miro تنفذ حركة الروبوت بمسافة معينة (
dist ، بالأمتار)
بسرعات خطية معينة (
lin_speed ، m / s)
وسرعات (
ang_speed ، deg / s) زاوي. لم ننتبه بعد إلى المعلمة
en_break .
int Miro::moveDist(float lin_speed, float ang_speed, float dist, bool en_break) { float _wheelSetAngSpeed[WHEEL_COUNT]; _wheelSetAngSpeed[LEFT] = MIRO_PI2ANG * (lin_speed - (ROBOT_DIAMETER * ang_speed / (2 * MIRO_PI2ANG))) / WHEEL_RADIUS; _wheelSetAngSpeed[RIGHT] = MIRO_PI2ANG * (lin_speed + (ROBOT_DIAMETER * ang_speed / (2 * MIRO_PI2ANG))) / WHEEL_RADIUS; float _wheelSetAng[WHEEL_COUNT]; _wheelSetAng[RIGHT] = _wheelSetAngSpeed[RIGHT] * dist / lin_speed; _wheelSetAng[LEFT] = _wheelSetAngSpeed[LEFT] * dist / lin_speed; return this->chassis.wheelRotateAng(_wheelSetAngSpeed, _wheelSetAng, en_break); }
في هذه الطريقة ، يتم أولاً حساب السرعات الزاوية المطلوبة للمحركات اليمنى واليسرى. وفقا لصيغ واضحة إلى حد ما ، والتي ليست مشكلة لاستنتاج. من الضروري فقط مراعاة أن السرعة الخطية في الطريقة محددة بالأمتار في الثانية ، والسرعة الزاوية بالدرجات في الثانية (وليس بالراديان). لذلك ، نحن نقوم بحساب
MIRO_PI2ANG الثابت
مسبقًا = 57.29 = 180 / pi. ROBOT_DIAMETER - المسافة بين العجلات اليسرى واليمنى للروبوت (بالأمتار) ،
WHEEL_RADIUS - نصف قطر العجلة (بالأمتار أيضًا). جميع الثوابت العددية لهذه الحالات موجودة في ملف defs.h ، والمعلمات المخصصة للروبوت والهيكل موجودة في ملف config.h.
بعد ذلك ، يتم حساب الزاوية التي يجب أن تُدار بها كل عجلة بحيث ينتقل الروبوت المسافة (بالأمتار أيضًا).
وبالتالي ، في هذه المرحلة ، نحصل على السرعة والزاوية التي تحتاجها لتدوير كل عجلة في هيكل الروبوت. ثم يتم
استدعاء طريقة
wheelRotateAng () لكائن
الهيكل .
يتم
استخدام طريقة
wheelRotateAng (السرعة العائمة * ، العائمة * ang ، bool en_break) لتدوير عجلات الروبوت بالسرعات الزاوية المحددة في صفيف
السرعة [] (م / ث) بواسطة الزوايا المحددة بواسطة مصفوفة
ang [] بالدرجات). تحدد المعلمة الأخيرة
en_break (التي سبق أن
استوفيناها سابقًا) المتطلبات لإيقاف العجلات بشدة بعد إجراء منعطف عن طريق استخدام جهد عكسي قصير الأجل لها. هذا ضروري لقمع القصور الذاتي للروبوت ، ومنعه من التحرك بعد المسافة المطلوبة بالفعل بعد إزالة الجهد السيطرة من المحركات. للرضا التام ، بالطبع ، هناك طريقة
wheelRotateAngRad () ، مماثلة
لعجلة wheelRotateAng () مع الفارق الذي يأخذ قيم زوايا الدوران والسرعات الزاوية في الراديان والراديان في الثانية كمعلمات.
خوارزمية طريقة
wheelRotateAng () هي كما يلي.
1. أولاً ، يتم فحص مراسلات القيم من
السرعة [] و
[ang] إلى بعض شروط الحدود. من الواضح أن الهيكل له قيود مادية على الحد الأقصى للسرعة الزاوية لتناوب العجلات ، وعلى الحد الأدنى (الحد الأدنى لسرعة الابتعاد). أيضًا ، لا يمكن أن تكون الزوايا في الزاوية
[] أقل من الحد الأدنى لزاوية الدوران الثابتة ، والتي تحددها دقة برامج التشفير.
2. بعد ذلك ، يتم حساب اتجاه دوران كل عجلة. من الواضح من خلال علامة المنتج
ang [i] * speed [i] ؛
3. يتم حساب "مسافة الدوران"
Dw [i] لكل عجلة - عدد عينات التشفير التي يجب القيام بها للتدوير بواسطة
ang [i] المحددة .
يتم تحديد هذه القيمة بواسطة الصيغة:
Dw [i] = ang [i] * WHEEL_SEGMENTS / 360 ،
حيث
WHEEL_SEGMENTS هو عدد مقاطع عجلة التشفير (الثورة الكاملة).
4. يتم تسجيل قيمة الجهد على سائق المحرك.
حول الجهد على المحركات* يتم استخدام PWM للتحكم في دوران المحركات ، وبالتالي ، من أجل معرفة الجهد الذي يتم توفيره لكل محرك ، من الضروري معرفة الجهد الكهربائي لسائق المحرك. في روبوت MIRO ، يتصل السائق مباشرة بدائرة طاقة البطارية. وظيفة تعويم getVoltage () ؛ تقوم بإرجاع الجهد من مقسم الجهد بعامل VOLTAGE_DIVIDER. الجهد المرجعي ADC: 5V. في الوقت الحالي ، تبلغ قيمة VOLTAGE_DIVIDER في الروبوت 2 ، ويتم توفير الجهد من بنك واحد (1S) من البطارية لمدخل ADC (PIN_VBAT). هذا ليس صحيحًا تمامًا نظرًا لحقيقة أن بنوك البطاريات يمكنها تفريغها بشكل مختلف وفقدان الرصيد ، ولكن كما أظهرت الممارسة ، مع وجود شحن مستمر للبطارية مع الموازنة ، يعمل الحل تمامًا. في المستقبل ، نخطط لعمل مقسم طبيعي مع عبوتين من البطاريات.
5. وفقًا لجدول المعايرة لكل عجلة ، يتم تحديد القيمة الأولية لإشارة PWM ، مما يضمن دوران العجلة بسرعة
السرعة المطلوبة
[i] . ما نوع طاولة المعايرة ومن أين أتت - سنحلل أكثر.
6. يتم بدء دوران المحركات وفقًا للقيم المحسوبة للسرعة واتجاه الدوران. في نص تطبيق الفصل ، تكون الطريقة الخاصة
_wheel_rotate_sync () مسؤولة عن هذا.
نذهب أعمق. تعمل طريقة
_wheel_rotate_sync () وفقًا للخوارزمية التالية:
1. في حلقة لا نهائية ، يتم إجراء فحص لتحقيق عداد استجابات المشفر لمسافة الدوران
Dw [i] لكل عجلة. إذا تم الوصول إلى أي من العدادات
Dw [i] ، تتوقف جميع العجلات وتخرج من الدورة ثم تخرج من الوظيفة (الخطوة 5). يتم ذلك للأسباب التالية. نظرًا لتقدير قياس زاوية الدوران ، يكون الوضع شائعًا للغاية عندما يتم الحصول على المسافة المحسوبة
Dw [i] لعجلة واحدة عن طريق تقريب قيمة غير صحيحة إلى جانب أصغر و
Dw [j] من العجلة الثانية إلى زاوية أكبر. هذا يؤدي إلى حقيقة أنه بعد إيقاف إحدى العجلات ، تستمر العجلة الثانية في الدوران. بالنسبة للهيكل الذي يحتوي على محرك تفاضلي (وبالنسبة للعديد من الأجهزة الأخرى) ، يؤدي هذا إلى "منعطف" غير مخطط له من الروبوت في نهاية المهمة. لذلك ، في حالة تنظيم الحركة المكانية للهيكل بالكامل ، من الضروري إيقاف جميع المحركات في وقت واحد.
2. إذا لم
يتم الوصول إلى
Dw [i] ، في هذه الحلقة يتم التحقق من حقيقة العملية التالية
لجهاز التشفير (المتغير
_syncloop [w] ، يتم تحديثه من مقاطعة المشفر وإعادة ضبطه في هذه الحلقة اللانهائية). عند حدوث التقاطع التالي ، يقوم البرنامج بحساب الوحدة النمطية للسرعة الزاوية الحالية لكل عجلة (درجة / ثانية) ، وفقًا للصيغة الواضحة:
W [i] = (360 * tau [i]) / WHEEL_SEGMENTS ،
حيث:
tau [i] - متوسط قيمة الوقت بين آخر استجابتين من الترميز. يتم تحديد "عمق" مرشح
التوسط عن طريق
MEAN_DEPTH والافتراضات إلى 8.
3. بناءً على سرعات العجلة المحسوبة ، يتم حساب الأخطاء المطلقة كالفوارق بين السرعات الزاوية والسرعة الفعلية.
4. استنادًا إلى الأخطاء المحسوبة ، يتم تصحيح إجراء التحكم (قيمة إشارة PWM) لكل محرك.
5. بعد الوصول إلى
Dw [i] ، في حالة
اندلاع نشط ، يتم تطبيق الجهد العكسي قصير الأجل على المحركات. يتم تحديد مدة هذا التأثير من جدول المعايرة (انظر أدناه) وعادة ما تتراوح بين 15 إلى 40 مللي ثانية.
6. هناك إصدار كامل من الإجهاد من المحركات والخروج
_wheel_rotate_sync () .
لقد ذكرت بالفعل جدول معايرة معين مرتين. لذلك ، يوجد في المكتبة جدول خاص للقيم المخزنة في EEPROM لذاكرة الروبوت ويحتوي على سجلات من ثلاث قيم ذات صلة:
1. الجهد في محطات السيارات. يتم حسابه عن طريق ترجمة قيمة إشارة PWM إلى الجهد الفعلي. لهذا ، في الخطوة 4 من طريقة
wheelRotateAng () ، يتم تسجيل الجهد الفعلي على برنامج تشغيل المحرك.
2. السرعة الزاوية لتناوب العجلة (بدون حمل) المقابلة لجهد معين.
3. مدة إشارة توقف الثابت المقابلة لهذه السرعة الزاوية.
بشكل افتراضي ، يكون حجم جدول المعايرة 10 سجلات (يتم تحديدها بواسطة
WHEEL_TABLE_SIZE الثابت في ملف
config.h ) - 10 أضعاف قيم "الجهد - السرعة الزاوية - مدة إشارة التوقف".
لتحديد القيم من 2 و 3 إدخالات في هذا الجدول ، يتم استخدام طريقة خاصة -
wheelCalibrate (byte wheel) .
دعونا ننظر قليلا في ذلك. تطبق هذه الطريقة سلسلة من الإجراءات لتحديد القيم المفقودة في جدول معايرة المحرك / العجلة ، وكذلك لمعرفة الحد الأدنى للسرعة الزاوية لبدء التشغيل والحد الأقصى للسرعة الزاوية للعجلة.
لإجراء المعايرة ، يتم تثبيت الروبوت على حامل ؛ يتم إجراء جميع دوران العجلات أثناء المعايرة دون تحميل.
1. تحتاج أولاً إلى تحديد الحد الأدنى لسرعة البدء. يتم ذلك ببساطة شديدة. في دورة ما ، يتم تغذية عنصر التحكم PWM للمحرك ، بدءًا من 0 ، بزيادة قدرها 1. في كل خطوة ، ينتظر البرنامج لبعض الوقت ،
ويتم تحديده بواسطة ثابت
WHEEL_TIME_MAX (
تأخير عادي
() ). بعد انقضاء وقت الانتظار ، يتحقق الأمر لمعرفة ما إذا كانت البداية قد اكتملت (عن طريق تغيير قيمة عداد التشفير). في حالة اكتمال الانسحاب ، يتم حساب السرعة الزاوية للعجلة. لمزيد من اليقين ، تتم إضافة قيمة 10 إلى قيمة PWM المقابلة لسرعة البدء هذه ، وهذا يعطي الزوج الأول من القيم "الجهد على المحرك" - "السرعة الزاوية".
2. بعد العثور على سرعة البدء ، يتم حساب خطوة PWM لملء جدول المعايرة بشكل موحد.
3. في الدورة ، لكل قيمة PWM جديدة ، يتم تدوير العجلة
بفترتين كاملتين ويتم قياس السرعة الزاوية وفقًا لخوارزمية مشابهة
لطريقة _wheel_rotate_sync () . في نفس الدورة ، وأيضًا عن طريق التقريب المتتابع ، يتم قياس القيمة المثلى لفترة إشارة التوقف الثابت. في البداية ، يتم اتخاذ بعض قيمة كبيرة بشكل واضح. وبعد ذلك يتم اختباره في وضع "التوقف". باعتبارها القيمة المثلى ، يتم تحديد الحد الأقصى لقيمة مدة إشارة التوقف ، حيث لا يتم تجاوز "مسافة الدوران" المحددة. وبعبارة أخرى ، يتم إيقاف هذه القيمة لمدة الإشارة ، عند تزويد المحرك من ناحية ، بالقصور الذاتي ، ومن ناحية أخرى ، لا توجد حركة عكسية قصيرة الأجل (يتم تثبيتها بواسطة نفس المشفر).
4. بعد اكتمال المعايرة ، يتوقف جهد التحكم في المحرك المعاير ليتم تسجيل جدول معايرة هذه العجلة في EEPROM.
لقد حذفت كل أنواع التفاهات في التنفيذ وحاولت أن أذكر الجوهر. قد تلاحظ أن
طريقتي wheelRotateAng () و
wheelRotateAngRad () تحظران الوظائف. هذا هو ثمن دقة الحركة وتكامل بسيط إلى حد ما في الرسومات المستخدم. قد يكون من الممكن إنشاء مدير مهام صغير مع توقيت ثابت ، ولكن هذا سيتطلب من المستخدم تضمين وظائفه بدقة في الحصة الزمنية المخصصة.
وبالنسبة للتطبيقات غير
المحظورة ، يحتوي API على
تدوير للعجلة الوظيفية
(السرعة العائمة) . كما هو واضح من قائمة المعلمات ، يؤدي ببساطة دوران العجلات بسرعات محددة. ويتم ضبط سرعة الدوران في طريقة
Sync () لهيكل الروبوت ، والتي تسمى في طريقة
Sync () لكائن فئة Miro الذي يحمل نفس الاسم. ووفقًا لمتطلبات بنية رسم المستخدم ، يجب أن تسمى هذه الطريقة كل تكرار
للحلقة الرئيسية
() من حلقة ARDUINO.
في الخطوة 4 ، في وصف طريقة
_wheel_rotate_sync () ، ذكرت "تصحيح التحكم" الخاص بالمحرك. كيف تخمين)؟ هذا هو وحدة تحكم PID). حسنا ، أكثر دقة تحكم PD. كما تعلمون (في الواقع - ليس دائمًا) ، فإن أفضل طريقة لتحديد معاملات المنظم هي الاختيار). يوجد تعريف واحد في ملف التكوين config.h:
#define DEBUG_WHEEL_PID
إذا قمت
بالإلغاء ، عند استدعاء الأسلوب
moveDist () لفئة Miro ، سيتم عرض الرسم البياني المقلوب التالي للخطأ النسبي في التحكم في السرعة الزاوية لإحدى عجلات الروبوت (يسار) في وحدة التحكم الآلية.

لا يشبه أي شيء)؟ أسفل هو الوقت (كل شريط هو خطوة من دورة التحكم) ، ويتم حفظ قيمة الخطأ إلى اليمين (مع الحفاظ على علامة). فيما يلي زوجان من الرسوم البيانية على نفس المقياس مع معاملات مختلفة لوحدة تحكم PD. "الحدب" هي مجرد "موجات" من التجاوز. الأرقام على الأشرطة الأفقية هي خطأ نسبي (مع الحفاظ على علامة). تصور بسيط للمنظم ، مما يساعد على ضبط المعاملات يدويا. مع مرور الوقت ، آمل أن أقوم بإعداد تلقائي ، لكن الآن.
هنا مثل هذا adok :-)
حسنًا ، أخيرًا ، دعونا ننظر إلى مثال. مباشرة من مكتبة API_Miro_moveDist:
#include <Miro.h> using namespace miro; byte PWM_pins[2] = { 5, 6 }; byte DIR_pins[2] = { 4, 7 }; byte ENCODER_pins[2] = { 2, 3 }; Miro robot(PWM_pins, DIR_pins, ENCODER_pins); int laps = 0; void setup() { Serial.begin(115200); } void loop() { for (unsigned char i = 0; i < 4; i++) { robot.moveDist(robot.getOptLinSpeed(), 0, 1, true); delay(500); robot.rotateAng(0.5*robot.getOptAngSpeed(), -90, true); delay(500); } Serial.print("Laps: "); Serial.println(laps); laps++; }
من نص البرنامج يجب أن يكون كل شيء واضحًا. كيف يعمل - في الفيديو.
600 × 600 ملم البلاط و 5 ملم الفجوات البلاط. من الناحية النظرية ، يجب أن يتجول الروبوت حول مربع مع جانب متر واحد. بالطبع ، المسار "يطفو بعيدا". لكن من الإنصاف ، يجدر القول أنه في إصدار الروبوت الذي تركته للاختبارات ، توجد محركات دوّارة يصعب السير فيها ببطء. ولكن في السرعة العالية والانزلاق ، هناك مكان مناسب ، والقصور الذاتي ليس من السهل التغلب عليه. يجب أن تتصرف المحركات ذات نسبة التروس الأعلى (مثل حتى في روبوتات MIRO الخاصة بنا ، التي لم تكن موجودة أثناء الاختبار) بشكل أفضل إلى حد ما.
إذا كانت هناك لحظات غير مفهومة - يسعدني التوضيح والمناقشة والتحسين. ردود الفعل مثيرة للاهتمام بشكل عام.