







السمعة:
- إنضم26 يونيو 2023
- المشاركات 1,711
- الحلول 31
- مستوى التفاعل 2,949
- النقاط 113
موضوعنا اليوم موضوع بسيط إن شاء الله و لن يكون شرح كامل بكل التفاصيل بل وضع قدمك على الطريق
بداية ال Line follower هي مسابقة فكرتها أن تقوم بعمل روبوت يتحرك وحده من دون تدخل بشري و الهدف أن ينهي مسار مرسوم بلون أسود بأسرع وقت و أن تحاول عدم الخروج من المسار
فأنت بالمسابقة تعتمد على عدد من القطع و الأمور لعمل الروبوت و أهمها :
[B]الماتورات :[/B]
ففي المسابقة العزم في الماتورات ليس مهم بل المهم هو السرعة, لذلك في حالتنا و بعد تجربة اخترنا هذا النوع من الماتورات من Temu التي كانت سرعتها 1360 و بالحقيقة كانت مناسبة جدا في وسط البيئة التي كنا نتنافس بها
المتحكم :
نحن كما قلنا فإن الهدف الإنهاء بأقرب وقت لذلك تحتاج لمتحكم سريع يمكنه أن يرسل و يستقبل بسرعة لذلك لم نتجه لل Arduino علما أن هناك الكثير من المتاسبقين الذين استخدموه و لكننا فضلنا أن لا نواجه مشاكل بسبب نوع المتحكم لذلك اتجهنا لل ESP32ال driver :
قمنا بإعتماد ال driver التقليدي L298N, علما أنه يمكنك من باب الإحتياط إن كان الماتورين يسحبا أكثر مما يعطي ال driver الواحد أن تضع 2 drivers (لكل ماتور ال driver الخاص به)
البطارية :
بالواقع و بناء على الماتورات التي اخترناها و ال driver فنحتاج بطارية 12 فولت
و لكن ملاحظة مهمة جدا : ال ESP32 تعمل على 3.3 فولت فقط و إن زادت عن هذا المقدار فستحترق و تتلف, لذلك لا تنسى أن تضع منظم جهد 3.3v
يمكنك أن تقوم بعمل السيركت من خلال منظم الجهد و المكثفات أو أن تقوم بشراء هذه القطعة الجاهزة التي تحتوي على مكثفات من نوع SMD داخلها
- حساسات ال IR : بالطبع الأمر لا يمكن تحديده بنوع حساس معين, فروبوتنا و لعدم توافر الحساسات في السوق بسبب المسابقة و زيادة الطلب قمنا باللجوء لحساس يعتبر سيء نسبيا و هناك كان الكثير من الحساسات التي كانت أفضل منا و لكننا فزنا و الحمد لله و فضله
فنقطة حساسات ال IR اختيارية لك حسب التوافر و الميزانية و لكن لا تنسى أن الإبداع في البرمجة أهم من قوة الحساسات لديك
الحساس الذي استخدمناه : https://mikroelectron.com/product/5-channel-ir-sensor-tcrt5000-line-track-follower-module
و هو الحساس الذي تم بنأ الكود عليه - العجلات : ربما هذه النقطة ليست ذا فرق كبير جدا و لكن صديق لنا -الله يجزيه الخير- قام بتجهيز تصميم لها على برنامج solidwork للعجلات و حتى ل cover ليقوم بتثبيت الماتور في التصميم المصنوع من مادة ال الأكرليك ثم قصها بالليزر و قام أيضا بعمل قالب و شراء مادة من السيلكون السائل و صبها بالقالب كما هي و بعد حوالي 10 ساعات أو اقل بقليل تكون قد تحولت للحالة الصلبة و يمكنك إستخدامها ك rubber للعجلات بدلا من شراء rubber جاهز بتكلفة كبيرة نسبيا
ملاحظة مهمة : كل التصاميم ستجدها إن شاء الله بالمرفقات
و لقد ركزت على نقطة "في حالتك" لأن ال ESP32 ربما تصل ل1 أو 2 أمبير إن كنت تستخدم ال wifi و ال bluetooth و لكننا لن نستخدمها في مشروعنا لذلك لن نهتم بها و سنركز على السحب في الحالات العادية فق
قبل توصيل القطع تأكد دائما من التوصيل الكهربائي و الفولتيات من خلال ال Multimeter
هذه أهم الأمور الخاصة بالروبوت و تصميمه و لكن هناك ملاحظة على الروبوت بالصورة
يفضل أن تقوم بتقصير أكثر لأن أحد أسباب المشاكل في المسابقة كانت أنه طويل نسبيا و بنفس الوقت لا تجعله قصير كثيرا
الجانب البرمجي
في البرمجة اعتمدنا تقنية أو خوارزمية تسمى ال PID و بالطبع هي موضوع كبير و إن شاء الله أن نتمكن من شرحه مستقبلا
و لكن بعد بناء الكود الأساسي يجب أن تقوم بالتجربة و معرفة الأخطاء و تعديل سرعة الماتورات أو قيم ال P و I و D حسب النتائج التي تراها
كما قلنا سيكون لدينا شروحات أخرى للأمر مستقبلا و لكن يمكنك البحث عنه لأنه عبارة عن خوارزمية تحكم أساسية تستخدم على نطاق واسع في الروبوتات و المصانع و المصاعد لضبط إخراج النظام بناءً على الفرق (الخطأ) بين نقطة التحديد المطلوبة والحالة الحالية
هذه بعض المصادر لفهم ما هي ال PID :

