بناء سيارة روبوتية لتجنب العوائق باستخدام Arduino ودرع المحركات Motor Shield Rev3 ومحرك سيرفو ومستشعر الموجات فوق الصوتية

يحتوي هذا المقال على روابط تسويق بالعمولة مُعلَّمة بعلامة *. بصفتي شريكًا في برنامج Amazon Associates، أربح من عمليات الشراء المؤهلة، دون أي تكلفة إضافية عليك. يساعد ذلك على إبقاء الشروحات مجانية.

تسير هذه السيارة الروبوتية بمفردها وتلتف حول العوائق. وهي تعتمد على أربعة دروس سابقة: أساسيات برمجة Arduino, التحكم في محرك السيرفو, قياس المسافة بالموجات فوق الصوتية و التحكم في محركات التيار المستمر باستخدام وحدات قيادة المحركات.

يُحمَّل الفيديو من YouTube (Google)، وقد يضع ملفات تعريف الارتباط (Cookies). سياسة الخصوصية

📋 ملخص سريع

يقيس مستشعر الموجات فوق الصوتية HC-SR04 المثبَّت على محرك سيرفو SG90 المسافةَ أمام الروبوت وعلى جانبيه. وتقرر لوحة Arduino Uno المزوَّدة بدرع المحركات Arduino Motor Shield Rev3 الاتجاهَ الذي يسلكه الروبوت، وتقود محركَي التيار المستمر. يتناول هذا المقال قائمة القطع، والتجميع، والتوصيلات، والبرنامج الكامل (Sketch، أي برنامج Arduino) مع شرح له، وقيم الضبط، واستكشاف الأخطاء وإصلاحها.

كيف يعمل الروبوت

يكرر الروبوت حلقة «استشعر-فكّر-نفّذ» (sense-think-act): يقيس المسافة (الاستشعار)، ثم يقارنها بعتبات ثابتة (التفكير)، ثم يقود المحركات وفقًا لذلك (التنفيذ). العتبة (Threshold) قيمة مسافة ثابتة في الكود يتغير عندها سلوك الروبوت. ويستخدم الروبوت عتبتين: عندما تقل المسافة عن العتبة البعيدة يُبطئ، وعندما تقل عن العتبة القريبة يتوقف.

مستشعر الموجات فوق الصوتية HC-SR04 (قياس المسافة)

‏ مستشعر الموجات فوق الصوتية HC-SR04 يُرسل نبضة فوق صوتية قصيرة ويستمع إلى الصدى المرتد من أقرب جسم. وله طرفا إشارة: يستقبل الطرف Trig نبضة البدء من Arduino، ويُعيد الطرف Echo نبضةً تدوم بقدر الزمن الذي استغرقه الصوت في الذهاب والعودة. تقيس لوحة Arduino هذا الزمن وتحوّله إلى مسافة. ويتكرر هذا القياس مرات عديدة في الثانية أثناء سير الروبوت.

محرك السيرفو SG90 (المسح بالمستشعر)

يُثبَّت المستشعر على محرك سيرفو SG90، وهو سيرفو صغير من فئة الوزن 9 g. أثناء تقدم الروبوت إلى الأمام، يحرّك السيرفو المستشعرَ يسارًا ويمينًا، فيرى الروبوت المسار أمامه وعلى جانبيه. وعندما يقترب عائق لمسافة أقل من العتبة القريبة، يدير السيرفو المستشعرَ إلى أقصى اليسار ثم إلى أقصى اليمين، ويقيس الروبوت الجهة التي فيها مساحة أكبر.

Arduino Motor Shield Rev3 (وحدة قيادة المحركات)

‏ Arduino Motor Shield Rev3 هو لوحة إضافية (درع، Shield) تُركَّب فوق أطراف التوصيل في لوحة Uno (صفّا المقابس الممتدان على حافتيها). وهو يقود محركَي التيار المستمر. تحتوي شريحة القيادة فيه، وهي L298P، على جسرَي H (H-bridge). جسر H دائرة من أربعة مفاتيح تُمرّر التيار عبر المحرك في أيٍّ من الاتجاهين، وبهذه الطريقة يحدد الدرع اتجاه كل محرك (للأمام أو للخلف) على حدة. ويحدد الدرع أيضًا سرعة كل محرك. تُضبط السرعة بتقنية PWM (تعديل عرض النبضة): تشغّل لوحة Arduino المحرك وتطفئه نحو 490 مرة في الثانية، وتحدد نسبةُ زمن التشغيل (دورة التشغيل، Duty Cycle) السرعةَ. والدالة analogWrite() في Arduino هي التي تُخرج إشارة PWM هذه. وإذا شغّلت المحركين الأيسر والأيمن في اتجاهين متعاكسين، يدور الروبوت في مكانه.

