دليل التركيب · مبتدئ · من سنّ 9 سنوات

رَكِّب روبوتك Mark I

نُركِّب Mark I معًا، خطوةً خطوة. في كلِّ خطوة: رسمٌ متحرّك ثلاثيُّ الأبعاد تستطيع أن تُديره بإصبعك أو بالفأرة لتراه من كلِّ الزوايا، وفيديو حقيقيٌّ يُريك الحركة. خُذ وقتك — وعندما تكون مستعدًّا، انتقل إلى الخطوة التالية.

اسحب للتدوير · قرّب/مرّر للتكبير
جارٍ تحميل النموذج ثلاثي الأبعاد…
الخطوة 1
سيبدأ التركيب…

لم تحصل بعد على Mark I الخاص بك؟ صمّم نسختك في ثلاثي الأبعاد واختر ألوانك.

تخصيص Mark I الخاص بي ← مركز التعلّم
دماغ الروبوت

كيف يعمل الكود؟

والآن وقد أصبح Mark I مُركَّبًا، لنكتشف دماغه: الكود. لست بحاجةٍ إلى أن تعرف البرمجة — سنقرؤه معًا، قطعةً صغيرةً تلو الأخرى. البرنامج ما هو إلّا قائمةٌ من التعليمات البسيطة جدًّا التي يتّبعها الروبوت بسرعةٍ كبيرة، مرّةً بعد مرّة.

#include <AFMotor_R4.h>

نُعطي الروبوت كتاب وصفاتٍ جاهزًا للتحكّم في مُحرّكاته. هكذا لا نحتاج إلى أن نشرح كلَّ شيءٍ من جديد: نُعيد استعمال وصفاتٍ كُتِبت لنا مُسبقًا.

AF_DCMotor moteur1(1);
AF_DCMotor moteur2(2);

نُقدِّم المُحرّكين إلى الروبوت ونُعطيهما اسمًا: moteur1 وmoteur2. الرقم (1) والرقم (2) يدُلّان على المنفذ الموجود على اللوحة الذي وُصِلا به. والآن، عندما نقول «moteur1»، سيعرف الروبوت عمّن نتحدّث.

#define TRIG A0
#define ECHO A1

للروبوت عينان خاصّتان: حسّاسٌ يُرسِل «بيب» صغيرًا ويُنصِت إلى الصدى العائد، تمامًا مثل الخفّاش. TRIG هو السلك الذي يُرسِل الـ«بيب»؛ وECHO هو السلك الذي يُنصِت إلى العائد. نحن فقط نقول على أيِّ منفذين وُصِلا (A0 وA1).

const int SEUIL_CM = 20;
const int VITESSE  = 200;

نضبط رقمين مرّةً واحدةً وإلى الأبد. SEUIL_CM = 20، وهي مسافة الأمان: إذا كان عائقٌ على بُعد أقلّ من 20 سنتيمترًا، فسيقول الروبوت «انتبه!». VITESSE = 200، وهي سرعة المُحرّكات، على سُلّمٍ من 0 (متوقّف) إلى 255 (بأقصى قوّة).

long lireDistanceCm() {
  digitalWrite(TRIG, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG, LOW);
  long duree = pulseIn(ECHO, HIGH, 30000UL);
  if (duree == 0) return 999;
  return duree * 0.034 / 2;
}

هذه وصفة «قِسِ المسافة». يُرسِل الروبوت «بيب» صغيرًا جدًّا (يُشغِّل TRIG لِلحظةٍ قصيرة)، ثمّ يقيس الوقت الذي يستغرقه الصدى ليعود (ECHO). كلّما طال وقتُ الصدى، كان الجدار أبعد. والسطر الأخير يُحوِّل هذا الوقت إلى سنتيمترات. وإن لم يسمع أيَّ صدًى، أجاب 999 — وهي طريقته في قول «لا أرى شيئًا أمامي، الطريق سالكة».

void avancer() {
  moteur1.run(BACKWARD);
  moteur2.run(FORWARD);
}
void reculer() {
  moteur1.run(FORWARD);
  moteur2.run(BACKWARD);
}
void pivoter() {
  moteur1.run(FORWARD);
  moteur2.run(FORWARD);
}
void arret() {
  moteur1.run(RELEASE);
  moteur2.run(RELEASE);
}

ها هي أرجل الروبوت: أربع وصفاتٍ صغيرةٍ للحركة. avancer = إلى الأمام مباشرةً، reculer = إلى الخلف، pivoter = الدوران في المكان، وarret = التوقّف (نُرخي المُحرّكات). الكلمتان BACKWARD/FORWARD (إلى الخلف/إلى الأمام) ضُبِطتا قصدًا لتُطابِقا الطريقة التي رُكِّب بها المُحرّكان على روبوتك أنت: المُهمّ أن يجعله «avancer» يسير حقًّا إلى الأمام مباشرةً.

