4.18. المستشعرات المتعددة

تقرن حفنة من كاميرات OpenMV Cam مستشعرَي صورة على اللوحة نفسها -- وأكثرها شيوعاً كاميرا ملونة إلى جانب مستشعر حراري FLIR® Lepton®، لكن الشكل نفسه ينطبق على لوحات الألوان المقترنة بالأحداث وأي عتاد ثنائي المستشعر مستقبلي. لكل مستشعر مصفوفة بكسل خاصة به، وناقل تحكم خاص به، ويشغّل خط معالجته الخاص بمعدل إطاراته الخاص. تتوسع واجهة CSI لتغطيتها بأن تتيح للتطبيق إنشاء كائن CSI واحد لكل مستشعر فيزيائي.

4.18.1. تحديد المستشعر

يأخذ مُنشئ CSI وسيطاً cid يسمّي مستشعراً محدداً على اللوحة. تختار cid=-1 (القيمة الافتراضية) المستشعر الأساسي؛ بينما تختار ثوابت cid المسماة مستشعراً ثانوياً حسب معرّف الشريحة:

import csi

csi_rgb     = csi.CSI()                    # primary colour sensor
csi_thermal = csi.CSI(cid=csi.LEPTON)      # FLIR® Lepton®

تمتلك كل نسخة تكوينها الخاص -- تنسيق البكسل وحجم الإطار وأزرار التعريض/الكسب وتجمع مخزن الإطارات -- وتُعاد ضبطها وتُكوَّن وتُقرأ بشكل مستقل عن الأخرى. ثوابت المستشعرات الثانوية المدعومة (LEPTON وGENX320 وغيرها مما هو مدرج في مرجع CSI) تسمّي الشريحة التي يتوقعها التطبيق على المنفذ الثانوي؛ ويُفشل المشغّل عملية الإنشاء إذا لم تتطابق الشريحة الفعلية.

4.18.2. الالتقاط من كلا المستشعرين

يشغّل كل مستشعر خط التقاطه بشكل مستقل عن الآخر -- فقد يسلّم المستشعر الملون ثلاثين إطاراً في الثانية بينما يسلّم Lepton® تسعة. الطريقة المباشرة للتعامل مع هذا التفاوت هي أن ندع المستشعر الأسرع يقود الحلقة وأن نقرأ المستشعر الأبطأ بشكل غير حاجب، فنأخذ ما هو جاهز ونتخطى التكرار عندما لا يكون هناك شيء:

import csi

csi_rgb     = csi.CSI()
csi_thermal = csi.CSI(cid=csi.LEPTON)

csi_rgb.reset()                        # powers the rail, pulses RESET
csi_rgb.pixformat(csi.RGB565)
csi_rgb.framesize(csi.QVGA)

csi_thermal.reset(hard=False)          # I2C reconfigure only
csi_thermal.pixformat(csi.GRAYSCALE)
csi_thermal.framesize(csi.QQVGA)

while True:
    rgb_img     = csi_rgb.snapshot()                  # blocks for next colour frame
    thermal_img = csi_thermal.snapshot(blocking=False)  # returns None if not ready
    if thermal_img is not None:
        # process aligned colour + thermal pair
        pass
    else:
        # process colour only on this iteration
        pass

تنظّم snapshot() الحاجبة وتيرة الحلقة؛ بينما تعيد النسخة غير الحاجبة أحدث إطار حراري عندما يكون قد وصل إطار جديد منذ الاستدعاء السابق، وتعيد None بخلاف ذلك. يستمر التطبيق في العمل بمعدل إطارات المستشعر الملون ويحصل على إطار حراري كلما أنتج Lepton® واحداً.

النمط المعاكس -- لقطتان حاجبتان متتاليتان -- يعمل أيضاً، لكن الحلقة عندئذ تعمل بمعدل المستشعر الأبطأ من الاثنين، مع توقف خط معالجة المستشعر الأسرع بين التكرارات. اختر أي معدل تريد معالجة التطبيق اللاحقة قيادته فعلياً.

4.18.3. إعادة الضبط على خطوط الطاقة المشتركة

تشغّل بعض اللوحات ثنائية المستشعر كلتا الشريحتين من خط طاقة واحد أو تتشارك خط إعادة ضبط. على تلك اللوحات، يرفع أول استدعاء لـ reset() خط الطاقة ويرسل نبضة على الإشارة المشتركة؛ أما عمليات إعادة الضبط اللاحقة على نسخ CSI الأخرى فينبغي أن تمرر hard=False حتى تعيد برمجة شريحتها الخاصة فقط دون جرّ الشريحة المجاورة عبر إعادة ضبط:

csi_rgb.reset()                        # primary -- powers the rail, pulses RESET
csi_thermal.reset(hard=False)          # secondary -- I2C reconfigure only

إن تمرير hard=True على مستشعر ثانوي في هذا الشكل سيعيد ضبط المستشعر الأساسي كأثر جانبي، فيلغي أي إعداد كان التطبيق قد دفعه بالفعل. تشير صفحة المرجع لكل لوحة ثنائية المستشعر إلى ما إذا كانت خطوط الطاقة مشتركة.

4.18.4. تحديد مصدر البث

تملك الكاميرات ذات المستشعرين نسختين من CSI لكن مع ذلك مخزن إطارات بث واحد فقط بينهما. يختار وسيط في المُنشئ أي مستشعر تغذّي إطاراته المعاينة:

csi_rgb     = csi.CSI()                    # primary
csi_thermal = csi.CSI(cid=csi.LEPTON,
                      stream=True)         # preview source

تجعل stream=True النسخة المسماة هي المصدر. وبدون وسيط stream= يكون المستشعر الأساسي (cid=-1، القيمة الافتراضية) هو المصدر؛ أما النسخ المبنية بـ cid= لمستشعر ثانوي فتبقى صامتة في المعاينة ما لم تُمرَّر stream=True صراحةً. ولا تزال استدعاءات snapshot() على المستشعر غير المحدد تلتقط الإطارات إلى مخازن إطارات ذلك المستشعر بشكل طبيعي -- إنها فقط لا تحدّث المعاينة.