Bewegungserkennung durch Infrarot-Strahlung

Der Pragmatische Architekt  –  0 Kommentare

In dieser und der nächsten Folge geht es um Sensoren, die mittels Infrarot oder Ultraschall entfernte Objekte erkennen. Beide Arten von Sensoren lernen wir in kleinen Projekten kennen. Der erste Teil behandelt Bewegungssensoren.

Infrarot-Strahlung ist in Blockbustern ein häufig verwendetes Hilfsmittel, um bewegte Objekte selbst im Dunkeln zu erkennen. Mittels Bewegungserkennung durch Infrarotsensoren lassen sich vielfältige Anwendungen umsetzen. Sei es der selbstgebaute Einbruchsalarm, die Warnung vor dem eintreffenden Chef oder als Abschreckung, wenn sich die eigene Katze wieder in verbotenes Terrain begibt.

Zum Einbau in eigenen Projekte stehen die entsprechenden Sensoren als PIR-Sensoren (Passive Infra-Red) zur Verfügung. Ihr Preis bewegt sich für billigere Varianten bei etwa ein bis drei Euro. Wer die Sensoren für größere Areale einsetzen möchte, muss allerdings tiefer in die Tasche greifen.

Wie funktioniert ein PIR Sensor?

Der Sensor selbst besteht aus kristallinem Material, das bei Eintritt von Infrarotstrahlung eine elektrische Spannung erzeugt. Die Spannungen misst ein integrierter Verstärker. Um den Sensor herum befindet sich eine halbkugelförmige Fresnellinse, die eintretende Infrarotstrahlen bündelt und konzentriert zum Infrarotsensor weiterleitet. Dadurch erhöht sie die Reichweite des Sensors. Fresnellinsen sind im Gegensatz zu konventionellen aus Segmenten von Linsen zusammengesetzt. Sie benötigen weniger Material bei gleicher Wirkung. Beispielsweise können Leuchttürme durch Fresnellinsen ihre Sichtbarkeit auf eine weitere Distanz erhöhen.

Ein PIR-Sensor der Firma Parallax. Die "Kuppel" fungiert als Fresnellinse



Im Datenblatt des PIR-Sensors von Parallax findet sich als abgedeckter Umgebungsbereich eine Umgebungsradius von sechs Metern. Der Sensor kann dabei ein horizontales Umfeld von 110 Grad und einen vertikalen Bereich von 70 Grad abdecken. Die Versorgungsspannung darf zwischen 3 V und 5 V liegen, wobei 5 V einen idealen Wert darstellen.

Schaltung

Im Beispielprojekt soll bei Bewegungserkennung die integrierte LED an Ausgang 13 leuchten und gleichzeitig ein Ton auf dem Piezo-Buzzer bzw. Lautsprecher ausgegeben werden.

Für die Schaltung benötigen wir folgende Bauteile:

  • PIR-Sensor
  • Piezo-Buzzer (zur Tonausgabe)
  • kleines Breadboard
  • Verbindungsdrähte

Der PIR-Sensor hat einen Spannungsanschluss (links, rot), einen Erde-Anschluss (rechts, schwarz) und einen Datenanschluss (gelb, mittig). Wir verbinden 5V-Versorgungsspannung des Arduino mit dem positiven Terminalstrip des Breadboards und Arduino-GND mit dem negativem Terminalstrips des Breadboards.

Dann schließen wir den PIR-Sensor über die Terminalstrips des Breadboards an und verbinden den Datenausgang des Sensors mit Digital-Pin 2 des Arduino. Zudem verbinden wir ein Beinchen des Piezo-Buzzers mit dem negativen Terminalstrip und das andere mit Digital-Pin 9 des Arduino. Daraus ergibt sich folgende Schaltung:

Die Schaltung mit Lautsprecher (Piezo) und PIR-Sensor

Beispiels-Sketch