Arduino Motor Shield Rev3 top view with screw terminals for motor A, motor B and Vin, the L298P driver chip and the header pins for the Uno
Arduino Motor Shield Rev3: أطراف التثبيت اللولبية للمحركين A وB ومدخل الطاقة Vin (حيث يدخل جهد البطارية) على اليسار، وشريحة القيادة L298P في المنتصف.

تتصل المحركات بأطراف تثبيت لولبية (يُثبَّت السلك فيها ببرغي صغير). لكل محرك على لوحة Arduino طرف خاص للاتجاه، وآخر للسرعة، وثالث للفرملة؛ ويوقف طرف الفرملة المحرك بسرعة.

الوظيفةالمحرك A (الأيسر)المحرك B (الأيمن)
الاتجاه1213
السرعة (PWM)311
الفرملة98

يقود الدرع محركَي تيار مستمر أو محركًا خطويًا واحدًا (محرك يتحرك بخطوات زاوية ثابتة)، ويتحمل تيارًا متواصلًا يصل إلى 2 A لكل قناة. لمزيد من المعلومات حول جسور H ووحدات قيادة المحركات، راجع شرحي عن جسر H المزدوج L298N ودرع المحركات Arduino Motor Shield.

PWM waveforms for analogWrite values 255, 0, 64 and 128 and the resulting motor speed
التحكم في السرعة بتقنية PWM: تضبط الدالة analogWrite() دورة التشغيل. القيمة 255 تعني السرعة القصوى، والقيمة 0 توقف المحرك، والقيمتان 64 و128 تشغّلانه بسرعة أقل.

منطق اتخاذ القرار

تقارن لوحة Arduino كل قراءة للمسافة بعتبتين. فعندما يكون الروبوت بعيدًا عن أي عائق، يسير بسرعة التجوال ويحرّك المستشعر للمسح. وعندما تقل المسافة عن العتبة البعيدة يُبطئ. وعندما تقل عن العتبة القريبة يتوقف، ويقيس المسافة إلى اليسار وإلى اليمين، ثم ينعطف نحو الجهة التي فيها مساحة أكبر. وإذا كانت الجهتان مسدودتين (طريق مسدود)، يرجع إلى الخلف أولًا ثم يعيد المسح.

المكونات (قائمة المواد)

المكوّنالكميةمطلوب / اختياريالغرضملاحظات
Arduino Uno1مطلوبالمتحكم الدقيق الرئيسي: يشغّل الكودتصلح أي لوحة متوافقة مع Uno
Arduino Motor Shield Rev31مطلوبيقود محركَي التيار المستمر ويتحكم فيهمايُركَّب مباشرة فوق لوحة Arduino Uno
محركات تيار مستمر (من 3 إلى 6 V)2مطلوبعجلتا الدفع اليسرى واليمنىمحركات تيار مستمر قياسية بعلبة تروس من فئة الهواة (علبة التروس المركبة على المحرك تخفض السرعة وترفع عزم الدوران، أي قوة الدوران)
محرك السيرفو (SG90)1مطلوبيحرّك مستشعر الموجات فوق الصوتية يسارًا ويمينًايصلح أي سيرفو صغير من فئة الوزن 9 g
مستشعر الموجات فوق الصوتية (HC-SR04)1مطلوبيقيس المسافة إلى العوائقوحدة بـ 4 أطراف: VCC (تغذية 5 V)، وTrig، وEcho، وGND (الأرضي، 0 V)
أسلاك التوصيل (Jumper)عدةمطلوبجميع التوصيلات الكهربائية بين المكوناتذكر-ذكر وذكر-أنثى (الذكر = طرف بدبوس، الأنثى = طرف بمقبس)
خلايا AA قابلة لإعادة الشحن (NiMH، 1.2 V)8مطلوبمزود الطاقة للمحركات وللوحة ArduinoNiMH اختصار لـ nickel-metal hydride (هيدريد النيكل المعدني). الجهد الاسمي: 8 × 1.2 V = 9.6 V. يقبل الدرع جهدًا من 5 إلى 12 V ويمرّره إلى لوحة Uno.
حاملات البطاريات (4 × AA)2مطلوبتحمل الخلايا وتوصلها على التواليموصولة على التوالي للحصول على 9.6 V
مفتاح ذو ذراع (Toggle Switch)1مطلوبمفتاح الطاقة الرئيسيمفتاح بذراع يبقى في وضع التشغيل أو الإيقاف. يُركَّب في مسار السلك الموجب للبطارية.
هيكل الروبوت1مطلوبإطار يحمل جميع المكوناتلوح أكريليك أو لوح مطبوع بطابعة ثلاثية الأبعاد
العجلات2مطلوبتُركَّب على أعمدة محركات التيار المستمراختر ما يناسب مقاس عمود المحرك لديك
عجلة كروية (عجلة مساندة)1مطلوبنقطة التلامس الثالثة مع الأرضتصلح أيضًا عجلة دوّارة
البراغي والصواميل وفواصل التباعدمتنوعةمطلوبالتجميع الميكانيكيالمقاس M3 (سن متري بقطر 3 mm) هو المقاس الشائع لتثبيت لوحات Arduino والهياكل
حوامل مطبوعة بطابعة ثلاثية الأبعادمجموعةاختياريحوامل للسيرفو ومستشعر الموجات فوق الصوتية ولوحة Arduinoيمكن استخدام الغراء الحراري أو الأربطة البلاستيكية أو الزوايا المعدنية بدلًا منها

