חיבור מצלמה לרספברי פיי

חיבור מצלמה לרספברי פיי, עבודה עם תמונות סטילס וניתוח שלהן עם בינה מלאכותית והכל בכמעט אפס קוד.

אחד הדברים המעניינים והכיפיים לעשות הוא לחבר מצלמה לרספברי פיי, זה לא יקר (ממש כמה דולרים) פשוט לחיבור, עובד יפה עם Node-RED או עם פקודה בטרמינל (שזה אומר שניתן לעבוד עם זה ב-Node.js או בפייתון או בווטאבר).

המצלמה

יש מצלמות "רשמיות" של רספברי פיי. אני בחרתי במצלמה זולה יחסית מעלי אקספרס מכמה סיבות – ראשית היא זולה (היקרות ביותר עולות שם 6 דולר) וקלה יחסית להתקנה. ישנן כמה סוגי מצלמות ואני בחרתי את הפופולרית שהיא מודול ov5647. פשוט מחפשים בעלי אקספרס או כל מקום אחר את המילה ov5647. תראו שלל של מצלמות. יש מצלמות שעובדות גם עם ראיית לילה ומגיעות עם פנס אינפרא אחד או יותר. אני קניתי את המודל היוקרתי ביותר ונפרדתי משישה דולר טבין ותקילין. אחרי כשבועיים המצלמה הגיעה אלי.

אז איך מחברים את המצלמה? המצלמה מגיעה עם Ribbon, סרט חביב שנכנס בקלות רבה אל חריץ ברספברי פיי. יש שני חריצים, אנו צריכים את החריץ שנמצא באמצע הרספברי פיי (3 או 4). לא זה שבקצה. כך זה נראה כשהמצלמה מחוברת.

מצלמת ov5647 מחוברת

את הריבון מחברים כך – מרימים עם מברג קטנטן את הסוגר הפלסטי השחור שיש בחריץ ואז משחילים את הסרט כשהפס הכחול לא בכיוון של המגעים ולכיוון השקע עם האוזניות.

חיבור הסרט לחריץ ההרחבה
כך זה נראה מהצד השני

בדרך כלל יהיה איזה חיווי של נורה או חיווי אחר שהמצלמה מקבלת מתח. אצלי זה היה פשוט אורות קטנים בפנסי האינפרא אדום.

עכשיו יש לבצע התקנה. בדרך כלל, אם לא קניתם ov5647, כדאי למצוא את סוג המצלמה ולחפש הוראות. במקרה של ov5647 יש לבצע את הפעולות הבאות:

כניסה למסך הקונפיגורציה באמצעות של sudo raspi-config וכניסה אל Interface Options ואז בחירה ב-Camera והפעלתה.

עדכון הקרנל באמצעות sudo rpi-update ואז ריסטארט.

התקנת חבילת התוכנה באמצעות sudo apt install libcamera-apps

במידה ומדובר במצלמה לא סטנדרטית, כניסה אל הקונפיגורציה באמצעות sudo nano /boot/config.txt והכנסת ההגדרות הבאות:

camera_auto_detect=0
dtoverlay=ov5647

ריסטארט.

בדיקה שהכל עובד באמצעות הקלדה של הפקודה libcamera-hello –list-cameras

אם יש Errors לא לדאוג, כל עוד לא מופיעה השורה

ERROR: *** no cameras available ***

אמור להופיע משהו כזה:

כדי לבצע תמונה נקליד:

libcamera-jpeg -o test1.jpg -t 2000 --width 640 --height 480

האם נוצרה תמונה כזו? אם יש חיווי שכן, נצפה בה. אפשר להוריד אותה למחשב שלנו עם SCP או להתקין VNC ולהתחבר מרחוק.

אם יש תקלה כלשהי – זה הזמן לחפש ברשת אחר בעיות ושגיאות. אבל אם אין בעיות, ולא צריכות להיות בעיות – עכשיו החגיגה מתחילה!

ראשית עכשיו אפשר לצלם או להסריט. אני אתמקד במאמר הזה בהסבר על צילום סטטי, באמצעות הפקודה

