Script 15-10-2019

Dependencies:   Servoaansturing mbed QEI HIDScope biquadFilter MODSERIAL FastPWM

main.cpp

Committer:
Renate
Date:
2019-11-05
Revision:
43:2f946b617d62
Parent:
42:ca4bbc3e0239

File content as of revision 43:2f946b617d62:

#include "mbed.h"
#include "HIDScope.h"
#include "QEI.h"
#include "MODSERIAL.h"
#include "BiQuad.h"
#include "FastPWM.h"
#define  M_PI 3.14159265358979323846  /* pi */
#include <math.h>
#include "Servo.h"
#include <cmath>
#include <complex>

Serial pc(USBTX, USBRX);

// TICKERS
Ticker loop_ticker;                                                                     // Ticker aanmaken die ervoor zorgt dat de ProcessStateMachine met een frequentie vsn 500 Hz kan worden aangeroepen.

// BENODIGD VOOR PROCESS STATE MACHINE
enum states {Motors_off, Calib_motor, Calib_EMG, Homing, Operation_mode};               // Alle states definiëren.
states currentState = Motors_off;                                                       // State waarin wordt begonnen definiëren.
bool stateChanged = true;                                                               // Toevoegen zodat de initialisatie van de eerste state plaatsvindt.

// INPUTS
DigitalIn Power_button_pressed(D1);                                                     // Definiëren van alle buttons, we gebruiken hiervoor geen InterruptIn, maar DigitalIn.
DigitalIn Emergency_button_pressed(D2);
DigitalIn Motor_calib_button_pressed(SW2);
DigitalIn servo_button_pressed(SW3);

AnalogIn EMG_biceps_right_raw (A0);                                                     // Definiëren van de ruwe EMG-signalen die binnenkomen via AnalogIn.
AnalogIn EMG_biceps_left_raw (A1);                                                      // We gebruiken signalen van de kuit en de linker en rechter biceps.
AnalogIn EMG_calf_raw (A2);

QEI Encoder1(D13, D12, NC, 64);                                                         // Definities voor de encoders op motor 1 (Encoder1) en 2 (Encoder2). Hiervoor wordt de QEI library gebruikt
QEI Encoder2(D10, D9, NC, 64);                                                          // We gebruiken X2 encoding, wat standaard is en dus niet hoeft worden toegevoegd aan deze defninitie.
                                                                                        // Het aantal counts per omwenteling is gelijk aan 64.
// OUTPUTS
PwmOut motor1(D6);                                                                      // Definities voor de motorsnelheden door middel van PwmOut. Er kan een getal tussen 0 en 1 worden ingevoerd.
PwmOut motor2(D5);

DigitalOut motor1_dir(D7);                                                              // Definities voor de richtingen van de motoren. Het getal 0 zorgt voor de ene richting, het getal 1 voor de andere.
DigitalOut motor2_dir(D4);                                                              // In ons geval zijn beide motoren rechtsom draaiend vanaf de assen bekeken, wanneer de richting op 1 wordt gezet.

Servo mijnServo(D7);                                                                    // Definitie voor de output naar de servo motor, benodigd voor het controleren van de spatelpositie.

// VARIABELEN VOOR ENCODER, MOTORHOEK ETC.
double counts1;                                                                         // Global variables definiëren voor het aantal counts dat uit de encoder komt en een begindefinitie voor
double counts2;                                                                         // de offset opstellen.
double offset1 = 0.0;
double offset2 = 0.0;
const double conversion_factor = (2.0*M_PI)/((64.0*131.25)/2);                          // Omrekeningsfactor om de encoder counts om te zetten naar de huidige motorhoek.
double theta_h_1_rad;                                                                   // Actuele motorhoek in radialen (motor 1).
double theta_h_2_rad;                                                                   // Actuele motorhoek in radialen (motor 2).
double q1;                                                                              // Hoek joint 1 die volgt uit de actuele motorhoeken.
double q2;                                                                              // Hoek joint 2 die volgt uit de actuele motorhoeken.

// DEFINITIES VOOR FILTERS

// BICEPS-RECHTS
// Definities voor eerste BiQuadChain (High-pass en Notch) opstellen
BiQuadChain bqcbr;
BiQuad bqbr1(0.8006, -1.6012, 0.8006, -1.5610, 0.6414);                                 // High-pass filter met een cut off frequentie van 25 Hz.
BiQuad bqbr2(1, -1.6180, 1, -1.6019, 0.9801);                                           // Notch filter met een frequentie van 50 Hz en een notchwidth van 0.01 Hz.
// Na het nemen van de absolute waarde (later) moet de tweede BiQuadChain worden
// toegepast. Definieer (twee Low-pass filters-> vierde orde filter verkrijgen):
BiQuadChain bqcbr2;
BiQuad bqbr3(1.5515e-4, 3.1030e-4, 1.5515e-4, -1.9645, 0.9651);                         // Twee low-pass filters met een cut off frequentie van 2 Hz.
BiQuad bqbr4(1.5515e-4, 3.1030e-4, 1.5515e-4, -1.9645, 0.9651);