التجميع خطوة بخطوة

صمّمتُ الحوامل الثلاثة للوحة Arduino والسيرفو ومستشعر الموجات فوق الصوتية في Autodesk Inventor (برنامج تصميم 3D CAD)، ثم طبعتها على طابعة ثلاثية الأبعاد الخاصة بي.

الخطوة 1: ثبّت حاملات البطاريات

ثبّت حاملَي البطاريات (4 × AA) بالبراغي على الجهة السفلية أو الخلفية للهيكل. الحم الحاملين على التوالي (الطرف الموجب لأحدهما بالطرف السالب للآخر)، فتصبح الخلايا الـ 8 جميعها موصولة على التوالي بجهد اسمي 9.6 V. ويشكّل الحاملان مع خلاياهما حزمة البطاريات.

الخطوة 2: ركّب مفتاح التشغيل

الحم المفتاح ذا الذراع في السلك الموجب لحزمة البطاريات. وثبّته على الهيكل في مكان تصل إليه بسهولة لتطفئ الروبوت بسرعة.

الخطوة 3: ثبّت محركَي التيار المستمر

الحم سلكًا بكل طرف من طرفَي كل محرك. ثبّت محركًا على الجانب الأيسر للهيكل وآخر على الجانب الأيمن باستخدام البراغي والصواميل أو حوامل المحركات.

الخطوة 4: جهّز حامل Arduino

إذا كنت تستخدم حامل Arduino المطبوع بطابعة ثلاثية الأبعاد، فإنه يحتاج إلى حشوات نحاسية ملولبة (جُلَب نحاسية ذات سن داخلي). سخّن كل حشوة بكاوية اللحام واضغطها في ثقب التثبيت الخاص بها. وبذلك تحصل على أسنان لولبية متينة لتثبيت Arduino.

الخطوة 5: ثبّت لوحة Arduino والدرع

ثبّت الحامل المطبوع بطابعة ثلاثية الأبعاد (أو فواصل التباعد من قائمة القطع) على الهيكل بالبراغي، ثم ثبّت لوحة Arduino Uno على الحامل أو الفواصل. ركّب الدرع فوق لوحة Arduino، وتأكد من أن كل طرف من أطراف التوصيل يستقر في مقبسه قبل أن تضغطه إلى الأسفل.

الخطوة 6: ركّب محرك السيرفو

ثبّت حامل السيرفو المطبوع بطابعة ثلاثية الأبعاد في مقدمة الهيكل بشريط لاصق مزدوج الوجه أو بالبراغي، ثم ركّب السيرفو SG90 فيه. قبل تركيب ذراع السيرفو (Horn، وهو الذراع البلاستيكي على عمود السيرفو)، اضبط السيرفو على 90°. ارفع برنامجًا قصيرًا يحتوي على myservo.write(90)، أو أدِر العمود يدويًا إلى منتصف مداه. ركّب الذراع بحيث يتجه المستشعر إلى الأمام مباشرة. ويجب أن يدور الذراع بحرية من اليسار إلى اليمين.

الخطوة 7: ثبّت مستشعر الموجات فوق الصوتية

أدخِل المستشعر HC-SR04 في حامله المطبوع بطابعة ثلاثية الأبعاد (أو ثبّته بالغراء الحراري). ثبّت الحامل على ذراع السيرفو ببرغي صغير، حتى يدور المستشعر مع السيرفو.

الخطوة 8: ثبّت العجلة الكروية

ثبّت العجلة الكروية في مقدمة الهيكل أو مؤخرته، في الجهة المقابلة لمحركَي التيار المستمر. تمنح عجلتا الدفع والعجلة الكروية الروبوتَ تلامسًا ثابتًا مع الأرض في ثلاث نقاط.

الخطوة 9: ركّب عجلتَي الدفع

اضغط العجلتين على عمودَي المحركين في الجانبين. يجب أن تكونا مثبتتين بإحكام وأن تدورا دون أن تحتكا بالهيكل.

توصيلات السيارة الروبوتية

Wiring diagram: Arduino Uno with the Arduino Motor Shield Rev3, HC-SR04 ultrasonic sensor, SG90 servo motor, two DC motors and battery pack
مخطط التوصيل الكامل: لوحة Arduino Uno مع درع المحركات Arduino Motor Shield Rev3، ومستشعر الموجات فوق الصوتية HC-SR04، ومحرك السيرفو SG90، ومحركا التيار المستمر، وحزمة البطاريات.

