Raspberry Pi

Picamera2 ile Raspberry Pi Kamera Hareket Algılama ve MQTT Alarmları

Bir Raspberry Pi kamerası ve Picamera2 ile hareket dedektörü kurun, bir şey hareket ettiğinde anlık görüntü kaydedin ve panoların, telefonların ve otomasyonların tepki verebilmesi için görüntü bağlantılı hareket olaylarını MQTT üzerinden yayınlayın.

Bir kapı girişini izleyen ve anlık görüntü bağlantılı hareket alarmları gönderen Raspberry Pi kamera modülü; Raspberry Pi kamera hareket algılama MQTT kurulumu
Bunu kendi MQTT broker’ınızda çalıştırınFree planla özel, yönetilen bir MQTT broker alın ve hazır kod örnekleriyle başlayın.
Ücretsiz deneyin Kod örnekleri Kart gerekmez

Bir Raspberry Pi kamera hareket algılama MQTT dedektörü, kamera modülünü bir olay kaynağına dönüştürür: Pi gün boyu video akışı göndermek yerine düşük çözünürlüklü bir görüntüyü izler, bir şey değiştiğinde anlık görüntü kaydeder ve herhangi bir şeyin abone olabileceği küçük bir JSON olayı yayınlar. Bu eğitimde bu dedektörü Picamera2 ve Python ile kuracak, yanlış alarmları önlemek için ayarlayacak ve olayları bir MQTT broker üzerinden ileteceksiniz.

Dedektör Nasıl Çalışır?

Picamera2, kameradan aynı anda iki akış çalıştırabilir: tam çözünürlüklü bir main akışı ve küçük bir lores akışı. Dedektör ardışık gri tonlamalı lores karelerini karşılaştırır; bu, bir Pi Zero 2 W'de bile çok az işlemci gücü harcar ve main akışına yalnızca hareket bulunduğunda bir JPEG kaydetmek için dokunur.

Akış şöyledir:

  1. 320x240 lores karesinin parlaklık (luminance) düzlemini alın.
  2. Önceki kareden ortalama kare farkını hesaplayın.
  3. Fark bir eşiği aşarsa bir hareket olayı başlatın, tam boyutlu bir anlık görüntü kaydedin ve bağlantısını içeren bir MQTT mesajı yayınlayın.
  4. Bir bekleme (cooldown) süresi boyunca sonraki tetiklemeleri yok sayın.
  5. Sessiz bir süreden sonra "hareket bitti" olayı yayınlayın ve retained durumu yeniden idle olarak ayarlayın.

Görüntüleri MQTT üzerinden göndermek mümkündür, ancak bir broker kötü bir dosya sunucusudur: büyük payload'lar yavaştır ve genel test broker'ı mesajları 64 KB ile sınırlar. Sağlam model, bağlantı içeren bir olay yayınlamak ve görüntünün sizin kontrol ettiğiniz bir web sunucusunda veya nesne depolamada durmasına izin vermektir.

Donanım ve Yazılım

Öğe Notlar
Raspberry Pi 4, 5 veya Zero 2 W Raspberry Pi OS Bookworm
Camera Module 2, 3 veya HQ kamera Pi 5 ve Zero, uyumlu dar şerit kabloya ihtiyaç duyar
Picamera2 Raspberry Pi OS'ta önceden kurulu gelir
paho-mqtt 2.x Bir sanal ortama kurulur

Picamera2 ve NumPy işletim sistemi paketlerinden gelir; bu yüzden sanal ortamı bunlara erişebilecek şekilde oluşturun:

sudo apt update
sudo apt install -y python3-picamera2 python3-numpy python3-venv
rpicam-hello --list-cameras
mkdir -p ~/motion-mqtt/snapshots && cd ~/motion-mqtt
python3 -m venv --system-site-packages .venv
source .venv/bin/activate
pip install "paho-mqtt>=2.0"

rpicam-hello --list-cameras hiçbir kamera göstermiyorsa gücü kapatın, şerit kabloyu kontaklar doğru yöne bakacak şekilde yeniden takın ve tekrar deneyin. Picamera2 kılavuzu, aşağıda kullanılan akış yapılandırmasını belgeler.

Olay Topic'leri

Topic Payload Retained
connectmq/demo/e71b04/frontdoor/motion JSON olayı: başlangıç veya bitiş, skor, anlık görüntü URL'si Hayır
connectmq/demo/e71b04/frontdoor/motion/state active veya idle Evet
connectmq/demo/e71b04/frontdoor/status online veya offline (last will) Evet