Im folgenden Programm gibt es einige Besonderheiten:

  • Wir merken uns den jeweils letzten Zustand des Datenausgangs des PIR-Sensors in der Variablen pirState, die anfangs mit LOW besetzt ist.
  • Die Variable val dient zum Auslesen des Datenausgangs des Sensors.
  • In der setup()-Methode rufen wir die Methode calibration() auf. Ein PIR-Sensor benötigt eine gewisse Zeit, bis er aufnahmefähig ist. Die Kalibrierungszeit ist im Sketch sehr großzügig mit 10 Sekunden bemessen (Variable calibrationTime).
  • Die Hilfsmethode detectionAlarm() wird aufgerufen, sobald der Sensor eine Bewegung entdeckt. Als Reaktion darauf schaltet der Sketch die integrierte LED am Ausgang 13 ein und sendet den Ton (Note a) an den Lautsprecher.
  • Die Hilfsmethode endDetectionAlarm() schaltet LED und Tonausgabe wieder aus.
  • In jedem Durchlauf von loop() lesen wir den Zustand des PIR-Sensors aus.
    • Ist der Wert HIGH und war der PIR-Sensor zuvor auf LOW (pirState), also bei steigender Signalflanke, hat der Sensor eine Bewegung entdeckt, weshalb ein Aufruf von detectionAlarm() erfolgt. pirState enthält als neuen Wert HIGH.
    • Ist der Wert LOW, war aber vorher auf HIGH (pirState), also bei abfallender Flanke, ist die Bewegungserkennung beendet, weshalb ein Aufruf von endDetectionAlarm() erfolgt und pirState auf LOW gesetzt wird.
    • In allen anderen Fällen gab es keine Signaländerung, weshalb der Sketch auch nichts zu tun braucht.

Programmcode:

/* 
Sketch, um Bewegungen über einen PIR-Sensor zu entdecken
und audiovisuell anzuzeigen
*/

const int ledPin = 13; // Bei Bewegungserkennung => LED-Ausgabe
const int piezoPin = 9; // Alarmton
const int pirPin = 2; // Eingangspin des IR-Sensors
int pirState = LOW; // Anfangs keine Bewegung
int val = 0; // Status des PIR-Sensors
const int calibrationTime = 10;

void setup() {
pinMode(ledPin, OUTPUT); // LED als Ausgang
pinMode(piezoPin, OUTPUT); // Piezo als Ausgang
pinMode(pirPin, INPUT); // PIR-Sensor als Eingang
Serial.begin(9600);
calibration(); // Sensorkalibrierung
}

void calibration() {
for (int i=0; i < calibrationTime; i++) {
Serial.print(".");
delay(1000); // 1 Sekunde Pause
}
Serial.println("PIR bereit!");
}

void detectionAlarm() {
digitalWrite(ledPin, HIGH); // Licht an
tone(piezoPin, 440); // Note a ausgeben
}

void endDetectionAlarm() {
digitalWrite(ledPin, LOW); // Licht aus
noTone(piezoPin); // Ton aus
}

void loop(){
val = digitalRead(pirPin); // Status des Sensors lesen
if (val == HIGH) { // Falls HIGH => Bewegung gemeldet
if (pirState == LOW) {
// Es gab also eine Flanke von LOW -> HIGH
detectionAlarm();
Serial.println("Bewegung erkannt!");
// pirState auf neuen Wert setzen
pirState = HIGH;
}
} else {
if (pirState == HIGH){
// Flanke von HIGH -> LOW Bewegungsmeldung vorüber
endDetectionAlarm();
Serial.println("Bewegung beendet!");
// pirState auf neuen Wert setzen
pirState = LOW;
}
}
}

Interrupts als Alternative

Bei dieser Gelegenheit können wir eine alternative Lösung untersuchen. Der Arduino Uno besitzt zwei spezielle Interrupts 0 und 1 für Vorgänge an externen Pins: Interrupt 0 ist mit dem digitalen Pin 2 und Interrupt 1 mit dem digitalen Pin 3 verknüpft.

Mittels der Methode attachInterrupt() können wir eine Ereignisbehandlungsroutine definieren und festlegen, bei welchem Ereignis der betreffende Interrupt ausgelöst werden soll, zum Beispiel bei steigender Flanke (RISING), fallender Flanke (FALLING) oder bei Signaländerungen (CHANGE). Sobald am betreffenden Pin die spezifizierte Änderung auftritt, erfolgt ein Aufruf der Ereignisbehandlungsroutine.

Falls der PIR-Sensor eine Bewegung erkennt, legt er an seinem Ausgang HIGH an. Es entsteht damit eine steigende Signalflanke von LOW auf HIGH. Wir können daher einen Interrupthandler bereitstellen, der auf die steigende Flanke reagiert.

Ich habe dazu nachfolgenden Sketch entwickelt, der eine abgespeckte Variante des vorigen Sketches darstellt. Das wichtigste Element ist dabei die folgende Codezeile in setup():

attachInterrupt(interruptNo, detectionAlarm, RISING);

