Projekt 86: Low-Cost-Drohne mit Ardunio

Aus HSHL Mechatronik
Zur Navigation springen Zur Suche springen
Messe Poster
Messe Poster
Low-Cost-Drohne
Low-Cost-Drohne

Autoren: Sergej Vogel, Janis Ostermann
Betreuer: Prof. Schneider

→ zurück zur Übersicht: WS 18/19: Angewandte Elektrotechnik (BSE)

Einleitung

Im Rahmen des Moduls "Angewandte Elektrotechnik" aus dem Studiengang "Business and Systemsengineering" der Hochschule Hamm-Lippstadt in Lippstadt konnten die Studenten ihr bereits erlerntes Wissen unter Beweis stellen und praktische Erfahrungen sammeln, indem sie eine elektrotechnisches Projekt realisieren. Thema dieses Projekts soll die Auslegung und Konstruktion einer kostengünstigen Drohne bei Zuhilfenahme eines Arduino Mikrocontrollers sein. Über die Entwicklung der Hardware hinaus soll zusätzlich ein rudimentäres Programm zur Präsentation der Funktionsweise der Drohne entwickelt werden. Genauere Anforderungen sind unter dem Punkt "Erwartungen an die Projektlösung" einsehbar.

Erwartungen an die Projektlösung

  • Recherche bisheriger Lösungen
  • Entwurf der Drone, deren elektrischer Schaltung (Sensoren, Aktoren), Konstruktion und Beschaffung der Bauteile
  • Realisierung des Aufbaus
  • Darstellung der regelungstechnischen Theorie
  • Systemidentifikation (Übertragungsfunktion der Regelstrecke bestimmen)
  • Vergleichen und bewerten Sie verschiedene Regleransätze (P, PI, PID und andere).
  • Test und wiss. Dokumentation
  • Machen Sie ein tolles Videos, welches die Funktion visualisiert.
  • Live Vorführung während der Abschlusspräsentation

Kür: Modellbasierte Programmierung der Hardware via Simulink

Projekt

Zielsetzung

Das Ziel des Projektes war es die Drohne von Null auf zu konzipieren und zusammenzubauen. Dabei sollte der Kostenaspekt mit beachtet werden. Ein weiteres Ziel stellt das geregelte Schweben der Drohne auf einer bestimmten Höhe dar.

Projektplan

Das Projekt „Low-Cost-Drohne“ sollte in einer Zeitspanne von 16 Wochen realisiert werden. Um einen reibungslosen Ablauf zu gewährleisten, wird das Projekt in folgende Projektschritte gegliedert, die in bestimmten Zeitspannen zu bewältigen sind. Daraus resultiert der Projektplan (siehe Abbildung 1).

Abbildung 1: Drohne Ablaufplan




















Projektdurchführung

Die Recherche rund um den Bereich "Drohne" stellt den Start in das Projekt dar. Da die Studenten seitens der Hochschule finanziell unterstützt wurden, gab es eine zeitliche Vorgabe zur Erstellung einer Einkaufsliste, mit den Teilen die für das jeweilige Projekt benötigt werden. Deshalb war es wichtig, die Hauptrecherche (zur Realisierung der Drohne) schnell abzuschließen. In dieser Phase haben sich mehrere Lösungen zur Realisierung der Drohne angeboten. Diese waren zwar einzeln für sich nicht überzeugend, aber durch die Entnahme einiger Teilbereiche und Indizierung eigener Ideen ist ein individueller Realisierungsplan entstanden. Dabei dient das YouTube-Video[1] als Inspiration und zentrale Grundlage für den technischen Bereich. Im Softwarebereich stehen ebenfalls viele Beispiele und Bibliotheken zur Verfügung. Doch auch da gibt es keine ideale Lösung für die Steuerung und Regelung der Drohne. Somit besteht auch hier die Aufgabe, Bibliotheken/Teillösungen mit einander zu kombinieren bzw. aufeinander abzustimmen.

Entwurf der Drohne

Abbildung 2: Drohne CAD-Konstruktion

In der Recherchephase war es zunächst geplant, den Rahmen der Drohne selbstständig mittels CAD zu entwerfen und mit dem 3-D-Druckverfahren zu fertigen. Da sich die Realisierung des Rahmens als enormer zeitlicher Aufwand herausgestellt hat, wurde die Idee verworfen. Stattdessen wird eine vorgefertigte CAD Konstruktion eines Drohnenrahmen [2] verwendet, bei der die Größenverhältnisse anzupassen sind, da:

  1. der 3-D-Drucker nur einen begrenzten Druckraum besitzt, weswegen die Gesamtkonstruktion verkleinert werden muss und
  2. die Bauteile wie Rotoren und Akku bereits bestellt sind, weswegen die Auflageflächen nochmals angepasst werden müssen.

Um den Abstand der Drohne zum Boden zu messen, soll ein Ultraschallsensor zum Einsatz kommen, der mittig der Drohne, mit der Ausrichtung nach unten zum Boden, angebracht werden soll. Das Problem liegt darin, dass der Sensor erst einen Abstand von mindestens 2cm messen kann. Aus diesem Grund werden längere Standbeine benötigt, die aus einer weiteren CAD Konstruktion [3] entnommen und auf unserer Konstruktion angepasst werden. Das Ergebnis ist die CAD-Konstruktion in der Abbildung 2.





Verwendete Komponenten

