Ein Arduino misst nicht direkt Temperatur, Entfernung, Luftqualität oder Bodenfeuchte. Er liest elektrische Signale ein und wandelt sie in Zahlen um. Zwischen dem echten physikalischen Wert und dem angezeigten Messwert liegen deshalb viele Fehlerquellen: Serienstreuung, Versorgungsspannung, Kabellängen, Temperatur, Alterung, Störungen und die Einbausituation.
Eine Kalibrierung sorgt dafür, dass ein Sensor möglichst gut zu einer verlässlichen Referenzmessung passt. Filterung verbessert dagegen die Stabilität einzelner Messwerte. Beides gehört zusammen: Ein geglätteter, aber falsch kalibrierter Sensor liefert weiterhin falsche Ergebnisse.
Die wichtigsten Begriffe
Offset
Ein Offset ist eine konstante Abweichung. Zeigt ein Temperatursensor bei einer tatsächlichen Temperatur von 20,0 °C immer etwa 21,2 °C an, weicht er dauerhaft um +1,2 °C nach oben ab. Diese Abweichung gleichst du mit einem Offset aus, den du zum Rohwert addierst:
korrigierterWert = roherWert + offset;
In diesem Beispiel wäre der Offset also -1,2 °C: korrigierterWert = 21,2 + (-1,2) = 20,0. Der Offset hat immer das umgekehrte Vorzeichen der gemessenen Abweichung.
Skalierung und Steigung
Manche Sensoren liegen nicht nur um einen festen Betrag daneben. Ihre Abweichung verändert sich über den Messbereich. Dann reicht ein Offset nicht aus. Du benötigst eine lineare Umrechnung mit Steigung und Achsenabschnitt.
korrigierterWert = steigung * roherWert + offset;
Das ist besonders typisch für analoge Feuchte-, Gas- und Lichtsensoren.
Referenzmessung
Eine Referenz ist ein Messwert, dem du mehr vertraust als deinem Arduino-Sensor. Gute Referenzen sind beispielsweise:
- ein geprüftes Thermometer für Temperatur,
- eine Wetterstation oder ein hochwertiges Barometer für Luftdruck,
- ein Messbecher und eine Waage für Füllstands- oder Durchflussexperimente,
- ein Lineal oder Messschieber für Distanzsensoren,
- definierte trockene und nasse Zustände für Bodenfeuchtesensoren,
- bekannte Farbflächen für Farbsensoren.
Ein Smartphone, eine günstige Wetter-App oder ein zweiter ungeprüfter Sensor sind nur bedingt brauchbare Referenzen. Sie können ebenfalls deutlich falsch liegen.
Rauschen, Ausreißer und Drift
- Rauschen: Kleine zufällige Schwankungen von Messung zu Messung.
- Ausreißer: Einzelne unrealistische Werte, etwa durch Funkstörungen, Reflexionen oder fehlgeschlagene Sensorabfragen.
- Drift: Langsame Veränderung des Messverhaltens über Zeit, Temperatur oder Alterung.
- Hysterese: Der Messwert hängt davon ab, aus welcher Richtung sich eine Größe verändert. Ein Feuchtesensor kann beim Trocknen anders reagieren als beim Befeuchten.
Saubere Messbedingungen schaffen
Bevor du Software schreibst, sollte der Aufbau elektrisch und mechanisch solide sein. Viele vermeintliche Kalibrierprobleme sind in Wahrheit Verdrahtungs- oder Einbauprobleme.
- Versorge den Sensor mit der vorgesehenen Spannung. Prüfe besonders, ob ein Modul 3,3 V oder 5 V erwartet.
- Verbinde alle Komponenten über eine gemeinsame Masse.
- Halte analoge Signalleitungen kurz und fern von Motoren, Relais und Schaltnetzteilen.
- Ergänze bei störanfälligen Aufbauten einen 100-nF-Kondensator nahe am Sensor zwischen VCC und GND.
- Miss nicht unmittelbar nach dem Einschalten. Viele Sensoren benötigen eine Aufwärm- oder Einschwingzeit.
- Baue Sensoren passend ein: Ein Temperatursensor neben einem Spannungsregler misst dessen Abwärme, nicht die Raumtemperatur.
- Dokumentiere Versorgungsspannung, Standort, Datum, Referenzgerät und Umgebungsbedingungen.
Messwerte zunächst sichtbar machen
Kalibriere nie blind. Gib zuerst Rohwerte im Seriellen Monitor aus. Bei einem analogen Sensor ist der Rohwert auf einem Arduino Uno typischerweise eine Zahl von 0 bis 1023. Bei digitalen Sensoren erhältst du häufig bereits Einheiten wie Grad Celsius, Prozent oder Pascal.
const byte sensorPin = A0;
void setup() {
Serial.begin(115200);
}
void loop() {
int raw = analogRead(sensorPin);
Serial.print("Rohwert: ");
Serial.println(raw);
delay(500);
}
Stelle im Seriellen Monitor 115200 Baud ein. Beobachte die Werte über mehrere Minuten. Notiere dabei:
- minimalen und maximalen Wert bei ruhigen Bedingungen,
- typische Schwankungsbreite,
- offensichtliche Sprünge oder ungültige Werte,
- Reaktionszeit bei einer gezielten Veränderung.
Referenzmessung richtig durchführen
Ein-Punkt-Kalibrierung: Offset bestimmen
Die Ein-Punkt-Kalibrierung eignet sich, wenn der Sensor über den wichtigen Bereich ungefähr korrekt skaliert ist, aber dauerhaft etwas zu hoch oder zu niedrig liegt. Das ist oft bei Temperatur- oder Drucksensoren sinnvoll.
___STEADY_PAYWALL___
- Bringe Sensor und Referenz in dieselbe Umgebung.
- Warte, bis beide Werte stabil sind. Je nach Aufbau können das 5 bis 30 Minuten sein.
- Nimm nicht nur einen Wert, sondern bilde einen Mittelwert aus mehreren Messungen.
- Berechne den Offset: Referenzwert minus Sensorwert.
- Speichere den Offset als Konstante oder dauerhaft im EEPROM.
float temperaturSensor = 21.4;
float temperaturReferenz = 20.7;
float offset = temperaturReferenz - temperaturSensor;
// offset = -0.7
float temperaturKorrigiert = temperaturSensor + offset;
Zwei-Punkt-Kalibrierung: Steigung und Offset bestimmen
Bei analogen Sensoren ist die Zwei-Punkt-Kalibrierung oft deutlich besser. Du misst zwei bekannte Zustände, etwa trocken und nass bei einem Bodenfeuchtesensor. Daraus wird eine lineare Umrechnung berechnet.
Für zwei Messpunkte gilt:
steigung = (referenz2 - referenz1) / (sensor2 - sensor1)
offset = referenz1 - steigung * sensor1
korrigierterWert = steigung * roherWert + offset
Ein Beispiel: Ein Bodenfeuchtesensor liefert im trockenen Substrat den Rohwert 820 und im gut durchfeuchteten Substrat 360. Du möchtest daraus 0 bis 100 Prozent machen.
const int RAW_TROCKEN = 820;
const int RAW_NASS = 360;
int raw = analogRead(A0);
float feuchteProzent = 100.0 *
(RAW_TROCKEN - raw) /
(RAW_TROCKEN - RAW_NASS);
feuchteProzent = constrain(feuchteProzent, 0.0, 100.0);
Die Richtung ist absichtlich umgedreht: Bei vielen kapazitiven Feuchtesensoren bedeutet ein kleinerer ADC-Wert mehr Feuchtigkeit. Prüfe das immer mit deinem konkreten Modul.
Mehrpunkt-Kalibrierung
Wenn ein Sensor deutlich nichtlinear reagiert, reichen zwei Punkte nicht. Miss dann mehrere Referenzpunkte und erstelle eine Kennlinie. Für einfache Arduino-Projekte genügt häufig eine stückweise lineare Umrechnung. Du definierst dazu mehrere Messbereiche und rechnest jeweils zwischen benachbarten Punkten um.
Das ist beispielsweise bei manchen Gas- und Farbsensoren sinnvoll. Für wissenschaftlich belastbare Messungen brauchst du allerdings oft eine aufwendigere Kalibrierung, kontrollierte Bedingungen und geeignete Referenzmaterialien.
Mittelwert: einfache Glättung für ruhige Signale
Der arithmetische Mittelwert bildet den Durchschnitt aus mehreren Messungen. Er eignet sich gut für leicht verrauschte Werte ohne starke Ausreißer, etwa einen stabil montierten analogen Feuchtesensor oder einen Distanzsensor in ruhiger Umgebung.
const byte sensorPin = A0;
const byte ANZAHL_MESSUNGEN = 20;
int leseMittelwert() {
long summe = 0;
for (byte i = 0; i < ANZAHL_MESSUNGEN; i++) {
summe += analogRead(sensorPin);
delay(10);
}
return summe / ANZAHL_MESSUNGEN;
}
void setup() {
Serial.begin(115200);
}
void loop() {
int mittelwert = leseMittelwert();
Serial.println(mittelwert);
delay(500);
}
Mehr Messungen glätten stärker, machen das System aber langsamer. Bei 20 Messungen mit je 10 ms Wartezeit dauert allein die Erfassung etwa 200 ms. Für eine träge Zimmerpflanze ist das unproblematisch; für einen schnell fahrenden Roboter nicht.
Gleitender Mittelwert
Ein gleitender Mittelwert speichert die letzten Messwerte und berechnet daraus fortlaufend einen Durchschnitt. Dadurch reagiert der Messwert gleichmäßiger als ein Einzelwert, ohne dass du jedes Mal eine lange Messschleife abwarten musst.
const byte sensorPin = A0;
const byte FENSTER = 10;
int werte[FENSTER];
byte indexWert = 0;
long summe = 0;
int gleitenderMittelwert() {
summe -= werte[indexWert];
werte[indexWert] = analogRead(sensorPin);
summe += werte[indexWert];
indexWert++;
if (indexWert >= FENSTER) {
indexWert = 0;
}
return summe / FENSTER;
}
void setup() {
Serial.begin(115200);
for (byte i = 0; i < FENSTER; i++) {
werte[i] = analogRead(sensorPin);
summe += werte[i];
}
}
void loop() {
int gefiltert = gleitenderMittelwert();
Serial.println(gefiltert);
delay(100);
}
Der Startwert des Arrays wird bewusst mit realen Messungen gefüllt. Ohne diese Initialisierung würde der Filter beim Start zunächst durch Nullen verfälscht.
Medianfilter: wirksam gegen Ausreißer
Ein Mittelwert ist empfindlich gegenüber Ausreißern. Enthalten neun Messungen den Wert 400 und eine Messung den Wert 900, wird der Durchschnitt deutlich verfälscht. Der Median sortiert die Werte und verwendet den mittleren Wert. Einzelne extreme Messungen fallen dadurch kaum ins Gewicht.
Der Medianfilter ist besonders nützlich bei:
- Ultraschall-Distanzsensoren mit gelegentlichen Echo-Fehlern,
- IR-Distanzsensoren mit störenden Reflexionen,
- analogen Leitungen in elektrisch lauten Umgebungen,
- Sensoren, die sporadisch unrealistische Einzelwerte liefern.
int median5(int a, int b, int c, int d, int e) {
int werte[5] = {a, b, c, d, e};
for (byte i = 0; i < 4; i++) {
for (byte j = i + 1; j < 5; j++) {
if (werte[j] < werte[i]) {
int temp = werte[i];
werte[i] = werte[j];
werte[j] = temp;
}
}
}
return werte[2];
}
int leseMedian() {
int a = analogRead(A0);
delay(5);
int b = analogRead(A0);
delay(5);
int c = analogRead(A0);
delay(5);
int d = analogRead(A0);
delay(5);
int e = analogRead(A0);
return median5(a, b, c, d, e);
}
Eine bewährte Kombination ist: zuerst Median gegen Ausreißer, danach ein kurzer Mittelwert zur Beruhigung des verbleibenden Rauschens.
Exponentielle Glättung: wenig Speicher, gute Praxislösung
Ein exponentieller gleitender Mittelwert benötigt nur den vorherigen Filterwert. Er ist daher ideal für Mikrocontroller mit wenig RAM und für kontinuierliche Messungen.
float gefiltert = 0;
const float ALPHA = 0.15;
float exponentialFilter(float neu) {
gefiltert = ALPHA * neu + (1.0 - ALPHA) * gefiltert;
return gefiltert;
}
Je kleiner ALPHA ist, desto stärker wird geglättet und desto langsamer reagiert der Filter. Typische Startwerte liegen zwischen 0,05 und 0,3.
- ALPHA = 0,05: sehr ruhig, aber träge.
- ALPHA = 0,15: guter Kompromiss für viele Umweltmessungen.
- ALPHA = 0,5: reagiert schneller, glättet aber deutlich weniger.
Initialisiere den Filter beim Start mit einem echten Messwert. Sonst nähert sich der Wert von 0 aus langsam an.
float gefiltert = 0;
const float ALPHA = 0.15;
float exponentialFilter(float neu) {
gefiltert = ALPHA * neu + (1.0 - ALPHA) * gefiltert;
return gefiltert;
}
void setup() {
Serial.begin(115200);
gefiltert = analogRead(A0); // Filter mit echtem Startwert initialisieren
}
void loop() {
float raw = analogRead(A0);
float wert = exponentialFilter(raw);
Serial.println(wert);
delay(100);
}
Ungültige Werte und Ausreißer gezielt verwerfen
Ein Filter allein macht keine defekten Messungen gültig. Prüfe Werte immer auf einen plausiblen Bereich. Ein DHT-Sensorwert von -999 °C, eine Distanz von 0 cm ohne nahes Hindernis oder ein sprunghafter Druckwert sollten nicht direkt weiterverarbeitet werden.
bool istPlausibel(float temperatur) {
return temperatur >= -40.0 && temperatur <= 85.0;
}
Zusätzlich kannst du starke Änderungen gegenüber dem letzten gültigen Wert begrenzen. Das ist sinnvoll, wenn sich die echte Messgröße physikalisch nur langsam ändern kann.
float letzterGueltigerWert = 20.0;
const float MAX_AENDERUNG = 2.0;
float pruefeSprung(float neu) {
if (fabs(neu - letzterGueltigerWert) > MAX_AENDERUNG) {
return letzterGueltigerWert;
}
letzterGueltigerWert = neu;
return neu;
}
Die Schwelle MAX_AENDERUNG gilt pro Messung, nicht pro Sekunde. Wähle sie also passend zu deinem Messintervall: Fragst du den Sensor jede Sekunde ab, sind 2 °C Sprung bei Raumtemperatur meist unrealistisch. Setze Grenzwerte trotzdem mit Bedacht – bei einem Sensor, der plötzlich aus dem Kühlschrank in die Sonne gelegt wird, kann ein zu strenger Grenzwert echte Änderungen verschlucken.
Hysterese: Schaltvorgänge stabil machen
Hysterese ist keine Kalibrierung und kein Messfilter, aber sie verhindert nervöses Schalten an Grenzwerten. Ohne Hysterese kann ein Relais bei einem Messwert um 50 % ständig ein- und ausschalten.
Beispiel für eine Pflanzenbewässerung:
- Pumpe einschalten, wenn die Feuchte unter 35 % fällt.
- Pumpe erst wieder ausschalten, wenn die Feuchte über 45 % steigt.
bool pumpeAn = false;
const byte PUMPE_PIN = 8; // Relais/Transistor, in setup() als OUTPUT setzen
void steuerePumpe(float feuchteProzent) {
if (!pumpeAn && feuchteProzent < 35.0) {
pumpeAn = true;
digitalWrite(PUMPE_PIN, HIGH);
}
if (pumpeAn && feuchteProzent > 45.0) {
pumpeAn = false;
digitalWrite(PUMPE_PIN, LOW);
}
}
Der Bereich zwischen 35 und 45 % ist die Hysterese. Innerhalb dieses Bereichs bleibt der bisherige Schaltzustand erhalten.
Bei Pumpen, Heizungen und Relais solltest du zusätzlich Mindestlaufzeiten und Sperrzeiten einbauen. Hysterese allein schützt nicht vor zu häufigen Schaltzyklen bei ungünstigen Bedingungen.
DHT11 und DHT22: Temperatur und Luftfeuchte sinnvoll bewerten
DHT11 und DHT22 sind beliebt, aber keine Präzisionsinstrumente. Der DHT11 ist deutlich gröber als der DHT22. Beide Sensoren reagieren relativ langsam und dürfen nicht zu häufig abgefragt werden.
- Beim DHT11 ist ein Messintervall von mindestens etwa 1 Sekunde sinnvoll.
- Beim DHT22 solltest du üblicherweise mindestens 2 Sekunden zwischen Abfragen lassen.
- Direkte Sonne, Atemluft, Zugluft und die Abwärme des Arduino verfälschen Messungen stark.
- Feuchtewerte brauchen nach Ortswechseln oder starken Änderungen Zeit zum Einschwingen.
Prüfe bei jeder Abfrage auf ungültige Werte. Die verbreitete DHT-Bibliothek liefert bei Kommunikationsproblemen häufig NAN.
#include <DHT.h>
#define DHTPIN 2
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
float tempOffset = -0.4;
float humOffset = 2.0;
void setup() {
Serial.begin(115200);
dht.begin();
}
void loop() {
float luftfeuchte = dht.readHumidity();
float temperatur = dht.readTemperature();
if (isnan(luftfeuchte) || isnan(temperatur)) {
Serial.println("DHT-Messung fehlgeschlagen");
delay(2000);
return;
}
temperatur += tempOffset;
luftfeuchte += humOffset;
luftfeuchte = constrain(luftfeuchte, 0.0, 100.0);
Serial.print("Temperatur: ");
Serial.print(temperatur, 1);
Serial.print(" C, Feuchte: ");
Serial.print(luftfeuchte, 1);
Serial.println(" %");
delay(2000);
}
Ein einfacher Offset kann helfen, wenn du gegen ein gutes Referenzgerät vergleichst. Er ersetzt aber keine professionelle Feuchtekalibrierung über den gesamten Bereich.
BMP280 und BME280: Luftdruck, Temperatur und Höhenkorrektur
BMP280 und BME280 messen Luftdruck sehr fein. Der BME280 misst zusätzlich Luftfeuchte. Die absolute Höhe aus Luftdruck ist jedoch ohne Referenz unzuverlässig, weil sich der Wetterdruck laufend ändert.
Für Höhenangaben brauchst du den aktuellen Luftdruck auf Meereshöhe, auch QNH genannt. Diesen Wert kannst du von einer nahe gelegenen Wetterstation beziehen. Für relative Höhenänderungen, etwa bei einem Modellraketen- oder Aufzugsexperiment, ist der Sensor hingegen sehr gut geeignet.
#include <Wire.h>
#include <Adafruit_BMP280.h>
Adafruit_BMP280 bmp;
const float QNH_HPA = 1018.6;
void setup() {
Serial.begin(115200);
if (!bmp.begin(0x76)) {
Serial.println("BMP280 nicht gefunden");
while (true) {
delay(10);
}
}
}
void loop() {
float druckHpa = bmp.readPressure() / 100.0;
float temperatur = bmp.readTemperature();
float hoehe = bmp.readAltitude(QNH_HPA);
Serial.print("Druck: ");
Serial.print(druckHpa, 1);
Serial.print(" hPa, Temperatur: ");
Serial.print(temperatur, 1);
Serial.print(" C, Hoehe: ");
Serial.print(hoehe, 1);
Serial.println(" m");
delay(1000);
}
Die Temperatur eines BMP280 kann höher als die Raumtemperatur sein, wenn das Breakout-Board nahe an warmen Bauteilen sitzt. Montiere den Sensor mit Abstand zum Mikrocontroller und schütze ihn bei Außenmessungen vor direkter Sonne und Regen, ohne den Luftaustausch zu blockieren.
MQ-3 und andere MQ-Gassensoren: nur mit realistischer Erwartung kalibrieren
MQ-3-Sensoren werden häufig für Alkohol-Dampf verwendet. Die preiswerten Module sind für Maker-Projekte interessant, aber anspruchsvoll: Sie besitzen ein Heizelement, reagieren auf mehrere Gase, benötigen lange Aufwärmzeiten und zeigen deutliche Serienstreuung.
Ein MQ-3 ist kein Atemalkoholmessgerät und darf nicht für Sicherheits-, Medizin- oder Rechtsentscheidungen verwendet werden. Die Angabe in ppm ist ohne kontrollierte Referenzgase und korrekte Kennlinienauswertung oft nur eine grobe Schätzung.
Wichtige Praxisregeln für MQ-3
- Lass den Sensor nach dem Einschalten mindestens einige Minuten aufwärmen; für stabile Vergleichsmessungen ist deutlich mehr Zeit sinnvoll.
- Plane bei einem neuen Sensor eine Einbrennphase nach Datenblatt beziehungsweise Modulhinweisen ein.
- Miss immer unter möglichst ähnlichen Temperatur- und Feuchtebedingungen.
- Verwende den Wert besser als relativen Trend oder Schwellwert statt als vermeintlich exakte Konzentration.
- Miss den Analogausgang. Das digitale Modul-Signal ist nur ein Komparator-Schwellwert und keine präzise Messung.
Eine praktikable Kalibrierung für ein Projekt kann darin bestehen, einen stabilen Grundwert in sauberer Umgebungsluft als Basis zu erfassen. Danach reagierst du auf deutliche Abweichungen vom Grundwert.
const byte MQ3_PIN = A0;
float grundwert = 0;
float leseMQ3Mittelwert(byte anzahl) {
long summe = 0;
for (byte i = 0; i < anzahl; i++) {
summe += analogRead(MQ3_PIN);
delay(50);
}
return summe / (float)anzahl;
}
void setup() {
Serial.begin(115200);
Serial.println("MQ-3 aufwaermen lassen...");
delay(60000);
grundwert = leseMQ3Mittelwert(50);
Serial.print("Grundwert: ");
Serial.println(grundwert);
}
void loop() {
float aktuell = leseMQ3Mittelwert(10);
float abweichung = aktuell - grundwert;
Serial.print("MQ-3: ");
Serial.print(aktuell);
Serial.print(", Differenz zum Grundwert: ");
Serial.println(abweichung);
delay(1000);
}
Beachte: Die Richtung der Änderung und die passende Schwelle hängen vom Modul, der Beschaltung und der Versorgung ab. Ermittele sie experimentell und dokumentiere deine Bedingungen.
Bodenfeuchtesensoren: Kalibrierung im echten Substrat
Für Bewässerungsprojekte sind kapazitive Sensoren meist besser als günstige Widerstands-Sonden. Widerstands-Sonden korrodieren durch Elektrolyse häufig schnell, insbesondere bei dauerhafter Versorgung.
Trocken- und Nasspunkt richtig bestimmen
Kalibriere nicht einfach in Luft und in einem Glas Wasser, wenn du später Blumenerde messen willst. Die elektrische Eigenschaft von Erde, Kokossubstrat oder Hydrokultur unterscheidet sich erheblich.
- Stecke den Sensor in das Substrat, das du wirklich verwenden möchtest.
- Miss den Wert bei einem Zustand, den du für deine Pflanze als trocken definierst.
- Gieße bis zum gewünschten gut feuchten Zustand, aber nicht zwingend bis zur Staunässe.
- Warte, bis sich Wasser im Substrat verteilt hat.
- Miss erneut und speichere beide Werte als Kalibrierpunkte.
- Wiederhole das über mehrere Gießzyklen und passe die Grenzwerte an das Pflanzenverhalten an.
Die Prozentanzeige ist dabei eine projektbezogene relative Skala, keine universelle volumetrische Bodenfeuchte in Prozent. Für die Bewässerungssteuerung ist das meistens völlig ausreichend.
Sensor nur während der Messung versorgen
Bei einem analogen Sensor kann es sinnvoll sein, die Versorgung über einen Digitalpin oder besser einen Transistor beziehungsweise MOSFET nur kurz für die Messung einzuschalten. Das spart Strom und reduziert bei Widerstandssonden Korrosion. Achte darauf, dass ein Arduino-Pin nicht mehr Strom liefern darf, als zulässig ist.
const byte SENSOR_POWER = 7;
const byte SENSOR_PIN = A0;
int leseFeuchteRohwert() {
digitalWrite(SENSOR_POWER, HIGH);
delay(20);
int raw = analogRead(SENSOR_PIN);
digitalWrite(SENSOR_POWER, LOW);
return raw;
}
void setup() {
pinMode(SENSOR_POWER, OUTPUT);
digitalWrite(SENSOR_POWER, LOW);
Serial.begin(115200);
}
void loop() {
Serial.println(leseFeuchteRohwert());
delay(60000);
}
Farbsensoren: Umgebungslicht und Weißabgleich beachten
Farbsensoren wie TCS3200, TCS34725 oder ähnliche Module reagieren stark auf Beleuchtung, Abstand, Oberflächenstruktur und Fremdlicht. Eine rote Fläche wirkt unter warmweißem Licht anders als unter Tageslicht. Deshalb ist eine Kalibrierung ohne definierte Beleuchtung kaum reproduzierbar.
Für zuverlässige Farberkennung solltest du:
- einen festen Abstand zwischen Sensor und Objekt verwenden,
- die Messstelle gegen Fremdlicht abschirmen,
- eine konstante LED-Beleuchtung einbauen,
- weiße und schwarze Referenzflächen messen,
- mehrere bekannte Farbproben erfassen,
- nicht nur absolute RGB-Werte, sondern auch Verhältnisse bewerten.
Ein einfacher Weißabgleich normiert die Kanäle auf eine gemessene weiße Referenz. Dabei wird jeder Rohwert durch den passenden Weißwert geteilt.
float rWeiss = 1200.0;
float gWeiss = 1100.0;
float bWeiss = 900.0;
float rRaw = 600.0;
float gRaw = 300.0;
float bRaw = 150.0;
float rNorm = rRaw / rWeiss;
float gNorm = gRaw / gWeiss;
float bNorm = bRaw / bWeiss;
Zur Klassifikation kannst du dann prüfen, welcher normierte Kanal dominiert. Für anspruchsvollere Projekte sind Farbverhältnisse, HSV-Werte oder ein Abstand zu zuvor gespeicherten Referenzfarben robuster als einfache feste RGB-Grenzen.
Distanzsensoren: Messbereich und Einbau sind entscheidend
Ultraschall mit HC-SR04
Der HC-SR04 ist günstig, aber anfällig für schräg reflektierende, weiche oder kleine Objekte. Temperatur beeinflusst außerdem die Schallgeschwindigkeit. Ein einzelner fehlgeschlagener Echoimpuls darf daher nicht direkt als echte Entfernung verwendet werden.
Eine Einzelmessung liefert die Distanz in Zentimetern und meldet einen ausbleibenden Echoimpuls über den Timeout als -1:
const byte TRIG_PIN = 9;
const byte ECHO_PIN = 10;
float leseDistanzCm() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
unsigned long dauer = pulseIn(ECHO_PIN, HIGH, 30000);
if (dauer == 0) {
return -1.0;
}
return dauer * 0.0343 / 2.0;
}
Für zuverlässige Werte misst du wie oben beschrieben mehrfach, verwirfst Timeouts und unrealistische Entfernungen und bildest aus den gültigen Messungen den Median:
const float MIN_CM = 2.0;
const float MAX_CM = 400.0;
float robusteDistanzCm() {
const byte N = 5;
float werte[N];
byte gueltig = 0;
for (byte i = 0; i < N; i++) {
float d = leseDistanzCm();
if (d >= MIN_CM && d <= MAX_CM) { // nur plausible Werte behalten
werte[gueltig] = d;
gueltig++;
}
delay(10);
}
if (gueltig == 0) {
return -1.0; // keine einzige gueltige Messung
}
// gueltige Werte sortieren und den mittleren zurueckgeben
for (byte i = 0; i < gueltig - 1; i++) {
for (byte j = i + 1; j < gueltig; j++) {
if (werte[j] < werte[i]) {
float temp = werte[i];
werte[i] = werte[j];
werte[j] = temp;
}
}
}
return werte[gueltig / 2];
}
Die Formel verwendet näherungsweise 0,0343 cm pro Mikrosekunde als Schallgeschwindigkeit. Für ein normales Hindernisprojekt genügt das. Bei großen Temperaturschwankungen kannst du die Schallgeschwindigkeit temperaturabhängig korrigieren.
float schallgeschwindigkeitCmUs(float temperaturC) {
return (331.3 + 0.606 * temperaturC) / 10000.0;
}
float distanzCm(unsigned long dauerUs, float temperaturC) {
return dauerUs * schallgeschwindigkeitCmUs(temperaturC) / 2.0;
}
IR- und ToF-Sensoren
Infrarot-Distanzsensoren können abhängig von Farbe und Reflexion der Oberfläche abweichen. Time-of-Flight-Sensoren wie VL53L0X oder VL53L1X sind häufig komfortabler, haben aber ebenfalls Grenzen bei starkem Sonnenlicht, glänzenden Flächen oder ungünstigen Winkeln.
Kalibriere Distanzsensoren mit einem Lineal an mehreren Abständen im tatsächlichen Einsatzbereich. Miss beispielsweise bei 10, 20, 50 und 100 cm. Wenn die Abweichung nicht konstant ist, verwende eine Kennlinie oder korrigiere nur in dem Bereich, der für dein Projekt relevant ist.
Kalibrierwerte dauerhaft im EEPROM speichern
Ein Offset soll nach einem Neustart nicht verloren gehen. Auf klassischen Arduino-Boards kannst du dafür EEPROM verwenden. Schreibe nicht bei jeder Messung in den EEPROM, denn der Speicher hat nur eine begrenzte Zahl an Schreibzyklen. Speichere Werte nur nach einer bewussten Kalibrierung durch dich.
#include <EEPROM.h>
float temperaturOffset = 0.0;
void setup() {
Serial.begin(115200);
EEPROM.get(0, temperaturOffset);
if (isnan(temperaturOffset) ||
temperaturOffset < -20.0 ||
temperaturOffset > 20.0) {
temperaturOffset = 0.0;
}
Serial.print("Geladener Offset: ");
Serial.println(temperaturOffset, 2);
}
void speichereOffset(float neuerOffset) {
EEPROM.put(0, neuerOffset);
temperaturOffset = neuerOffset;
}
void loop() {
}
Bei komplexeren Projekten speicherst du eine Struktur mit Versionsnummer, Prüfsumme und mehreren Kalibrierwerten. So erkennst du ungültige oder veraltete Daten zuverlässig.
Empfohlener Ablauf für ein zuverlässiges Sensorprojekt
- Sensordatenblatt lesen: Versorgung, Messbereich, Genauigkeit, Aufwärmzeit und Abfrageintervall prüfen.
- Rohwerte erfassen: Werte über den Seriellen Monitor beobachten und auf Plausibilität prüfen.
- Mechanik verbessern: Sensor sinnvoll platzieren, vor Wärmequellen, Sonne und Feuchtigkeit schützen.
- Referenz auswählen: Ein möglichst vertrauenswürdiges Vergleichsgerät oder einen definierten Zustand verwenden.
- Kalibrieren: Offset, Zwei-Punkt-Skala oder Kennlinie passend zum Sensor bestimmen.
- Ausreißer behandeln: Ungültige Werte verwerfen und bei Bedarf Medianfilter einsetzen.
- Rauschen glätten: Mittelwert oder exponentielle Glättung passend zur Reaktionszeit wählen.
- Schaltschwellen absichern: Hysterese, Mindestzeiten und Plausibilitätsprüfungen verwenden.
- Werte speichern: Kalibrierdaten im EEPROM oder Flash sichern, aber selten schreiben.
- Langzeitverhalten prüfen: Messwerte über Tage oder Wochen protokollieren und Drift erkennen.
Typische Fehler und ihre Ursachen
- Der Wert springt stark: Versorgung instabil, Kabel zu lang, fehlende Masseverbindung, elektromagnetische Störung oder ungeeigneter Filter.
- Der Wert ist immer zu hoch oder zu niedrig: Offset fehlt, Sensor sitzt an einer Wärmequelle oder Referenzmessung war nicht vergleichbar.
- Die Messung reagiert zu langsam: Filterfenster zu groß, ALPHA zu klein oder der Sensor selbst ist träge.
- Die Steuerung flattert: Keine Hysterese, Schwellwert zu knapp gewählt oder Messrauschen wird direkt verarbeitet.
- Der Feuchtesensor liefert nach Wochen andere Werte: Korrosion, Salzablagerungen, geändertes Substrat oder Drift durch Alterung.
- Der MQ-Sensor zeigt jeden Tag andere Werte: Unzureichende Aufwärmzeit, wechselnde Luftfeuchte und Temperatur oder unrealistische Erwartung an absolute Genauigkeit.
- Der Distanzsensor misst manchmal riesige Entfernungen: Echo nicht empfangen, Reflexion ungeeignet oder Timeout nicht sauber behandelt.
Fazit
Zuverlässige Arduino-Messungen entstehen nicht durch einen einzelnen perfekten Sketch. Entscheidend ist eine Kette aus sauberem Aufbau, passenden Referenzmessungen, realistischer Kalibrierung, Plausibilitätsprüfungen und sinnvoller Filterung.
Für viele Projekte reicht eine einfache Kombination aus Offsetkorrektur, Median gegen Ausreißer, exponentieller Glättung und Hysterese für Schaltentscheidungen. Bei analogen Feuchte- und Gassensoren solltest du zusätzlich immer die tatsächlichen Bedingungen deines Aufbaus berücksichtigen. So werden aus unruhigen Rohdaten brauchbare Messwerte, mit denen dein Arduino zuverlässig handeln kann.