Projekt 86: Low-Cost-Drohne mit Ardunio: Unterschied zwischen den Versionen

Aus HSHL Mechatronik
Zur Navigation springen Zur Suche springen
Zeile 597: Zeile 597:
Im Detail:
Im Detail:
*Teillösungen, mit einander kombinieren, aufeinander aufbauen und eigene Ideen einfließen lassen, um daraus eine neue Lösung zu erarbeiten
*Teillösungen, mit einander kombinieren, aufeinander aufbauen und eigene Ideen einfließen lassen, um daraus eine neue Lösung zu erarbeiten
*Planung, wie die Hardware optimal umgesetzt werden kann. Dabei unterschiedliche Betrachtungswinkel einnehmen. Nicht nur funktionstechnischer Aufbau sondern auch einen Blick darauf haben, dass Teile schnell ausgetauscht werden müssten, wenn diese defekt gehen.
*Planung, wie die Hardware optimal umgesetzt werden kann. Dabei unterschiedliche Betrachtungswinkel einnehmen. Nicht nur funktionstechnischen Aufbau realisieren, sondern so konzepieren, dass defekte Teile schnell und problemlos ausgetauscht werden können
*Praktisches Arbeiten und Umgehen mit Werkzeugen z.B.: beim Sägen und Löten
*Praktisches Arbeiten und Umgehen mit Werkzeugen z.B.: beim Sägen und Löten
*
*Sicherheit nicht vernachlässigen, da von der fliegenden Drohne eine gewisse Gefahr ausgeht
*Sicherheit nicht vernachlässigen, da von der fliegenden Drohne eine Gefahr ausgeht verletzt zu werden
ßßßßßßßßß
ß´
ßß
ß
ß
ß
Nicht nur funktionstechnisch betrachten sondern auch solche Aspekte wie schnell


== Projektunterlagen ==
== Projektunterlagen ==

Version vom 18. Januar 2019, 07:52 Uhr

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




















Verwendete Komponenten

In der Tabelle ? werden alle Komponenten aufgeführt, die für den Zusammenbau der Drohne, sowie für einen Bau einer Sicherungsfläche (Erläuterung folgt) benötigt werden.

Tabelle ß: Verwendete Komponenten
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

????
asdasd

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
Abbildung 3: Fertigung des Drohnengerüsts

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.

Umsetzung der Drohne

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: Arduino Position
Abbildung 5: Lipo-Akku Position
Abbildung 6: Sensoren Position
Abbildung 7: Postion Rotoren
Abbildung 8: Position ESCs
Abbildung 9: Position Startknopf
Abbildung 10: Umsetzung Drohne













Systemidentifikation

???????????

Softwareentwicklung

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

// Arduino Wire library is required if I2Cdev I2CDEV_ARDUINO_WIRE implementation
// is used in I2Cdev.h
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
    #include "Wire.h"
#endif

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//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)

MPU6050 mpu;

UltraSonicDistanceSensor distanceSensor(ULTRASONIC_TRIGGER_PIN, ULTRASONIC_ECHO_PIN);

int State = 0, TextShown = 0;

volatile unsigned long alteZeit=0, entprellZeit=100;

bool EnalbedFlying = 0;

int throttle = 1400;//1450; // Gas zwischen 1000 und 2000 (besser 1800)
double throttleWithPID = 0;
double hoehe = 30;

double dist_offset, winkel_offset_roll = 4.97, winkel_offset_pitch = 2.2, winkel_offset_yaw = 0;

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);   // ESC_Name (ESC PIN, Minimum Value, Maximum Value, Arm Value)
ESC myESC2 (MOTOR_PIN2, SPEED_MIN, SPEED_MAX, SPEED_ARM);   // ESC_Name (ESC PIN, Minimum Value, Maximum Value, Arm Value)
ESC myESC3 (MOTOR_PIN3, SPEED_MIN, SPEED_MAX, SPEED_ARM);   // ESC_Name (ESC PIN, Minimum Value, Maximum Value, Arm Value)
ESC myESC4 (MOTOR_PIN4, SPEED_MIN, SPEED_MAX, SPEED_ARM);   // ESC_Name (ESC PIN, Minimum Value, Maximum Value, Arm Value)


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//PID gain and limit settings
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
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;

boolean auto_level = true;                 //Auto level on (true) or off (false)
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;
double maDist, rawDist, DistDiff;

PID rollPID(&pid_angle_roll, &pid_output_roll, &pid_roll_setpoint, pid_p_gain_roll, pid_i_gain_roll, pid_d_gain_roll, DIRECT);
PID pitchPID(&pid_angle_pitch, &pid_output_pitch, &pid_pitch_setpoint, pid_p_gain_pitch, pid_i_gain_pitch, pid_d_gain_pitch, DIRECT);

// MPU control/status vars
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

// ================================================================
// ===               INTERRUPT DETECTION ROUTINE                ===
// ================================================================

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