Tipik bir başlangıç olayı şöyle görünür:

{
  "event": "motion_start",
  "score": 18.4,
  "snapshot": "http://192.168.1.50:8000/20261016-142233.jpg",
  "ts": 1792160553
}

Raspberry Pi Kamera Hareket Algılama MQTT Betiği

motion_mqtt.py olarak kaydedin:

#!/usr/bin/env python3
"""Detect motion with Picamera2 and publish MQTT events with snapshot links."""
import json
import os
import socket
import time
import uuid
from datetime import datetime
from pathlib import Path

import numpy as np
import paho.mqtt.client as mqtt
from picamera2 import Picamera2

BROKER = os.getenv("MQTT_HOST", "broker.connectmq.com")
PORT = int(os.getenv("MQTT_PORT", "1883"))
USERNAME = os.getenv("MQTT_USERNAME")
PASSWORD = os.getenv("MQTT_PASSWORD")
CLIENT_ID = os.getenv("MQTT_CLIENT_ID", f"cam-{uuid.uuid4().hex[:8]}")
PREFIX = os.getenv("MQTT_PREFIX", "connectmq/demo/e71b04/frontdoor")

THRESHOLD = float(os.getenv("MOTION_THRESHOLD", "12"))
COOLDOWN = 10       # seconds between start events
QUIET_TIME = 15     # seconds without motion before "motion_end"
SNAP_DIR = Path(os.getenv("SNAP_DIR", str(Path.home() / "motion-mqtt" / "snapshots")))
HTTP_PORT = int(os.getenv("SNAP_HTTP_PORT", "8000"))
LORES = (320, 240)


def local_ip():
    with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
        s.connect(("8.8.8.8", 80))
        return s.getsockname()[0]


BASE_URL = os.getenv("SNAP_BASE_URL", f"http://{local_ip()}:{HTTP_PORT}")


def on_connect(client, userdata, flags, reason_code, properties):
    if reason_code.is_failure:
        print(f"Connection refused: {reason_code}")
        return
    print(f"Connected as {CLIENT_ID}")
    client.publish(f"{PREFIX}/status", "online", qos=1, retain=True)
    client.publish(f"{PREFIX}/motion/state", "idle", qos=1, retain=True)


client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=CLIENT_ID)
client.on_connect = on_connect
client.will_set(f"{PREFIX}/status", "offline", qos=1, retain=True)
client.reconnect_delay_set(min_delay=1, max_delay=60)
if USERNAME:
    client.username_pw_set(USERNAME, PASSWORD)
if PORT == 8883:
    client.tls_set()
client.connect(BROKER, PORT, keepalive=30)
client.loop_start()

picam2 = Picamera2()
config = picam2.create_video_configuration(
    main={"size": (1280, 720), "format": "RGB888"},
    lores={"size": LORES, "format": "YUV420"},
)
picam2.configure(config)
picam2.start()
time.sleep(2)  # let exposure and white balance settle

SNAP_DIR.mkdir(parents=True, exist_ok=True)
w, h = LORES
previous = None
active = False
last_start = 0.0
last_motion = 0.0


def publish_event(event, score, snapshot=None):
    payload = {"event": event, "score": round(score, 1), "ts": int(time.time())}
    if snapshot:
        payload["snapshot"] = snapshot
    client.publish(f"{PREFIX}/motion", json.dumps(payload), qos=1)
    print(payload)


try:
    while True:
        frame = picam2.capture_buffer("lores")
        current = frame[: w * h].reshape(h, w).astype(np.int16)
        if previous is not None:
            score = float(np.square(current - previous).mean())
            now = time.time()
            if score > THRESHOLD:
                last_motion = now
                if not active and now - last_start > COOLDOWN:
                    name = datetime.now().strftime("%Y%m%d-%H%M%S") + ".jpg"
                    picam2.capture_file(str(SNAP_DIR / name))
                    publish_event("motion_start", score, f"{BASE_URL}/{name}")
                    client.publish(f"{PREFIX}/motion/state", "active", qos=1, retain=True)
                    active = True
                    last_start = now
            elif active and now - last_motion > QUIET_TIME:
                publish_event("motion_end", score)
                client.publish(f"{PREFIX}/motion/state", "idle", qos=1, retain=True)
                active = False
        previous = current
        time.sleep(0.1)
