Pflanzengesundheit mit TinyML bewerten: Praktischer Leitfaden
Komponenten und Zubehör
| Komponente | Menge |
|---|---|
| Arduino Nano 33 BLE Sense | 1 |
| Digilent Schrittmotor | 1 |
| Theremino DRV8825 Schrittmotortreiber | 1 |
Benötigte Werkzeuge
- 3D-Drucker
- Lötkolben
Apps und Onlinedienste
- Microsoft VS Code
- Arduino IDE
- Edge Impulse Studio
Über dieses Projekt
Die Prämisse
Wie Menschen können auch Pflanzen krank werden. Pilzbefall oder andere Pathogene verfärben Blätter gelb oder fleckig. Mit Machine Learning lassen sich Farbwerte scannen und ein Modell trainieren, das abweichende Blattfarben erkennt.
Die Hardware
Das Herz ist der Arduino Nano 33 BLE Sense – gewählt wegen seiner Sensoren: 9-DoF IMU, APDS-9960 (Farbe, Gesten, Nähe, Helligkeit), Mikrofon, Temperatur/Luftfeuchte/Druck. Zum Bewegen des Sensors über das Blatt: zwei Schrittmotoren mit DRV8825-Treibern.
TinyML einrichten
Die eingebauten Sensoren des Nano 33 BLE Sense werden auf Edge Impulse nicht vollständig unterstützt (nur Beschleunigung und Mikrofon gelistet). Daher: Data Forwarder statt Serial Daemon.
- Neues Edge Impulse Projekt anlegen
- Edge Impulse CLI installieren:
npm install -g edge-impulse-cli(Node.js/NPM nötig, ggf. PATH anpassen) edge-impulse-data-forwardertesten, mit Ctrl+C beenden
Farbe auslesen
APDS-9960 misst Farbe durch Infrarot-Reflexion und Analyse nicht absorbierter Wellenlängen. Bibliothek: Arduino_APDS9960. Im Code: Sensor initialisieren, in loop() auf Farbdaten warten. Verfügbar? APDS.readColor(r,g,b,c) + Nähe APDS.readProximity(). Jeder RGB-Wert (0–65535) wird zum Verhältnis zum Gesamtwert normiert.
Der Scan-Rig
Zweiachsiges System (X/Y) bewegt Sensor über das Blatt. Schrittmotoren drehen Gewindestangen → Linearbewegung. In Fusion 360 entworfen:
X-Achse ruht auf Y-Achse → Bewegung in beiden Achsen. Zusatz-Laufrad an Y-Achse stützt Motorgewicht. PLA, ~45 % Infill.
Daten sammeln
Start: Schrittmotoren referenzieren (Homing via Endschalter). APDS-9960 initialisieren. Bounding Box (zwei gegenüberliegende Ecken) definieren. Zufälligen Punkt in Box wählen, Schrittmotoren dorthin fahren, dabei kontinuierlich Farben messen.
Farbdaten verarbeiten & senden
APDS.readColor() → Summe bilden → Prozentwerte berechnen → via USB senden: Serial.printf("%1.3f,%1.3f,%1.3f\n", rr, gr, br). Werte kommagetrennt, jede Messung neue Zeile. Data Forwarder sendet an Edge Impulse Cloud als Trainingsdaten mit Label (gesund/krank).
Modell trainieren
Impulse: 3-Achsen-Zeitreihen → Spektralanalyse-Block → Keras-Klassifikator. Feature-Generierung per Spektralanalyse (Screenshot).
Testen
Neue Testdaten von krankem Blatt gesammelt. Genauigkeit: ~63 %. Modell klassifizierte meist korrekt.
Genauigkeit steigerbar durch mehr Trainingsdaten und langsameres Training.
Code
leafReader.ino
#include
#include
#include
#include "pinDefs.h"
int r, g, b, c, p;
float sum;
AccelStepper xStepper(AccelStepper::DRIVER, STEPPER_1_STEP, STEPPER_1_DIR);
AccelStepper yStepper(AccelStepper::DRIVER, STEPPER_2_STEP, STEPPER_2_DIR);
MultiStepper steppers;
const long boundingBox[2][2] = { {0, 0}, {40, 40} };
void setup() {
Serial.begin(115200);
while (!Serial);
if (!APDS.begin()) {
Serial.println("Could not init APDS9960");
while (1);
}
pinMode(X_AXIS_HOMING_SW, INPUT_PULLUP);
pinMode(Y_AXIS_HOMING_SW, INPUT_PULLUP);
xStepper.setPinsInverted(X_AXIS_DIR);
yStepper.setPinsInverted(Y_AXIS_DIR);
xStepper.setMaxSpeed(150);
yStepper.setMaxSpeed(150);
steppers.addStepper(xStepper);
steppers.addStepper(yStepper);
homeMotors();
}
void loop() {
long randomPos[2];
randomPos[0] = random(boundingBox[0][0], boundingBox[1][0]) * STEPS_PER_MM;
randomPos[1] = random(boundingBox[0][1], boundingBox[1][1]) * STEPS_PER_MM;
steppers.moveTo(randomPos);
while (steppers.run()) {
if (!APDS.colorAvailable() || !APDS.proximityAvailable()) continue;
APDS.readColor(r, g, b, c);
sum = r + g + b;
p = APDS.readProximity();
if (!p && c > 10 && sum >= 0) {
float rr = r / sum, gr = g / sum, br = b / sum;
Serial.printf("%1.3f,%1.3f,%1.3f\n", rr, gr, br);
}
}
}
void homeMotors() {
while (digitalRead(X_AXIS_HOMING_SW)) xStepper.move(-1);
while (digitalRead(Y_AXIS_HOMING_SW)) yStepper.move(-1);
xStepper.setCurrentPosition(0);
yStepper.setCurrentPosition(0);
}
pinDefs.h
#define STEPPER_1_STEP 2
#define STEPPER_1_DIR 3
#define STEPPER_2_STEP 4
#define STEPPER_2_DIR 5
#define X_AXIS_HOMING_SW 6
#define Y_AXIS_HOMING_SW 7
#define X_AXIS_DIR false
#define Y_AXIS_DIR false
#define MICROSTEPS 1
#define STEPS_PER_MM 160 * MICROSTEPS
3D-Druckdateien
CAD-Dateien auf Thingiverse.
Schaltplan
Herstellungsprozess
- K30 CO₂-Sensor: Echtzeit-Luftqualitätsmessung mit Arduino
- Kommunikationshilfe für Taubblinde: 1Sheeld + Arduino + Sprachausgabe & Anzeige
- Smartes Sparschwein: Münzprüfer mit Arduino Nano steuern
- LED per Bluetooth mit Arduino steuern – Einfache DIY-Anleitung
- DFRobot Kapazitiver Fingerabdrucksensor mit Arduino oder ESP8266 einbinden
- Interaktives Nextion HMI Projekt mit Arduino Mega und Sensoren
- Arduino-basierter 6-DoF Roboterarm mit Nunchuk-Steuerung
- Zwei Schrittmotoren mit Arduino Nano & Joystick steuern – Einfaches Tutorial
- Präzise Solareinstrahlungsmessung mit Arduino UNO & Ethernet Shield
- Kompaktes Arduino-Radar mit HC-SR04 Ultraschallsensor & 1,8″ Display