PID Control
PID Control stands for P roportional- I ntegral- D erivative feedback control and corresponds to one of the most commonly used controllers used in industry. It's success is based on its capacity to...
www.autonomousrobotslab.com

Espresso Machine PID controller using ESP32 and RainMaker
The developer resources in just one place!


PID Controlled Thermostat Using ESP32 (Applied to a Rancilio Silvia Coffee Machine)
PID Controlled Thermostat Using ESP32 (Applied to a Rancilio Silvia Coffee Machine): WarningThe Rancilio Silvia machine runs on high voltage. Any modification to it might be fatal to you or your machine, you act at your own risk and responsible for your modification. Any modification to the...

Make a FAST Line Follower Robot Using PID!
Make a FAST Line Follower Robot Using PID!: Making a line follower robot is a great way to get started with robotics and arduino. But I had assumed it was a simple project and never tried to make one. But when a friend of mine challenged me to join a line follower robot competition, I had to …
و هذا هو الكود النهائي الذي استخدمناه لروبوتنا (لا تستخدمه نفسه و لكن تعلم منه و طور و حسن عليه و ناقشني بتلك التحسينات لنستفيد جميعا)
C++:
#define IR_L_2 32
#define IR_L_1 33
#define IR_O_1 25
#define IR_R_1 26
#define IR_R_2 27
const int sensorPins[5] = {IR_L_2 , IR_L_1 , IR_O_1 , IR_R_1 , IR_R_2};
#define Push_Button 22
/*************** تعريف دبابيس التحكم بالمحركات ***************/
// ------ محرك الجانب الأيمن ------
#define EN_A 2 // للمحرك الأيمن (التحكم بالسرعة) PWM دبوس تمكين
#define IN_1 5 // دبوس التحكم بالاتجاه 3 للمحرك الأيمن
#define IN_2 4 // دبوس التحكم بالاتجاه 4 للمحرك الأيمن
// ------ محرك الجانب الأيسر ------
#define EN_B 15 // للمحرك الأيسر (التحكم بالسرعة) PWM دبوس تمكين
#define IN_3 18 // دبوس التحكم بالاتجاه 1 للمحرك الأيسر
#define IN_4 19 // دبوس التحكم بالاتجاه 2 للمحرك الأيسر
/*************** ESP32 المتقدمة لـ PWM إعدادات ***************/
#define PWM_Channel_R 0 // رقم 0 للمحرك الأيمن PWM قناة
#define PWM_Channel_L 1 // رقم 1 للمحرك الأيسر PWM قناة
#define Frequency 6000 // (5 كيلوهرتز) PWM تردد إشارة
#define Resolution 8 // (تساوي 8 بت = قيم من 0 إلى 255) PWM دقة
double error = 0;
double lastError = 0;
double totalError = 0;
double integral = 0;
double output = 0;
double weightedSum = 0;
int sum = 0;
int R_Speed = 0;
int L_Speed = 0;
int total_Sensor = 10;
uint8_t i = 0; // يمكن أن يأخذ قيمًا موجبة من 0 إلى 255 فقط (uint8_t) تـعـريـف مُـتـغـيـر من نـوع
bool Run = 0;
unsigned long Print_1 = 0;
int Values[8];
int sensorValues[5];
// ━━━━━━━ إعدادات PID ━━━━━━━
double Kp = 65; // معامل التناسب
double Ki = 0.0002; // معامل التكامل
double Kd = 93; // معامل التفاضل
int baseSpeed = 160; // السرعة الأساسية (0-255)
int maxSpeed = 193; // أقصى سرعة
// أوزان الحساسات
int sensorWeights[5] = { -4, -2, 0, 2, 4 };
int skip = 0;
// Forward Declarations
void Stop_motors();
void calculateError();
void PID();
void Move_motors();
void Print();
void readSensors();
void setup() {
Serial.begin(115200); // تهيئة الاتصال التسلسلي لعرض النتائج (سرعة 115200 باود)
for (int i = 0; i < 5; i++) {
pinMode(sensorPins[i], INPUT);
}
pinMode(Push_Button, INPUT_PULLUP);
// ------ تهيئة دبابيس التحكم بالاتجاه كمخرجات ------
pinMode(IN_3, OUTPUT); // تهيئة IN3 كمخرج
pinMode(IN_4, OUTPUT); // تهيئة IN4 كمخرج
pinMode(IN_1, OUTPUT); // تهيئة IN1 كمخرج
pinMode(IN_2, OUTPUT); // تهيئة IN2 كمخرج
// ------ المتقدمة PWM تهيئة قنوات ------
ledcSetup(PWM_Channel_R, Frequency, Resolution); // للمحرك الأيمن مع التردد والدقة المحددة PWM إعداد قناة
ledcSetup(PWM_Channel_L, Frequency, Resolution); // للمحرك الأيسر مع التردد والدقة المحددة PWM إعداد قناة
// ------ PWM ربط دبابيس التمكين مع قنوات ------
ledcAttachPin(EN_B, PWM_Channel_R); // بالقناة 0 ENB ربط
ledcAttachPin(EN_A, PWM_Channel_L); // بالقناة 1 ENA ربط
Stop_motors(); // إيقاف كلا المحركين عند بداية التشغيل
}
void loop() {
if (digitalRead(Push_Button) == LOW) {
Run = 1;
Serial.println("Run");
delay(1000);
}
if (Run == 1) {
readSensors();
calculateError();
PID();
Move_motors();
Print();
//delay(1000);
}
}
void readSensors() {
for (int i = 0; i < 5; i++) {
sensorValues[i] = digitalRead(sensorPins[i]);
}
sensorValues[0] = ! sensorValues[0];
sensorValues[1] = ! sensorValues[1];
sensorValues[2] = ! sensorValues[2];
sensorValues[3] = ! sensorValues[3];
}
void calculateError() {
weightedSum = 0;
sum = 0;
for (int i = 0; i < 5; i++) {
weightedSum += sensorValues[i] * sensorWeights[i];
sum += sensorValues[i];
}
if (sensorValues[0] == 0 &&
sensorValues[1] == 0 &&
sensorValues[2] == 0 &&
sensorValues[3] == 0 &&
sensorValues[4] == 0 ){
skip = 1;
Serial.print("wwwwwwww");
if (R_Speed > L_Speed){
// يمين
digitalWrite(IN_3, LOW);
digitalWrite(IN_4, HIGH);
digitalWrite(IN_1, HIGH);
digitalWrite(IN_2, LOW);
ledcWrite(PWM_Channel_R , 165);
ledcWrite(PWM_Channel_L , 165);
Serial.print("rrrrrrrrrrrrrrr");
}
if (R_Speed < L_Speed){
// يسار
digitalWrite(IN_3, HIGH);
digitalWrite(IN_4, LOW);
digitalWrite(IN_1, LOW);
digitalWrite(IN_2, HIGH);
ledcWrite(PWM_Channel_R , 165);
ledcWrite(PWM_Channel_L , 165);
Serial.print("lllllllllllll");
}
return;
} else if (sensorValues[2] == 1){
L_Speed = 170;
R_Speed = 170;
skip = 1;
}
else if (sum == 0) { // إذا فقد الخط، استخدم آخر قيمة خطأ
error = lastError;
skip = 0;
} else {
error = weightedSum / sum;
skip = 0;
}
}
void PID() {
if (skip == 1){
return;
}
int derivative = error - lastError;
totalError += error;
output = (Kp * error) + (Ki * totalError) + (Kd * derivative);
lastError = error; // حفظ الخطأ السابق
// تحديث السرعات
R_Speed = baseSpeed + output;
L_Speed = baseSpeed - output;
L_Speed = constrain(L_Speed, 0, maxSpeed);
R_Speed = constrain(R_Speed, 0, maxSpeed);
}
// تحريك المواتير
void Move_motors() {
// التحكم بالاتجاه للأمام
digitalWrite(IN_3, LOW);
digitalWrite(IN_4, HIGH);
digitalWrite(IN_1, LOW);
digitalWrite(IN_2, HIGH);
// تحديد السرعات
ledcWrite(PWM_Channel_R, R_Speed);
ledcWrite(PWM_Channel_L, L_Speed);
}
// يقاف المواتير
void Stop_motors() {
digitalWrite(IN_3, LOW);
digitalWrite(IN_4, LOW);
digitalWrite(IN_1, LOW);
digitalWrite(IN_2, LOW);
ledcWrite(PWM_Channel_R, 0);
ledcWrite(PWM_Channel_L, 0);
}
// طباعة
void Print() {
if (millis() - Print_1 > 1000) {
Serial.print("\t");
for (i = 0; i < 5; i++) {
Serial.print(sensorValues[i]); // عرض قيمة الحساس الحالي
Serial.print("\t"); // (Tab) إضافة تباعد بين القيم
}
Serial.println(); // إنهاء السطر بعد عرض جميع القيم
Serial.println();
Serial.print(" Error= ");
Serial.print(error);
Serial.print(" Output= ");
Serial.print(output);
Serial.print(" Left= ");
Serial.print(L_Speed);
Serial.print(" Right= ");
Serial.println(R_Speed);
Serial.println();
//delay(1000); // تأخير 100 مللي ثانية قبل القراءة التالية
Print_1 = millis();
}
}
و لماذا تمت برمجته ب C++ و ليس Python علما أن ال ESP يمكن برمجتها ب Python خلافا لل Arduino الذي لا يقبل إلا C++ .. بكل بساطة لأن C++ أسرع في التعامل مع المتحكم من Python
بكل بساطة و أترك المجال لمن لديه خلفية تقنية أكبر مني بالإجابة على هذا السؤال و توضيحه بشكل أكبر
لأن ال C++ هي "compiled" language لذلك يتم ترجمة كل الأوامر دفعة واحده للغة الألة (صفر و واحد) و يتم القيام بهذه العملية قبل رفع ال sketch للمتحكم .. خلافا ل Python التي تعتبر "interpreted" language ما يعني أن الأسطر تترجم و ترسل للمتحكم أو المعالج سطر سطر
ربما تتساءل عن النتيجة التي حصلنا عليها و أظن بأني اخربتك أننا فزنا, بالواقع حصلنا على المركز الثالث و لله الحمد علما أن روبوتنا هو الذي أنهى الحلبة بأقصر وقت بناء على حديث أحد الحكام معنا و لكن لبعض الأخطاء (مثل خروج الروبوت عن الحلبة و اعادتنا له تم خصم عدد من النقاط)
و من أفضل الأمور بالمسابقة هي العمل الجماعي, فنحن كفريق طلاب بالجامعة انضممنا بأكثر من مجموعة و الحمد لله كان التعاون و العمل الجماعي بيننا كبير و لله الحمد و فوز أحدنا كان فوز لنا جميعا, فصحيح كنا بمنافسة و لكن الفوز و الخسارة لم تكن إلا بإرادة الله و ليس لنا أن نعترض على إرادة الله لذلك نسعى بكل ما لدينا و نرضى بالنتيجة و هذه أراها قاعدة في كل المسابقات أحببت التنويه لها لأننا جميعا كفريق واحد و حتى المنافسين من الفرق و الجامعات الأخرى كلنا مسلمين فلا نجعل مسابقة تفيد روح الإخوة بيننا و حتى اخواننا في العروبة من الديانات الأخرى (بشرط أن لا يعتدوا علينا أو على دينا)
و في الختام أتمنى أن لا يتم إستخدام الكود و هذه النصائح من دون تفكير copy and past .. بل ابحث و تحقق من أين قلت أنك أن ال ESP32 إن ادخلت لها أكثر من 3.3v ستعمل فذهب لجوجل و اكتب ESP32 datasheet و اقرأ و تعلم
دائما تعلم التفكير و البحث, لا تأخذ المعلومة على طبق من ذهب
أتمنى أن يكون في هذا الشرح الإفادة لإخواننا المسلمين و المسلمات و لا تنسوا الدعاء لغزة و لكل العالم الإسلامي
و إن احتجت أو فريقك لمساعدة تعلم ما عليك فعله أن تبحث و تتعلم لتحلها و تسهر اليالي و إن لم تعلم فأرسل لي رسالة على هذا المنتدى الكريم و سأسهر معك الليالي لحلها إن شاء الله ( :
ستجدون كل التصاميم مضغوطة بصغة .rar في المرفقات
و بالتوفيق