libcamera-jpeg -o test1.jpg -t 2000 --width 640 --height 480

ניתן לבצע צילום. אפשר להפעיל אותה מכל סקריפט אבל גם מ-Node-RED!

על מנת לעבוד עם סטילס למתחילים עם Node-RED אנו נעבוד עם Image-output שניתן להוריד אותו מפה: https://flows.nodered.org/node/node-red-contrib-image-output או התקנה ישירות מהפלטה.

המודול הזה מאפשר לנו לראות את התמונות שאנו מצלמים.

אפשר לבנות את ה-flow ידנית באופן הבא ראשית לשים node מסוג Inject שיתחיל את הפרוסס. אליו מחובר node של exec שפשוט מריץ את הפקודה:

libcamera-jpeg -o /home/pi/picturecam.jpg --width 320 --height 320

אחרי שהפקודה הזו רצה, אנו ניצור node של read file שיקרא את הקובץ וישגר אותו הלאה. אני רוצה שהוא ישגר אותו הלאה כ-stream.

אני רוצה את הפלט כ-stream כי זה מה ש-node-red-contrib-image-output צריך. כל מה שנשאר לי לעשות זה לחבר אותו ולראות את התמונה מייד אחרי שאני עושה inject (לא ממש מייד כי זה לוקח כמה שניות).

אפשר כמובן לייבא את ה-flow ישירות אליכם עם הקוד הזה:

[
    {
        "id": "ca01d49f43f828dd",
        "type": "tab",
        "label": "Simple photo flow",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "8561783e093986a2",
        "type": "inject",
        "z": "ca01d49f43f828dd",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "1",
        "payloadType": "str",
        "x": 190,
        "y": 160,
        "wires": [
            [
                "cc0182c5ef3f5ab6"
            ]
        ]
    },
    {
        "id": "cc0182c5ef3f5ab6",
        "type": "exec",
        "z": "ca01d49f43f828dd",
        "command": "libcamera-jpeg -o /home/pi/picturecam.jpg --width 320 --height 320 >> /dev/null && echo \"/home/pi/picturecam.jpg\"",
        "addpay": "",
        "append": "",
        "useSpawn": "false",
        "timer": "",
        "winHide": false,
        "oldrc": false,
        "name": "Take a picture",
        "x": 420,
        "y": 160,
        "wires": [
            [],
            [],
            [
                "b8dbe1d8af4befd1"
            ]
        ]
    },
    {
        "id": "9cedad573843e5dd",
        "type": "image",
        "z": "ca01d49f43f828dd",
        "name": "",
        "width": 160,
        "data": "payload",
        "dataType": "msg",
        "thumbnail": false,
        "active": true,
        "pass": false,
        "outputs": 0,
        "x": 980,
        "y": 160,
        "wires": []
    },
    {
        "id": "b8dbe1d8af4befd1",
        "type": "file in",
        "z": "ca01d49f43f828dd",
        "name": "",
        "filename": "/home/pi/picturecam.jpg",
        "format": "",
        "chunk": false,
        "sendError": false,
        "encoding": "none",
        "allProps": false,
        "x": 690,
        "y": 160,
        "wires": [
            [
                "9cedad573843e5dd"
            ]
        ]
    }
]

עכשיו אפשר להפוך את זה ליותר מעניין עם Tenserflow, שזה זיהוי אובייקטים בתמונה. TensorFlow, למי שמכיר זו פלטפורמת למידת מכונה פתוחה שמאוד קל להשתמש בה. הפוסט הזה הוא לא על TensorFlow אבל היתרון העצום שלה בעניינינו היא שיש לה אינטגרציה ממש טובה עם ג'אווהסקריפט ועם Node-RED. במקרה הזה עם node-red-contrib-tensorflow. המודול הזה מקבל תמונה ומחזיר את האובייקטים שהוא מזהה שם.

אחרי שאנו מורידים ומתקינים את node-red-contrib-tensorflow אנו יכולים לשלוח אליו תמונה, כשהשליחה היא פשוט המיקום של התמונה ולקבל ניתוח. אחרי ההתקנה אנו נראה שיש לנו 4 אפשרויות חדשות – אנו רוצים את cocossd (על השאר אפשר לקרוא בדוקומנטציה).

