في مشروعنا السابق، قمنا ببناء ذراع روبوتية بست درجات حرية (DIY 6-DOF) باستخدام مقاومات متغيرة، التي كانت تعمل في البداية عن طريق توصيل المقاومات المتغيرة مباشرة إلى Arduino. هذا الإعداد سمح لنا بالتحكم اليدوي في كل محرك سيرفو عن طريق ضبط المقاومات المتغيرة. على الرغم من أن هذه الطريقة كانت تعمل بشكل جيد، إلا أنها كانت محدودة بالتحكم اليدوي فقط.
لزيادة المرونة وسهولة الاستخدام، نحن نقوم الآن بترقية المشروع إلى ذراع روبوتية بست درجات حرية (DIY 6-DOF) مع تحكم عبر البلوتوث. هذه الترقية تتيح التحكم اللاسلكي باستخدام تطبيق على الهاتف الذكي، مما يجعلها أكثر سهولة وفعالية في الاستخدام. من خلال دمج تقنية البلوتوث، يمكننا الآن التحكم في الذراع الروبوتية عن بُعد، مما يلغي الحاجة إلى التعديلات اليدوية ويضيف سهولة كبيرة إلى العملية.
في هذا الدليل، سنرشدك خلال خطوات تعديل الذراع الروبوتية بست درجات حرية (DIY 6-DOF) مع تحكم عبر البلوتوث من خلال تصميم تطبيق مخصص باستخدام MIT App Inventor. يعتمد هذا المشروع على عملنا السابق، حيث يقدم قدرات التحكم اللاسلكي مع الحفاظ على هيكل بسيط، مما يجعل الذراع الروبوتية أكثر تنوعًا وقابلة للاستخدام في مجموعة واسعة من المهام.
لنغوص في عملية ترقية الذراع الروبوتية بست درجات حرية (DIY 6-DOF) مع التحكم عبر البلوتوث.
التصميم باستخدام Autodesk Inventor
المرحلة الأولى هي تصميم كل مكون من مكونات ذراع الروبوت باستخدام Autodesk Inventor. تتيح هذه البرمجية إنشاء نماذج ثلاثية الأبعاد بدقة، مما يضمن أن تكون المكونات مصممة بشكل مثالي لتناسب SG90 Micro Servos وMG996R Servos 180°، التي تم اختيارها لتوازنها بين الوزن والحجم وقدرات عزم الدوران: SG90 Micro Servos مثالية للمفاصل الصغيرة للذراع نظرًا لصغر حجمها ودقتها الكافية. MG996R Servos 180° تُستخدم في القاعدة والمناطق ذات الحمل الثقيل بفضل قدراتها القوية على توليد عزم الدوران.
طباعة مكونات الذراع ثلاثية الأبعاد
بمجرد اكتمال النماذج، تأتي خطوة الطباعة ثلاثية الأبعاد. يتم طباعة كل جزء باستخدام خيوط مثل PLA لسهولة استخدامها ودقتها الجيدة في التفاصيل. يجب تحسين إعدادات الطباعة للقوة، خصوصًا للمفاصل التي ستتعرض لأحمال ميكانيكية أعلى. قد تشمل معالجة ما بعد الطباعة تعديلات التجميع وتحسينات جمالية.
تجميع الذراع الروبوتية
بمجرد طباعة جميع الأجزاء، يتضمن التجميع وضع المحركات السيرفو بعناية في مواقعها المخصصة وربط أجزاء الذراع بدقة. يجب اختبار المفاصل لضمان عملها بسلاسة للتأكد من أنها يمكن أن تتحمل نطاق الحركة المتوقع دون إجهاد المحركات.
الخطوة 1: تجميع القاعدة
- ابدأ بتركيب المحرك السيرفو الأول على قاعدة ذراعك الروبوتي. استخدم البراغي المرفقة مع مجموعة المحرك للتأكد من تثبيته بإحكام (يمكنك استخدام برغي ذاتي اللولب متقاطع M3x12). يعمل هذا المحرك كنقطة الدوران المحورية للهيكل الكامل للذراع، مما يسمح بالحركة الأساسية.
- إضافة محمل كروي 6806ZZ بحجم 30*42*7.
قم بتركيب المحمل لتسهيل الحركة الدورانية السلسة بين القاعدة والجزء العلوي الدوار للذراع. ضع المحمل بعناية في الفتحة المخصصة على القاعدة. هذا المكون حيوي لتقليل الاحتكاك والتآكل أثناء عمليات الذراع، مما يعزز كل من دقة وطول عمر الحركة.
- توصيل قرن السيرفو
ثبّت قرن السيرفو (servo horn) في الجزء العلوي الدوّار للذراع. استخدم البراغي المرفقة في مجموعة السيرفو لتثبيت الذراع مباشرة في الجزء الدوّار الذي يجلس فوق المحمل الكروي (يمكنك استخدام برغي ذاتي اللولب متقاطع M2x12). تأكد من أن الاتصال محكم وآمن، حيث سينقل هذا الاتصال القوى الدورانية من السيرفو إلى الأجزاء العلوية للذراع.
الخطوة 2: تركيب قرون السيرفو على وصلات ذراع الروبوت
لتجميع قرون السيرفو على وصلات ذراع الروبوت، قم بمحاذاة قرون السيرفو مع نقاط التثبيت على الذراع، مع التأكد من تطابق الثقوب بشكل صحيح. ثبت كل قرن ببرغيين، مع شدّهما بإحكام لضمان الاستقرار ومنع الانزلاق أثناء التشغيل. تحقق مرة أخرى من كل اتصال للتأكد من أن القرون مثبتة بأمان، حيث سيكون ذلك حاسمًا للعمل الدقيق والموثوق للذراع.
الخطوة 3: تركيب محركات السيرفو على وصلات ذراع الروبوت
لتركيب محركات السيرفو على وصلات ذراع الروبوت، قم بمحاذاة كل محرك مع نقاط التثبيت المحددة على وصلات الذراع، مع التأكد من الاتجاه الصحيح. ثبت كل محرك باستخدام برغيين، مرورًا بهما من خلال الذراع إلى ثقوب التثبيت في المحرك لضمان التوافق الجيد. بعد التثبيت، اختبر كل اتصال للتأكد من استقراره عن طريق تطبيق ضغط خفيف للتأكد من أن السيرفو مثبت بإحكام دون أي حركة.
الخطوة 4: توصيل وصلات ذراع الروبوت
بعد تثبيت محركات السيرفو والقرون على وصلات ذراع الروبوت، قم بمحاذاة وربط الوصلات. ضع الوصلات بحيث تتطابق الثقوب في قرن إحدى الوصلات مع نقاط التعليق في الوصلة التالية. ثبتها باستخدام براغي لضمان اتصال محكم ضروري لحركة المفصل السلسة. شد البراغي بشكل صحيح للحفاظ على الاستقرار والوظيفة أثناء تشغيل ذراع الروبوت. تضمن هذه الخطوة الاستمرارية الهيكلية والحركة الفعالة عبر مفاصل ذراع الروبوت.
الخطوة 5: تجميع القابض
في الخطوة النهائية من التجميع، ثبّت الممسك (Gripper) في الوصلة النهائية لذراع الروبوت. تأكد من محاذاته بشكل صحيح وقم بتثبيته باستخدام البراغي (البراغي المستخدمة في مفاصل الممسك هي M3x20)، مع التأكد من أن الممسك يعمل بسلاسة ودون أي عوائق. تكمل هذه الخطوة عملية البناء الفيزيائي للذراع الروبوتي، مما يهيئها للاختبار الوظيفي وإجراء التعديلات اللازمة لضمان دقة الحركات المفصلية.
الخطوة 6: تركيب ذراع الروبوت على قاعدة خشبية
ثبت الذراع الروبوتي المُجمّع بالكامل على قاعدة خشبية لضمان الثبات أثناء التشغيل. استخدم البراغي لتثبيت قاعدة الذراع بإحكام على الخشب، مما يوفر أساسًا متينًا يمنع أي حركة أثناء الاستخدام. لقد استخدمت برغي ذاتي اللولب متقاطع M3x12.
التكوين الإلكتروني والتوصيلات الكهربائية للذراع الروبوتية بست درجات حرية (DIY 6-DOF) مع التحكم عبر البلوتوث
الإلكترونيات في هذا المشروع تركز على دمج مشغل السيرفو PCA9685 مع Arduino Uno للتحكم في الستة محركات سيرفو عبر الاتصال بالبلوتوث للذراع الروبوتية بست درجات حرية (DIY 6-DOF) مع التحكم عبر البلوتوث:
Arduino Uno: يعمل كوحدة التحكم الرئيسية. لوحة مشغل السيرفو PCA9685: هذا المشغل الذي يتم التحكم فيه عبر I2C يدير بفعالية ما يصل إلى 16 محرك سيرفو، مما يوفر إمكانية التوسع إلى أكثر من الستة محركات المستخدمة في هذا المشروع.
دليل التوصيلات:
- تزويد PCA9685 ومحركات السيرفو بالطاقة: من الضروري استخدام مصدر طاقة مستقل (مثل مصدر طاقة 5V 10A) لتلبية متطلبات التيار دون التحميل الزائد على منظم الطاقة الخاص بـ Arduino.
- توصيل كل محرك سيرفو بـ PCA9685: يتم توصيل محركات السيرفو بقنوات فردية على لوحة PCA9685، مع التأكد من التوجيه الصحيح للطاقة، الأرضي، وإشارات التحكم.
- توصيل وحدة البلوتوث HC-05 بـ Arduino: يتم توصيل وحدة HC-05 بأطراف RX و TX في Arduino، مما يتيح الاتصال اللاسلكي بين Arduino وتطبيق الهاتف الذكي للتحكم في محركات السيرفو.
المزيد عن لوحة تشغيل المحركات سيرفو PCA9685
يعد التحكم في محركات السيرفو مباشرةً من Arduino أمرًا سهلاً، ولكن قد تواجه قيودًا، خاصةً إذا كان مشروعك يتضمن عدة أجهزة أو مكونات أخرى تعتمد على PWM. يعد استخدام جميع دبابيس PWM المتاحة أو تعارضات المكتبة المحتملة على موارد المؤقت من التحديات الشائعة التي يمكن أن تعيق قابلية التوسع في تصميماتك.
لإدارة أجهزة سيرفومتعددة بكفاءة دون زيادة التحميل على الأردوينو، فإن دمج لوحة تشغيل السيرفو مخصصة، مثل تلك المزودة بشريحة PCA9685، هي استراتيجية ممتازة. تستخدم هذه اللوحة اتصال I2C، مما يبسط التوصيلات إلى سلكين فقط - CL (الساعة) و SDA (البيانات) - بغض النظر عن عدد محركات السيرفو التي يتم التحكم فيها. وهي تدعم التحكم في ما يصل إلى 16 سيرفو لكل لوحة، ويمكن توصيلها بسلاسل متسلسلة مع لوحات PCA9685 الأخرى. يسمح هذا الإعداد بالتحكم في ما يصل إلى 992 سيرفوعن طريق تسلسل لوحات متعددة، وهو مثالي للمشاريع الكبيرة.
تحتوي كل لوحة على توصيلات بسيطة:
GND: الوصلة الأرضية.
OE: تمكين الإخراج، وعادةً ما تُترك غير موصولة للحفاظ على تمكين جميع المخرجات.
SCL: خط الساعة لاتصالات I2C.
SDA: إشارة بيانات لاتصال I2C.
VCC: الإمداد المنطقي عند +5 فولت.
V+: طاقة لمحركات السيرفو، والتي يمكن توفيرها أيضًا عبر موصل محمي في الجزء العلوي من اللوحة لتوفير حماية أكبر ضد القطبية العكسية.
تتميز اللوحة بمجموعات من الموصلات ذات 3 سنون لكل محرك سيرفو، مما يبسّط عملية التوصيل إلى حد كبير ويوفر حلاً قويًا للمشاريع التي تتطلب تحكمًا مؤازرًا واسع النطاق.
لإدارة 16 محركًا والتحكم بها بفعالية باستخدام برنامج تشغيل قائم على PCA9685 مع Arduino، اتبع هذه الخطوات المفصلة لإعداد المكونات وتوصيلها بشكل صحيح:
توصيل بالأردوينو
تستخدم لوحة تشغيل محرك السيرفو PCA9685 اتصال I2C ويتطلب أربع وصلات فقط إلى Arduino الخاص بك. إليك كيفية توصيله اعتمادًا على طراز Arduino الخاص بك:
أردوينو كلاسيكي (أونو، إلخ)
+ 5 فولت -> VCC (يعمل على تشغيل شريحة PCA9685، وليس محركات السيرفو)
GND -> GND
Analog 4 -> SDA
Analog 5 -> SCL
تشغيل محركات السيرفو:
يعمل VCC على لوحة PCA9685 على تشغيل الرقاقة نفسها فقط. لتشغيل محركات السيرفو، يجب عليك أيضًا توصيل دبوس V+، والذي يمكنه التعامل مع ما يصل إلى 6 فولت حتى لو كان VCC عند 3.3 فولت.
يوصى باستخدام كتلة طرفية مستقطبة لتوصيل طاقة محركات السيرفو.
نظرًا لأن محركات السيرفو يمكن أن تسحب تيارًا كبيرًا، خاصةً تحت الحمل، يجب عليك التفكير في مصادر الطاقة هذه:
مزود طاقة تبديلي بجهد 5 فولت 2 أمبير لمزودات الطاقة الصغيرة أو الأقل.
حامل بطارية 4xAA - يوفر 6 فولت من الخلايا.
توصيل محركات السيرفو:
قم بتوصيل كل محرك السيرفو بلوحة PCA9685 باستخدام مقبسها القياسي ذو 3 سنون. تأكد من أن السلك الأرضي (عادة ما يكون أسود أو بني) يطابق الصف السفلي من رؤوس المسامير على اللوحة وأن سلك الإشارة (عادة ما يكون أصفر أو أبيض) يطابق الصف العلوي.
برمجة للتحكم التفاعلي
الكود الخاص بـ Arduino يتحكم في ذراع روبوتية بست درجات حرية (6-DOF) عبر الاتصال اللاسلكي باستخدام وحدة البلوتوث HC-05. يتيح لك هذا الكود حفظ عدة وضعيات (مواقع محركات السيرفو للذراع) وتشغيلها بشكل متتابع أو في حلقة مستمرة.
#include <Wire.h> #include <Adafruit_PWMServoDriver.h> #include <SoftwareSerial.h> // Constants and definitions const int numServos = 6; // Number of servos const int maxConfigurations = 10; // Maximum number of storable poses const int stepDelay = 10; // Delay between each step to slow down the servo movement const int stepSize = 1; // The number of degrees to move per step // Servo channels on the PCA9685 const int servoChannels[numServos] = {0, 4, 8, 9, 12, 13}; // Storage structures int savedConfigurations[maxConfigurations][numServos]; int currentServoPositions[numServos] = {375, 375, 375, 375, 375, 375}; // Set default position to middle (around 90 degrees) int configCount = 0; // Counter for stored poses bool isPlaying = false; // Status indicating if poses are being played bool loopPlayback = false; // Status indicating if poses should be played in a loop bool stopPlaying = false; // Status indicating if playback should be stopped int currentPoseIndex = 0; // Index of the current pose during playback bool initialized = false; // Flag to check if initialization happened Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(); SoftwareSerial BTSerial(10, 11); // RX, TX for Bluetooth communication void setup() { Serial.begin(9600); // Start serial communication for the monitor BTSerial.begin(9600); // Start Bluetooth serial communication pwm.begin(); pwm.setPWMFreq(60); // Set frequency to 60 Hz for the servos // Move servos to initial positions (middle position) to avoid any jumps for (int i = 0; i < numServos; i++) { pwm.setPWM(servoChannels[i], 0, currentServoPositions[i]); } initialized = true; // Set initialized to true once the servos are moved to the middle position Serial.println("Bluetooth Servo Controller started. Waiting for commands..."); } void loop() { if (BTSerial.available()) { String input = BTSerial.readStringUntil('\n'); // Read the entire line until a newline character input.trim(); // Remove unwanted spaces and newline characters // Output the received data to the serial monitor Serial.println("Received data: " + input); if (input.equalsIgnoreCase("S")) { saveCurrentPose(); } else if (input.equalsIgnoreCase("P")) { startPlayingPoses(); } else if (input.equalsIgnoreCase("R")) { resetPoses(); } else if (input.equalsIgnoreCase("St")) { stopPlayingPoses(); } else if (input.equalsIgnoreCase("LoopON")) { // When the switch is turned ON loopPlayback = true; Serial.println("Loop playback enabled."); } else if (input.equalsIgnoreCase("LoopOFF")) { // When the switch is turned OFF loopPlayback = false; Serial.println("Loop playback disabled."); } else if (input.indexOf(',') > 0) { processLastValue(input); } else { Serial.println("Unknown command: " + input); } } if (isPlaying && !stopPlaying) { if (loopPlayback) { playPosesInLoop(); } else { playNextPose(); } } } // Function to process the last value in the line void processLastValue(String command) { int lastCommaIndex = command.lastIndexOf(','); // Find the last comma in the line if (lastCommaIndex > 0 && lastCommaIndex < command.length() - 1) { int servoIndex = command.substring(0, lastCommaIndex).toInt() - 1; // First value: servo index float lastValue = command.substring(lastCommaIndex + 1).toFloat(); // Last value in the line // Directly use the last value int targetPos = (int)lastValue; // Map the position from 0-180 to PWM values from 150-600 (may vary depending on the servo) int targetPwmValue = map(targetPos, 0, 180, 150, 600); // Smooth movement to target position moveToPositionSmoothly(servoIndex, targetPwmValue); } else { Serial.println("Invalid command for servo control: " + command); } } // Function to move servo smoothly to the target position void moveToPositionSmoothly(int servoIndex, int targetPwmValue) { int currentPwmValue = currentServoPositions[servoIndex]; if (targetPwmValue > currentPwmValue) { for (int pos = currentPwmValue; pos <= targetPwmValue; pos += stepSize) { pwm.setPWM(servoChannels[servoIndex], 0, pos); delay(stepDelay); } } else { for (int pos = currentPwmValue; pos >= targetPwmValue; pos -= stepSize) { pwm.setPWM(servoChannels[servoIndex], 0, pos); delay(stepDelay); } } currentServoPositions[servoIndex] = targetPwmValue; // Update current position Serial.println("Servo " + String(servoIndex + 1) + " set to PWM value: " + String(targetPwmValue)); } // Function to save the current servo positions as a pose void saveCurrentPose() { if (configCount < maxConfigurations) { for (int i = 0; i < numServos; i++) { savedConfigurations[configCount][i] = currentServoPositions[i]; Serial.print("Servo "); Serial.print(i + 1); Serial.print(" saved at PWM value: "); Serial.println(savedConfigurations[configCount][i]); } configCount++; Serial.println("Pose saved. Total saved poses: " + String(configCount)); } else { Serial.println("Memory full, cannot save more poses."); } } // Function to start playing the saved poses void startPlayingPoses() { if (configCount == 0) { Serial.println("No poses saved, nothing to play."); return; } Serial.println("Playback of saved poses started..."); isPlaying = true; stopPlaying = false; currentPoseIndex = 0; } // Function to play the next pose void playNextPose() { if (currentPoseIndex < configCount) { Serial.println("Playing pose " + String(currentPoseIndex + 1)); for (int i = 0; i < numServos; i++) { moveToPositionSmoothly(i, savedConfigurations[currentPoseIndex][i]); } delay(1000); // Standard delay between poses currentPoseIndex++; } else { isPlaying = false; Serial.println("Playback of poses finished."); } } // Function to play the poses in a loop void playPosesInLoop() { for (int j = 0; j < configCount; j++) { Serial.println("Playing pose " + String(j + 1)); for (int i = 0; i < numServos; i++) { moveToPositionSmoothly(i, savedConfigurations[j][i]); } delay(1000); // Standard delay between poses if (stopPlaying) break; } } // Function to stop playback void stopPlayingPoses() { stopPlaying = true; isPlaying = false; Serial.println("Playback of poses stopped."); } // Function to reset all saved poses void resetPoses() { configCount = 0; isPlaying = false; loopPlayback = false; currentPoseIndex = 0; Serial.println("All saved poses have been deleted."); }
إليك تفصيل لكل جزء من الكود والغرض منه:
1. إضافة المكتبات
#include <Wire.h> #include <Adafruit_PWMServoDriver.h> #include <SoftwareSerial.h>
- Wire.h: تسهل هذه المكتبة الاتصال عبر I2C بين Arduino ولوحة تشغيل السيرفو PCA9685.
- Adafruit_PWMServoDriver.h: تُستخدم هذه المكتبة للتحكم في إشارات PWM المرسلة إلى محركات السيرفو عبر لوحة PCA9685.
- SoftwareSerial.h: تتيح هذه المكتبة الاتصال التسلسلي عبر الدبابيس الرقمية، وتُستخدم هنا للتواصل مع وحدة البلوتوث HC-05.
2. الثوابت والتعاريف
const int numServos = 6; // Number of servos const int maxConfigurations = 10; // Maximum number of storable poses const int stepDelay = 10; // Delay between each step to slow down the servo movement const int stepSize = 1; // The number of degrees to move per step const int servoChannels[numServos] = {0, 4, 8, 9, 12, 13};
- numServos: يحدد العدد الإجمالي لمحركات السيرفو في الذراع الروبوتية.
- maxConfigurations: يحدد عدد الوضعيات (مجموعة من أوضاع السيرفو) التي يمكن تخزينها في الذاكرة.
- stepDelay: يُدخل تأخيرًا بين كل خطوة من حركة السيرفو لمنع الحركات المفاجئة.
- stepSize: يحدد درجات الحركة لكل خطوة لضمان انتقالات أكثر سلاسة.
- servoChannels: يُعرّف هذا المصفوفة القنوات التي تتصل بها المحركات (السيرفو) في وحدة التحكم PCA9685 (على سبيل المثال، المحرك 1 متصل بالقناة 0، المحرك 2 متصل بالقناة 4، وهكذا).
3. هياكل التخزين
int savedConfigurations[maxConfigurations][numServos]; int currentServoPositions[numServos] = {375, 375, 375, 375, 375, 375}; // Initial positions int configCount = 0; bool isPlaying = false; bool loopPlayback = false; bool stopPlaying = false; int currentPoseIndex = 0;
- savedConfigurations: مصفوفة ثنائية الأبعاد تخزن قيم PWM لكل محرك سيرفو في كل وضعية.
- currentServoPositions: مصفوفة تتعقب المواقع الحالية لمحركات السيرفو.
- configCount: يحتفظ بعدد الوضعيات التي تم حفظها.
- isPlaying: علامة تشير إلى ما إذا كان النظام يقوم حالياً بتشغيل الحركات المحفوظة.
- loopPlayback: علامة لتحديد ما إذا كان التشغيل يجب أن يتم بشكل متكرر (حلقة).
- stopPlaying: علامة لإيقاف عملية التشغيل.
- currentPoseIndex: يتتبع الوضعية الحالية التي يتم تشغيلها.
4. تهيئة في دالة setup()
void setup() { Serial.begin(9600); BTSerial.begin(9600); pwm.begin(); pwm.setPWMFreq(60); // Initialize the servos to a safe start position (midpoint) for (int i = 0; i < numServos; i++) { pwm.setPWM(servoChannels[i], 0, currentServoPositions[i]); } Serial.println("Bluetooth Servo Controller started. Waiting for commands..."); }
- Serial.begin(9600): يقوم بتهيئة الشاشة التسلسلية لمراقبة الأخطاء وتصحيحها.
- BTSerial.begin(9600): يقوم بتهيئة الاتصال عبر البلوتوث بسرعة 9600 بود.
- pwm.begin(): يقوم بتهيئة مشغل السيرفو PCA9685.
- pwm.setPWMFreq(60): يقوم بتعيين تردد PWM إلى 60 هرتز، وهو مناسب للتحكم في محركات السيرفو.
- بعد تهيئة المحركات، يتم تحريكها إلى الموضع المتوسط لتجنب أي قفزات مفاجئة عند بدء النظام.
5. الحلقة الرئيسية (loop())
void loop() { if (BTSerial.available()) { String input = BTSerial.readStringUntil('\n'); input.trim(); Serial.println("Received data: " + input); // Process the received command } // Handle playback of poses }
تقوم دالة loop()
بفحص مستمر للبيانات الواردة عبر البلوتوث ومعالجة الأوامر مثل حفظ، تشغيل، إيقاف أو إعادة تعيين الوضعيات.
6. معالجة الأوامر
if (input.equalsIgnoreCase("S")) { saveCurrentPose(); } else if (input.equalsIgnoreCase("P")) { startPlayingPoses(); } else if (input.equalsIgnoreCase("R")) { resetPoses(); } else if (input.equalsIgnoreCase("St")) { stopPlayingPoses(); } else if (input.equalsIgnoreCase("LoopON")) { loopPlayback = true; Serial.println("Loop playback enabled."); } else if (input.equalsIgnoreCase("LoopOFF")) { loopPlayback = false; Serial.println("Loop playback disabled."); } else if (input.indexOf(',') > 0) { processLastValue(input); } else { Serial.println("Unknown command: " + input); }
- S: يحفظ أوضاع السيرفو الحالية كـ "وضعية" (Pose).
- S: يحفظ أوضاع السيرفو الحالية كـ "وضعية" (Pose).
- R: يعيد تعيين جميع الأوضاع المحفوظة.
- St: يوقف تشغيل الحركات المحفوظة.
- LoopON/LoopOFF: يقوم بتشغيل أو إيقاف تكرار تشغيل الأوضاع المحفوظة.
- أوامر أخرى: إذا كان الإدخال يحتوي على فاصلة، يُفترض أنه أمر لتحديد موضع السيرفو.
7. معالجة القيمة الأخيرة في الأمر
void processLastValue(String command) { int lastCommaIndex = command.lastIndexOf(','); int servoIndex = command.substring(0, lastCommaIndex).toInt() - 1; float lastValue = command.substring(lastCommaIndex + 1).toFloat(); int targetPos = (int)lastValue; int targetPwmValue = map(targetPos, 0, 180, 150, 600); // Smoothly move the servo to the target position moveToPositionSmoothly(servoIndex, targetPwmValue); } void moveToPositionSmoothly(int servoIndex, int targetPwmValue) { int currentPwmValue = currentServoPositions[servoIndex]; if (targetPwmValue > currentPwmValue) { for (int pos = currentPwmValue; pos <= targetPwmValue; pos += stepSize) { pwm.setPWM(servoChannels[servoIndex], 0, pos); delay(stepDelay); } } else { for (int pos = currentPwmValue; pos >= targetPwmValue; pos -= stepSize) { pwm.setPWM(servoChannels[servoIndex], 0, pos); delay(stepDelay); } } currentServoPositions[servoIndex] = targetPwmValue; // Update current position Serial.println("Servo " + String(servoIndex + 1) + " set to PWM value: " + String(targetPwmValue)); }
processLastValue: يتعامل مع الأوامر التي تتضمن أوضاعًا محددة للسيرفو، حيث يقوم بترجمة الزاوية (0-180 درجة) إلى قيم PWM اللازمة لتحريك السيرفو.
moveToPositionSmoothly: تقوم هذه الدالة بتحريك السيرفو بسلاسة إلى الموضع المطلوب بدلاً من القيام بحركات مفاجئة أو متشنجة.
8. حفظ الوضع الحالي
void saveCurrentPose() { if (configCount < maxConfigurations) { for (int i = 0; i < numServos; i++) { savedConfigurations[configCount][i] = currentServoPositions[i]; Serial.print("Servo "); Serial.print(i + 1); Serial.print(" saved at PWM value: "); Serial.println(savedConfigurations[configCount][i]); } configCount++; Serial.println("Pose saved. Total saved poses: " + String(configCount)); } else { Serial.println("Memory full, cannot save more poses."); } }
saveCurrentPose: يقوم بحفظ المواضع الحالية لجميع محركات السيرفو في مصفوفة savedConfigurations
ويزيد عدد الحركات المخزنة.
9. بدء تشغيل الوضعيات
void startPlayingPoses() { if (configCount == 0) { Serial.println("No poses saved, nothing to play."); return; } Serial.println("Playback of saved poses started..."); isPlaying = true; stopPlaying = false; currentPoseIndex = 0; }
startPlayingPoses: يبدأ تشغيل الوضعيات المخزنة من البداية.
10. تشغيل الوضعية التالية
void playNextPose() { if (currentPoseIndex < configCount) { Serial.println("Playing pose " + String(currentPoseIndex + 1)); for (int i = 0; i < numServos; i++) { pwm.setPWM(i, 0, savedConfigurations[currentPoseIndex][i]); Serial.print("Servo "); Serial.print(i + 1); Serial.print(" set to PWM value: "); Serial.println(savedConfigurations[currentPoseIndex][i]); } delay(1000); // Standard delay between poses currentPoseIndex++; } else { isPlaying = false; Serial.println("Playback of poses finished."); } }
- playNextPose: تقوم بتشغيل الوضعية المحفوظة التالية عن طريق ضبط كل محرك سيرفو على موضعه المخزن.
11. تشغيل الوضعيات في حلقة
void playPosesInLoop() { for (int j = 0; j < configCount; j++) { Serial.println("Playing pose " + String(j + 1)); for (int i = 0; i < numServos; i++) { pwm.setPWM(i, 0, savedConfigurations[j][i]); Serial.print("Servo "); Serial.print(i + 1); Serial.print(" set to PWM value: "); Serial.println(savedConfigurations[j][i]); } delay(1000); // Standard delay between poses if (stopPlaying) break; } }
playPosesInLoop: يقوم بتشغيل جميع الحركات المحفوظة بشكل مستمر في حلقة حتى يتم استقبال أمر الإيقاف.
12. إيقاف التشغيل
void stopPlayingPoses() { stopPlaying = true; isPlaying = false; Serial.println("Playback of poses stopped."); }
- stopPlayingPoses: يوقف تشغيل الأوضاع الجاري تشغيلها.
13. إعادة ضبط الوضعيات
void stopPlayingPoses() { stopPlaying = true; isPlaying = false; Serial.println("Playback of poses stopped."); }
- resetPoses: يحذف جميع الأوضاع المحفوظة ويعيد ضبط النظام لبدء جديد.
يوفر هذا الكود إطارًا قويًا للتحكم في ذراع روبوتية بست درجات حرية (6-DOF) عبر البلوتوث، مما يتيح تخزين وتشغيل الحركات المعقدة.
تطوير تطبيق RobotArmControl للذراع الروبوتية بست درجات حرية (DIY 6-DOF) مع التحكم عبر البلوتوث
تطبيق RobotArmControl هو واجهة مستخدم مصممة للتحكم في ذراع روبوتية بست درجات حرية (DIY 6-DOF) مع التحكم عبر البلوتوث لاسلكيًا عبر البلوتوث. يتيح هذا التطبيق للمستخدمين التحكم في كل مفصل من مفاصل الذراع الروبوتية في الوقت الفعلي، وحفظ أوضاع مختلفة للذراع، وإعادة تشغيل هذه الأوضاع إما مرة واحدة أو في حلقة مستمرة.
الميزات الرئيسية للتطبيق:
الاتصال عبر البلوتوث:
- يبدأ التطبيق بميزة الاتصال بوحدة بلوتوث متصلة بالذراع الروبوتية، مما يتيح التحكم اللاسلكي.
أشرطة التمرير للتحكم في محركات السيرفو:
- تحتوي الواجهة على ستة أشرطة تمرير (sliders)، كل منها يتوافق مع واحد من المفاصل الستة للذراع الروبوتية (مثل القبضة، ميل المعصم، دوران المعصم، الكوع، الكتف، والخصر). تحريك هذه الأشرطة يرسل بيانات الموقع في الوقت الفعلي إلى محرك السيرفو المقابل، مما يتيح تحكمًا يدويًا دقيقًا في حركات الذراع الروبوتية.
- التحكم في السرعة:
- هناك شريط تمرير مخصص للتحكم في سرعة محركات السيرفو، مما يتيح لك ضبط سرعة انتقال الذراع بين الوضعيات المحفوظة.
التحكم في الوضعية:
- يشمل التطبيق أزرار لحفظ الوضعية، تشغيل الوضعيات المحفوظة، إعادة تعيين الوضعيات المحفوظة، وإيقاف أي تشغيل مستمر. زر "حفظ" يقوم بتخزين الوضعيات الحالية لجميع المحركات، وزر "تشغيل" يبدأ تشغيل الوضعيات المحفوظة، وزر "إعادة تعيين" يمسح البيانات المحفوظة.
التشغيل المتكرر:
- يتيح مفتاح التبديل تشغيل أو إيقاف تشغيل الحركات المحفوظة في حلقة. عند التمكين، ستستمر الذراع الروبوتية في تكرار الحركات المحفوظة حتى يتم إيقاف الحلقة.
عرض عدد الأوضاع:
- يعرض التطبيق العدد الحالي للوضعيات المحفوظة، مما يساعد المستخدم على تتبع تكويناته المخزنة.
لنقم بتفصيل وشرح كل كتلة من واجهة App Inventor التي قدمتها:
1. التهيئة العامة (تعيين القيمة العالمية receivedText إلى 0
)
- الهدف: يقوم هذا البلوك بتهيئة متغير عام يسمى
receivedText
ويحدد قيمته الأولية بـ0
. سيتم استخدام هذا المتغير لاحقًا في التطبيق لتخزين وإدارة البيانات المستلمة من اتصال البلوتوث.
2. قبل اختيار جهاز البلوتوث (when ListPicker1.BeforePicking do
)
- الغرض: يتم تنفيذ هذا الكود عند قيام المستخدم باختيار جهاز بلوتوث من القائمة. يتم ملء
ListPicker1.Elements
بأسماء وعناوين أجهزة البلوتوث المتاحة باستخدام طريقةBluetoothClient1.AddressesAndNames
. يضمن ذلك أن يتمكن المستخدم من اختيار جهاز البلوتوث المتاح للاتصال.
3. بعد اختيار جهاز البلوتوث (when ListPicker1.AfterPicking do
)
- الهدف: يتم تنفيذ هذا الجزء بعد أن يختار المستخدم جهاز البلوتوث من القائمة.
- الخطوات:
- محاولة الاتصال: يحاول التطبيق الاتصال بالجهاز الذي تم اختياره عبر البلوتوث باستخدام
BluetoothClient1.Connect
مع العنوان المختار منListPicker1.Selection
. - إذا نجح الاتصال: إذا تم الاتصال بنجاح، يتم تحديث
Label1.Text
ليعرض "متصل" وتغييرLabel1.BackgroundColor
إلى اللون الأخضر للإشارة إلى نجاح الاتصال. - إذا فشلت الاتصال: في حال فشل الاتصال، يتم تحديث
Label1.Text
إلى "غير متصل" وتغييرLabel1.BackgroundColor
إلى اللون الأحمر للإشارة إلى الفشل.
- محاولة الاتصال: يحاول التطبيق الاتصال بالجهاز الذي تم اختياره عبر البلوتوث باستخدام
4. تبديل مفتاح التكرار (when LoopSwitch.Changed do
)
- الغرض: يتعامل هذا الكتلة مع مفتاح التبديل الذي يتحكم فيما إذا كان يجب تشغيل تكرار أوضاع الذراع الروبوتية في حلقة.
- الخطوات:
- If Switch is ON: If the
LoopSwitch
is turned ON, it sends the text “LoopON\n” via Bluetooth to the robot usingBluetoothClient1.SendText
. - If Switch is OFF: If the
LoopSwitch
is turned OFF, it sends the text “LoopOFF\n” to the robot.
- If Switch is ON: If the
5. تغيير موضع المنزلق (عند تغيير موضع SliderX
)
- الهدف: كل من هذه الكتل يتعامل مع تغيير الموضع لأشرطة التمرير الخاصة بالتحكم في السيرفو (مثل
Slider1
،Slider2
، إلخ). - الخطوات:
- إرسال موضع السيرفو: عند تحريك المنزلق، يقوم التطبيق بإرسال القيمة الجديدة لموضع المنزلق عبر البلوتوث. يتم تنسيق القيمة على شكل "X,Y\n"، حيث يمثل
X
رقم السيرفو (من 1 إلى 6 ويقابلSlider1
إلىSlider6
)، ويمثلY
الموضع الحالي للمنزلق (thumbPosition
). - Purpose for Each Slider:
Slider1
sends the position for Servo 1.Slider2
يرسل الموقع الخاص بالمحرك سيرفو 2، وهكذا.
- إرسال موضع السيرفو: عند تحريك المنزلق، يقوم التطبيق بإرسال القيمة الجديدة لموضع المنزلق عبر البلوتوث. يتم تنسيق القيمة على شكل "X,Y\n"، حيث يمثل
6. زر الإيقاف (when StopButton.Click do
)
- الهدف: يتم تنفيذ هذا الجزء عند النقر على زر "إيقاف".
- الخطوات:
- التحقق من الاتصال: يتحقق أولاً مما إذا كان التطبيق متصلاً بجهاز البلوتوث.
- التحقق من الاتصال: يقوم أولاً بالتحقق مما إذا كان التطبيق متصلاً بجهاز البلوتوث.
7. النقر على زر الحفظ (when SaveButton.Click do
)
- الهدف: يتم تنفيذ هذا الكود عند النقر على زر "حفظ"، مما يسمح للمستخدم بحفظ الوضع الحالي لمحركات السيرفو كإحدى الوضعيات.
- الخطوات:
- التحقق من الاتصال: يقوم التطبيق بالتحقق مما إذا كان متصلاً بجهاز البلوتوث.
- إرسال أمر الحفظ: إذا تم الاتصال، يقوم بإرسال النص "S\n" إلى الذراع الروبوتية، مما يأمرها بحفظ الوضعية الحالية.
- تحديث عدد الوضعيات: يتم زيادة المتغير العام
receivedText
بمقدار 1، ويتم تحديثPoseCountLabel.Text
ليعكس العدد الإجمالي للوضعيات المحفوظة.
8. النقر على زر التشغيل (when PlayButton.Click do
)
- الغرض: يتم تنفيذ هذا الكتلة عند النقر على زر "تشغيل"، حيث يبدأ تشغيل الحركات المحفوظة.
- الخطوات:
- التحقق من الاتصال: يتحقق أولاً مما إذا كان التطبيق متصلاً بجهاز البلوتوث.
- إرسال أمر التشغيل: إذا كان متصلاً، فإنه يرسل النص "P\n" إلى الذراع الروبوتية، مما يوجهها لبدء تشغيل الأوضاع المحفوظة.
9. نقرة زر إعادة الضبط (عند النقر على ResetButton.Click
)
- الهدف: يتم تنفيذ هذا الكود عند الضغط على زر "إعادة التعيين"، حيث يتم مسح جميع الحركات المحفوظة وإعادة تعيين عداد الحركات.
- الخطوات:
- التحقق من الاتصال: يتحقق أولاً مما إذا كان التطبيق متصلاً بجهاز البلوتوث.
- إرسال أمر إعادة التعيين: إذا كان الجهاز متصلاً، يتم إرسال النص "R\n" إلى الذراع الروبوتية، مما يطلب منها إعادة تعيين جميع الأوضاع المحفوظة.
- إعادة تعيين عدد الوضعيات: يتم إعادة تعيين المتغير العالمي
receivedText
إلى 0، ويتم تحديثPoseCountLabel.Text
لعرض "0"، مما يشير إلى عدم حفظ أي وضعيات حاليًا.
المعايرة والاختبار
بعد الانتهاء من البرمجة وإعداد الأجهزة، تكون الخطوة التالية الحاسمة هي إجراء معايرة واختبارات شاملة. تهدف هذه المرحلة إلى ضمان أن كل محرك سيرفو في الذراع الروبوتية يعمل بسلاسة ويستجيب بدقة للأوامر المرسلة من تطبيق الهاتف الذكي.
تثبيت التطبيق والاتصال
قبل البدء في المعايرة، يجب عليك تثبيت وإعداد تطبيق RobotArmControl على جهاز Android الخاص بك للتفاعل مع الذراع الروبوتية.
تنزيل التطبيق:
- قم بتحميل تطبيق RobotArmControl عبر الرابط المقدم (يمكن أن يكون هذا الملف مشتركا عبر خدمة سحابية أو منقولاً مباشرة من الكمبيوتر).
تثبيت التطبيق:
- افتح ملف APK الذي تم تنزيله لبدء عملية التثبيت. قد تحتاج إلى السماح بالتثبيت من مصادر غير معروفة في إعدادات جهازك إذا لم يكن التطبيق من متجر Google Play.
منح الأذونات:
- أثناء التثبيت أو عند تشغيل التطبيق لأول مرة، تأكد من منح جميع الأذونات اللازمة، خاصة الوصول إلى الأجهزة القريبة و البلوتوث. هذه الأذونات مطلوبة لكي يتمكن التطبيق من اكتشاف وحدة البلوتوث (HC-05) المتصلة بـ Arduino والاتصال بها.
الاتصال عبر البلوتوث:
- افتح التطبيق وانقر على زر اختيار الجهاز، والذي سيعرض قائمة بالأجهزة المتاحة التي تدعم البلوتوث.
- حدد وحدة البلوتوث (عادةً ما تكون مُسماة
HC-05
أو شيء مشابه) المتصلة بـ Arduino الخاصة بذراعك الروبوتية. - بمجرد أن يتم الاتصال، ستعرض التطبيق حالة "متصل"، ويمكنك البدء في إرسال الأوامر إلى الذراع الروبوتية.
عملية المعايرة
بمجرد أن يتم توصيل الهاتف الذكي بنجاح بالذراع الروبوتية عبر التطبيق، يمكن أن تبدأ عملية معايرة الذراع:
معايرة السيرفو:
- اختبر كل محرك سيرفو بشكل فردي للتأكد من أنه يتحرك إلى الوضعيات الصحيحة وفقًا للأوامر المرسلة من التطبيق.
- قد يكون من الضروري فك الروابط المتصلة بمحركات السيرفو، ومعايرة المحركات، ثم إعادة تجميعها للتأكد من أن الحركات دقيقة وتتطابق مع المواضع المتوقعة. هذه الخطوة مهمة بشكل خاص للمفاصل التي تتطلب زوايا محددة.
تعديل النطاق:
- إذا لزم الأمر، قم بتعديل نطاق واستجابة محركات السيرفو. يمكن القيام بذلك في الكود أو عن طريق إعادة وضع وصلات السيرفو بشكل فعلي لضمان أن يصل كل مفصل إلى النطاق المطلوب للحركة دون إجهاد المحركات.
اختبار تشغيل الوضعيات:
- اختبر تشغيل الأوضاع المخزنة باستخدام التطبيق للتأكد من أن الذراع الروبوتية تتبع التسلسلات المخزنة بسلاسة ودقة.
- خلال مرحلة الاختبار هذه، راقب حركات الذراع للتأكد من عدم حدوث أي عوائق أو اختلالات. إذا لزم الأمر، أعد المعايرة عن طريق تعديل مواضع السيرفو أو إعادة برمجة قيم الوضعيات في الكود.
الاختبار النهائي:
- بعد المعايرة، قم بإجراء اختبار كامل عن طريق حفظ الوضعيات وتشغيلها بالتسلسل. تأكد من أن الذراع تتحرك بسلاسة عبر جميع الوضعيات، سواء في الوضع الفردي أو وضع التكرار.
- راقب الذراع أثناء هذه الاختبارات للتأكد من أن المعايرة صحيحة وأن المحركات السيرفو لا تتعرض للإجهاد الزائد أو تصل إلى حدودها الميكانيكية.
الخاتمة
بناء ذراع روبوتية بست درجات حرية (6-DOF) هو مشروع معقد ولكنه مُجزي للغاية، حيث يجمع بين عدة مجالات هندسية تشمل التصميم الميكانيكي، والإلكترونيات، والبرمجة. من خلال تنفيذ هذا المشروع، لن تقوم فقط بتطوير مهاراتك التقنية، بل ستحصل أيضًا على خبرة عملية قيمة تمهد الطريق لاستكشاف المزيد من التطبيقات المتقدمة في مجال الروبوتات والأتمتة. سواء كنت هاويًا يسعى لتعميق فهمه للروبوتات أو مهندسًا يتطلع لتوسيع مهاراته، فإن هذا المشروع يقدم تجربة تعليمية شاملة تجمع بين الإبداع والدقة والخبرة التقنية.
I bought the model but it doesn’t have the bearing measurement valueReport
Thanks for your comment, Fernando! Please add a ball bearing 6806ZZ with dimensions 30*42*7.
Hi, interesting project also to do with a teenager. Regarding the servo MG996R, it exists in 2 configurations : either 180° or 360°. What you recommend?
Best regards,
Emmanuel
Hi Emmanuel, thank you for your kind words! I recommend using the 180° version of the MG996R servo for this project. This is because the 180° servo allows precise control of the angle, which is essential for positioning and movement in robotic arms or similar applications. The 360° servo is designed for continuous rotation and is better suited for tasks like driving wheels, where angle control is not required. Best regards, OmArTronics.
sir i did the same connection but it is not working ,please tell me how much
current give the pca9685 servo controller
The PCA9685 servo controller itself draws very little current, typically less than 10mA. However, the total current needed depends on the servos you are using. Each MG996R servo, for example, can draw up to 2A (max 2,5 A) under load. Make sure your power supply can provide enough current for all connected servos, considering their combined maximum draw.