// ================================================================
// ===                      INITIAL 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

    //Use the led on the Arduino for startup indication.
    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 2000 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.
  }
  //Now that we have 100 measures, we need to devide by 2000 to get the average offset.
  dist_offset /= 100;                                                      //Devide the distance by 2000.
  Serial.println("Kalibrierung beendet!");

  pitchPID.SetOutputLimits(pid_max_pitch*(-1), pid_max_pitch);
  rollPID.SetOutputLimits(pid_max_roll*(-1), pid_max_roll);

  pitchPID.SetMode(AUTOMATIC);
  rollPID.SetMode(AUTOMATIC);

  pitchPID.SetControllerDirection(REVERSE);
  rollPID.SetControllerDirection(REVERSE);

  State = 0;                                                                //Set State back to 0.

  //When everything is done, turn off the led.
    digitalWrite(13,LOW); //Turn on the warning led.
}



// ================================================================
// ===                    MAIN PROGRAM LOOP                     ===
// ================================================================

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();
        }  
        // other program behavior stuff here
    }

    // 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: ");Serial.print((int)maDist);
      Serial.print(" \t\tWinkelX (Roll): ");Serial.print(angle_roll_raw);
      Serial.print("\tWinkelY (Pitch): ");Serial.println(angle_pitch_raw);
//      Serial.print("\tAbstand: ");Serial.println(maDist-dist_offset);
  
      if(EnalbedFlying == 1){
        State = 3;
        TextShown = 0;      
      }
      
      break;
    ////////////////////////////////////////////////////////////////////////////////////////////////////// Flug
    case 3:
      if(TextShown == 0){ 
        Serial.println("State: Flug gestartet");
        TextShown = 1;
      }
  
      pid_roll_setpoint = 0;
      pid_pitch_setpoint = 0;
      pid_yaw_setpoint = 0;
      
      calculate_pid();    //PID inputs are known. So we can calculate the pid output.
      
      esc_1 = (pid_output_roll/2) - (pid_output_pitch/2);// - pid_output_yaw; //Calculate the pulse for esc 1 (front-right - CCW) !!!! Drehrichtung beachten / kontrollieren!!!!
      esc_2 = (pid_output_roll/2) + (pid_output_pitch/2);// + pid_output_yaw; //Calculate the pulse for esc 2 (rear-right - CW)   !!!! Drehrichtung beachten / kontrollieren!!!!
      esc_3 = ((pid_output_roll/2)*(-1)) + (pid_output_pitch/2);// - pid_output_yaw; //Calculate the pulse for esc 3 (rear-left - CCW)   !!!! Drehrichtung beachten / kontrollieren!!!!
      esc_4 = ((pid_output_roll/2)*(-1)) - (pid_output_pitch/2);// + pid_output_yaw; //Calculate the pulse for esc 4 (front-left - CW)   !!!! Drehrichtung beachten / kontrollieren!!!!
  
      if (esc_1 < -30) esc_1 = -30; //Keep the motors running.
      if (esc_2 < -30) esc_2 = -30; //Keep the motors running.
      if (esc_3 < -30) esc_3 = -30; //Keep the motors running.
      if (esc_4 < -30) esc_4 = -30; //Keep the motors running.
  
      if(esc_1 > 30)esc_1 = 30; //Limit the esc-1 pulse to 2000us.
      if(esc_2 > 30)esc_2 = 30; //Limit the esc-2 pulse to 2000us.
      if(esc_3 > 30)esc_3 = 30; //Limit the esc-3 pulse to 2000us.
      if(esc_4 > 30)esc_4 = 30; //Limit the esc-4 pulse to 2000us.
      
      esc_1 = (esc_1);//+30;
      esc_2 = (esc_2);//+30;
      esc_3 = (esc_3);//+30;
      esc_4 = (esc_4);//+30;

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

      Serial.print("Höhe: ");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            
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Subroutine for calculating pid outputs
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
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;
}




Tests & Vergleich der Regleransätze

?????????

Ergebnis

Hardware

Die Zielsetzung zur Hardware wurde vollständig erfüllt. Es mag zwar klingen, dass eine 120€ teure Drohne keine „Low-Cost-Drohne“ ist, jedoch sollte bedacht werden, dass 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.

Software

????


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. In der zweiten Hauptphase ging es um die Entwicklung der Software.?????????????????????????????????????????????????????????????????????????????????

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 wieder. Das erlernte Grundwissen aus dem Mechatronik Bachelor sowie dem Business and Systems Engineering Master in den Bereichen der Elektrotechnik und Softwareentwicklung stellten 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, wie die Hardware optimal umgesetzt werden kann. Dabei unterschiedliche Betrachtungswinkel einnehmen. Nicht nur funktionstechnischen Aufbau realisieren, sondern so konzepieren, dass defekte Teile schnell und problemlos ausgetauscht werden können
  • Praktisches Arbeiten und Umgehen mit Werkzeugen z.B.: beim Sägen und Löten
  • Sicherheit nicht vernachlässigen, da von der fliegenden Drohne eine gewisse Gefahr ausgeht

Projektunterlagen

YouTube Video

Weblinks

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)