توصيلات محركات التيار المستمر

المحركطرف التوصيل على الدرع
المحرك A (الأيسر)، السلك 1A−
المحرك A (الأيسر)، السلك 2A+
المحرك B (الأيمن)، السلك 1B−
المحرك B (الأيمن)، السلك 2B+

توصيلات محرك السيرفو

سلك السيرفويتصل بـ
الإشارة (برتقالي أو أصفر)الطرف الرقمي 5 في Arduino (على أطراف توصيل الدرع)
VCC (أحمر)الطرف 5 V على الدرع
GND (بني أو أسود)الطرف GND على الدرع

مهم: وصّل سلك الطاقة الخاص بالسيرفو بالطرف 5 V على الدرع، لا بالطرف 3.3 V. يحتاج السيرفو SG90 إلى 5 V؛ أما على 3.3 V فإنه يرتجف ويتحرك بضعف.

توصيلات مستشعر الموجات فوق الصوتية (HC-SR04)

طرف HC-SR04يتصل بـ
VCC5 V على الدرع
Trigالطرف الرقمي 7 في Arduino
Echoالطرف الرقمي 6 في Arduino
GNDGND على الدرع

توصيلات مزود الطاقة

سلك البطارية أو المفتاحيتصل بـ
الموجب (من مخرج المفتاح)طرف الطاقة Vin على الدرع (+)
السالب (أرضي البطارية)طرف الطاقة GND على الدرع (−)

يمرّر الدرع جهد البطارية عبر وصلة Vin Connect (وصلة Jumper صغيرة على ظهر الدرع) إلى الطرف VIN في لوحة Uno. ويحوّله منظم الجهد الموجود على لوحة Uno إلى 5 V، فلا حاجة إلى كابل USB ما دامت حزمة البطاريات موصولة. وفي النسخة التي بنيتُها تبقى هذه الوصلة موصولة، فتتشارك لوحة Uno والمحركات حزمة البطاريات نفسها.

افحص جميع التوصيلات قبل تركيب الخلايا: عكس القطبية قد يتلف الدرع ولوحة Arduino. لا تضع الخلايا الـ 8 في الحاملات إلا بعد هذا الفحص، واترك المفتاح في وضع الإيقاف حتى يتم رفع البرنامج.

كود Arduino

يستخدم البرنامج مكتبة Servo (كود جاهز للتحكم في محركات السيرفو)، وهي مضمّنة مع Arduino IDE (البرنامج الذي تكتب فيه برامج Arduino وترفعها إلى اللوحة).

الإعداد وتعريف الأطراف

تعمل الدالة setup() مرة واحدة بعد تشغيل الطاقة: فهي تربط السيرفو بطرفه، وتضبط أوضاع الأطراف، وتفعّل الفرامل، وتعيد السيرفو إلى المنتصف.

/**
 * Author: Omar Draidrya
 * Date: 2024-06-09
 * This code controls a robot with ultrasonic sensors and servo motors to avoid obstacles.
 */
#include <Servo.h>
// Create a Servo object to control the servo motor
Servo myservo;
int pos = 0;  // Variable to store the servo position
// Define motor pins
int directionA = 12;  // Pin for direction of Motor A
int speedA = 3;  // Pin for speed of Motor A
int brakeA = 9;  // Pin for brake of Motor A
int directionB = 13;  // Pin for direction of Motor B
int speedB = 11;  // Pin for speed of Motor B
int brakeB = 8;  // Pin for brake of Motor B
// Define ultrasonic sensor pins
int trigger = 7;  // Trigger pin
int echo = 6;  // Echo pin
long duration = 0;  // Variable to store the duration of the echo
long distance = 0;  // Variable to store the distance from the obstacle
int currentAngle = 90;  // Start with the sensor facing forward
int scanDirection = 1;  // 1 for left, -1 for right
void setup() {
    // Attach the servo on pin 5 to the servo object
    myservo.attach(5);
    // Set the trigger pin as OUTPUT and the echo pin as INPUT
    pinMode(trigger, OUTPUT);
    pinMode(echo, INPUT);
    // Initialize Motor A pins
    pinMode(directionA, OUTPUT);
    pinMode(directionB, OUTPUT);
    pinMode(brakeA, OUTPUT);
    pinMode(brakeB, OUTPUT);
    // Apply brakes (stop the motors)
    digitalWrite(brakeA, HIGH);
    digitalWrite(brakeB, HIGH);
    // Set the servo to the center position initially
    myservo.write(90);
    delay(500);
}

الحلقة الرئيسية والدوال المساعدة

تعمل الدالة loop() مرارًا وتكرارًا بعد انتهاء setup().