except KeyboardInterrupt:
    pass
finally:
    picam2.stop()
    client.publish(f"{PREFIX}/status", "offline", qos=1, retain=True).wait_for_publish(2)
    client.loop_stop()
    client.disconnect()

astype(np.int16) dönüşümü önemlidir: iki işaretsiz 8 bitlik diziyi çıkarmak taşma (wrap around) yapar ve devasa yanlış farklar üretir. YUV420 tamponunun yalnızca ilk w * h baytını almak size gri tonlamalı parlaklık düzlemini verir; hareket algılamanın ihtiyaç duyduğu da yalnızca budur.

Anlık Görüntüleri Sunmak

Hızlı bir yerel kurulum için anlık görüntü klasörünü ikinci bir terminalde Python'un yerleşik web sunucusuyla sunun:

python3 -m http.server 8000 --directory ~/motion-mqtt/snapshots

Artık her olaydaki bağlantılar ağınızdaki herhangi bir cihazda açılır. Bu sunucuda kimlik doğrulama yoktur; bu yüzden yalnızca yerel ağınızda tutun. Dışarıdan erişim için anlık görüntüleri kendi betiğinizle kontrol ettiğiniz bir depolamaya yükleyin ve SNAP_BASE_URL değerini o konuma ayarlayın; MQTT olay formatı değişmez. Ayrıca eski görüntüleri silmek için bir cron işi veya systemd zamanlayıcısı ekleyin; aksi halde SD kart dolar.

Test ve Ayar

Olayları herhangi bir makineden izleyin:

mosquitto_sub -h broker.connectmq.com -p 1883 -i viewer-e71b04 \
  -t 'connectmq/demo/e71b04/frontdoor/#' -v

Lensin önünde elinizi sallayın; anlık görüntü bağlantısıyla birlikte motion_start ve 15 saniyelik sessizlikten sonra motion_end görmelisiniz. Ayar çoğunlukla eşikle ilgilidir:

Belirti Ayar
Bulutlardan, titreşen ışıktan veya otomatik pozlama değişikliklerinden gelen olaylar MOTION_THRESHOLD değerini artırın
Önünden geçen insanlar kaçırılıyor Eşiği veya bekleme aralığını düşürün
Sürekli başlangıç ve bitiş olayları QUIET_TIME değerini artırın
Gece alarmları gürültülü Bir IR ışık ekleyin veya alarmları takvime göre devre dışı bırakın
Kadrajdaki ağaçlar veya yol tetiklemeye neden oluyor Karşılaştırmadan önce lores dizisini bir ilgi bölgesine kırpın

Sahnenizin gürültü tabanını görmek için hiçbir şey hareket etmezken birkaç dakika skoru yazdırın, ardından eşiği bunun rahatça üzerine ayarlayın.

Kamera ile Algılama ve PIR Sensörü

Pasif kızılötesi (PIR) sensörü hareketi algılamanın diğer yaygın yoludur ve her birinin ne zaman uygun olduğunu bilmeye değer. Bir PIR sensörü vücut ısısına tepki verir, neredeyse hiç güç tüketmez ve ışıktaki değişiklikleri yok sayar; bu yüzden gölgelerden ve bulutlardan daha az yanlış alarm üretir. Ancak neyin hareket ettiğini size söyleyemez ve ekleyebileceği bir görüntü yoktur. Kamerayla kare farkı yöntemi, rüzgarda sallanan bir perde dahil her görsel değişikliğe tepki verir; ancak size bir anlık görüntü verir ve algılamayı görüntünün belirli bir bölgesiyle sınırlamanıza izin verir.

İkisi iyi bir araya gelir. Bir PIR sensörünü boş bir GPIO pinine bağlayın ve bir kamera tetiklemesini yalnızca PIR hattı da aynı bir iki saniye içinde yüksekse kabul edin. Böylece kamera PIR'in algıladığını doğrular ve belgeler; MQTT olayı da abonelerin ona ne kadar güveneceğine karar verebilmesi için her iki sinyali içerebilir. Yayınlanan olayın basit kalması için bu doğrulama mantığını Pi'de tutun: bir motion_start, bir motion_end, bir bağlantı.

Hareket Olaylarına Tepki Vermek