In der Tabelle 1 werden alle Komponenten aufgeführt, die für den Zusammenbau der Drohne, sowie für einen Bau einer Sicherungsplattform (Erläuterung folgt im Abschnitt "3.5.2 Umsetzung der Drohne"), benötigt werden.

Tabelle 1: Komponentenliste
Anzahl Bauteilbezeichnung Link letzter Zugriff
1 Arduino Uno R3 https://www.amazon.de/gp/product/B006H06TVG/ref=oh_aui_search_asin_title?ie=UTF8&psc=1 14.01.2019
1 HC-SR04 Ultraschallsensor https://eckstein-shop.de/HC-SR04-Abstandsmessung-Ultraschall-Ultrasonic-Sensor-Module 14.01.2019
1 MPU-6050 Gyroskop Beschleunigungssensor https://www.ebay.de/itm/GY-521-MPU-6050-Gyroskop-Beschleunigungssensor-3-Achsen-Raspberry-Pi-Arduino/162449468153?hash=item25d2be2af9:g:fdcAAOSwxtVcL0g2:rk:2:pf:1&frcectupt=true 14.01.2019
2 Brushless Motor CW https://hobbyking.com/de_de/brushless-motor-d2205-2300kv-cw.html 14.01.2019
2 Brushless Motor CCW https://hobbyking.com/de_de/brushless-motor-d2205-2300kv-ccw.html?wrh_pdp=7 14.01.2019
1 Lipo-Akku https://www.amazon.de/gp/product/B071GNHDC2/ref=ppx_yo_dt_b_asin_title_o00__o00_s00?ie=UTF8&psc=1 14.01.2019
1 Rotoren-Pack https://hobbyking.com/de_de/kingkong-5045-black.html 14.01.2019
4 Afro Mini https://hobbyking.com/de_de/afro-30a-race-spec-mini-esc-w-bec.html 14.01.2019
1 XT60 Stecker (male) Anschlusskabel für LiPo, Quadrocopter https://www.ebay.de/itm/XT60-Stecker-male-3-5-mm-mit-10-cm-Anschlusskabel-fur-LiPo-Quadrocopter/273090940709?hash=item3f957d0f25:g:t24AAOSw4zdcNzYc:rk:1:pf:1&frcectupt=true 14.01.2019
1 Ladekabel XT60 für Lipo Akku https://www.ebay.de/itm/Ladekabel-4mm-Bananenstecker-Goldstecker-XT60-Gold-Stecker-Lipo-Akku-30-37cm/321786509309?hash=item4aebf877fd:g:sEoAAOSw~gRVhZJY:rk:1:pf:1&frcectupt=true 14.01.2019
1 USB-Platte Globus Baumarkt, Lippstadt -
4 Schraubhacken Globus Baumarkt, Lippstadt -
4 Ketten Globus Baumarkt, Lippstadt -
4 Low-Cost-Karabiner Globus Baumarkt, Lippstadt -
Kosten der Drohne

In der Tabelle 2 werden die Kosten für die Bauteile aufgeführt, die für den Bau der Drohne benötigt werden.

Tabelle 2: Kostenübersicht
Anzahl Bauteilbezeichnung Kosten Pro Teil in € Gesamtkosten in €
1 Arduino Uno R3 22,99 22,99
1 Ultraschall-Sensor 2,95 6,29
1 MPU-6050 Gyroskop Beschleunigungssensor 3,40 3,40
2 Brushless Motor CW 6,29 12,58
2 Brushless Motor CCW 6,29 12,58
1 Lipo-Akku 23,75 23,75
1 Rotoren-Pack 2,72 2,72
4 Afro Mini ESC 6,33 25,32
1 XT60 Stecker (male) Anschlusskabel für LiPo, Quadrocopter 3,89 3,89
1 Ladekabel XT60 für Lipo Akku 4,95 4,95
1 3D-Druck-Rahmen 0,00 0,00
1 Taster 0,00 0,00
1 Schrauben 3,09 3,09
- Summe - 118,22
Schaltung (Sensoren/Aktoren)
Abbildung 2: Drohne CAD-Konstruktion

Alle Sensoren und Motorcontroller (ESCs) sind direkt mit dem Arduino Uno verbunden. Dazu werden Kabel durch die Kabeldurchführungen zwischen den verschiedenen Ebenen der Drohne verlegt. Die Aktoren, bzw. die Motor Controller sind zusammen mit dem Arduino UNO in einer Parallelschaltung mit dem Lithium Polymer Akku verbunden. Die Sensoren erhalten ihre Spannung über den 5 Volt Pin des Arduino. So können auch noch Sensordaten ausgelesen werden wenn der Arduino per Serieller Schnittstelle mit dem PC verbunden ist, während der Akku von der Drohne getrennt ist. Um Problemen durch Potentialunterschiede vorzubeugen, sind alle Komponenten über einen gemeinsamen Ground verbunden. Die ESCs erhalten ein PWM (Pulsweiten Moulation) Signal über die PWM fähigen Digital-Pins des Arduino. Die ESCs regeln einen Drehstrom in Abhängigkeit vom PWM Signal, um so die Geschwindigkeit der Motoren zu regulieren. Der Ultraschallsensor ist an das 5 Volt Netz des Arduino angeschlossen und überträgt seine Daten über die Digital Pins 11 Und 12 an den Arduino. Die Kommunikation des MPU-6050 Sensors wird über die I2C Schnittstelle des Arduino realisiert. Dazu wird der Sensor mit den SLC und SDA Pins des Mikrocontrollers verbunden. Das zur Kommunikation benötigte Interrupt Signal liegt an Interrupt Pin 1 des Arduions an. Der Modus-Wechsel-Taster ist über Interrupt Pin 2 und den Ground Pin an den Arduino angebunden. Beim Druck des Tasters wird der Interrupt Pin auf das Potential des Ground gezogen. Der Interrupt Pin ist mit einem Pull Up Widerstand auf 5 Volt gelegt. Dadurch Erkennt der Arduino das drücken des Tasters und löst eine Interrupt Routine aus.