ארבעה nodes חדשים

cocossd פשוט מקבל תמונה כמחרוזת טקסט ומחזיר אובייקט שבו יש את המידע על מה שהוא איתר. כאן למשל הוא צילם אותי מחזיק בקבוק וזיהה גם אותי (person) וגם את הבקבוק. הנה ה-flow. אנחנו מצלמים את התמונות, שולח את מחרוזת הטקסט של התמונה ואז ל-cocossd, משם ל-preview שנראה מה צילמנו ול-debug node שיראה לנו מה יצא.

אם אתם לא רוצים לבנות את ה-flow הזה מאפס? אז הנה הקוד:

[
    {
        "id": "30dfb6ba5e6b6aab",
        "type": "tab",
        "label": "cocossdn flow",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "26debfd0701b92fb",
        "type": "image",
        "z": "30dfb6ba5e6b6aab",
        "name": "",
        "width": 160,
        "data": "annotatedInput",
        "dataType": "msg",
        "thumbnail": false,
        "active": true,
        "pass": false,
        "outputs": 0,
        "x": 1160,
        "y": 520,
        "wires": []
    },
    {
        "id": "bf47260552d1374e",
        "type": "cocossd",
        "z": "30dfb6ba5e6b6aab",
        "name": "",
        "x": 960,
        "y": 520,
        "wires": [
            [
                "26debfd0701b92fb",
                "e816de779cee2778"
            ]
        ]
    },
    {
        "id": "eaaf835901c8194f",
        "type": "change",
        "z": "30dfb6ba5e6b6aab",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "/home/pi/picturecam.jpg",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 780,
        "y": 520,
        "wires": [
            [
                "bf47260552d1374e"
            ]
        ]
    },
    {
        "id": "c1e2bf49d33e516a",
        "type": "exec",
        "z": "30dfb6ba5e6b6aab",
        "command": "libcamera-jpeg -o /home/pi/picturecam.jpg --width 320 --height 320 >> /dev/null && echo \"/home/pi/picturecam.jpg\"",
        "addpay": "",
        "append": "",
        "useSpawn": "false",
        "timer": "",
        "winHide": false,
        "oldrc": false,
        "name": "Take a picture",
        "x": 520,
        "y": 520,
        "wires": [
            [
                "eaaf835901c8194f"
            ],
            [],
            []
        ]
    },
    {
        "id": "a19ab65c3b7acde9",
        "type": "inject",
        "z": "30dfb6ba5e6b6aab",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "1",
        "payloadType": "str",
        "x": 350,
        "y": 520,
        "wires": [
            [
                "c1e2bf49d33e516a"
            ]
        ]
    },
    {
        "id": "e816de779cee2778",
        "type": "debug",
        "z": "30dfb6ba5e6b6aab",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1130,
        "y": 400,
        "wires": []
    }
]

אם הצלחתם להתגבר על זה, אז אפשר לעבור ל-flow מורכבים הרבה יותר. הנה דוגמה לאחד כזה שמורכב יחסית מבחינת מתי המצלמה מצלמת. במקרה הזה כל שישים שניות אם חיישן הקרבה מזהה משהו וכן יש נורת לד שמאירה כל פעם בצבע אחר בהתאם לסטטוס (מצלם תמונה, שולח תמונה לניתוח, יש תוצאות).

כאמור, יש שעשועים רבים שאפשר לעשות. המטרה של המאמר הזה היא להראות כמה קל לחבר ולעבוד עם מצלמה וכמעט אפס קוד עם Node-RED.

פוסטים נוספים שכדאי לקרוא

פתרונות ומאמרים על פיתוח אינטרנט

נגישות טכנית – פודקאסט ומבוא

פרק בפודקאסטעל נגישות בעברית שצולל לכלים האוטומטיים ולפן המאד מאד טכני של הנגישות.

צילום מסך של סוואגר
יסודות בתכנות

openAPI

שימוש בתשתית הפופולרית למיפוי ותיעוד של API וגם הסבר בסיסי על מה זה API

גלילה לראש העמוד