ستتعلم اليوم كيف تدعم روبوتك بمستشعر الأشعة الحمراء. كما سيتبين لك كيف يمكنك التحكم بروبوتك عن بعد باستعمال أي جهاز عن تحكم عن بعد يعمل باﻷشعة تحت الحمراء. سيخولك هذا بالتواصل بالروبوت سواء كان مدرعة أو طائرة، حيوانا روبوتيا أو إنسان آليا، أو أي جهاز آخر في مجال محدد.
|
النحل، وما أدراك ما النحل، مخلوق غاية في العجب. وله نظام رؤية عجيب غريب. وعسى أن يسعنا الوقت لنحوم حول ما تم اكتشافه عن النظام المعقد والحياة الغريبة للنحل. كيف لا، وخالقه ميزه كما ميز النمل بسورة له في كتابه إلـــــى يوم يبعثون.
فرؤية اﻷزهار عند النحل مثلا ليست كالتي نرى بها نحن معاشر البشر.ولا أظن أن لنا القدرة على الحكم بجودة رحيق الإزهار انطلاقا من النظر إليها ﻷول اﻷمر. لكن النحل، فهو قادر على تحديد الزهرة ذات الرحيق الأجود من التي دونها، وهذا يتم بسبب امتلاكها نظام رؤية يعتمد على اﻷشعة تحت الحمراء حسب ما تم التوصل إليه علميا. فالنحل لا يرى اللون الأحمر، لكن يرى ما تحت الأحمر.
| |
رؤية الانسان
|
محاكاة لرؤية النحل
|
تكره البعوض؟ نعم، وأصارحك القول بأني أكرهه أنا أيضا. يبدوا أن دمي شهي لإناث البعوض.
لهذا المخلوق من القدرات العجيبة التي ما زال العلم يكتشف الواحدة منها تلوى اﻷخرى.
أنا إن سألتك عن المنطقة الأكثر حرارة في جسمك فقد لا تعلم ذلك. لكن البعوض له من القدرة ما تمكنه من اختيار المنطقة اﻷوفر دما في جسمك ولو كان ذلك في جنح الظلام.
يعتمد البعوض على نظام رؤية مختلف عنا أيضا، فهو يعتمد على الأشعة تحت الحمراء حيث تساعده على رؤية الأجسام عن طريق رسم خرائط حرارية لها في دماغه.
|
الكثير من الحشرات يعتمد على الرؤية بالأشعة تحت الحمراء، أما في عالم الزواحف فنجد الثعابين كالثعبان ذات الحشرجة. هذا الأخير هو أعمى في الحقيقة، لكن نظام الرؤية أو قل الاستشعار بواسطة الأشعة تحت الحمراء يخوله من ضرب فريسته في المناطق القاتلة في جسمها.
الأسهم الحمراء المبينة في الصورة جانبه تبين حفر مستشعرات الأشعة تحت الحمراء لكل من الثعبانpython في الأعلى والثعبان ذي الحشرجة في الأسفل.
أما السهم الأسود فهو يبين فقط الأنف.
|
لمستشعر الأشعة الحمراء ثلاث أقطاب (أو مرابط)، يمكنك أن تصلهم كالتالي:
• المربط 1 هو المخرج (output) وبالتالي يمكنك إيصاله بصمام ثنائي ضوئي
• المربط 2 هو اﻷرضية (ground)
• المربط 3 يوصل بجهد كهربائي ذي 5 فولط
|
يحتاج مستشعر اﻷشعة الحمراء لـ 5 فولط حتى يعمل على أحسن ما يرام. في مثالنا هذا، استعملنا أربع بطاريات ذي 1.5 فولط، وبالتالي سيكون مجموع الجهد الكهربائي هو 6 فولط.
يمكنك الحصول على 5 فولط من بطاقة Arduino أيضا إذا استعملتها.
قم بإيصال المربط اﻷطول للصمام الضوئي بالقطب 6 فولط والمربط اﻷقصر بالمربط 1 للمستشعر لكن اجعل بينهما مقاومة كهربائية (مقاومتها من 200 إلى 1000 أوم).
عندما يحدد المستشعر أي إشارة تدل على اﻷشعة الحمراء، سيجعل المخرج في حالة منخفظة (أي بشحنة كهربائية ضعيفة في المربط 1)، وبالتالي يشتعل الصمام الضوئي.
حاول أن تحصل على جهاز للتحكم عن بعد للتلفاز أو لجهاز DVD أو للحاسوب أو لأي جهاز مشابه.يمكنك أن تسرقها من والديك مثلا هههه.
ستلاحظ أن الصمام الضوئي يشتعل كلما ضغطت على أزرار جهاز التحكم عن بعد.
|
يمكنك أن تركب مستشعر الأشعة تحت الحمراء في روبوتك أو أي جهاز تفكر فيه عن طريق استعمال بطاقة 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};");
}
|
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};
|
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++;
}
}
|
void loop(void)
{
int numberpulses;
numberpulses = listenForIR(); //ننصت لذبذبات الأشعة تحت الحمراء
Serial.print("Heard "); // عدد الذبذبات التي تم التنصت لها
Serial.print(numberpulses);
Serial.println("-pulse long IR signal");
}
|
إذا قمت بتنفيذ الشيفرة من جديد، وضغطت على زر أداة التحكم عدد من المرات سيكون الخارج كالتالي:
|
#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();
}
}
|
عند تنفيذك لهذه الشيفرة، سيكون الخارج على الشاشة كالتالي:
|
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;
}
|
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");
}
}
|
بعد امتحان هذا المثال تم الحصول على التالي:
|