Umsetzung der Drohne

Abbildung 3: Fertigung des Drohnengerüsts

Für die Realisierungsphase wird das Drohnengerüst benötigt. Hierzu wird die Drohne innerhalb der CAD-Konstruktion in ihre Einzelkomponenten zerlegt. Nun können die einzelnen Komponenten so positioniert werden, dass sich das Drucken von Hilfsstrukturen reduziert. Das hat den Vorteil, dass die Komponenten eine bessere Struktur erhalten und sich zusätzlich die Druckzeit, sowie die Nachbearbeitung reduziert. Sind die Einzelteile fertig, so werden sie von der Hilfstruktur befreit und geschliffen. Im letzten Schritt werden die Komponenten zu einem Drohnenrahmen zusammengeschraubt (siehe Abbildung 3). Gut zu erkennen sind die nutzbaren Flächen, die im weiteren Verlauf mit den Bauteilen wie Motoren, Akku, Arduino und Sensoren bestückt werden. Doch zuvor werden einige Richtlinien bzw. Punkte für den Zusammenbau aufgestellt, die dabei helfen sollen, die Drohne direkt beim ersten Versuch so zu fertigen, dass weitere Veränderungen vorgebeugt werden. Hier einige Richtlinien:

  • Die Komponenten wie den Arduino, den Lipo-Akku und die Sensoren so anordnen, dass sich der Gewichtsschwerpunkt in der Mitte der Drohne befindet
    • Rotoren haben dadurch in etwa dieselbe Belastung
    • Im Hinblick auf die Bestimmung der Drohnenlage und die Messung des Abstandes zum Boden ergibt es einen Vorteil, wenn sich die Sensoren zentral an der Drohne befinden
  • Verkabelung kurz halten (sieht ordentlich aus und keine Gefahr das die Kabel in die Rotoren geraten)
  • Der Startknopf soll so positioniert sein, dass beim Drücken des Knopfes keine Gefahr für die Hände entsteht
  • Komponenten so anbringen, dass diese schnell und problemlos ausgetauscht werden können

Die Struktur des Gerüstes ist für die Einhaltung der Richtlinien bestens geeignet. So gibt es in der Mitte des Gerüstes eine obere und eine untere Auflagefläche, wo die weiteren Bauteile angebracht werden können, um den Schwerpunkt der Drohne zentral zu halten. Dies ist auch der Plan.
Der Arduino soll einfach zugänglich sein, da die meisten Kabel dort zusammenlaufen und somit ein einfacher bzw. schneller Zugriff von Vorteil ist. Aus diesem Grund wird der Arduino auf die obere Ablagefläche positioniert und mit einem Kabelbinder fixiert (siehe Abbildung 4)
Die beiden Auflageflächen sind durch kleine Balken mit einander verbunden und grenzen dadurch einen optimalen Raum für den Akku ein. So befindet sich der Akku in der Mitte der Drohne (siehe Abbildung 5) und kann durch kurze Leitungen sowohl den Arduino, die ESCs und die Rotoren oben, als auch die Sensoren unten mit Strom versorgen.
Wie bereits erwähnt sollen der Gyro-Sensor (Beschleunigungs- und Lagesensor) und der Ultraschallsensor (für die Messung des Abstandes zum Boden) unterhalb der zweiten Platte angebracht werden. Da sich dort aber bereits Versorgungsleitungen befinden, wird eine weitere Fläche benötigt, damit die Sensoren parallel zum Boden angebracht werden können. Dafür wird die obere Auflagenfläche aus der CAD-Konstruktion entnommen und nochmals mit dem 3-D-Druckverfahren angefertigt. Daraufhin werden die Sensoren mit Schrauben an der Platte befestigt und die Platte wird mit Kabelbindern mit der nun mittleren Auflagefläche verbunden (siehe Abbildung 6).
Die Rotoren haben bereits einen vordefinierten Platz und die dazugehörigen ESCs werden jeweils unterhalb des Drohnenarms mit Kabelbindern befestigt (siehe Abbildung 7 und Abbildung 8).
Zum Schluss wird der Startknopf der Drohne neben dem Akku angebracht (siehe Abbildung 9), sodass kein Verletzungsrisiko besteht, sobald man die Drohne startet.
Durch die Zusammenführung der Komponenten entsteht eine vollwertige Drohne (siehe Abbildung 10) deren Schwerpunkt sich im Zentrum befindet, die Komponenten leicht zugänglich sind und somit jederzeit ohne viel Aufwand ausgewechselt werden können.
Für den Softwareteil wird zusätzlich eine Sicherheitsplattform gebaut (siehe Abbildung 10), die die Bewegungen der Drohne einschränken soll, falls diese nicht wie gewünscht reagiert.

