A08|04 Der Piezo-Vibrationssensor
Analog Piezoelectric Vibration

Sensorwerte / Neigung oder Erschütterungen messen

Der Piezo-Vibrationssensor ist ein rundes elektronisches Modul, das eine Spannung erzeugt, indem es durch eine Vibration, Schallwelle oder mechanische Belastung physikalisch verformt wird. Wir können damit Stöße, Vibrationen, Klopfen und bei empfindlicheren Modellen sogar Luftbewegungen (Pusten) messen. Der runde Schwingungssensor basiert auf dem analogen Schwingungsprinzip eines piezoelektrischen Keramikchips. Der Keramikchip erzeugt unter Belastung eine kleine elektrische Spannung, die man über einen der Analog-Eingänge des Arduino messen und entsprechend darauf reagieren kann.

 

Spezifikationen
- Signalart: analog
- Spannung: 3,3V – 5V
- Pinabstand: 2,54mm
-Stromaufnahme:<1mA
- Arbeitstemperatur:
-10℃~~+70℃

Wir hatten bereits mit dem Piezo im Kapitel aktiver und passiver Piezo-Lautsprecher zu tun. Im inneren dieser beiden Module ist das gleiche runde Piezo-Element verbaut wie hier. Du siehst, dass wir das Piezo als Tonerzeuger als auch einen Sensor benutzen können. Wenn du also den Code und die Verkabelung aus dem Kapitel passiver Piezo-Lautsprecher mit dem Piezo-Vibrationssensor anwendest, wirst du mit diesem Modul auch Töne erzeugen können.

 

Der Aufbau

Verbinde dein Arduino mithilfe der Jumper Wire mit dem Modul:

 
 

Der minimal Sketch

Der minimal Sketch liest den Piezo-Ausgang mit dem Befehl analogRead() aus und kodiert den Spannungsbereich von 0 bis 5 Volt in einen numerischen Bereich von 0 bis 1023 in einem Prozess, der als Analog-Digital-Wandlung (ADC) bezeichnet wird.

 

Wenn der Ausgang des Sensors stärker als ein bestimmter Schwellenwert ist, sendet das Board die Zeichenkette "Knock!" über die serielle Schnittstelle an den Computer. Kopiere den unteren Sketch, füge ihn in die Arduino IDE ein und lade ihn auf das Arduino-Board rauf.

/********************************************************
A08|04 Der Piezo-Vibrationssensor A
Der minimal Sketch
Mr Robot UXSD / www.mrrobotuxsd.com
*********************************************************/

int piezoPin = A0;  //Pin-Nummer des Potentiometers
int ledPin = 13;    //Pin-Nummer der LED
int piezoValue = 0; //Variable zum Speichern der Potentiometer-Werte


void setup() 
{
  //Initialisiere die Serielle Kommunikation mit 9600 Bits pro Sekunde:
  Serial.begin(9600);
  
  pinMode(ledPin, OUTPUT); //den ledPin als OUTPUT deklarieren
}


void loop() 
{
  piezoValue = analogRead(piezoPin); //lies den aktuellen Potentiometer-Wert

  Serial.println(piezoValue);

  if(piezoValue > 10)
  {
    Serial.println("Knock! "); 
  }

  delay(100);
}
 

Schalte den seriellen Monitor ein, indem du in der Arduino IDE in der Menüleiste auf Werkzeuge > Serieller Monitor gehst.

Du solltest nun die folgende Anzeige sehen:

 

Das Ergebnis mit einer Durchschnittsberechnung verbessern

Der obere Code ist ein klassischer Sketch für den Piezo-Vibrationssensor, den man standardmäßig im Internet findet. Er ist leider unpräzise. Wie du in der unteren Abbildung sehen kannst wurde der Knock! zweimal ausgeführt, obwohl nur einmal auf den Sensor geklopft wurde. Das verfälscht das Ergebnis und führt zu Fehlauslösern.

 

Mithilfe einer Durchschnittsberechnung oder Glättung der eingelesenen Werte wird das Ergebnis verlässlicher. Hierfür nutzt der untere Sketch eine externe Funktion, der einige Eingangswerte in einem Array einliest und daraus den Durchschnittswert berechnet. Durch Klopfen am Sensor wird die LED ein- oder ausgeschaltet.

/********************************************************
A08|04 Der Piezo-Vibrationssensor B
Das Ergebnis mit einem Durchschnitt verbessern
Mr Robot UXSD / www.mrrobotuxsd.com
*********************************************************/

const int ledPin = 13;      //LED angeschlossen an Digitalpin 13
const int knockSensor = A0; //der Piezo ist an den analogen Pin 0 angeschlossen

//Schwellenwert, um die Klopfstärke zu bestimmen
const int threshold = 8;  

//Variable zum Speichern des vom Sensor-Pin gelesenen Wertes
int sensorReading = 0; 