// BICEPS-LINKS
// Definities voor eerste BiQuadChain (High-pass en Notch) opstellen
BiQuadChain bqcbl;
BiQuad bqbl1(0.8006, -1.6012, 0.8006, -1.5610, 0.6414);                                 // High-pass filter met een cut off frequentie van 25 Hz.
BiQuad bqbl2(1, -1.6180, 1, -1.6019, 0.9801);                                           // Notch filter met een frequentie van 50 Hz en een notchwidth van 0.01 Hz.
// Na het nemen van de absolute waarde (later) moet de tweede BiQuadChain worden
// toegepast. Definieer (twee Low-pass filters-> vierde orde filter verkrijgen):
BiQuadChain bqcbl2;
BiQuad bqbl3(1.5515e-4, 3.1030e-4, 1.5515e-4, -1.9645, 0.9651);                         // Twee low-pass filters met een cut off frequentie van 2 Hz.
BiQuad bqbl4(1.5515e-4, 3.1030e-4, 1.5515e-4, -1.9645, 0.9651);

// KUIT
// Definities voor eerste BiQuadChain (High-pass en Notch) opstellen
BiQuadChain bqck;
BiQuad bqk1(0.8006, -1.6012, 0.8006, -1.5610, 0.6414);                                  // High-pass filter met een cut off frequentie van 25 Hz.
BiQuad bqk2(1, -1.6180, 1, -1.6019, 0.9801);                                            // Notch filter met een frequentie van 50 Hz en een notchwidth van 0.01 Hz.
// Na het nemen van de absolute waarde (later) moet de tweede BiQuadChain worden
// toegepast. Definieer (twee Low-pass filters-> vierde orde filter verkrijgen):
BiQuadChain bqck2;
BiQuad bqk3(1.5515e-4, 3.1030e-4, 1.5515e-4, -1.9645, 0.9651);                          // Twee low-pass filters met een cut off frequentie van 2 Hz.
BiQuad bqk4(1.5515e-4, 3.1030e-4, 1.5515e-4, -1.9645, 0.9651);

// VARIABELEN VOOR EMG + FILTEREN
double filtered_EMG_biceps_right_1;                                                     // Definities voor ruwe EMG-signalen, gefilterd met de high-pass en notch filter.
double filtered_EMG_biceps_left_1;
double filtered_EMG_calf_1;

double filtered_EMG_biceps_right_abs;                                                   // Definities voor de signalen, waarbij de absolute waarden genomen zijn van de eerste filterketen.
double filtered_EMG_biceps_left_abs;
double filtered_EMG_calf_abs;

double filtered_EMG_biceps_right;                                                       // Definities voor de gefilterde EMG-signalen, na de tweede filter keten.
double filtered_EMG_biceps_left;
double filtered_EMG_calf;

// Variabelen voor HIDScope
HIDScope scope(3);                                                                      // Op deze manier kunnen drie signalen worden weergegeven in HIDScope.

// VARIABELEN VOOR (INITIATIE VAN) EMG KALIBRATIE LOOP
bool calib = false;                                                                     // Benodigde bool en 'runner' (i_calib) voor het uitvoeren van de EMG-kalbratie.
static int i_calib = 0;

double filtered_EMG_biceps_right_total;                                                 // Benodigde variabelen voor het berekenen van een gemiddelde maximale EMG-waarde tijdens de EMG-kalibratie.
double filtered_EMG_biceps_left_total;                                                  // Dit totaal is een sommatie van de signalen over 5 seconden.
double filtered_EMG_calf_total;

double mean_EMG_biceps_right;                                                           // Global variables definiëren voor het gemiddelde maximale EMG-signaal, verkregen d.m.v. de EMG-kalibratie.
double mean_EMG_biceps_left;
double mean_EMG_calf;

// VARIABELEN VOOR OPERATION MODE (EMG)
double normalized_EMG_biceps_right;                                                     // Global variables definiëren voor het normaliseren van het gemeten EMG-signaal naar het gemiddelde maximale EMG-signaal.
double normalized_EMG_biceps_left;
double normalized_EMG_calf;

// VARIABELEN VOOR OPERATION MODE (RKI)
double vx;                                                                              // Geeft de definitie voor de 'desired velocity' in x-richting.
double vy;                                                                              // Geeft de definitie voor de 'desired velocity' in y-richting.

double Inverse_jacobian[2][2];                                                          // Matrixen opstellen voor de inverse jacobian en de gewenste snelheden (vx en vy).
double desired_velocity[2][1];

double Joint_velocity[2][1] = {{0.0}, {0.0}};                                           // Nulmatrix opstellen voor de joint snelheden, beginwaarden.

const double delta_t = 0.002;                                                           // Tijdsverschil dat wordt gebruikt om de joint velocities te integreren, is gelijk aan de duur van een 'ticker ronde'.

double Joint_1_position = 0.0;                                                          // Begindefinities opstellen voor de joint en motor posities.
double Joint_2_position = 0.0;

double Joint_1_position_prev = 0.0;
double Joint_2_position_prev = 0.0;

double Motor_1_position = 0.0;
double Motor_2_position = 0.0;

double error_M1;                                                                        // Definiëren van de motorerrors, het verschil tussen de daadwerkelijke hoek en de gewenste hoek.
double error_M2;

// VARIABELEN VOOR OPERATION MODE (PI-CONTROLLER)
const double Kp = 17.5;                                                                 // Kp en Ki waarden voor de PI-controller definiëren.
const double Ki = 1.02;                                                                 // Zijn theoretisch getest met goede resultaten, in de praktijk konden geen goede tests worden verricht, doordat de
                                                                                        // robot oncontroleerbaar gedrag vertoonde.
double theta_k_1 = 0.0;                                                                 // Begin definities opstellen voor de motorhoeken na de proportional part.
double theta_k_2 = 0.0;

double error_integral_1 = 0.0;                                                          // Begin definities opstellen voor de integralen van de errors.
double error_integral_2 = 0.0;