Abbildung 4: Position Arduino
Abbildung 5: Position Lipo-Akku
Abbildung 6: Position Sensoren
Abbildung 7: Postion Rotoren














Abbildung 8: Position ESCs
Abbildung 9: Position Startknopf
Abbildung 10: Umsetzung Drohne














Softwareentwicklung

Abbildung 2: Projektablaufplan

Die Softwareentwicklung findet in der Arduino IDE, der Softwareentwicklungsumgebung von Arduino, statt. Es wird zunächst eine Recherche zu bestehenden Bibliotheken durchgeführt. Diese werden im Anschluss dazu verwendet um mit den verschiedenen Komponenten der Drohne zu kommunizieren. Ansätze zur Implementierung geben dabei die Beispielprogramme, welche in den Bibliotheken enthalten sind. Für die Realisierung der verschiedenen Zustände der Drohne wird sich der Switch/Case Funktion bedient. Mit Hilfe dieser wird ein Automat implementiert, welcher durch betätigen des Tasters seinen Modus wechselt.
Weitere Informationen sind den Kommentaren im Code zu entnehmen.

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Bibliotheken
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include "HCSR04.h"
#include <ESC.h>
#include <PID_v1.h>
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
#include "Wire.h"

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Defines
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define MOTOR_PIN1 (6) // ESC Pin von Motor 1 -> BEI UNS Nr. 3  VORNE RECHTS
#define MOTOR_PIN2 (9) // ESC Pin von Motor 2 -> BEI UNS Nr. 4  HINTEN RECHTS
#define MOTOR_PIN3 (10) // ESC Pin von Motor 3 -> BEI UNS Nr. 1  HINTEN LINKS
#define MOTOR_PIN4 (5) // ESC Pin von Motor 4 -> BEI UNS Nr. 2  VORNE LINKS

#define SPEED_MIN (1440) // Set the Minimum ESC Speed in microseconds -> mindestens 1060 -> Drehen ab 1440
#define SPEED_MAX (1550) // Set the Maximum ESC Speed in microseconds  -> Bis zu 1860
#define SPEED_ARM (1000) // Set the Arm ESC Speed in microseconds -> noch austesten

#define ULTRASONIC_TRIGGER_PIN (12) // Trigger Pin von Ultraschallsensor
#define ULTRASONIC_ECHO_PIN (11) // Echo Pin von Ultraschallsensor

#define INTERRUPT_PIN 2  // use pin 2 on Arduino Uno & most boards
#define LED_PIN 13 // (Arduino is 13, Teensy is 11, Teensy++ is 6)

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//FLug Variablen
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

MPU6050 mpu; // Neues Objekt MPU Sensor aus der MPU Bibliothek erzeugen

UltraSonicDistanceSensor distanceSensor(ULTRASONIC_TRIGGER_PIN, ULTRASONIC_ECHO_PIN);

int State = 0, TextShown = 0;

volatile unsigned long alteZeit=0, entprellZeit=100; // Interrupt Taster Variablen

bool EnalbedFlying = 1; // bei 1 springt Stateflow von State 2 sofort in den State 3 in dem die Motoren gestartete werden

int throttle = 1400; // Gas zwischen 1080 und maximal 1980
double throttleWithPID = 0;
double dist_offset, winkel_offset_roll = 5.13, winkel_offset_pitch = 2.75, winkel_offset_yaw = 0;

double maDist, rawDist, DistDiff; // Ultraschallsensor Filter Variablen

int cal_int;

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//ESC Library Variablen
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

double esc_1, esc_2, esc_3, esc_4;

ESC myESC1 (MOTOR_PIN1, SPEED_MIN, SPEED_MAX, SPEED_ARM);   // Neues Objekt ESC aus der ESC Bibliothek erzeugen
ESC myESC2 (MOTOR_PIN2, SPEED_MIN, SPEED_MAX, SPEED_ARM);   // Neues Objekt ESC aus der ESC Bibliothek erzeugen
ESC myESC3 (MOTOR_PIN3, SPEED_MIN, SPEED_MAX, SPEED_ARM);   // Neues Objekt ESC aus der ESC Bibliothek erzeugen
ESC myESC4 (MOTOR_PIN4, SPEED_MIN, SPEED_MAX, SPEED_ARM);   // Neues Objekt ESC aus der ESC Bibliothek erzeugen

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//PID Variablen
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
double pid_p_gain_roll = 0.3;               //Gain setting for the roll P-controller
double pid_i_gain_roll = 0;//.006;//0.04;      //Gain setting for the roll I-controller
double pid_d_gain_roll = 0.2;//.06;//18.0;      //Gain setting for the roll D-controller
int pid_max_roll = 20;                   //Maximum output of the PID-controller (+/-)
double pid_pitch_setpoint = 0;

double pid_p_gain_pitch = pid_p_gain_roll;  //Gain setting for the pitch P-controller.
double pid_i_gain_pitch = pid_i_gain_roll;  //Gain setting for the pitch I-controller.
double pid_d_gain_pitch = pid_d_gain_roll;  //Gain setting for the pitch D-controller.
int pid_max_pitch = pid_max_roll;           //Maximum output of the PID-controller (+/-)
double pid_roll_setpoint = 0;