void loop() {
    // Measure the distance to the nearest obstacle
    distance = measureDistance();
    if (distance < 40) {
        // Gradually reduce speed as the obstacle approaches
        int speed = map(distance, 20, 40, 75, 128);
        motorTurn(directionA, speedA, HIGH, speed);
        motorTurn(directionB, speedB, HIGH, speed);
        if (distance < 15) {
            // Stop the motors
            digitalWrite(brakeA, HIGH);
            digitalWrite(brakeB, HIGH);
            delay(500);
            // Check surroundings
            int leftDistance = checkDirection(175);
            int rightDistance = checkDirection(5);
            // Determine if the robot is in a dead end
            if (leftDistance < 15 && rightDistance < 15) {
                // Move backward
                motorTurn(directionA, speedA, LOW, 128);
                motorTurn(directionB, speedB, LOW, 128);
                delay(500);
                // Apply brakes
                digitalWrite(brakeA, HIGH);
                digitalWrite(brakeB, HIGH);
                delay(500);
                // Scan again after moving back
                leftDistance = checkDirection(150);
                rightDistance = checkDirection(30);
            }
            // Determine the best direction to turn
            if (leftDistance > rightDistance) {
                // Turn left
                motorTurn(directionA, speedA, LOW, 128);
                motorTurn(directionB, speedB, HIGH, 128);
            } else {
                // Turn right
                motorTurn(directionA, speedA, HIGH, 128);
                motorTurn(directionB, speedB, LOW, 128);
            }
            delay(500);
            // Apply brakes
            digitalWrite(brakeA, HIGH);
            digitalWrite(brakeB, HIGH);
            delay(500);
            // Set the servo back to the center position
            myservo.write(90);
            delay(500);
        }
    } else {
        // Move forward at a reduced speed
        motorTurn(directionA, speedA, HIGH, 100);
        motorTurn(directionB, speedB, HIGH, 100);
        // Continuously scan left and right
        currentAngle += scanDirection * 10;
        if (currentAngle >= 150 || currentAngle <= 30) {
            scanDirection = -scanDirection;
        }
        myservo.write(currentAngle);
        delay(40);  // Adjusted delay for more realistic servo movement
    }
}

الدوال motorTurn() وmeasureDistance() وcheckDirection() هي الدوال المساعدة الثلاث التي تستدعيها loop().

// Function to set the direction and speed of motors
void motorTurn(int directionPin, int speedPin, int direction, int speed) {
    digitalWrite(directionPin, direction);
    analogWrite(speedPin, speed);
    if (directionPin == directionA) {
        digitalWrite(brakeA, LOW);
    } else {
        digitalWrite(brakeB, LOW);
    }
}
// Function to measure the distance using the ultrasonic sensor
int measureDistance() {
    digitalWrite(trigger, LOW);
    delayMicroseconds(2);
    digitalWrite(trigger, HIGH);
    delayMicroseconds(10);
    digitalWrite(trigger, LOW);
    duration = pulseIn(echo, HIGH);
    distance = (duration / 2) / 29.1;
    return distance;
}
// Function to check the distance in a specific direction
int checkDirection(int angle) {
    myservo.write(angle);
    delay(500);  // Give the servo time to move
    int measuredDistance = measureDistance();
    return measuredDistance;
}

وظيفة كل جزء من البرنامج

تخصيص الأطراف

أطراف المحركات (12 و3 و9 للمحرك A، و13 و11 و8 للمحرك B) يحددها الدرع ولا يمكن تغييرها. أما الأطراف 7 (Trig) و6 (Echo) و5 (إشارة السيرفو) فهي أطراف حرة لا يستخدمها الدرع؛ ويمكنك نقلها إلى أطراف حرة أخرى إذا غيّرت أرقامها في الكود.

قياس المسافة

ترسل الدالة measureDistance() نبضة مدتها 10 µs على الطرف Trig (المتغير trigger في الكود) وتنتظر الصدى. ثم تحسب المسافة بالسنتيمتر وفق المعادلة distance = (duration / 2) / 29.1. والقسمة على 2 سببها أن الموجة الصوتية تقطع المسافة ذهابًا وإيابًا، أما 29.1 فهو العدد التقريبي من الميكروثواني الذي يحتاجه الصوت لقطع سنتيمتر واحد في الهواء.

التحكم في المحركات

تضبط الدالة motorTurn() اتجاه محرك واحد وسرعته. وHIGH وLOW هما مستويا الإشارة في الطرف الرقمي: HIGH يعني 5 V، وLOW يعني 0 V. تكتب motorTurn() القيمة HIGH (للأمام) أو LOW (للخلف) على طرف الاتجاه، وتضبط السرعة باستخدام analogWrite() (من 0 إلى 255)، ثم تحرر الفرامل. وكتابة HIGH على طرف الفرملة توقف ذلك المحرك بسرعة.

كيف يُبطئ الروبوت