double u_i_1;                                                                           // Uitkomst variabelen definiëren voor de vermenigvuldiging van de error integraal met Ki.
double u_i_2;

double theta_t_1;                                                                       // Variabelen opstellen voor de sommatie van theta_k en u_i.
double theta_t_2;

double abs_theta_t_1;                                                                   // Variabele opstellen voor de absolute waarde van theta_t.
double abs_theta_t_2;

// VARIABELEN VOOR OPERATION MODE (SERVO)
double theta_f= 580;                                                                    // Dit zijn allemaal waarden tussen de 500 en 2400.
double theta_sout;                                                                      // Dit zijn allemaal waarden tussen de 500 en 2400.
double q1_ser;                                                                          // Dit zijn allemaal waarden tussen de 500 en 2400.
double q2_ser;                                                                          // Dit zijn allemaal waarden tussen de 500 en 2400.

// VOIDS

void emergency()                                                                        // Noodfunctie waarbij de motoren uit worden gezet en de ProcessStateMachine wordt losgekoppeld
{                                                                                       // van de ticker. De robot doet dan niks meer. De enige optie is om de mbed te resetten, waarna
    loop_ticker.detach();                                                               // het script opnieuw moet worden opgestart.
    motor1.write(0);
    motor2.write(0);
    pc.printf("Ik ga exploderen!!!\r\n");
}

void motors_off()                                                                       // Functie waarbij beide motoren worden uitgezet.
{
    motor1.write(0);
    motor2.write(0);
    pc.printf("Motoren uit functie\r\n");
}

void EMG_calibration()                                                                  // Functie die wordt uitgevoerd tijdens de EMG kalibratie. Wordt geinitialiseerd door de bool calib,
{                                                                                       // die alleen waar is tijdens de EMG kalibratie. Er wordt gebruikgemaakt van een runner i_calib, zodat
    if (i_calib == 0) {                                                                 // EMG-waarden tijdens maximale spierspanning gedurende 5 seconden bij elkaar op worden geteld. Het gemiddelde
        filtered_EMG_biceps_right_total=0;                                              // hiervan kan worden bepaald door te delen door het aantal samples dat genomen is in dit interval, namelijk 2500.
        filtered_EMG_biceps_left_total=0;
        filtered_EMG_calf_total=0;
    }
    if (i_calib <= 2500) {
        filtered_EMG_biceps_right_total+=filtered_EMG_biceps_right;
        filtered_EMG_biceps_left_total+=filtered_EMG_biceps_left;
        filtered_EMG_calf_total+=filtered_EMG_calf;
        i_calib++;
    }
    if (i_calib > 2500) {
        mean_EMG_biceps_right=filtered_EMG_biceps_right_total/2500.0;
        mean_EMG_biceps_left=filtered_EMG_biceps_left_total/2500.0;
        mean_EMG_calf=filtered_EMG_calf_total/2500.0;
        pc.printf("Ontspan spieren\r\n");
        pc.printf("Rechterbiceps_max = %f, Linkerbiceps_max = %f, Kuit_max = %f\r\n", mean_EMG_biceps_right, mean_EMG_biceps_left, mean_EMG_calf);
        calib = false;
    }
}

void Homing_function()                                                                  // Functie die ervoor zorgt dat homing wordt uitgevoerd. De motoren worden met een lage snelheid bewogen, tot de motorhoeken
{                                                                                       // weer in de referentie positie staan (0 in ons geval, doordat we compenseren met een offset voor encoder counts). Wanneer
    if (theta_h_1_rad != 0.0) {                                                         // deze positie is bereikt, wordt de snelheid op nul gezet. In de ProcessStateMachine wordt overgegaan naar de volgende state,
        if (theta_h_1_rad < 0) {                                                        // wanneer dit voor beide motoren is gebeurd.
            motor1.write(0.3);
            motor1_dir.write(0);
        } else {
            motor1.write(0.3);
            motor1_dir.write(1);
        }
    }
    if (theta_h_1_rad == 0.0) {
        motor1.write(0);
    }
    if (theta_h_2_rad != 0.0) {
        if (theta_h_2_rad < 0) {
            motor2.write(0.3);
            motor2_dir.write(0);
        } else {
            motor2.write(0.3);
            motor2_dir.write(1);
        }
    }
    if (theta_h_2_rad == 0.0) {
        motor2.write(0);
    }
}