//Variable zur Speicherung des letzten LED-Status, um das Licht umzuschalten
int ledState = LOW; 

//Variablen um den Durchschnittswert zu berechnen
#define numSamples 25
int samples[numSamples];
uint8_t i;
float average;


void setup() 
{
  pinMode(ledPin, OUTPUT); // declare the ledPin as as OUTPUT
  Serial.begin(9600);       // use the serial port
}


void loop()
{
  averageKnock();
 
  //wenn der Sensormesswert größer als der Schwellenwert ist:
  if (average >= threshold) 
  {
    //Schaltet den Status des ledPin um:
    ledState = !ledState;
    
    //ob HIGH oder LOW, ledState wird hier eingesetzt
    digitalWrite(ledPin, ledState);
    
    //sendet die Zeichenkette "Knock!" zurück an den Computer, 
    //gefolgt von einem Zeilenumbruch > ln  
    Serial.println("Knock! ");
  }
  
  //Verzögerung, um eine Überlastung des Puffers der seriellen 
  //Schnittstelle zu vermeiden
  delay(1);  
}


void averageKnock()
{
  //5 Proben nacheinander nehmen, mit einer leichten Verzögerung
  for (i=0; i< numSamples; i++) 
  {
   samples[i] = analogRead(knockSensor);
   delay(4);
  }

  //Den zuvor berechneten Durchschnitt resetten
  average = 0;

  //Den Durchschnitt aller Stichproben berechnen
  for (i=0; i< numSamples; i++) 
  {
     average += samples[i];
  }
  average /= numSamples;

  //Sende das Ergebnis zum seriellen Monitor
  Serial.println(average);
}
 

Anfangs initialiesieren wir mehrere Variablen wie die LED (ledPin), den Piezo (knockSensor), den Schwellwert (threshold), den Sensorwert (sensorReading) und den Status der LED (ledState).

const int ledPin = 13;      //LED angeschlossen an Digitalpin 13
const int knockSensor = A0; //der Piezo ist an den analogen Pin 0 angeschlossen

//Schwellenwert, um die Klopfstärke zu bestimmen
const int threshold = 8;  

//Variable zum Speichern des vom Sensor-Pin gelesenen Wertes
int sensorReading = 0; 

//Variable zur Speicherung des letzten LED-Status, um das Licht umzuschalten
int ledState = LOW;
 

Und nun zum interessanten Part: Es geht um die Variablen der Durchschnittsberechnung. Mit numSamples 25 bestimmen wir wie viele Werte für den Durchschnitt einlesen werden sollen. Je mehr Werte wir einlesen, umso länger dauert die Berechnung. Aber eine zu lange Zahlenprobe kann dazu führen, dass einer von zwei aufeinander folgenden schnellen Klopf-Ereignissen untergeht. Die Zahlenkette sollte aber auch nicht zu kurz sein, da wir wieder Probleme mit der Genauigkeit bekommen. Ich habe gute Ergebnisse mit 25 Werten erzielt.

//Variablen um den Durchschnittswert zu berechnen
#define numSamples 25
 

Mit int samples[numSamples] initialisieren wir das Array in dem alle eingelesenen Werte gespeichert werden sollen. Die Variable uint8_t i; wird in zwei for-loops gebraucht. Schließlich werden die summierten Werte in der Variable float average; mathematisch verarbeitet.

uint8_t i;
float average;

Gleich zu Beginn innerhalb des void-loop() rufen wir die externe Funktion averageKnock() auf, in der wir den Durchschnitt ausrechnen. Die Auslagerung der Berechnung in eine externe Funktion macht den Sketch viel übersichtlicher.

void loop()
{
 averageKnock();
 ...
}
 