double pid_p_gain_yaw = 0.025; //1.0; //4.0  //Gain setting for the pitch P-controller. //4.0
double pid_i_gain_yaw = 0.0; //0.02;         //Gain setting for the pitch I-controller. //0.02
double pid_d_gain_yaw = 0.0;                 //Gain setting for the pitch D-controller.
int pid_max_yaw = 400;                       //Maximum output of the PID-controller (+/-)
double pid_yaw_setpoint = 0;

double pid_p_gain_throttle = 3; //1.0; //4.0  //Gain setting for the pitch P-controller. //4.0
double pid_i_gain_throttle = 15; //0.02;         //Gain setting for the pitch I-controller. //0.02
double pid_d_gain_throttle = 0.5;                 //Gain setting for the pitch D-controller.
double pid_max_throttle = 200;                       //Maximum output of the PID-controller (+/-)
double pid_min_throttle = 0;                       //Maximum output of the PID-controller (+/-)
double pid_dist_setpoint = 30;

double angle_roll = 0, angle_pitch = 0, angle_yaw = 0;
double pid_angle_roll = 0, pid_angle_pitch = 0, pid_angle_yaw = 0;
double angle_roll_raw = 0, angle_pitch_raw = 0, angle_yaw_raw = 0;
double last_angle_roll_raw = 0, last_angle_pitch_raw = 0, last_angle_yaw_raw = 0;
double pid_output_roll = 0, pid_output_pitch = 0, pid_output_yaw = 0;

PID rollPID(&pid_angle_roll, &pid_output_roll, &pid_roll_setpoint, pid_p_gain_roll, pid_i_gain_roll, pid_d_gain_roll, DIRECT); // Neues Objekt PID Regler aus der PID Bibliothek erzeugen
PID pitchPID(&pid_angle_pitch, &pid_output_pitch, &pid_pitch_setpoint, pid_p_gain_pitch, pid_i_gain_pitch, pid_d_gain_pitch, DIRECT); // Neues Objekt PID Regler aus der PID Bibliothek erzeugen
//PID yawPID(&pid_angle_yaw, &pid_output_yaw, &pid_yaw_setpoint, pid_p_gain_yaw, pid_i_gain_yaw, pid_d_gain_yaw, DIRECT); /// Neues Objekt PID Regler aus der PID Bibliothek erzeugen
//PID throttlePID(&maDist, &throttleWithPID, &pid_dist_setpoint, pid_p_gain_throttle, pid_i_gain_throttle, pid_d_gain_throttle, DIRECT); // Neues Objekt PID Regler aus der PID Bibliothek erzeugen

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//MPU Variablen
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool dmpReady = false;  // set true if DMP init was successful
uint8_t mpuIntStatus;   // holds actual interrupt status byte from MPU
uint8_t devStatus;      // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize;    // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount;     // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer

// orientation/motion vars
Quaternion q;           // [w, x, y, z]         quaternion container
VectorInt16 aa;         // [x, y, z]            accel sensor measurements
VectorInt16 aaReal;     // [x, y, z]            gravity-free accel sensor measurements
VectorInt16 aaWorld;    // [x, y, z]            world-frame accel sensor measurements
VectorFloat gravity;    // [x, y, z]            gravity vector
float euler[3];         // [psi, theta, phi]    Euler angle container
float ypr[3];           // [yaw, pitch, roll]   yaw/pitch/roll container and gravity vector

volatile bool mpuInterrupt = false;     // indicates whether MPU interrupt pin has gone high
void dmpDataReady() {
    mpuInterrupt = true;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Setup
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void setup() {
    // join I2C bus (I2Cdev library doesn't do this automatically)
    #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
        Wire.begin();
        Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having compilation difficulties
    #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
        Fastwire::setup(400, true);
    #endif

    Serial.begin(115200);

    //Interrupt Pin
    pinMode(3, INPUT);       // Pin 2 ist INT0
    digitalWrite(3, HIGH);   // interner Pull up Widerstand auf 5V
    attachInterrupt(1, interruptKnopfRoutine, LOW); // Pin 2 (INT 0) geht auf 0V (LOW) dann interruptRoutine aufrufen

    //LED einschalten um Setup zu signalisieren
    pinMode(13, OUTPUT);
    digitalWrite(13,HIGH); //Turn on the warning led.

    // initialize device
    Serial.println(F("Initializing I2C devices..."));
    mpu.initialize();
    pinMode(INTERRUPT_PIN, INPUT);

    // verify connection
    Serial.println(F("Testing device connections..."));
    Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));

    // load and configure the DMP
    Serial.println(F("Initializing DMP..."));
    devStatus = mpu.dmpInitialize();

    // supply your own gyro offsets here, scaled for min sensitivity
    mpu.setXGyroOffset(71);
    mpu.setYGyroOffset(47);
    mpu.setZGyroOffset(-74);
    mpu.setXAccelOffset(-1287); // 1688 factory default for my test chip
    mpu.setYAccelOffset(-1412);
    mpu.setZAccelOffset(1788);

    // make sure it worked (returns 0 if so)
    if (devStatus == 0) {
        // turn on the DMP, now that it's ready
        Serial.println(F("Enabling DMP..."));
        mpu.setDMPEnabled(true);

        // enable Arduino interrupt detection
        Serial.print(F("Enabling interrupt detection (Arduino external interrupt "));
        Serial.print(digitalPinToInterrupt(INTERRUPT_PIN));
        Serial.println(F(")..."));
        attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);
        mpuIntStatus = mpu.getIntStatus();

        // set our DMP Ready flag so the main loop() function knows it's okay to use it
        Serial.println(F("DMP ready! Waiting for first interrupt..."));
        dmpReady = true;

        // get expected DMP packet size for later comparison
        packetSize = mpu.dmpGetFIFOPacketSize();
    } else {
        // ERROR!
        // 1 = initial memory load failed
        // 2 = DMP configuration updates failed
        // (if it's going to break, usually the code will be 1)
        Serial.print(F("DMP Initialization failed (code "));
        Serial.print(devStatus);
        Serial.println(F(")"));
    }

 Serial.println("Kalibrierung gestartet! Drohne nicht bewegen!");
  for (cal_int = 0; cal_int < 100 ; cal_int ++){                           //Take 100 readings for calibration.
    rawDist = distanceSensor.measureDistanceCm();                           //Messung der Distanz zum Boden in cm
    maDist = movingAverage(rawDist);
    dist_offset += maDist;                                                  //Ad distance value to dist_offset
    delay(3);                                                               //Wait 3 milliseconds before the next loop.
  }
  //Aus 100 Messung den Mittelwert bilden
  dist_offset /= 100;
  Serial.println("Kalibrierung beendet!");

  pitchPID.SetOutputLimits(pid_max_pitch*(-1), pid_max_pitch);
  rollPID.SetOutputLimits(pid_max_roll*(-1), pid_max_roll);