void Inverse_Kinematics()                                                               // Functie die de inverse kinematica uitvoert. De inverse Jacobian is afkosmtig uit eerdere berekeningen in Matlab.
{                                                                                       // The desired velocity komt voort uit de operation mode, waar hier een waarde aan wordt toegekend.
    // Defining joint velocities based on calculations of matlab                        // Outputs van deze functie zijn motorposities, die later worden gebruikt bij het berekenen van een positie error.
    Inverse_jacobian[0][0] = ((cos(q1+3.141592653589793/6.0)*-8.5E2-sin(q1)*4.25E2+cos(q1)*cos(q2)*2.25E2+cos(q1)*sin(q2)*6.77E2+cos(q2)*sin(q1)*6.77E2-sin(q1)*sin(q2)*2.25E2+sqrt(3.0)*cos(q1)*4.25E2-sqrt(3.0)*cos(q1)*cos(q2)*4.25E2+sqrt(3.0)*sin(q1)*sin(q2)*4.25E2)*(4.0E1/1.7E1))/(cos(q1)*cos(q1+3.141592653589793/6.0)*4.25E2+sin(q1)*sin(q1+3.141592653589793/6.0)*4.25E2-cos(q1)*sin(q2)*sin(q1+3.141592653589793/6.0)*6.77E2-cos(q2)*sin(q1)*sin(q1+3.141592653589793/6.0)*6.77E2+cos(q1+3.141592653589793/6.0)*sin(q1)*sin(q2)*6.77E2+sin(q1)*sin(q2)*sin(q1+3.141592653589793/6.0)*2.25E2-sqrt(3.0)*cos(q1)*sin(q1+3.141592653589793/6.0)*4.25E2+sqrt(3.0)*cos(q1+3.141592653589793/6.0)*sin(q1)*4.25E2-cos(q1)*cos(q2)*cos(q1+3.141592653589793/6.0)*6.77E2-cos(q1)*cos(q2)*sin(q1+3.141592653589793/6.0)*2.25E2+cos(q1)*cos(q1+3.141592653589793/6.0)*sin(q2)*2.25E2+cos(q2)*cos(q1+3.141592653589793/6.0)*sin(q1)*2.25E2+sqrt(3.0)*cos(q1)*cos(q2)*sin(q1+3.141592653589793/6.0)*4.25E2-sqrt(3.0)*cos(q1)*cos(q1+3.141592653589793/6.0)*sin(q2)*4.25E2-sqrt(3.0)*cos(q2)*cos(q1+3.141592653589793/6.0)*sin(q1)*4.25E2-sqrt(3.0)*sin(q1)*sin(q2)*sin(q1+3.141592653589793/6.0)*4.25E2);
    Inverse_jacobian[0][1] = ((cos(q1)*4.25E2-sin(q1+3.141592653589793/6.0)*8.5E2-cos(q1)*cos(q2)*6.77E2+cos(q1)*sin(q2)*2.25E2+cos(q2)*sin(q1)*2.25E2+sin(q1)*sin(q2)*6.77E2+sqrt(3.0)*sin(q1)*4.25E2-sqrt(3.0)*cos(q1)*sin(q2)*4.25E2-sqrt(3.0)*cos(q2)*sin(q1)*4.25E2)*(4.0E1/1.7E1))/(cos(q1)*cos(q1+3.141592653589793/6.0)*4.25E2+sin(q1)*sin(q1+3.141592653589793/6.0)*4.25E2-cos(q1)*sin(q2)*sin(q1+3.141592653589793/6.0)*6.77E2-cos(q2)*sin(q1)*sin(q1+3.141592653589793/6.0)*6.77E2+cos(q1+3.141592653589793/6.0)*sin(q1)*sin(q2)*6.77E2+sin(q1)*sin(q2)*sin(q1+3.141592653589793/6.0)*2.25E2-sqrt(3.0)*cos(q1)*sin(q1+3.141592653589793/6.0)*4.25E2+sqrt(3.0)*cos(q1+3.141592653589793/6.0)*sin(q1)*4.25E2-cos(q1)*cos(q2)*cos(q1+3.141592653589793/6.0)*6.77E2-cos(q1)*cos(q2)*sin(q1+3.141592653589793/6.0)*2.25E2+cos(q1)*cos(q1+3.141592653589793/6.0)*sin(q2)*2.25E2+cos(q2)*cos(q1+3.141592653589793/6.0)*sin(q1)*2.25E2+sqrt(3.0)*cos(q1)*cos(q2)*sin(q1+3.141592653589793/6.0)*4.25E2-sqrt(3.0)*cos(q1)*cos(q1+3.141592653589793/6.0)*sin(q2)*4.25E2-sqrt(3.0)*cos(q2)*cos(q1+3.141592653589793/6.0)*sin(q1)*4.25E2-sqrt(3.0)*sin(q1)*sin(q2)*sin(q1+3.141592653589793/6.0)*4.25E2);
    Inverse_jacobian[1][0] = ((sin(q1)*-4.25E2+cos(q1)*cos(q2)*2.25E2+cos(q1)*sin(q2)*6.77E2+cos(q2)*sin(q1)*6.77E2-sin(q1)*sin(q2)*2.25E2+sqrt(3.0)*cos(q1)*4.25E2-sqrt(3.0)*cos(q1)*cos(q2)*4.25E2+sqrt(3.0)*sin(q1)*sin(q2)*4.25E2)*(-4.0E1/1.7E1))/(cos(q1)*cos(q1+3.141592653589793/6.0)*4.25E2+sin(q1)*sin(q1+3.141592653589793/6.0)*4.25E2-cos(q1)*sin(q2)*sin(q1+3.141592653589793/6.0)*6.77E2-cos(q2)*sin(q1)*sin(q1+3.141592653589793/6.0)*6.77E2+cos(q1+3.141592653589793/6.0)*sin(q1)*sin(q2)*6.77E2+sin(q1)*sin(q2)*sin(q1+3.141592653589793/6.0)*2.25E2-sqrt(3.0)*cos(q1)*sin(q1+3.141592653589793/6.0)*4.25E2+sqrt(3.0)*cos(q1+3.141592653589793/6.0)*sin(q1)*4.25E2-cos(q1)*cos(q2)*cos(q1+3.141592653589793/6.0)*6.77E2-cos(q1)*cos(q2)*sin(q1+3.141592653589793/6.0)*2.25E2+cos(q1)*cos(q1+3.141592653589793/6.0)*sin(q2)*2.25E2+cos(q2)*cos(q1+3.141592653589793/6.0)*sin(q1)*2.25E2+sqrt(3.0)*cos(q1)*cos(q2)*sin(q1+3.141592653589793/6.0)*4.25E2-sqrt(3.0)*cos(q1)*cos(q1+3.141592653589793/6.0)*sin(q2)*4.25E2-sqrt(3.0)*cos(q2)*cos(q1+3.141592653589793/6.0)*sin(q1)*4.25E2-sqrt(3.0)*sin(q1)*sin(q2)*sin(q1+3.141592653589793/6.0)*4.25E2);
    Inverse_jacobian[1][1] = ((cos(q1)*4.25E2-cos(q1)*cos(q2)*6.77E2+cos(q1)*sin(q2)*2.25E2+cos(q2)*sin(q1)*2.25E2+sin(q1)*sin(q2)*6.77E2+sqrt(3.0)*sin(q1)*4.25E2-sqrt(3.0)*cos(q1)*sin(q2)*4.25E2-sqrt(3.0)*cos(q2)*sin(q1)*4.25E2)*(-4.0E1/1.7E1))/(cos(q1)*cos(q1+3.141592653589793/6.0)*4.25E2+sin(q1)*sin(q1+3.141592653589793/6.0)*4.25E2-cos(q1)*sin(q2)*sin(q1+3.141592653589793/6.0)*6.77E2-cos(q2)*sin(q1)*sin(q1+3.141592653589793/6.0)*6.77E2+cos(q1+3.141592653589793/6.0)*sin(q1)*sin(q2)*6.77E2+sin(q1)*sin(q2)*sin(q1+3.141592653589793/6.0)*2.25E2-sqrt(3.0)*cos(q1)*sin(q1+3.141592653589793/6.0)*4.25E2+sqrt(3.0)*cos(q1+3.141592653589793/6.0)*sin(q1)*4.25E2-cos(q1)*cos(q2)*cos(q1+3.141592653589793/6.0)*6.77E2-cos(q1)*cos(q2)*sin(q1+3.141592653589793/6.0)*2.25E2+cos(q1)*cos(q1+3.141592653589793/6.0)*sin(q2)*2.25E2+cos(q2)*cos(q1+3.141592653589793/6.0)*sin(q1)*2.25E2+sqrt(3.0)*cos(q1)*cos(q2)*sin(q1+3.141592653589793/6.0)*4.25E2-sqrt(3.0)*cos(q1)*cos(q1+3.141592653589793/6.0)*sin(q2)*4.25E2-sqrt(3.0)*cos(q2)*cos(q1+3.141592653589793/6.0)*sin(q1)*4.25E2-sqrt(3.0)*sin(q1)*sin(q2)*sin(q1+3.141592653589793/6.0)*4.25E2);

    desired_velocity[0][0] = vx;
    desired_velocity[1][0] = vy;

    Joint_velocity[0][0] = Inverse_jacobian[0][0]*desired_velocity[0][0] + Inverse_jacobian[0][1]*desired_velocity[1][0];
    Joint_velocity[1][0] = Inverse_jacobian[1][0]*desired_velocity[0][0] + Inverse_jacobian[1][1]*desired_velocity[1][0];

    // Integration
    Joint_1_position = Joint_1_position_prev + Joint_velocity[0][0]*delta_t;
    Joint_2_position = Joint_2_position_prev + Joint_velocity[1][0]*delta_t;

    Joint_1_position_prev = Joint_1_position;
    Joint_2_position_prev = Joint_2_position;

    Motor_1_position = Joint_1_position;
    Motor_2_position = Joint_1_position + Joint_2_position;
}