In der ausgelagerten averageKnock()-Funktion nehmen wir zunächst mithilfe einer for-Schleife unsere 25 Proben (#define numSamples 25). In jedem Schleifendurchgang wird in samples[i] eine fortlaufende Nummer eingesetzt und damit 25x den Sensor abgefragt (analogRead(knockSensor);) => samples[0], samples[1], samples[2], samples[3], samples[4], usw.

Zu jedem dieser samples werden die aktuellen Sensorwerte gespeichert. Dieser Prozess läuft im Mikrocontroller ab und ist für uns nicht sichtbar. Aber der Speicherprozess könnte so aussehen:
samples[0] = 552 //erster Schleifendurchgang mit dem entsprechenden Wert
samples[1] = 553 //zweiter Schleifendurchgang mit dem entsprechenden Wert
samples[2] = 556 //dritter Schleifendurchgang mit dem entsprechenden Wert
…usw.

void averageKnock()
{
  //25 Proben nacheinander nehmen, mit einer leichten Verzögerung
  for (i=0; i< numSamples; i++) 
  {
   samples[i] = analogRead(knockSensor);
   delay(4);
  }
  ...
}

Das sehr kurze delay(4); wird als künstliche Verzögerung aus Stabilitätsgründen eingesetzt. Zusätzlich kannst du mit dem delay()-Wert auch dein Ergebnis beeinflussen. Du bestimmst mit der Länge, wie viel Abstand die einzelnen Messungen zueinander haben sollen. Du kannst das Ergebnis streuen oder bündeln. Wenn du streust, wird die Messung glatter und ruhiger - dafür etwas weniger präziser. Falls du bündelst, wird das Ergebnis zwar genauer, dafür flattern die Ergebnisse und es kommt zu Fehlmessungen.

delay(4);

Unten ist eine konzeptionelle Darstellung, die dir die Auswirkungen der Streuung und der Fehlauslösung visualisieren soll. Wenn du dir die vielen Balkengrafen ansiehst, die die eingelesenen Sensorwerte darstellen, erkennst du 3 Piecks. Es sind 3 Werte, die den Schwellenwert überschritten haben und das Knock-Signal an den seriellen Monitor senden. In Wirklichkeit wurde nicht 3x auf den Sensor geklopft, sondern nur einmal. Der Sensor zeigt aber trotzdem mehrere Ausschläge an. Das liegt an der Natur des Sensors.

In der Messung A werden immer gleichzeitig 8 Messungen ohne überspringen gespeichert und der Durchschnitt ausgerechnet. Es wird also jeder Balken in Reihe gemessen. Wenn das Programm die nächsten 8 Balken einließt erhalten wir zum einen eine Fehlauslösung, da die drei Knocks in Realität EIN Klopfen sind und zum anderen flattert das Ergebnis hoch und runter.

Eine bessere Lösung stellt die Messung B dar. Die 8 Messungen werden gestreut, in dem jeder zweite Balken gemessen wird. Dadurch vermeiden wir Fehlauslöser und die Messung wird ruhiger.

 

Die Variable für die Berechnung des Durchschnitts (average=0;) müssen wir in jedem Schleifendurchgang auf 0 setzen (Reset). Schließlich wollen wir ja nicht mit den alten Werten aus der letzten Schleife arbeiten. In der for-Schleife rufen wir alle bereits eingelesenen Werte aus der vorherigen for-Schleife samples[i] = analogRead(knockSensor); aus und summieren sie alle mit der Zeile average += samples[i]; in der Variable average zusammen. Es fehlt jetzt nur noch ein Schritt: Sobald die for-Schleife verlassen wurde, wird average mit der Anzahl der Lesevorgänge dividiert average /= numSamples;
=> Die Summe aller Werte durch die Anzahl der Werte. Hierdurch bekommen wir den Durchschnittswert für 25 eingelesene Sensorwerte.

//Den zuvor berechneten Durchschnitt resetten
average = 0;

//Den Durchschnitt aller Stichproben berechnen
for (i=0; i< numSamples; i++) 
 {
   average += samples[i];
 }
f
 

Sobald die externe Funktion averageKnock() durchlaufen ist springt das Programm wieder zur letzten Ausgangsposition. Wieder zurück im void loop() wird mit der Variable average in der for-Schleife gecheckt, ob der Schwellwert überschritten wurde: if (average >= threshold)

Ist der Schwellwert überschritten wird im Schleifenkörper der Status der LED mit der Zeile ledState = !ledState; um gekehrt - Also von HIGH zu LOW oder von LOW zu HIGH. Der Status der LED wird in digitalWrite(ledPin, ledState); übergeben und je nachdem wird die LED ein- oder ausgeschaltet. Schließlich wird noch eine Nachricht an den seriellen Monitor gesendet, dass der Schwellwert überschritten wurde.

//wenn der Sensormesswert größer als der Schwellenwert ist:
  if (average >= threshold) 
  {
    //Schaltet den Status des ledPin um:
    ledState = !ledState;
    
    //ob HIGH oder LOW, ledState wird hier eingesetzt
    digitalWrite(ledPin, ledState);
    
    //sendet die Zeichenkette "Knock!" zurück an den Computer, 
    //gefolgt von einem Zeilenumbruch > ln  
    Serial.println("Knock! ");
  }
  
  //Verzögerung, um eine Überlastung des Puffers der seriellen 
  //Schnittstelle zu vermeiden
  delay(1);  
}
 

Oft werden sehr kleine Verzögerung am Ende des Sketches eingebaut, um eine Überlastung des Puffers der seriellen Schnittstelle zu vermeiden.

//Verzögerung, um eine Überlastung des Puffers der seriellen 
//Schnittstelle zu vermeiden
delay(1);
 
 

Falls du die hier beschriebenen Elektronik-Module nicht hast, kannst du sie in meiner Einkaufsliste finden. Warum ich selber hauptsächlich mit Modulen der Marke Keyestudio arbeite, erläutere ich unter diesem Blog-Artikel.

In dieser Übersicht äußere ich Empfehlungen aller Art.

Hier kommst du wieder zur Übersicht aller Module.