//  yawPID.SetOutputLimits(pid_max_yaw*(-1), pid_max_yaw); // Yaw Regler ist momentan noch nicht in die ESC Berechnung eingebunden
//  throttlePID.SetOutputLimits(pid_min_throttle, pid_max_throttle); // Höhen Regler ist momentan noch nicht in die ESC Berechnung eingebunden

  pitchPID.SetMode(AUTOMATIC);
  rollPID.SetMode(AUTOMATIC);
//  yawPID.SetMode(AUTOMATIC); // Yaw Regler ist momentan noch nicht in die ESC Berechnung eingebunden
//  throttlePID.SetMode(AUTOMATIC); // Höhen Regler ist momentan noch nicht in die ESC Berechnung eingebunden

  pitchPID.SetControllerDirection(REVERSE);
  rollPID.SetControllerDirection(REVERSE);
//  yawPID.SetControllerDirection(REVERSE); // Yaw Regler ist momentan noch nicht in die ESC Berechnung eingebunden
//  throttlePID.SetControllerDirection(DIRECT); // Höhen Regler ist momentan noch nicht in die ESC Berechnung eingebunden

  State = 0;                                                                //Initial State auf 0 zurücksetzen

  //LED ausschalten wenn Setup beendet wurde
    digitalWrite(13,LOW); //Turn on the warning led.
}