void PI_controller()                                                                    // De PI-controller zorgt ervoor dat het proportionele en het integrale aandeel worden bepaald.
{                                                                                       // Na sommatie van beide aandelen, wordt theta_t als output verkregen. Deze wordt later als input
// Proportional part:                                                                   // voor de motorsnelheden gebruikt.
    theta_k_1= Kp * error_M1;
    theta_k_2= Kp * error_M2;

// Integral part
    error_integral_1 = error_integral_1+ error_M1*delta_t;
    error_integral_2 = error_integral_2+ error_M2*delta_t;
    u_i_1= Ki * error_integral_1;
    u_i_2= Ki * error_integral_2;

// Sum all parts and return it
    theta_t_1= theta_k_1 + u_i_1;
    theta_t_2= theta_k_2 + u_i_2;
}

void Define_motor_dir()                                                                 // Deze functie bepaalt de output richtingen die aan de motoren worden gegeven tijdens de operation mode.
{                                                                                       // Wanneer de input (wordt ook als snelheid gegeven aan de motoren) positief is, wordt de draairichting van
    if (theta_t_1 >= 0 && theta_t_2 >= 0) {                                             // de motoren op nul gezet en vice versa. Dit betekent dat de motoren linksom draaien, gezien vanaf de assen.
        motor1_dir.write(0);
        motor2_dir.write(0);
    }
    if (theta_t_1 < 0 && theta_t_2 >= 0) {
        motor1_dir.write(1);
        motor1_dir.write(0);
    }
    if (theta_t_1 >= 0 && theta_t_2 < 0) {
        motor1_dir.write(0);
        motor2_dir.write(1);
    } else {
        motor1_dir.write(1);
        motor2_dir.write(1);
    }
}