تقيس loop() المسافة أمام الروبوت في كل دورة، وتستجيب على مراحل:

  • أكثر من 40 cm (العتبة البعيدة): يسير الروبوت إلى الأمام بقيمة PWM 100 ويحرّك المستشعر للمسح.
  • من 40 cm إلى 20 cm: يُبطئ الروبوت بما يتناسب مع المسافة. تحوّل الدالة map() رقمًا من نطاق إلى نطاق آخر؛ وهنا تحوّل map(distance, 20, 40, 75, 128) المسافة إلى قيمة PWM تساوي 128 عند 40 cm و75 عند 20 cm. ولأن 128 أعلى من سرعة التجوال البالغة 100، يتسارع الروبوت لفترة قصيرة عندما يعبر حد 40 cm. وللحصول على تباطؤ منتظم، غيّر 128 في map() إلى 100.
  • من 20 cm إلى 15 cm: لا تقيّد map() ناتجها ضمن النطاق، لذلك تستمر السرعة في الانخفاض إلى ما دون 75 وتصل إلى نحو 62 عند 15 cm.
  • أقل من 15 cm (العتبة القريبة): يتوقف الروبوت ويمسح محيطه.

منطق المسح

عندما تنخفض المسافة إلى أقل من 15 cm، يفرمل الروبوت المحركين وينتظر 500 ms. ثم يستدعي checkDirection(175) للنظر إلى اليسار وcheckDirection(5) للنظر إلى اليمين. تدير الدالة checkDirection() السيرفو إلى الزاوية المحددة، وتنتظر 500 ms حتى يصل إليها، ثم تأخذ قراءة واحدة للمسافة وتعيدها. ينعطف الروبوت نحو الجهة ذات المسافة الأكبر: محرك إلى الأمام والآخر إلى الخلف، بقيمة PWM 128 لمدة 500 ms. وبعد الانعطاف يفرمل، وينتظر 500 ms، ويعيد السيرفو إلى المنتصف عند 90°.

سلوك الرجوع للخلف عند الطريق المسدود

إذا كانت المسافتان اليسرى واليمنى كلتاهما أقل من 15 cm، فالروبوت في طريق مسدود. فيرجع إلى الخلف لمدة 500 ms بقيمة PWM 128، ثم يفرمل لمدة 500 ms، ويعيد المسح عند 150° (يسارًا) و30° (يمينًا). ثم يقارن القراءتين الجديدتين وينعطف نحو الجهة التي فيها مساحة أكبر، كما يفعل بعد المسح الأول.

مسح السيرفو أثناء السير

عندما تتوفر مساحة خالية تزيد على 40 cm، يسير الروبوت بقيمة PWM 100. ويحرّك السيرفو المستشعر بين 30° و150° بخطوات مقدارها 10°، مع توقف مدته 40 ms عند كل خطوة. وبهذا المسح يكتشف الروبوت أيضًا العوائق الموجودة على يسار المسار ويمينه. وينعكس اتجاه المسح في كل مرة يصل فيها السيرفو إلى 30° أو 150°.

الرفع والاختبار والمراقبة

  • وصّل لوحة Arduino بحاسوبك بكابل USB وافتح Arduino IDE.
  • اختر Tools > Board > Arduino Uno، ثم اختر منفذ لوحتك من Tools > Port.
  • ارفع البرنامج (Sketch) إلى اللوحة.
  • افصل كابل USB، وضع الروبوت على الأرض بحيث تكون أمامه مساحة خالية، ثم شغّل الطاقة. يبدأ الروبوت بالسير بعد نحو نصف ثانية من التشغيل.

يجب أن يسير الروبوت إلى الأمام، ويُبطئ عند اقترابه من عائق، ثم يتوقف، ويمسح يسارًا ويمينًا، وينعطف نحو الجهة الأكثر خلوًّا. وإذا دارت إحدى العجلات إلى الخلف أو انعطف الروبوت إلى الجهة الخاطئة، فستجد الحل في جدول استكشاف الأخطاء وإصلاحها.

كيفية ضبط الروبوت

عدّل هذه القيم في الكود لتناسب محركاتك ونوع الأرضية. القيم مكتوبة داخل loop() كأرقام صريحة. ابحث في البرنامج عن 40 و20 و15 و100 و128 و500 لتغييرها. تظهر العتبة البعيدة 40 مرتين: في if (distance < 40) وفي map().