///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Hauptprogramm
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void loop() {
    // if programming failed, don't try to do anything
    if (!dmpReady) return;

    // wait for MPU interrupt or extra packet(s) available
    while (!mpuInterrupt && fifoCount < packetSize) {
        if (mpuInterrupt && fifoCount < packetSize) {
          // try to get out of the infinite loop 
          fifoCount = mpu.getFIFOCount();
        }  
    }

    // reset interrupt flag and get INT_STATUS byte
    mpuInterrupt = false;
    mpuIntStatus = mpu.getIntStatus();

    // get current FIFO count
    fifoCount = mpu.getFIFOCount();

    // check for overflow (this should never happen unless our code is too inefficient)
    if ((mpuIntStatus & _BV(MPU6050_INTERRUPT_FIFO_OFLOW_BIT)) || fifoCount >= 1024) {
        // reset so we can continue cleanly
        mpu.resetFIFO();
        fifoCount = mpu.getFIFOCount();
        Serial.println(F("FIFO overflow!"));

    // otherwise, check for DMP data ready interrupt (this should happen frequently)
    } else if (mpuIntStatus & _BV(MPU6050_INTERRUPT_DMP_INT_BIT)) {
        // wait for correct available data length, should be a VERY short wait
        while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();

        // read a packet from FIFO
        mpu.getFIFOBytes(fifoBuffer, packetSize);
        
        // track FIFO count here in case there is > 1 packet available
        // (this lets us immediately read more without waiting for an interrupt)
        fifoCount -= packetSize;
        
        // display Euler angles in degrees
        mpu.dmpGetQuaternion(&q, fifoBuffer);
        mpu.dmpGetGravity(&gravity, &q);
        mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
        
        angle_yaw_raw = (ypr[0] * 180/M_PI);
        angle_pitch_raw = (ypr[1] * 180/M_PI);
        angle_roll_raw = (ypr[2] * 180/M_PI);

        pid_angle_yaw = angle_yaw_raw-winkel_offset_yaw;
        pid_angle_pitch = angle_pitch_raw-winkel_offset_pitch;
        pid_angle_roll = angle_roll_raw-winkel_offset_roll;
       
    }

    rawDist = distanceSensor.measureDistanceCm();                             //Messung der Distanz zum Boden in cm
    maDist = movingAverage(rawDist)-dist_offset;                              //Filterung der Distanz Messwerte mit gleitendem Mittelwertfilter

    switch (State) {
    ////////////////////////////////////////////////////////////////////////////////////////////////////// Ruhezustand
    case 0:
      if(TextShown == 0){ 
        Serial.println("State: Stop");
        Serial.println("\nPress Button to Start!");
        TextShown = 1;
      }

      myESC1.stop();                                    // tell ESC to stop
      myESC2.stop();                                    // tell ESC to stop
      myESC3.stop();                                    // tell ESC to stop
      myESC4.stop();                                    // tell ESC to stop
      
      break;
    ////////////////////////////////////////////////////////////////////////////////////////////////////// Arm-Zustand
    case 1:
      if(TextShown == 0){ 
        Serial.println("State: Arm");
        TextShown = 1;
      }

      myESC1.arm();  
      myESC2.arm();  
      myESC3.arm();  
      myESC4.arm();
      delay(3000); // Wait for a while
      
      break;
    ////////////////////////////////////////////////////////////////////////////////////////////////////// PID-Initialisierung
    case 2:
      if(TextShown == 0){ 
        Serial.println("State: Initialisiere Flug");
        TextShown = 1;
      }
      
      Serial.print("Höhe in cm: ");Serial.print((int)maDist);
      Serial.print(" \t\tWinkelX in Grad (Roll): ");Serial.print(pid_angle_roll);
      Serial.print("\tWinkelY in Grad (Pitch): ");Serial.println(pid_angle_pitch);  
      if(EnalbedFlying == 1){
        State = 3;
        TextShown = 0;      
      }
      
      break;
    ////////////////////////////////////////////////////////////////////////////////////////////////////// Flug
    case 3:
      if(TextShown == 0){ 
        Serial.println("State: Flug gestartet");
        TextShown = 1;
      }

      // PID Parameter zurücksetzen
      pid_roll_setpoint = 0;
      pid_pitch_setpoint = 0;
      pid_yaw_setpoint = 0;
      
      calculate_pid();    //PID Regler Output berechnen
      
      esc_1 = (pid_output_roll/2) - (pid_output_pitch/2);// - pid_output_yaw; // Power für esc 1 berechnen (vorne-rechts - CCW)
      esc_2 = (pid_output_roll/2) + (pid_output_pitch/2);// + pid_output_yaw; // Power für esc 2 berechnen (hinten-rechts - CW)
      esc_3 = ((pid_output_roll/2)*(-1)) + (pid_output_pitch/2);// - pid_output_yaw; // Power für esc 3 berechnen (hinten-links - CCW)
      esc_4 = ((pid_output_roll/2)*(-1)) - (pid_output_pitch/2);// + pid_output_yaw; // Power für esc 4 berechnen (vorne-links - CW)
  
      if (esc_1 < -30) esc_1 = -30; //Limit the esc-1 power. unteres Limit
      if (esc_2 < -30) esc_2 = -30; //Limit the esc-1 power. unteres Limit
      if (esc_3 < -30) esc_3 = -30; //Limit the esc-1 power. unteres Limit
      if (esc_4 < -30) esc_4 = -30; //Limit the esc-1 power. unteres Limit
  
      if(esc_1 > 30)esc_1 = 30; //Limit the esc-1 power. oberes Limit
      if(esc_2 > 30)esc_2 = 30; //Limit the esc-2 power. oberes Limit
      if(esc_3 > 30)esc_3 = 30; //Limit the esc-3 power. oberes Limit
      if(esc_4 > 30)esc_4 = 30; //Limit the esc-4 power. oberes Limit
      
      esc_1 = (esc_1); 
      esc_2 = (esc_2); 
      esc_3 = (esc_3);
      esc_4 = (esc_4);

      myESC1.speed(esc_1+throttle); // tell ESC to go to the oESC speed value
      myESC2.speed(esc_2+throttle); // tell ESC to go to the oESC speed value
      myESC3.speed(esc_3+throttle); // tell ESC to go to the oESC speed value
      myESC4.speed(esc_4+throttle); // tell ESC to go to the oESC speed value

      Serial.print("Höhe in cm: ");Serial.print((int)maDist);
      Serial.print("\tESC 1: ");Serial.print(esc_1+throttle);
      Serial.print("\tESC 2: ");Serial.print(esc_2+throttle);
      Serial.print("\tESC 3: ");Serial.println(esc_3+throttle);
      Serial.print("\tESC 4: ");Serial.println(esc_4+throttle);
      
      delay(10);   
      break;
  }
    
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Interrupt Funktion
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void interruptKnopfRoutine() {

  if((millis() - alteZeit) > entprellZeit) { // innerhalb der entprellZeit nichts machen
    Serial.println("Knopf erkannt!");
    TextShown = 0;

    if(EnalbedFlying == 1){
        if (State < 3) {
          State += 1;
        }
        else {
          State = 0;
        }        
      }
    else{
        if (State < 2) {
          State += 1;
        }
        else {
          State = 0;
        }        
      }
  }
    
    alteZeit = millis(); // letzte Schaltzeit merken            
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//PID Parameter Berechnung
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void calculate_pid(){

  pitchPID.Compute();
  rollPID.Compute();
//  yawPID.Compute();
//  throttlePID.Compute();
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Moving Average Funktion
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
float movingAverage(float M) {
  #define LM_SIZE 3
  static float LM[LM_SIZE];      // LastMeasurements
  static byte index = 0;
  static float sum = 0;
  static byte count = 0;

  // keep sum updated to improve speed.
  sum -= LM[index];
  LM[index] = M;
  sum += LM[index];
  index++;
  index = index % LM_SIZE;
  if (count < LM_SIZE) count++;

  return sum / count;
}




Ergebnis

Hardware

Die Zielsetzung zur Hardware wurde vollständig erfüllt. Es mag zwar klingen, dass eine 118,22€ teure Drohne keine „Low-Cost-Drohne“ ist, jedoch sollte bedacht werden, dass es sich um einen Prototypen handelt und sich die Kosten der Drohne durch eine Massenfertigung deutlich reduzierten lassen. Des Weiteren wurde die Drohne so umgesetzt, dass sich einzelne Komponenten einfach austauschen lassen, falls diese beschädigt oder defekt sein sollten. Damit das Verletzungsrisiko durch die Drohne minimal ist, wurde eine Plattform aufgebaut, die die Bewegungen der Drohne einschränkt.

Software

Die Software der Drohne wurde den Anforderungen entsprechend entwickelt. Die Aktoren werden mit der Hilfe der ESC Bibliothek angesteuert. Des weiteren ist es möglich die Lage der Drohne und den Abstand zum Boden durch die gewonnenen Sensordaten zu bestimmen. Durch die Filterung und Sensordatenfusion sind die Messwerte sehr genau und zeigen wenig Latenz auf. Somit sollte eine Lage- und Höhenregelung möglich sein. Die Regelung der Drohne ist in Form eines PID Reglers umgesetzt. Dieser ist in der Software implementiert. Für das erfolgreiche Regeln der Drohne ist jedoch noch eine Auslegung der PID-Regelparameter nötig. Dies war aus Mangel an Zeit zum Ende des Projektes nicht mehr ausreichend umsetzbar.

Ausblick

In den nächsten Weiterentwicklungsschritten der Drohne kann durch die Arbeit an folgenden Punkten eine Weiterentwicklung des Projektes erfolgen:

  • Auslegung der PID Regelparameter
  • Implementierung von Steig- und Sinkflug bei dem autonomen Abheben der Drohne
  • Controller zur Steuerung der Drohne von Außen
  • Tiefenentladungsschutz für den LiPo Akku

Zusammenfassung

Um eine gewisse Orientierung zu erhalten, wurde zunächst Recherche betrieben und der Projektablaufplan aufgestellt. Im Anschluss darauf gelang es die notwendigen Bauteile zu erfassen und zu bestellen. In der ersten Hauptphase wurde die Hardware anhand von recherchierten Unterlagen und eigener Ideen realisiert. Dafür gab es eigene Richtlinien die bei der Umsetzung zu beachten waren um ein erfolgreiches Umsetzten des Projektes zu sichern. In der zweiten Hauptphase ging es um die Entwicklung der Software. Diese wurde ebenfalls durch das Kombinieren von bestehenden Lösungen und eigenen Entwicklungen umgesetzt. Das Ergebnis ist eine erfolgreiche Auswertung der Sensoren und die Ansteuerung der Aktoren, welches den Grundstein zur Regelung der Drohne legt. Insgesamt betragen die Kosten für den Bau der Drohne 118,22€. Dies sind jedoch lediglich die Kosten für den Prototypen und welche sich in einer Massenfertigung deutlich reduzieren ließen.

Lessons Learned

Die Realisierung einer kostengünstigen Drohne stellte eine enorme Herausforderung dar. Dabei konnte und musste man bereits erlerntes Wissen mit neuen Erfahrungen kombinieren, während man durchgehend unter Zeitdruck stand. Das simuliert mehr oder weniger das Leben eines Ingenieurs. Das erlernte Grundwissen aus dem Mechatronik Bachelor sowie dem Business and Systems Engineering Master in den Bereichen der Elektrotechnik und Softwareentwicklung stellte eine gute Grundlage für das Projekt dar. Des Weiteren konnten durch kleine Fehler und Unachtsamkeiten (leider auf Kosten der Zeit) Erfahrungen gesammelt werden.
Im Detail:

  • Teillösungen mit einander kombinieren, aufeinander aufbauen und eigene Ideen einfließen lassen, um daraus eine neue Lösung zu erarbeiten
  • Planung der optimalen Umsetzung der Hardware und damit verbundene unterschiedliche Betrachtungswinkel um nicht nur den funktionstechnischen Aufbau zu realisieren, sondern ihn auch so zu konzipieren, dass defekte Teile schnell und problemlos ausgetauscht werden können
  • Praktisches Arbeiten und Umgehen mit Werkzeugen z.B.: beim Sägen, Bohren und Löten
  • Den Sicherheitsaspekt in solchen Projekten ernst nehmen und nicht zu vernachlässigen
  • Schutz von elektrischen Komponenten wie z.B. einem Tiefenentladungsschutz
  • Beachtung von elektromagnetischen Einflüssen z.B. auf die die Sensoren


YouTube Video

Das YouTube Video als visueller Beleg steht unter folgendem Link zur Verfügung:

Low-Cost-Drohne mit Ardunio

Literatur

  1. How to build the YMFC-32 GPS hold quadcopter - With free Arduino code and schematics: [1]. Stand 16. Januar 2019.
  2. DJI Innovations' Flame Wheel F-450 Quadrotor Frame: [2]. Stand 17. Januar 2019.
  3. DJI F450 Quadcopter Drone: [3]. Stand 17. Januar 2019.



→ zurück zur Übersicht: WS 18/19: Angewandte Elektrotechnik (BSE)