void Controlling_system()                                                               // Hoofdfunctie die wordt toegepast tijdens de operation mode. Een gewenste snelheid in x- en y-richting wordt
{                                                                                       // hiervoor opgesteld en is vervolgens de input voor de inverse kinematica functie. Door middel van een gewenste
    Inverse_Kinematics();                                                               // motorhoek en de actuele motorhoek wordt een error bepaald. Deze errors worden als input gebruikt voor de
                                                                                        // PI-controller, wiens absolute output wordt gebruikt om de motorsnelheid aan te sturen. Tevens wordt een functie
    error_M1 = Motor_1_position + theta_h_1_rad;                                        // aangeroepen die ervoor zorgt dat de motoren in de juiste richting gaan draaien.
    error_M2 = Motor_2_position + theta_h_2_rad;

    PI_controller();

    abs_theta_t_1 = abs(theta_t_1);
    abs_theta_t_2 = abs(theta_t_2);

    motor1.write(abs_theta_t_1);
    motor2.write(abs_theta_t_2);
    Define_motor_dir();
}

void servo_horizontal()                                                                 // Functie die ervoor zorgt dat de spatel horizontaal wordt gehouden door de servo motor in de operation mode.
{
    q1_ser= q1*604.7887837;
    q2_ser= q2*604.7887837;
    theta_sout=theta_f+q1_ser+q2_ser;

    if (theta_sout>=2400) {
        theta_sout=2400;
    }
    if (theta_sout<=500) {
        theta_sout=500;

        mijnServo.SetPosition(theta_sout);
        pc.printf("De servo staat op %f graden\n\r", theta_sout);
    }
}

void servo_flip()                                                                       // Functie die ervoor zorgt dat de spatel flipt, wanneer er een button wordt ingedrukt. Wordt eveneens gerealiseerd
{                                                                                       // door de servo motor.
    theta_sout=theta_f+q1_ser+q2_ser+400;

    if (theta_sout>=2400) {
        theta_sout=2400;
    }
    if (theta_sout<=500) {
        theta_sout=500;
    } else {
        theta_sout;
    }

    mijnServo.SetPosition(theta_sout);
    pc.printf("De servo staat op %f graden, in de klapstand\n\r", theta_sout);
}

// Aanmaken van een bool om te testen of de berekeningen in the ProcessStatemachine
// meer tijd kosten dan wordt gegeven door de ticker. Dit zou mogelijk het encoder
// probleem kunnen verklaren. Indien er te weinig tijd is, zou de loop zichzelf in moeten
// halen. Start met een bool die true is, stel deze gelijk aan false in het begin van de loop
// en verander deze weer in true wanneer de hele loop voltooid is. In het geval dat de loop zichzelf
// inhaalt, blijft de bool false en wordt een string (There is a timing problem) geprint.
// RESULTAAT: de string wordt niet geprint, er zouden geen timing issues moeten zijn.
// Het script spreekt zichzelf dus tegen, experts komen ook niet uit dit probleem.

volatile bool loop_done = true;