المعاملالقيمة الافتراضيةما تتحكم فيهنصيحة للضبط
العتبة البعيدة40 cmالمسافة التي يبدأ الروبوت دونها بالإبطاءزِدها إذا فرمل الروبوت متأخرًا جدًا؛ وقلّلها إذا أبطأ مبكرًا جدًا
نهاية الإبطاء20 cmالمسافة التي تبلغ عندها map() سرعتها الدنيا 75 (PWM)تظهر فقط في map(distance, 20, 40, 75, 128)
العتبة القريبة15 cmالمسافة التي يتوقف عندها الروبوت ويمسح محيطهزِدها إذا اصطدم الروبوت قبل أن يتوقف؛ وقلّلها لتنقّل أدق في المساحات الضيقة
سرعة التقدم للأمام100 (PWM)سرعة التجوال عندما يكون المسار خاليًاالنطاق من 0 إلى 255. القيمة الأعلى أسرع لكن يصعب معها التوقف في الوقت المناسب
سرعة الانعطاف128 (PWM)السرعة أثناء الانعطاف يسارًا ويمينًا وأثناء الرجوع للخلفزِدها إذا لم يُكمل الروبوت الانعطاف؛ وقلّلها على الأرضيات الزلقة
مدة الانعطاف500 msالمدة التي ينعطف فيها الروبوت قبل أن يتوقفزِدها للحصول على انعطافات أوسع؛ وقلّلها للتصحيحات الصغيرة
مدة الرجوع للخلف500 msالمدة التي يرجع فيها الروبوت للخلف في الطريق المسدودزِدها إذا لم يرجع الروبوت إلى الخلف مسافة كافية
نطاق مسح السيرفومن 30° إلى 150°نطاق المسح أثناء التقدم للأمامنطاق أوسع: كشف أفضل للجانبين لكن المسح يصبح أبطأ
خطوة مسح السيرفو10°مقدار تغيّر الزاوية في كل دورة من الحلقةقيمة أصغر: مسح أكثر سلاسة لكن تغطية أبطأ
زمن التأخير في مسح السيرفو40 msالتوقف بين خطوات السيرفو، وبالتالي بين قراءتين للمسافةالقيم الأقل تجعل المسح أسرع لكنها تترك وقتًا أقل بين القراءات. زِدها إذا كان السيرفو يرتجف؛ واستخدم 60 ms إذا كانت القراءات متذبذبة (راجع قسم استكشاف الأخطاء وإصلاحها)

استكشاف الأخطاء وإصلاحها

المشكلةالسبب المحتملالحل
الروبوت لا يتحرك إطلاقًاانعدام التغذية بالطاقة، أو توصيلات مرتخية، أو عدم تحرير الفراملتحقق من مفتاح التشغيل ومن جهد البطارية (9.6 V اسميًا، ونحو 10.5 إلى 11 V مع خلايا مشحونة حديثًا). تحقق من توصيلات أطراف المحركات ومن أن برنامج Arduino (Sketch) يُرفع إلى اللوحة دون أخطاء.
يعمل محرك واحد فقطأحد المحركين غير موصول، أو يوجد سلك مقطوعوصّل المحرك السليم بأطراف المحرك الآخر لاختبار تلك القناة. افحص نقاط اللحام على المحرك.
إحدى العجلتين تدور إلى الخلف (يدور الروبوت حول نفسه أو ينحرف بدلًا من السير في خط مستقيم)أسلاك المحرك معكوسةبدّل بين سلكي ذلك المحرك (A+ وA−، أو B+ وB−).
ينعطف الروبوت يمينًا بينما يجب أن ينعطف يسارًاالمحرك A والمحرك B مُبدَّلانبدّل بين توصيلات المحرك A والمحرك B على الدرع.
مستشعر الموجات فوق الصوتية يعطي قراءات غير مستقرة أو صفريةتوصيلات مرتخية، أو أطراف خاطئة، أو جسم قريب جدًا أو بعيد جدًاتحقق من Trig (الطرف 7) وEcho (الطرف 6). يتراوح مدى HC-SR04 بين نحو 2 cm و400 cm. انتظر 60 ms على الأقل بين قياسين متتاليين حتى يتلاشى الصدى السابق. لا يتوقف المسح سوى 40 ms في كل خطوة، لذا زِد قيمة delay(40) في loop() إلى 60. لرؤية القراءات، أضف Serial.begin(9600); إلى setup() وSerial.println(distance); إلى loop()، ثم افتح الشاشة التسلسلية Serial Monitor (النافذة في Arduino IDE التي تعرض النص الذي ترسله اللوحة عبر USB).
السيرفو يهتز أو يرتعشالسيرفو موصول بـ 3.3 V، أو مزود طاقة ضعيف، أو مزود 5 V غير مستقروصّل VCC الخاص بالسيرفو بـ 5 V، وليس بـ 3.3 V. أضف مكثفًا كهروليتيًا سعته 100 µF (مكثف له ساق موجبة وساق سالبة، ويخفف الانخفاضات القصيرة في الجهد) بين خطي تغذية السيرفو، على أن تكون الساق المخططة (السالبة) على GND.
يستجيب الروبوت متأخرًا ويصطدم بالعوائققيم العتبة منخفضة جدًا، أو فترات التأخير في الحلقة طويلة جدًاارفع عتبة المسافة القريبة (مثلًا من 15 cm إلى 20 cm) حتى يتوقف الروبوت في وقت أبكر، أو عتبة المسافة البعيدة (مثلًا من 40 cm إلى 50 cm) حتى يخفض سرعته في وقت أبكر. قصّر فترات التأخير في الحلقة.
حزمة البطاريات تفرغ بسرعةالمحركات تسحب تيارًا عاليًا، أو الخلايا غير مشحونة بالكاملاستخدم خلايا NiMH مشحونة حديثًا. خفّض سرعة المحركات. تحقق من أن العجلات تدور دون احتكاك.
يُعاد تشغيل Arduino عند بدء دوران المحركاتانخفاض الجهد بسبب تيار المحركاتأضف مكثفًا كهروليتيًا سعته من 470 إلى 1000 µF (بجهد مقنن 16 V أو أكثر) بين طرفي التغذية في الدرع، على أن تكون الساق المخططة (السالبة) على GND. إذا استمرت إعادة التشغيل، فاقطع وصلة Vin Connect على ظهر الدرع، ليحصل الدرع ولوحة Uno على مصدري تغذية منفصلين. غذِّ لوحة Uno من بطارية خاصة بها عبر مقبس DC (مقبس الطاقة الدائري في Uno).