Dedektör yalnızca olay yayınladığı için tepki başka bir yerde yaşar ve kamera koduna dokunmadan değiştirilebilir. Bir Node-RED akışı anlık bildirim gönderebilir; küçük bir Python abonesi, Python ile MQTT e-posta alarmları yazısında gösterildiği gibi anlık görüntüyü e-postayla gönderebilir; ikinci bir Pi de motion/state topic'ine abone olarak Raspberry Pi röle MQTT denetleyicisiyle bir projektörü yakabilir. Home Assistant da retained durumu bir ikili sensör olarak kullanabilir. İkinci bir kapı için daha düşük maliyetli bir kamera düğümü istiyorsanız MQTT ile ESP32-CAM rehberi aynı olay fikrini bir mikrodenetleyici üzerinde uyguluyor.

Gizlilik ve Üretim Kurulumu

Hareket olayları insanların ne zaman evde olduğunu ortaya koyar; bu yüzden gerçek bir kurulumda asla genel bir broker üzerinden iletilmemelidir. broker.connectmq.com adresindeki genel MQTT test broker'ı herkes tarafından okunabilir ve yalnızca ilk test için tasarlanmıştır. Gerçek bir kamera için TLS ve otomatik oluşturulan kimlik bilgileriyle ConnectMQ üzerinde özel bir broker kullanın:

export MQTT_PORT=8883
export MQTT_USERNAME=YOUR_USERNAME
export MQTT_PASSWORD=YOUR_PASSWORD
export MQTT_CLIENT_ID=YOUR_CLIENT_ID
export MQTT_PREFIX=home/frontdoor
python motion_mqtt.py

Yönetilen bir MQTT broker ile telefonunuz, ev sunucunuz ve kamera aynı TLS uç noktasına dışarı doğru bağlanır; böylece port yönlendirmesine ihtiyaç duymazsınız. Dedektörü ve anlık görüntü sunucusunu bir elektrik kesintisinden sonra başlamaları için systemd servisleri olarak çalıştırın; Raspberry Pi MQTT eğitimi hazır bir servis şablonu içeriyor.

ConnectMQ ile Başlayın

Kameranıza alarmlarını gönderebileceği güvenli bir yer verin. Kredi kartı gerekmeden ConnectMQ'da ücretsiz bir Serverless broker oluşturun ve 8883 portunda TLS ile bağlanın. Portalın canlı topic gezgini her hareket olayını ve retained durumu anında gösterir; bu da eşikleri ayarlamayı çok kolaylaştırır.

Sık Sorulan Sorular

Anlık görüntünün kendisini MQTT üzerinden gönderebilir miyim?

JPEG baytlarını bir payload olarak yayınlayabilirsiniz, ancak bu verimsizdir ve mesaj boyutu sınırlarına hızla takılır. Görüntünün bağlantısını yayınlamak ve dosyayı başka bir yerde saklamak çok daha iyi ölçeklenir.

Picamera2 hareket algılaması ne kadar işlemci kullanır?

Karşılaştırma 320x240 gri tonlamalı bir akış üzerinde çalıştığı için çok az. Bir Raspberry Pi Zero 2 W bile bunu MQTT istemcisiyle birlikte çalıştırabilir.

Hiçbir şey hareket etmezken neden hareket olayları alıyorum?

Otomatik pozlama değişiklikleri, titreşen ışıklar ve hareket eden gölgeler piksel değerlerini değiştirir. Eşiği yükseltin, bir ilgi bölgesine kırpın veya Picamera2 kontrolleriyle pozlamayı sabitleyin.

Bu, Raspberry Pi Camera Module 3 ile çalışır mı?

Evet. Picamera2; Camera Module 2, Camera Module 3 ve HQ kamerayı destekler; kod değişmez.

İlgili yazılar

RC522 Okuyucu ile Raspberry Pi RFID MQTT Erişim Kaydı

Bir RC522 RFID okuyucuyu Raspberry Pi'ye bağlayın, etiketleri Python ile okuyun, yerel bir izin listesiyle karşılaştırın ve her erişim denemesini kayıt araçlarının, panellerin ve uyarı araçlarının tüketebileceği bir MQTT olayı olarak yayınlayın.

6 dk okuma

Tüm yazılar →

Özel MQTT broker’ınız birkaç tık uzağınızda

Hesabınızı oluşturun, broker kimlik bilgilerinizi alın ve ilk cihazınızı bağlayın — kurulacak, yamalanacak ya da ölçeklendirilecek sunucu yok.