void ProcessStateMachine(void)                                                          // Programmeren van de finite state machine.
{
    if (!loop_done) {
        pc.printf("There is a timing problem\r\n");

        return;
    }

    loop_done = false;

    // Berekenen van de motorhoeken (in radialen)
    counts1 = Encoder1.getPulses();                                                     // Verkrijgen van het aantal counts uit de encoders
    counts2 = Encoder2.getPulses();
    theta_h_1_rad = conversion_factor*(counts1-offset1);                                // Van het gemeten aantal counts wordt de offset afgetrokken, zodat het aantal counts in de referentie positie gelijk is aan 0.
    theta_h_2_rad = conversion_factor*(counts2-offset2);                                // Vermenigvuldiging met de conversion factor zorgt ervoor dat het aantal counts wordt omgezet naar de huidige motorhoeken.

    // Calculating joint angles based on motor angles (current encoder values)
    q1 = theta_h_1_rad;                                                                 // Relatieve hoek joint 1 in radialen.
    q2 = theta_h_2_rad - theta_h_1_rad;                                                 // Relatieve hoek joint 2 in radialen.

    // Eerste deel van de filters (High-pass + Notch) over het ruwe EMG signaal
    // doen. Het ruwe signaal wordt gelezen binnen een ticker en wordt daardoor 'gesampled'
    filtered_EMG_biceps_right_1=bqbr1.step(EMG_biceps_right_raw.read());
    filtered_EMG_biceps_left_1=bqcbl.step(EMG_biceps_left_raw.read());
    filtered_EMG_calf_1=bqck.step(EMG_calf_raw.read());

    // Vervolgens wordt de absolute waarde hiervan genomen
    filtered_EMG_biceps_right_abs=abs(filtered_EMG_biceps_right_1);
    filtered_EMG_biceps_left_abs=abs(filtered_EMG_biceps_left_1);
    filtered_EMG_calf_abs=abs(filtered_EMG_calf_1);

    // Tenslotte wordt het tweede deel van de filters (twee low-pass, voor 4e orde filter)
    // over het signaal gedaan
    filtered_EMG_biceps_right=bqcbr2.step(filtered_EMG_biceps_right_abs);
    filtered_EMG_biceps_left=bqcbl2.step(filtered_EMG_biceps_left_abs);
    filtered_EMG_calf=bqck2.step(filtered_EMG_calf_abs);

    // De gefilterde EMG-signalen kunnen tevens visueel worden weergegeven in de HIDScope
    scope.set(0, normalized_EMG_biceps_right);
    scope.set(1, normalized_EMG_biceps_left);
    scope.set(2, normalized_EMG_calf);
    scope.send();

    // Tijdens de kalibratie moet vervolgens een maximale spierspanning worden bepaald, die
    // later kan worden gebruikt voor een normalisatie. De spieren worden hiertoe gedurende
    // 5 seconden maximaal aangespannen. De EMG waarden worden bij elkaar opgeteld,
    // waarna het gemiddelde wordt bepaald.
    if (calib) {
        EMG_calibration();
    }

    // Genormaliseerde EMG's berekenen
    normalized_EMG_biceps_right=filtered_EMG_biceps_right/mean_EMG_biceps_right;
    normalized_EMG_biceps_left=filtered_EMG_biceps_left/mean_EMG_biceps_left;
    normalized_EMG_calf=filtered_EMG_calf/mean_EMG_calf;

    // Finite state machine
    switch (currentState) {
        case Motors_off:

            if (stateChanged) {
                motors_off();                                                           // Functie waarbij motoren uitgaan.
                stateChanged = false;
                pc.printf("Motors off state\r\n");
            }
            if  (Emergency_button_pressed.read() == false) {                            // Normaal krijgt de button een waarde 1 bij indrukken, nu nul -> false.
                emergency();                                                            // Bij het indrukken van de emergency button, wordt overgegaan op de emergency functie.
            }
            if  (Power_button_pressed.read() == false) {                                // Normaal krijgt de button een waarde 1 bij indrukken, nu nul -> false.
                currentState = Calib_motor;                                             // Er wordt doorgegaan naar de volgende state, wanneer de power button wordt ingedrukt.
                stateChanged = true;
                pc.printf("Moving to Calib_motor state\r\n");
            }
            break;

        case Calib_motor:                                                               // In deze state wordt de robot arm met de hand in de juiste referentie (home) positie geplaatst.
                                                                                        // Wanneer dit is gebeurd, wordt de motor kalibratie knop ingedrukt. De offset wordt dan ingesteld als
            if (stateChanged) {                                                         // het huidige aantal counts. Door deze offset in de komende metingen af te trekken van het aantal counts
                pc.printf("Zet motoren handmatig in home positie\r\n");                 // op dat moment, wordt ervoor gezorgd dat de motorhoeken in de referentiepositie gelijk zijn aan nul.
                stateChanged = false;
            }

            if  (Emergency_button_pressed.read() == false) {
                emergency();
            }
            if (Motor_calib_button_pressed.read() == false) {
                offset1 = counts1;
                offset2 = counts2;
                currentState = Calib_EMG;
                stateChanged = true;
                pc.printf("Moving to Calib_EMG state\r\n");
            }
            break;

        case Calib_EMG:                                                                 // In deze state wordt een kalibratie uitgevoerd van de EMG-signalen. Hiervoor wordt een bool (calib)
                                                                                        // samen met een teller (i_calib) in werking gezet, die ervoor zorgt dat de eerder beschreven EMG-kalibratie
            if (stateChanged) {                                                         // wordt doorlopen. De spieren worden tijdens de kalibratie gedurende 5 seconden maximaal aangespannen.
                i_calib = 0;                                                            // Na deze 5 seconden wordt doorgegaan naar de volgende state. De output van deze kalibratie is een gemiddelde
                calib = true;                                                           // waarde van het EMG-signaal tijdens het maximaal aanspannen.
                pc.printf("Span spieren aan\r\n");
                stateChanged = false;
            }

            if  (Emergency_button_pressed.read() == false) {
                emergency();
            }

            if (i_calib > 2500) {
                calib = false;
                currentState = Homing;
                stateChanged = true;
                pc.printf("Moving to Homing state\r\n");
            }
            break;

        case Homing:                                                                    // In deze state wordt de homing uitgevoerd. De motoren draaien hier op een langzame snelheid, todat de gewenste referentiepositie
                                                                                        // wordt bereikt. In dit geval worden de motorsnelheden op 0 gezet. Indien beide motoren de juiste posities hebben bereikt, wordt
            if (stateChanged) {                                                         // overgegaan naar de volgende state. De eerste keer dat de ProcessStateMachine in de homing state belandt, is dit al het geval,
                stateChanged = false;                                                   // waardoor gelijk doorgegaan kan worden naar de operation mode.
            }
            if  (Emergency_button_pressed.read() == false) {
                emergency();
            }

            Homing_function();

            if (theta_h_1_rad == 0.0 && theta_h_2_rad == 0.0) {
                currentState = Operation_mode;
                stateChanged = true;
                pc.printf("Moving to operation mode \r\n");
            }
            break;

        case Operation_mode:

            if (stateChanged) {                                                         // Aan het begin van de operation mode wordt aan allerlei variabelen een beginwaarde van 0 toegekend. Dit wordt één keer gedaan
                motors_off();                                                           // (stateChanged), waarna deze waarden verderop in deze state worden overschreven. In het geval dat er tussendoor teruggegaan is
                Joint_1_position = 0;                                                   // naar een andere state, wordt op deze manier elke keer weer begonnen met de juiste beginwaarden.
                Joint_2_position = 0;
                Joint_1_position_prev = Joint_1_position;
                Joint_2_position_prev = Joint_2_position;
                Joint_velocity[0][0] = 0;
                Joint_velocity[1][0] = 0;
                Motor_1_position = 0;
                Motor_2_position = 0;
                theta_k_1 = 0.0;
                theta_k_2 = 0.0;
                error_integral_1 = 0.0;
                error_integral_2 = 0.0;
                stateChanged = false;
            }

            servo_horizontal();                                                         // Zorgt ervoor dat de spatel horizontaal wordt gehouden.

            if  (servo_button_pressed.read()== false) {
                servo_flip();                                                           // Zorgt ervoor dat de spatel flipt, wanneer de servo knop wordt ingedrukt.
            }

            if (Power_button_pressed.read() == false) {                                 // Wanneer de power button wordt ingedrukt, worden de motoren uitgezet.
                motors_off();
                currentState = Motors_off;
                stateChanged = true;
                pc.printf("Terug naar de state Motors_off\r\n");
            }
            if (Emergency_button_pressed.read() == false) {                             // Wanneer de emergency button wordt ingedrukt, wordt de emergency functie aangeroepen.
                emergency();
            }
            if (Motor_calib_button_pressed.read() == false) {                           // Wanneer tijdens de operation mode teruggegaan moet worden naar de referentie positie, kan de button voor de motor kalibratie
                motors_off();                                                           // worden ingedrukt. In dit geval is het niet nodig om de motoren opnieuw te kalibreren, dus kan direct teruggegaan worden naar
                currentState = Homing;                                                  // de state Homing. De motoren bewegen dan totdat de referentie positie weer wordt bereikt.
                stateChanged = true;
                pc.printf("Terug naar de state Homing\r\n");
            }
            if (normalized_EMG_biceps_right >= 0.3) {                                   // Vanaf hier begint de daadwerkelijke besturing van de robot arm door middel van EMG signalen. Indien de rechter biceps wordt
                                                                                        // aangespannen, zal het genormaliseerde EMG signaal boven 0.3 uitkomen. Wanneer tegelijkertijd de kuit wordt aangespannen is er
                if (normalized_EMG_calf < 0.3) {                                        // een gewenste negatieve snelheid in de y-richting. Zo niet, dan is er een gewenste positieve snelheid in de y-richting.
                    vx = 0.0;                                                           // Deze gewenste snelheid wordt doorgevoerd in de functie Controlling_system, die onder andere inverse kinematics en een
                    vy = 0.02;                                                          // PI-controller bevat. Uiteindelijk wordt aan beide motoren een snelheid en richting toegekend, zodat de robot met de
                }                                                                       // gewenste snelheid in een rechte lijn verticaal kan bewegen.
                if (normalized_EMG_calf >= 0.3) {
                    vx = 0.0;
                    vy = -0.02;
                }

                Controlling_system();

            } else if (normalized_EMG_biceps_left >= 0.3) {                             // Indien de rechter biceps niet wordt aangespannen en de genormaliseerde EMG-waarde van de linker biceps groter is dan 0.3, geldt
                if (normalized_EMG_calf < 0.3) {                                        // hetzelfde als hierboven beschreven, maar dan voor de x-richting. Dit resulteert in een horizontale beweging in een rechte lijn.
                    vx = 0.02;
                    vy = 0.0;
                }
                if (normalized_EMG_calf >= 0.3) {
                    vx = -0.02;
                    vy = 0.0;
                }

                Controlling_system();

            } else {                                                                    // Indien beide biceps niet worden aangespannen, moet de robot arm niet bewegen. De snelheid in x- en y-richting moet dan gelijk zijn
                vx = 0.0;                                                               // aan nul. Toch zal wel gecompenseerd moeten worden voor de zwaartekracht om de arm in dezelfde positie te houden. Deze snelheden
                vy = 0.0;                                                               // worden daarom alsnog doorgevoerd in de functie Controlling_systeem. Dit resulteert in motorsnelheden en richtingen die ervoor zorgen
                                                                                        // dat de arm gebalanceerd wordt op de voorgaande positie.
                Controlling_system();

            }
            break;

        default:

            motors_off();                                                               // Hier wordt dezelfde functie als eerder toegepast om de motoren uit te schakelen -> safety!
            pc.printf("Unknown or uninplemented state reached!\r\n");

    }
    loop_done = true;                                                                   // Dit is nog een element voor het testen of de ProcessStateMachine zichzelf inhaalt. Indien de volledige
}                                                                                       // loop wel doorlopen is, verandert de bool hier in true. Hierdoor wordt de string aan het begin van de state machine
                                                                                        // NIET geprint. Zie een eerdere comment (vlak voor de definitie van de state machine) voor verdere toelichting.