Die erste Variable interruptNo enthält den gewünschten Interrupt, also 0 in unserem Fall, da wir die Änderungen an Pin 2 beobachten wollen. Die Methode detectionAlarm() (zweiter Parameter) soll immer dann aufgerufen werden, wenn sich eine steigende Flanke an Pin 2 ergibt (RISING als dritter Parameter). Wie wir wissen, ist das genau dann der Fall, wenn der PIR-Sensor eine Bewegung wahrnimmt und sich sein Ausgang von LOW nach HIGH ändert. In der Methode detectionAlarm() steuern wir diesmal weder eine LED noch einen Lautsprecher, sondern melden die erkannte Bewegung über den seriellen Monitor inklusive dem Zeitpunkt ihres Auftretens (in vergangenen Sekunden nach Systemstart).

Hier der Code des Sketches:

/*
Interrupt-basierte Sketch-Version zur Bewegungserkennung mit einem PIR-Sensor
*/
const int pirPin = 2; // Eingangspin PIR-Sensor
const int interruptNo = 0;// Interrupt für Pin 2
const int calibrationTime = 10;

void setup() {
pinMode(pirPin, INPUT); // PIR-Sensor als Eingang
Serial.begin(9600);
calibration(); // Sensorkalibrierung
// Interruptbehandlungsroutine anmelden
attachInterrupt(interruptNo, detectionAlarm, RISING);
}

void calibration() {
for (int i=0; i < calibrationTime; i++) {
Serial.print(".");
delay(1000); // 1 Sekunde Pause
}
Serial.println("PIR bereit!");
}

void detectionAlarm() {
Serial.print("Bewegung erkannt! Zeit = ");
Serial.print(millis()/1000);
Serial.println(" Sekunden nach Systemstart");
}
void loop(){} // absichtlich leerer Rumpf

Interrupts++

Für Interrupts gibt es weitere wichtige Funktionen :

  • detachInterrupt(interruptNo) bzw. detachInterrupt(digitalPinToInterrupt(pinNo))[/] melden den Ereignishandler für den betreffenden Interrupt wieder ab.
  • [i]noInterrupts() verhindert, dass weitere Interrupts ausgelöst werden. Das ist der erste Aufruf in Interruptbehandlungsroutinen, die sensible, zeitkritische Aktionen durchführen. Einige Funktionen stehen dann allerdings nicht mehr zur Verfügung, etwa Kommunikation.
  • Mit interrupts() lassen sich die Interrupts wieder aktivieren, weshalb diese Funktion den von noInterrupts() eingeleiteten kritischen Abschnitt beendet.

Motion mit Johnny-Five

Wollen wir den PIR-Sensor aus JavaScript ansprechen, benötigen wir die übliche Kombination aus Node.js und johnny-five. Zum Thema Motion findet sich in der API-Beschreibung die entsprechende detaillierte Information. Es gibt dort auch einen Sketch, bei dem wir nur den Pin für den PIR-Sensor von 7 auf 2 ändern müssen:

// Programm: j5-motion.js

var
five = require("johnny-five");
var board = new five.Board();

board.on("ready", function() {

// PIR-Sensor an Pin 2:
var motion = new five.Motion(2);

// Eventhandler für Kalibrierungsende:
motion.on("calibrated", function() {
console.log("calibrated");
});

// Eventhandler für erkannte Bewegung:
motion.on("motionstart", function() {
console.log("motionstart");
});

// Eventhandler für abgeschlossene Bewegungserkennung:
motion.on("motionend", function() {
console.log("motionend");
});
});

Übertragen Sie wie üblich den Sketch StandardFirmata auf Ihr Arduino-Board. Daraufhin kann unser Sketch mit dem Board kommunizieren. Den Sketch starten Sie mit:

node j5-motion.js

An der Konsole erfolgt daraufhin die Ausgabe der Ereignismeldungen.

Fazit

Dieser Teil beschäftigte sich damit, wie PIR-Sensoren funktionieren, wozu sie nützlich sind und wie man sie in Schaltungen effektiv einsetzt. Durch entsprechende Erweiterungen ließe sich etwa auch über das Internet feststellen, ob es Bewegungen im Umkreis eines Objekts gegeben hat. Der Fantasie sind keine Grenzen gesetzt.

Im nächsten Teil beschäftigen wir uns mit Ultraschallsensoren, die für bewegliche IoT-Objekte wie Drohnen oder Roboter unentbehrlich sind.