void setup() {
  Serial.begin(9600);
  pinMode(TRIG, OUTPUT);
  pinMode(ECHO, INPUT);
  moteur1.setSpeed(VITESSE);
  moteur2.setSpeed(VITESSE);

setup هو ما يفعله الروبوت مرّةً واحدةً فقط عند استيقاظه، حين نُشغِّله. يفتح خطَّ حوارٍ مع الحاسوب (كي يستطيع أن يكتب لنا الرسائل)، ويشرح أنّ TRIG يخدم للكلام وECHO للإنصات، ثمّ يضبط سرعة المُحرّكين.

  Serial.println(F("=== Demarrage ==="));
  long test = lireDistanceCm();
  if (test >= 999) {
    Serial.println(F("ATTENTION: capteur muet (AUCUN ECHO)."));
    Serial.println(F("-> corrige Trig/Echo / 5V / GND, le robot n'evitera RIEN."));
  } else {
    Serial.print(F("Capteur OK, distance initiale: "));
    Serial.print(test);
    Serial.println(F(" cm"));
  }
  delay(1500);
}

قبل أن يتحرّك، يُجري الروبوت اختبارًا صغيرًا لعينيه: يقيس المسافة مرّةً واحدة. إذا لم يُجِب الحسّاس (999)، كَتَب رسالة تنبيهٍ ليُنبِّهك إلى أنّه لن يتجنّب شيئًا (تحقَّق من: الأسلاك والتوصيل). وإلّا كَتَب «Capteur OK» مع المسافة. ثمّ ينتظر 1.5 ثانية، الوقتَ الكافي لتقرأ الرسالة وتضع الروبوت على الأرض.

void loop() {
  long distance = lireDistanceCm();
  Serial.println(distance);

loop هو ما يُكرِّره الروبوت في حلقةٍ، مرّةً بعد مرّة، بسرعةٍ كبيرة، ما دام مُشغَّلًا — وهي طريقته في التفكير طَوال الوقت. في كلِّ دورة، يقيس المسافة أمامه ويكتبها (كي نستطيع رؤيتها على الحاسوب).

  if (distance < SEUIL_CM) {
    arret();    delay(150);
    reculer();  delay(400);
    arret();    delay(150);

    pivoter();
    unsigned long debut = millis();
    while (lireDistanceCm() < SEUIL_CM && millis() - debut < 2000) {
      delay(50);
    }
    arret();    delay(150);
  } else {
    avancer();
  }
}

ثمّ يتّخذ قرارًا. إذا كان عائقٌ قريبًا أكثر من اللازم (أقلّ من 20 cm): يتوقّف، ويتراجع قليلًا، ويتوقّف من جديد، ثمّ يدور (يستدير في المكان) إلى أن تُصبح الطريق خاليةً — لكن لمدّة ثانيتين كحدٍّ أقصى، كي لا يدور بلا نهاية. وإلّا، فالطريق سالكة، فيسير إلى الأمام مباشرةً. وبما أنّ كلَّ هذا يتكرّر عشرات المرّات في الثانية، يبدو الروبوت وكأنّه يتنزّه وحده مُتجنّبًا الجدران!

وها أنت ذا — هذه هي البرمجة: أن تُعطي الروبوت قائمةً من التعليمات البسيطة جدًّا، التي يتّبعها بسرعةٍ كبيرة. وقريبًا، ستستطيع أن تُغيّر هذه الأرقام بنفسك: جرِّب VITESSE أخرى، أو SEUIL_CM أكبر، ولاحِظ كيف يتصرّف روبوتك Mark I!

عرض الكود كاملاً (للنسخ)
#include <AFMotor_R4.h>

AF_DCMotor moteur1(1);
AF_DCMotor moteur2(2);

#define TRIG A0
#define ECHO A1

const int SEUIL_CM = 20;
const int VITESSE  = 200;

long lireDistanceCm() {
  digitalWrite(TRIG, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG, LOW);
  long duree = pulseIn(ECHO, HIGH, 30000UL);
  if (duree == 0) return 999;
  return duree * 0.034 / 2;
}

void avancer() {
  moteur1.run(BACKWARD);
  moteur2.run(FORWARD);
}
void reculer() {
  moteur1.run(FORWARD);
  moteur2.run(BACKWARD);
}
void pivoter() {
  moteur1.run(FORWARD);
  moteur2.run(FORWARD);
}
void arret() {
  moteur1.run(RELEASE);
  moteur2.run(RELEASE);
}

void setup() {
  Serial.begin(9600);
  pinMode(TRIG, OUTPUT);
  pinMode(ECHO, INPUT);
  moteur1.setSpeed(VITESSE);
  moteur2.setSpeed(VITESSE);

  Serial.println(F("=== Demarrage ==="));
  long test = lireDistanceCm();
  if (test >= 999) {
    Serial.println(F("ATTENTION: capteur muet (AUCUN ECHO)."));
    Serial.println(F("-> corrige Trig/Echo / 5V / GND, le robot n'evitera RIEN."));
  } else {
    Serial.print(F("Capteur OK, distance initiale: "));
    Serial.print(test);
    Serial.println(F(" cm"));
  }
  delay(1500);
}

void loop() {
  long distance = lireDistanceCm();
  Serial.println(distance);

  if (distance < SEUIL_CM) {
    arret();    delay(150);
    reculer();  delay(400);
    arret();    delay(150);

    pivoter();
    unsigned long debut = millis();
    while (lireDistanceCm() < SEUIL_CM && millis() - debut < 2000) {
      delay(50);
    }
    arret();    delay(150);
  } else {
    avancer();
  }
}