int main(void)
{
    pc.baud(115200);                                                                    // De baud rate wordt op 115200 bits/sec gezet.

    motor1.period_us(56);                                                               // Idealiter ligt deze frequentie tussen de 15 en 18 kHz. We hebben gekozen voor de kleinst mogelijke motor periode,
    motor2.period_us(56);                                                               // die gelijk is aan 1/18000 = ongeveer 56 us.

    pc.printf("Opstarten\r\n");

    // Chain voor rechter biceps                                                        // De filterketens zijn apart voor elke spier gedefinieerd. Er wordt eerst een keten aangemaakt die bestaat uit een
    bqcbr.add(&bqbr1).add(&bqbr2);                                                      // high-pass filter, gevolgd door een notch filter. Na het nemen van de absolute waarde van het bewerkte EMG-signaal
    bqcbr2.add(&bqbr3).add(&bqbr4);                                                     // moet het tweede gedeelte van de filterketen worden toegepast. Hiervoor moeten twee low-pass filters aan elkaar
    // Chain voor linker biceps                                                         // geketend worden.
    bqcbl.add(&bqbl1).add(&bqbl2);
    bqcbl2.add(&bqbl3).add(&bqbl4);
    // Chain voor kuit
    bqck.add(&bqk1).add(&bqk2);
    bqck2.add(&bqk3).add(&bqk4);

    loop_ticker.attach(&ProcessStateMachine, 0.002f);                                   // We laten de ProcessStateMachine runnen met een frequentie van 500 Hz. Dit is gedaan om te kunnen matchen met de gewenste
                                                                                        // sampling frequentie voor EMG-signalen.
    while(true) {
        /* do nothing */
    }
}