الأشعة تحت الحمراء
ستتعلم اليوم كيف تدعم روبوتك بمستشعر الأشعة الحمراء. كما سيتبين لك كيف يمكنك التحكم بروبوتك عن بعد باستعمال أي جهاز عن تحكم عن بعد يعمل باﻷشعة تحت الحمراء. سيخولك هذا بالتواصل بالروبوت سواء كان مدرعة أو طائرة، حيوانا روبوتيا أو إنسان آليا، أو أي جهاز آخر في مجال محدد.
|
تقديم
يمكن القول أن هذا المقال العلمي مقسم إلى قسمين رئيسين:
تفكر : نقدم لك باختصار أصناف المخلوقات الحية التي زودها خالقها بالتقنية التي نتكلم عنها اليوم. وهذا القسم سيكون ممتعا لمن لا يريد أن يهتم بالأمور التقنية. وسيكون أمتع وأفيد لمن يحب عالم التقنيات حيث سيتعلم التمعن والتفكر في ما أبدعه خالقه الذي أمره بذلك في كثير من الآيات في كتابه.
تقنية : سنركز على تمكينك من اللعب بتقنية اليوم برمجيا. ولذلك ننصح الشغوف منكم أن يتمكن أولا من فهم برمجة الروبوت وبعض أساسيات البرمجة. أما من يتقنون البرمجة فلا خوف عليهم ولا هم يحزنون.
مختلف أشكالها
تفكر
النحل
النحل، وما أدراك ما النحل، مخلوق غاية في العجب. وله نظام رؤية عجيب غريب. وعسى أن يسعنا الوقت لنحوم حول ما تم اكتشافه عن النظام المعقد والحياة الغريبة للنحل. كيف لا، وخالقه ميزه كما ميز النمل بسورة له في كتابه إلـــــى يوم يبعثون.
فرؤية اﻷزهار عند النحل مثلا ليست كالتي نرى بها نحن معاشر البشر.ولا أظن أن لنا القدرة على الحكم بجودة رحيق الإزهار انطلاقا من النظر إليها ﻷول اﻷمر. لكن النحل، فهو قادر على تحديد الزهرة ذات الرحيق الأجود من التي دونها، وهذا يتم بسبب امتلاكها نظام رؤية يعتمد على اﻷشعة تحت الحمراء حسب ما تم التوصل إليه علميا. فالنحل لا يرى اللون الأحمر، لكن يرى ما تحت الأحمر.
| |
رؤية الانسان
|
محاكاة لرؤية النحل
|
البعوض
تكره البعوض؟ نعم، وأصارحك القول بأني أكرهه أنا أيضا. يبدوا أن دمي شهي لإناث البعوض.
لهذا المخلوق من القدرات العجيبة التي ما زال العلم يكتشف الواحدة منها تلوى اﻷخرى.
أنا إن سألتك عن المنطقة الأكثر حرارة في جسمك فقد لا تعلم ذلك. لكن البعوض له من القدرة ما تمكنه من اختيار المنطقة اﻷوفر دما في جسمك ولو كان ذلك في جنح الظلام.
يعتمد البعوض على نظام رؤية مختلف عنا أيضا، فهو يعتمد على الأشعة تحت الحمراء حيث تساعده على رؤية الأجسام عن طريق رسم خرائط حرارية لها في دماغه.
|
الأفاعي
الكثير من الحشرات يعتمد على الرؤية بالأشعة تحت الحمراء، أما في عالم الزواحف فنجد الثعابين كالثعبان ذات الحشرجة. هذا الأخير هو أعمى في الحقيقة، لكن نظام الرؤية أو قل الاستشعار بواسطة الأشعة تحت الحمراء يخوله من ضرب فريسته في المناطق القاتلة في جسمها.
الأسهم الحمراء المبينة في الصورة جانبه تبين حفر مستشعرات الأشعة تحت الحمراء لكل من الثعبانpython في الأعلى والثعبان ذي الحشرجة في الأسفل.
أما السهم الأسود فهو يبين فقط الأنف.
|
كيف تركب في الروبوت
قد تقوم بدعم الروبوت بعدة مستشعرات للرؤية مثلا، ومن بينها تجد مستشعرات الأشعة تحت الحمراء ومستشعرات الموجات فوق الصوتية والليدار أو بكل بساطة كاميرات، غير أن هذه الأخيرة تبقى هي الأصعب في برمجتها.
يمكنك استخدام مستشعر الأشعة تحت الحمراء كعيني الروبوت أو كوسيلة اتصال بينك وبين الروبوت من أجل التحكم به عن بعد بنفس الطريقة التي تتحكم بها بالتلفاز. وبالتالي يمكنك تركيبه في الروبوت بأي شكل من الأشكال التي تراها في الصور التالية:
امتحان مستشعر اﻷشعة الحمراء
لمستشعر الأشعة الحمراء ثلاث أقطاب (أو مرابط)، يمكنك أن تصلهم كالتالي:
• المربط 1 هو المخرج (output) وبالتالي يمكنك إيصاله بصمام ثنائي ضوئي
• المربط 2 هو اﻷرضية (ground)
• المربط 3 يوصل بجهد كهربائي ذي 5 فولط
|
يحتاج مستشعر اﻷشعة الحمراء لـ 5 فولط حتى يعمل على أحسن ما يرام. في مثالنا هذا، استعملنا أربع بطاريات ذي 1.5 فولط، وبالتالي سيكون مجموع الجهد الكهربائي هو 6 فولط.
يمكنك الحصول على 5 فولط من بطاقة Arduino أيضا إذا استعملتها.
قم بإيصال المربط اﻷطول للصمام الضوئي بالقطب 6 فولط والمربط اﻷقصر بالمربط 1 للمستشعر لكن اجعل بينهما مقاومة كهربائية (مقاومتها من 200 إلى 1000 أوم).
عندما يحدد المستشعر أي إشارة تدل على اﻷشعة الحمراء، سيجعل المخرج في حالة منخفظة (أي بشحنة كهربائية ضعيفة في المربط 1)، وبالتالي يشتعل الصمام الضوئي.
حاول أن تحصل على جهاز للتحكم عن بعد للتلفاز أو لجهاز DVD أو للحاسوب أو لأي جهاز مشابه.يمكنك أن تسرقها من والديك مثلا هههه.
ستلاحظ أن الصمام الضوئي يشتعل كلما ضغطت على أزرار جهاز التحكم عن بعد.
|
قراءة موجات الأشعة تحت الحمراء بواسطة بطاقة Arduino
يمكنك أن تركب مستشعر الأشعة تحت الحمراء في روبوتك أو أي جهاز تفكر فيه عن طريق استعمال بطاقة Arduino البرمجية كالتالي:
|
الشيء الجيد هنا هو أنه بإمكانك تركيب مستشعر الأشعة تحت الحمراء بسهولة. ما عليك إلا أن تقوم بإيصال المربط 1 للمستشعر (أي المخرج) بمربط رقمي (digital pin) للبطاقة Arduino.
والشي غير الجيد (لكن ليس سيئا) هو أن الدالة المعروفة digitalRead بطيئة عند قراءتها لإشارات سريعة حين مجيئها كالأشعة تحت الحمراء. وبالتالي سنقوم بقراءة الإشارات مباشرة من المربط D2 وهذا ما يفعله بالضبط السطر التالي الذي ستجده داخل الشيفرة:
IRpin_PIN & (1 << IRpin)
|
أداة التحكم عن بعد تسمى بالإنجليزية Remote control وبالفرنسية Telecommande لتفهم عما نتكلم هنا.
|
باستعمال الشيفرة التالية يمكنك قراءة الذبذبات من أداة التحكم عن بعد. هل تستطيع أن تتحدى نفسك وتفهمها؟
|
/* شيفرة لقراءة ذبذبات الأشعة تحت الحمراء
Arduino والبطاقة PNA4602 هذه الشيفرة خاصة لمستشعر الاشعة تحت الحمراء
يمكن استعمالها مجانا
www.ladyada.net والموقع adafruit.com يمكنك أن تتفحص الموقع
للمزيد من الأمثلة العملية
*/
// سنستعمل طريقة مباشرة لقراءة الاشارات من المربط
// ثقيلة جدا في هذه الحالةdigitalRead() لأن الدالة
// uint8_t IRpin = 2;
// يمكنك أن ترى التالي pin #2 هو نفسه Pin D2 المربط
// http://arduino.cc/en/Hacking/PinMapping168
#define IRpin_PIN PIND
#define IRpin 2
/* ه65 جزءا من الثانية هو الحد الأقصى الذي لا يمكن لذبذات الأشعة الحمراء أن تتجاوزه
*/
#define MAXPULSE 65000
/* بين كل ذبذبة وأخرى يجب الانتظار لبعض الوقت
كلما كان الانتظار أطول كلما كانت الدقة أعلى
لكن الانتظار لكدة طويلة جدا لن يساعدك على الدقة التي تريدها
*/
#define RESOLUTION 20
// سنقوم بتسجيل 100 ذبذبة رقمية
// أي أنها ستكون على شكل زوجي
// أعلى أسفل، مرتفع منخفض، 0 و 1 وما إلى ذلك من أسماء
uint16_t pulses[100][2]; // ه100 ذبذبة عدد كبير، لكنها عبارة عن مثال هنا فقط
uint8_t currentpulse =0;// مؤشر عداد
uint16_t lowpulse; // متغير مساعدة سيستعمل لحفظ الذبذبة في حالة الانخفاظ
uint16_t highpulse; // متغير مساعدة سيستعمل لحفظ الذبذبة في حالة الارتفاع
void setup(void)
{
Serial.begin(9600);
Serial.println("استعداد لقراءة ذبذبات الأشعة تحت الحمراء");
}
void loop(void)
{
highpulse = lowpulse =0; // تهيئة متغيري المساعدة
// 1) نقوم بحساب طول الذبذبة عندما تكون في حالة ارتفاع
// while (digitalRead(IRpin)) { // يمكنك استعمال هذه عوض التي تليها لكنها بطيئة جدا
while(IRpin_PIN &(1<< IRpin)) // طالما المربط 2 في حالة ارتفاع
{
// نحسب عدد مرات التي تكون فيها الذبذبة في حالة ارتفاع
highpulse++;
delayMicroseconds(RESOLUTION);// ننتظر لبعض الوقت
// إذا كانت الذبذبة طويلة جدا فهذا يعني
// أننا لم نستقبل شيئا من جهاز ارسال الأشعة تحت الحمراء
// وبالتالي ما علينا إلا أن ننهي تنفيذ الشيفرة
// ونكتب على الشاشة ما تم التوصل إليه
// ومن هنا سيتم إعادة تهيئة البطاقة مرة أخرى
if((highpulse >= MAXPULSE)&&(currentpulse !=0))
{
printpulses();
currentpulse=0;
return;
}
}
// نحتفظ بطول الذبذبة في حالة ارتفاع
pulses[currentpulse][0]= highpulse;
// 2) نقوم بحساب طول الذبذبة عندما تكون في حالة انخفاض
// while (!digitalRead(IRpin)) { // يمكنك استعمال هذه عوض التي تليها لكنها بطيئة جدا
while(!(IRpin_PIN & _BV(IRpin)))
{
// نحسب عدد مرات التي تكون فيها الذبذبة في حالة انخفاظ
lowpulse++;
delayMicroseconds(RESOLUTION);// ننتظر لبعض الوقت
// إذا كانت الذبذبة طويلة جدا فهذا يعني
// أننا لم نستقبل شيئا من جهاز ارسال الأشعة تحت الحمراء
// وبالتالي ما علينا إلا أن ننهي تنفيذ الشيفرة
// ونكتب على الشاشة ما تم التوصل إليه
// ومن هنا سيتم إعادة تهيئة البطاقة مرة أخرى
if((lowpulse >= MAXPULSE) &&(currentpulse !=0)){
printpulses();
currentpulse=0;
return;
}
}
// نحتفظ بطول الذبذبة في حالة ارتفاع
pulses[currentpulse][1]= lowpulse;
// 3) تمت قراءة الموجة السابقة بنجاح؟ لنتابع إذن نحو التالية
currentpulse++;
}
void printpulses(void)
{
Serial.println("\n\r\n\rReceived: \n\rOFF \tON");
for(uint8_t i =0; i < currentpulse; i++)
{
Serial.print(pulses[i][0]* RESOLUTION, DEC);
Serial.print(" usec, ");
Serial.print(pulses[i][1]* RESOLUTION, DEC);
Serial.println(" usec");
}
}
|
هل تجد عندك أداة تحكم عن بعد كالتي تستعمل للتلفاز مثلا؟ قم بتوجيهها نحو مستشعر الاشعة تحت الحمراء ثم اضغط على بعض من أزرارها. المثال التالي هو نتيجة لأداة تحكم عن بعد لشركة صوني (Sony IR remote)
|
قراءة أداة التحكم عن بعد
إذا استطعت فهم هذه الفقرة، سيمكنك برمجة روبوتك أو أي جهاز تود صنعه لتتحكم به عن بعد بكل سهولة، وسيمكنك كذلك استعمال أي أداة باعثة للأشعة الحمراء من أجل هذا.
اختر ما تشاء من أدوات التحكم عن بعد كهذه مثلا:
سندرج فيما يلي مثالا لقراءة اﻷوامر الصادرة من أداة تحكم لشركة آبل (Apple clicker remote). كما يمكنك استعمال أي نوع من أدوات التحكم تعجبك. إن لم تجدها يمكنك سرقتها من أمك مثلا هههه.
|
سأسألك سؤالا:
من المعلوم أن كل زر في أداة التحكم عن بعد له مهمة معينة، وهو يختلف بذلك مع باقي الأزرار الأخرى.بمعنى آخر، أن لكل زر تردد مختلف عن باقي الأزرار. وبالتالي ذبذبات الأشعة تحت الحمراء لكل زر لها تردد مختلف عن باقي الأزرار. وهذا جميل، لأنه سيصبح بإمكانك إصدار أوامر من أداة التحكم عن بعد إلى روبوتك أو جهازك. فمثلا يمكنك أن تقول لروبوتك أن يتقدم نحو الأمام أو أن يستدير يمينا أو يسار وما إلى ذلك.
وسؤالي هو الآتي: كيف ستقوم ببرمجة كل زر على حدة؟ أي، كيف سيمكنك التعرف أو تمييز تردد ذبذبات كل زر على حدة؟
الجواب:
سنقوم باستعمال نفس الشيفرة السابقة ونوجه أداة التحكم إلى مستشعر الأشعة تحت الحمراء. عندما يتم الضغط على زر من الأزرار ستقوم الشيفرة بطباعة (أو قل كتابة) قيم عددية على شاشة الحاسوب. هذه القيم هي التي تمثل تردد الذبذبة الصادرة عن ضغطك على الزر بكل بساطة.
والآن، سنستعمل نفس الشيفرة السابقة لكن مع بعض التعديلات لطباعة القيم العددية بشكل مفهوم لدينا. سنقوم أولا بتعديل الدالة السابقة printpulses من أجل إظهار القيم العددية على شكل جدول.
|
void printpulses(void)
{
// اكتبها على شكل جدول على الشاشة
Serial.println("int IRsignal[] = {");
Serial.println("// ON, OFF (in 10's of microseconds)");
for(uint8_t i =0; i < currentpulse-1; i++)
{
Serial.print("\t");// tab
Serial.print(pulses[i][1]* RESOLUTION /10, DEC);
Serial.print(", ");
Serial.print(pulses[i+1][0]* RESOLUTION /10, DEC);
Serial.println(",");
}
Serial.print("\t");// tab
Serial.print(pulses[currentpulse-1][1]* RESOLUTION /10, DEC);
Serial.print(", 0};");
}
|
عندما تعدل الدالة printpulses قم بتنفيذ الشيفرة ثم اضغط على زر من أزرار أدارة التحكم. بالنسبة لنا تم الحصول على التالي عندما تم الضغط على زر أداة التحكم آبل:
int IRsignal[] = { // ON, OFF (in 10's of microseconds)
912, 438,
68, 48,
68, 158,
68, 158,
68, 158,
68, 48,
68, 158,
68, 158,
68, 158,
70, 156,
70, 158,
68, 158,
68, 48,
68, 46,
70, 46,
68, 46,
68, 160,
68, 158,
70, 46,
68, 158,
68, 46,
70, 46,
68, 48,
68, 46,
68, 48,
66, 48,
68, 48,
66, 160,
66, 50,
66, 160,
66, 52,
64, 160,
66, 48,
66, 3950,
908, 214,
66, 3012,
908, 212,
68, 0};
|
بالتالي فقد حصلنا على القيم العددية التي تمثل تردد ذبذبات الزر. هذا الجدول مهم، فعليك أن تضعه في الشيفرة لنستعمله فيما بعد من أجل مقارنته بالقيم العددية عندما تضغط على نفس الزر مرة أخرى. هذه المقارنة ضرورية للروبوت لأنه تخوله التمييز بين هذا الزر والأزرار الأخرى.
كنا قدمنا في المثال السابق طريق مبسطة من أجل تصنت (أو قراءة) الأشعة تحت الحمراء (أعني الدالة loop في المثال السابق) . والآن، سنستخدم نفس المبدأ وسننشىء دالة تقوم بعملية التصنت حتى يتسنى لنا استخدامها كلما احتجنا إليها. (محتوى الدالة التالية ListenForIR هو نفس محتوى الدالة loop في المثال السابق)
int listenForIR(void)
{
uint16_t lowpulse, highpulse;
currentpulse = 0;
while (1) // كرر العمليات التالية إلى الأبد
{
highpulse = lowpulse =0;
while(IRpin_PIN &(1<< Irpin))
{
highpulse++;
delayMicroseconds(RESOLUTION);
if((highpulse >= MAXPULSE)&&(currentpulse !=0)) {
return currentpulse;
}
}
pulses[currentpulse][0]= highpulse;
while(!(IRpin_PIN & _BV(IRpin)))
{
lowpulse++;
delayMicroseconds(RESOLUTION);
if((lowpulse >= MAXPULSE) &&(currentpulse !=0)){
return currentpulse;
}
}
pulses[currentpulse][1]= lowpulse;
currentpulse++;
}
}
|
ستصبح الدالة الرئيسية loop كالتالي: (صغيرة الحجم أليس كذلك؟)
void loop(void)
{
int numberpulses;
numberpulses = listenForIR(); //ننصت لذبذبات الأشعة تحت الحمراء
Serial.print("Heard "); // عدد الذبذبات التي تم التنصت لها
Serial.print(numberpulses);
Serial.println("-pulse long IR signal");
}
|
إذا قمت بتنفيذ الشيفرة من جديد، وضغطت على زر أداة التحكم عدد من المرات سيكون الخارج كالتالي:
|
والآن بعدما اجتزنا كل المراحل السابقة سيبقى لنا أن نغير الدالة الرئيسية loop من أجل مقارنة قيم الترددات التي احتفظنا بها بقيم الترددات الصادرة من أداة التحكم عن بعد. القيم التي احتفظنا بها محفوظة في الجدول المسمى ApplePlaySignal.
يجب أن تعلم كذلك أنك لن تستطيع الحصول على نفس القيم المحفوظة عندك دائما. وذلك راجع إلى عدة أسباب منها تأثير الهواء والحواجز الطبيعية على قوة ذبذبات الأشعة تحت الحمراء. وبالتالي يمكننا أن نعتبر نسبة الاختلاف هي %20 بين القيم المحفوظة عندنا والقيم المستقبلة. لا تتفاجئ فهذه النسبة جيدة.
#define FUZZINESS 20 // ه 20 هي نسبة الاختلاف المسموح بها عند المقارنة
void loop(void)
{
int numberpulses;
numberpulses = listenForIR();
Serial.print("Heard ");
Serial.print(numberpulses);
Serial.println("-pulse long IR signal");
for(int i=0; i< numberpulses-1; i++)
{
int oncode = pulses[i][1]* RESOLUTION /10; // الذبذبة في حالة ارتفاع
int offcode = pulses[i+1][0]* RESOLUTION /10; // الذبذبة في حالة انخفاظ
Serial.print(oncode); // نظهر على الشاشة قيمة الذبذبة في حالة ارتفاع
Serial.print(" - ");
Serial.print(ApplePlaySignal[i*2+0]);// قيمة الذبذبة المرتفعة المحفوظة
// المقارنة
if( abs(oncode - ApplePlaySignal[i*2+0])(oncode * FUZZINESS /100))
{
Serial.print(" (ok)");
}
else
{
Serial.print(" (x)");
}
Serial.print(" \t");// tab
Serial.print(offcode); // نظهر على الشاشة قيمة الذبذبة في حالة انخفاظ
Serial.print(" - ");
Serial.print(ApplePlaySignal[i*2+1]);// قيمة الذبذبة المنخفظة المحفوظة
if( abs(offcode - ApplePlaySignal[i*2+1])(offcode * FUZZINESS /100))
{
Serial.print(" (ok)");
}
else
{
Serial.print(" (x)");
}
Serial.println();
}
}
|
عند تنفيذك لهذه الشيفرة، سيكون الخارج على الشاشة كالتالي:
|
برمجيا، ما تم انجازه لحد الآن كاف جدا. ولكن، إنشاء دوال خاصة يتم استدعاءها كلما دعت الحاجة لذلك في الدالة الرئيسية loop سيكون أفضل وأحسن تنسيقا. وبالتالي سنقوم بإنشاء دالة خاصة بالمقارنة نسميها IRcompare ونضع فيها ما زدناه سابقا في الدالة loop. الدالةIRcompare سترجع إحدى القيمتين فقط (صح=true أو خطأ=false).
boolean IRcompare(int numpulses, int Signal[])
{
for(int i=0; i< numpulses-1; i++)
{
int oncode = pulses[i][1]* RESOLUTION /10;
int offcode = pulses[i+1][0]* RESOLUTION /10;
/*
Serial.print(oncode);
Serial.print(" - ");
Serial.print(Signal[i*2 + 0]);
*/
if( abs(oncode - Signal[i*2+0])(Signal[i*2+0]* FUZZINESS /100)){
//Serial.print(" (ok)");
}else{
//Serial.print(" (x)");
// لم تكن أصغر من النسبة المختارة وبالتالي ارجع "خطأ"ه
return false;
}
/*
Serial.print(" \t"); // tab
Serial.print(offcode); // the OFF signal we heard
Serial.print(" - ");
Serial.print(Signal[i*2 + 1]); // the OFF signal we want
*/
if( abs(offcode - Signal[i*2+1])(Signal[i*2+1]* FUZZINESS /100)){
//Serial.print(" (ok)");
}else{
//Serial.print(" (x)");
// لم تكن أصغر من النسبة المختارة وبالتالي ارجع "خطأ"ه
return false;
}
//Serial.println();
}
// كل شيء مر على ما يرام، نرجع "صح" في هذه الحالة
return true;
}
|
إذا أزلت كل التعليقات فستصبح الدالة نقية وصافية كالتالي:
boolean IRcompare(int numpulses, int Signal[])
{
for(int i=0; i< numpulses-1; i++)
{
int oncode = pulses[i][1]* RESOLUTION /10;
int offcode = pulses[i+1][0]* RESOLUTION /10;
if( abs(oncode - Signal[i*2+0])>(Signal[i*2+0]* FUZZINESS /100))
return false;
if( abs(offcode - Signal[i*2+1]) >(Signal[i*2+1]* FUZZINESS /100))
return false;
}
return true;
}
|
يمكنك الآن استدعاء هذه الدالة كلما احتجت إليها في الدالة الرئيسية loop.
بالنسبة لنا، فكما ترى أن أداة التحكم آبل (Apple clicker remote) تحمل القليل من الأزرار. من بين هذه الأزرار تجد: PLAY وREWIND و FORWARD. تم القيام بحفظ القيم العددية لذبذبات هذه الأزرار الثلاثة في ملف تمت تسميتها بـ ircodes.h. المثال التالي هو نموذج لمعالجة ذبذبات الأشعة تحت الحمراء الصادرة من الأزرار الثلاثة:
void loop(void)
{
int numberpulses;
numberpulses = listenForIR();
Serial.print("Heard ");
Serial.print(numberpulses);
Serial.println("-pulse long IR signal");
if(IRcompare(numberpulses, ApplePlaySignal)){
Serial.println("PLAY");
}
if(IRcompare(numberpulses, AppleRewindSignal)){
Serial.println("REWIND");
}
if(IRcompare(numberpulses, AppleForwardSignal)){
Serial.println("FORWARD");
}
}
|
بعد امتحان هذا المثال تم الحصول على التالي:
|
وفي النهاية
يمكنك الآن تحميل الملف التالي واستعماله في شيء تريد صنعه، مثلا يمكنك أن تعدله لتتحكم بتحرك روبوتك عن بعد.
قم بتحميل الملف من هنا.