الأسئلة الشائعة

هل يمكنني استخدام Arduino Nano أو Mega بدلًا من Arduino Uno؟

يُركَّب Arduino Motor Shield Rev3 على أطراف التوصيل في Arduino Uno، ويناسب أيضًا Arduino Mega، لكنه لا يناسب Nano. مع Nano، استخدم وحدة L298N بدلًا منه؛ راجع ما كتبته في شرح وحدة قيادة المحركات L298N.

هل يمكنني استخدام وحدة قيادة المحركات L298N بدلًا من Arduino Motor Shield Rev3؟

نعم. تستخدم وحدة L298N عائلة شرائح القيادة نفسها التي يستخدمها الدرع (نحو 2 A لكل قناة)، لكنها لوحة منفصلة. توصلها بأسلاك التوصيل (Jumper) وتغيّر أرقام الأطراف في الكود. يشرح درسي عن وحدة قيادة المحركات L298N الخيارين كليهما.

ما الجهد المناسب، وأي حزمة بطاريات أستخدم؟

يستخدم هذا المشروع 8 خلايا NiMH بحجم AA موصولة على التوالي: 8 × 1.2 V = 9.6 V اسميًا. يقبل الدرع من 5 إلى 12 V على طرفي التغذية، وينقل هذا الجهد إلى الطرف VIN في Uno. تعمل أيضًا بطارية LiPo من نوع 2S (خليتان من الليثيوم بوليمر على التوالي، 7.4 V اسميًا و8.4 V عند الشحن الكامل). أما بطارية LiPo من نوع 3S فتصل إلى 12.6 V عند امتلائها، وهذا أعلى من الحد الأقصى للدرع البالغ 12 V.

توسعات ممكنة

  • أضف التحكم عبر بلوتوث أو واي فاي (Wi-Fi): اجمع بين تجنب العوائق والتحكم عن بُعد عبر وحدة بلوتوث HC-05 أو وحدة واي فاي ESP8266. يوضح مشروعي سيارة روبوتية يُتحكَّم بها عبر البلوتوث كيفية توصيل وحدة HC-05 وبرمجتها. تُدار محركات تلك السيارة بواسطة Adafruit Motor Shield V1.2؛ وتتصل وحدة HC-05 فيها بالطرفين 0 و1، وهما طرفان يتركهما Arduino Motor Shield Rev3 حرّين.
  • أضف وضع تتبع الخط: تكتشف مستشعرات الأشعة تحت الحمراء (IR) خطًا أسود على الأرض، وينتقل الروبوت بين تتبع الخط وتجنب العوائق.
  • انتقل إلى هيكل 4WD (أربع عجلات دافعة): لا يحتوي الدرع إلا على قناتين، لذا وصّل محركي كل جانب على التوازي بقناة واحدة. أبقِ التيار في كل قناة دون 2 A.

في سيارة روبوتية لاحقة، استبدلت المستشعر الوحيد المركّب على السيرفو بأربعة مستشعرات HC-SR04 ثابتة، واحد على كل جانب، وبلوحة Arduino Mega. وهذا المشروع هو سيارتي الروبوتية المتقدمة ذاتية القيادة والمتحكم بها عبر بلوتوث.

3 رأي حول “Building an Obstacle-Avoiding Robot Car with Arduino, Motor Shield Rev3, Servo Motor, and Ultrasonic Sensor”

  1. من أفضل ماشاهدت في هذا المجال بمافيه من توضيحات. سهل وميسر. تحياتي لكم أستاذي العزيز. أخوكم من سلطنة عمان

    رد
    • شكراً جزيلاً أخي سعيد على كلماتك الطيبة. سعيد جدًا أن الشرح كان واضحًا ومفيدًا لك، هذا هو هدفي. تحياتي لك من ألمانيا إلى سلطنة عمان

      رد

أضف تعليق