not working yet

Dependencies:   Motor_with_encoder MODSERIAL mbed HIDScope

main.cpp

Committer:
EvB
Date:
2017-10-30
Revision:
13:acd120520e80
Parent:
12:5be2001abe62
Child:
14:7a95e57b5364

File content as of revision 13:acd120520e80:

#include "mbed.h"
#include "MODSERIAL.h"
#include "encoder.h"
#include "math.h"
#include "HIDScope.h"
#include "BiQuad.h"
#include "EMG_filter.h"
#include "Matrix.h"
#include "MatrixMath.h"

const double pi = 3.14159265359;            // Defining the constant pi

MODSERIAL pc(USBTX,USBRX);                                  // Enabling communication with the pc.

enum r_states {R_HORIZONTAL, R_VERTICAL, R_BASE};           // Defining states of the robot
r_states state;

// ---- EMG parameters ----
//HIDScope scope (2);
EMG_filter emg1(A0);
EMG_filter emg2(A1);
float EMG_threshold = 0.2;                                  // Threshold for the EMG signal
// ------------------------

// ---- Motor input and outputs ----
PwmOut speed1(D5);
PwmOut speed2(D6);
PwmOut speedservo1(D11);
//PwmOut speedservo2(D8);
DigitalOut dir1(D4);
DigitalOut dir2(D7);
DigitalIn press(PTA4);
DigitalOut ledred(LED_RED);
DigitalOut ledgreen(LED_GREEN);
DigitalOut ledblue(LED_BLUE);
DigitalOut ledstateswitch(D3);
DigitalOut ledstatedef(D2);
AnalogIn pot(A2);
AnalogIn pot2(A3);
Encoder motor1(PTD0,PTC4);
Encoder motor2(D12,D13);
// ----------------------------------

// --- Define Ticker and Timeout ---
Ticker mainticker;          //Ticker that calls every function. Functions are called by means of a variable named go, e.g. go_EMG
Timeout calibrationgo;      // Timeout that will determine the duration of the calibration.
// ---------------------------------


float PwmPeriod = 0.0001f;

volatile double Pos1;                        // Encoder readout of motor 1 [counts]
volatile double Pos2;                        // Encoder readout of motor 2 [counts]
volatile int count = 0;                         // Loop counts 

double Kp = 250;// you can set these constants however you like depending on trial & error
double Ki = 100;
double Kd = 0;

float last_error = 0;
float error1 = 0;
float changeError = 0;
float totalError = 0;
float pidTerm = 0;
float pidTerm_scaled = 0;

float last_error2 = 0;
float error2 = 0;
float changeError2 = 0;
float totalError2 = 0;
float pidTerm2 = 0;
float pidTerm_scaled2 = 0;// if the total gain we get is not in the PWM range we scale it down so that it's not bigger than |255|

volatile double potvalue = 0.0;
volatile double potvalue2 = 0.0;
volatile double position = 0.0;
volatile double position2 = 0.0;

// --- Kinematics parameter ---
float L1 = 0.38;                // length of link 1 [m]
float L5 = 0.25;                // length of link 5 [m]
float d = 0.03;                 // diameter of the pulley [m]

volatile float xe;              // x-coordinate of end-effector
volatile float ye;              // y-coordinate of end-effector
volatile float ze;              // z-coordinate of end-effector

volatile float Q1;              // Position of joint 1 [m]
volatile float Q2;              // Position of joint 2 [rad]
volatile float Q3;              // Position of joint 3 [rad]

volatile float V1;              // Desired velocity of joint 1 [m/s];  --> or normalize it?
volatile float W2;              // Desired velocity of joint 2 [rad/s];
volatile float W3;              // Desired velocity of joint 3 [rad/s];

volatile float Vx_des;          // Desired velocity end-effector in x-direction [m/s]
volatile float Vy_des;          // Desired velocity end-effector in y-direction [m/s]
volatile float Vz_des;          // Desired velocity end-effector in z-direction [m/s]

const float T = 0.002;          // Time interval of the readout of the EMG --> Check this with the ticker, etc!!!


// --- Booleans for the maintickerfunction ---
//bool readoutsetpoint = true;
bool go_EMG;                    // Boolean to put on/off the EMG readout
bool go_calibration;            // Boolean to put on/off the calibration of the EMG
bool go_switch;                  // Boolean to go to different state
bool go_PID;                    // Boolean to calculate PID and motor aansturing --> probably replaced by go_move
bool go_move;                   //Boolean to move the robot arm and base
bool go_kinematics;               // Boolean which will determine whether a new position will be calculated or not --> replace by go_move?
// -------------------------------------------

// --- calibrate EMG signal ----

void calibrationGO()                   // Function for go or no go of calibration
{
    go_calibration = false;

}

/*
Calibration of the robot works as follows:
EMG signal is read out for 5 seconds, 1000 times a second. This signal is filtered and the maximum value is compared to the current value of the MVC.
The value of the MVC is overwritten when the value of the filtered signal is bigger than the current value.
During the calibration the user should exert maximum force.
*/
void calibrationEMG()                   // Function for calibrating the EMG signal
{
    if (go_calibration) {

        emg1.calibration();                 // Using the calibration function of the EMG_filter class
        emg2.calibration();
    }
}

// ------------------------------
//Function that reads out the raw EMG and filters the signal
void processEMG ()
{


    emg1.filter();                      //filter the incoming emg signal
    emg2.filter();
    //emg3.filter();

    /*  scope.set(0, emg1.normalized);      //views emg1.normalized on port 0 of HIDScope
      scope.set(1, emg1.emg0);
      scope.send();*/

}

// --- Kinematics functions ---

void Brockett(float q1, float q2, float q3)                    // Determine position of end-effector using Brockett
{

    Matrix E100b(4,4);           // Complete matrix form of 1st joint
    E100b = MatrixMath::Eye(4);
    E100b(3,4) = q1;


    Matrix E210b(4,4);              // Complete matrix form of 2nd joint
    E210b = MatrixMath::RotZ(q2);


    Matrix E320b(4,4);                  // Complete matrix form of 3rd joint
    E320b = MatrixMath::RotZ(q3-q2);
    E320b(1,4) = L1 - L1*cos(q3-q2);
    E320b(2,4) = -L1*sin(q3-q2);


    Matrix HE0ref(4,4);                 // H-matrix of the robot in reference position
    HE0ref = MatrixMath::Eye(4);
    HE0ref(1,4) = L1;
    HE0ref(2,4) = -L5;


    Matrix HE0(4,4);                    // H-matrix to find the position of the end-effector, expressed in frame 0.
    HE0 = E100b * E210b * E320b * HE0ref;       // This H-matrix is not really correct. Check with matlab!!!


    // Determine new position of end effector
    xe = HE0(1,4);                      // New x-coordinate of the end-effector
    ye = HE0(2,4);                      // New y-coordinate of the end-effector
    ze = HE0(3,4);                      // New z-coordinate of the end-effector


}

void Jacobian(float vx_des, float vy_des, float vz_des)                                 // Function to determine the velocities with desired velocity as input
{
    //kinematics_go = false;        // putting boolean to false

// Finding joint positions
    Q1 = Pos1;                            // Position of joint 1 [m]
    Q2 = Pos2;                                // Position of joint 2 [rad]
    Q3 = 0;                                                             // Position of joint 3 [rad]      --> need this from Servo!

    //pc.printf("\r\nq1 = %.3f, q2 = %.3f, q3 = %.3f\r\n", q1, q2, q3);

    Brockett(Q1,Q2,Q3);                          // Start with conducting Brockett to obtain the end-effector position

    Matrix T_des(3,1);                  // Twist of desired linear velocities
    T_des(1,1) = vx_des;
    T_des(2,1) = vy_des;
    T_des(3,1) = vz_des;


    Matrix T100j(1,4);                  // Unit twist of the first frame in current configuration
    T100j(1,4) = 1;

    Matrix T210j(1,4);                  // Unit twist of the second frame in current configuration
    T210j(1,1) = 1;

    Matrix T320j(1,4);                  // Unit twist of the third frame in current configuration
    T320j(1,1) = 1;
    T320j(1,2) = -L1*sin(Q2);
    T320j(1,3) = -L1 * cos(Q2);

    Matrix J(4,3);                      // Jacobian matrix

    for (int i = 1 ; i < 5 ; i++) {
        J(i,1) = T100j(1,i);
    }

    for (int i = 1 ; i < 5 ; i++) {
        J(i,2) = T210j(1,i);
    }

    for (int i = 1 ; i < 5 ; i++) {
        J(i,3) = T320j(1,i);
    }


    Matrix H0f(4,4);                        // H-matrix to transform Jacobian from frame 0 to frame f, which is attached to the end-effector, but not rotating with it.
    H0f = MatrixMath::Eye(4);
    H0f(1,4) = -xe;
    H0f(2,4) = -ye;
    H0f(3,4) = -ze;

    Matrix AdH0f(4,4);                      // Adjoint of H0f, necessary for twist transformations
    AdH0f = MatrixMath::Eye(4);
    AdH0f(2,1) = H0f(2,4);
    AdH0f(3,1) = -H0f(1,4);

    Matrix Jprime(4,3);                     // Transformed Jacobian
    Jprime =  AdH0f*J;

    Matrix Jprimeprime(3,3);                // Truncated Jacobian
    for (int i = 1 ; i <4 ; i++) {
        for (int j = 1 ; j < 4 ; j++) {
            Jprimeprime(i,j) = Jprime(i+1,j);
        }
    }

    Matrix JprimeprimeT(3,3);                   // The transpose of the truncated Jacobian
    JprimeprimeT = MatrixMath::Transpose(Jprimeprime);

    Matrix PseudoInverse(3,3);                  // The Pseudo-inverse of the truncated Jacobian
    PseudoInverse = MatrixMath::Inv(JprimeprimeT*Jprimeprime)*JprimeprimeT;

    Matrix Q_dot(1,3);                          // Desired joint velocities
    Q_dot = PseudoInverse * T_des;

    V1 = Q_dot(1,1);
    W2 = Q_dot(2,1);
    W3 = Q_dot(3,1);

    // pc.printf("\r\nv1 = %.3f, w2 = %.3f, w3 = %.3f\r\n", V1,W2,W3);

// Eva - not sure if we need this... --> I think for the setpoint of the PID controller
    Q1 += V1*T;                     // Predicted value for q1 [m]
    Q2 += W2*T;                     // Predicted value for q2 [rad]
    Q3 += W3*T;                    // Predicted value for q3 [rad]

    // pc.printf("\r\n q1 = %.3f, q2 = %.3f, q3 = %.3f\r\n", q1,q2,q3);

}

//--- State switch function -----
void r_processStateSwitch()
{
    go_switch = false;
    switch(state) {
        case R_HORIZONTAL:
            state = R_VERTICAL;
            ledblue = 1;
            ledred = 1;
            ledgreen = 0;
            pc.printf("state is vertical");
            break;
        case R_VERTICAL:
            state = R_BASE;
            ledgreen = 1;
            ledblue = 1;
            ledred = 0;
            pc.printf("state is base");
            break;
        case R_BASE:
            state = R_HORIZONTAL;
            ledgreen = 1;
            ledred = 1;
            ledblue = 0;
            pc.printf("state is horizontal");
            break;
    }
    wait(1.0f);                             // waits 1 second to continue with the main function. I think ticker does run, but main is on hold.
    ledstatedef = 1;
    ledstateswitch = 0;
    go_move = true;
}

// --- Determine the setpoints of the joint velocities
void setpointreadout(float v_des)
{
    /*
    potvalue = pot.read();
    setpoint = emg1.normalized;

    potvalue2 = pot2.read();
    setpoint2 = emg2.normalized;
    */

    Pos1 = motor1.getPosition()/16800*d*pi;                 // Position of link 1 [m]
    Pos2 = motor2.getPosition()/16800*2*pi;                 // Position of link 2 [m]

    if (emg1.normalized >= EMG_threshold && emg2.normalized <= EMG_threshold) { // Positive direction
        v_des = emg1.normalized;
    }

    if (emg1.normalized >= EMG_threshold && emg2.normalized <= EMG_threshold) { // Negative direction
        v_des = -emg2.normalized;
    }

//pc.printf("\r\nv_des in setpoint function= %f\r\n", v_des);
}
void PIDcalculation(float q1, float q2, float q3) // inputs: potvalue, motor#, setpoint
{

    //pc.printf("pos: %d, speed %f reference position = %.2f\r ",motor1.getPosition(), motor1.getSpeed(), setpoint);
    //motorpid = PID(potvalue - position, M1_KP, M1_KI, M1_KD, M1_TS, m1_err_int, m1_prev_err, m1_f_v1, m1_f_v2, M1_F_A1, M1_F_A2, M1_F_B0, M1_F_B1, M1_F_B2);


    error1 = q1 - Pos1;                             // Position error of link 1 [m]
    error2 = q2 - Pos2;                                  // Position error of link 2 [m]

    changeError = (error1 - last_error)/0.001f; // derivative term
    totalError += error1*0.001f; //accumalate errors to find integral term
    pidTerm = (Kp * error1) + (Ki * totalError) + (Kd * changeError);//total gain
    pidTerm = pidTerm;
    if (pidTerm >= 1000) {
        pidTerm = 1000;
    } else if (pidTerm <= -1000) {
        pidTerm = -1000;
    } else {
        pidTerm = pidTerm;
    }
    //constraining to appropriate value
    if (pidTerm >= 0) {
        dir1 = 0;// Forward motion
    } else {
        dir1 = 1;//Reverse motion
    }
    pidTerm_scaled = abs(pidTerm)/1000.0f; //make sure it's a positive value
    if(pidTerm_scaled >= 0.55f) {
        pidTerm_scaled = 0.55f;
    }

    changeError2 = (error2 - last_error2)/0.001f; // derivative term
    totalError2 += error2*0.001f; //accumalate errors to find integral term
    pidTerm2 = (Kp * error2) + (Ki * totalError2) + (Kd * changeError2);//total gain
    pidTerm2 = pidTerm2;
    if (pidTerm2 >= 1000) {
        pidTerm2 = 1000;
    } else if (pidTerm2 <= -1000) {
        pidTerm2 = -1000;
    } else {
        pidTerm2 = pidTerm2;
    }
    //constraining to appropriate value
    if (pidTerm2 >= 0) {
        dir2 = 1;// Forward motion
    } else {
        dir2 = 0;//Reverse motion
    }
    pidTerm_scaled2 = abs(pidTerm2)/1000.0f; //make sure it's a positive value
    if(pidTerm_scaled2 >= 0.55f) {
        pidTerm_scaled2 = 0.55f;
    }

    last_error = error1;
    speed1 = pidTerm_scaled+0.45f;
    //speedservo1 = speed1;
    last_error2 = error2;
    speed2 = pidTerm_scaled2+0.45f;
    //speedservo2 = speed2;
}

// --- Motor movements ---
void r_movehorizontal()
{
    setpointreadout(Vx_des);                       // Start with obtaining the position of the robot and the desired velocities
    //pc.printf("\r\n Vx_des outside setpoint function = %f\r\n", Vx_des);
    Jacobian(Vx_des, 0, 0);                         // Give the obtained end-effector velocities as input to the Jacobian function. Output of this function is necessary joint velocities and predicted joint positions
    PIDcalculation(Q1, Q2, Q3);                      // Ensure smooth motion. Uses new position of joints calculated by Jacobian. This function then converts the position into appropriate PWM out values.

}

void r_movevertical()
{
    setpointreadout(Vy_des);                       // Start with obtaining the position of the robot and the desired velocities
    //pc.printf("\r\n Vy_des outside setpoint function = %f\r\n", Vy_des);
    Jacobian(0, Vy_des, 0);                         // Give the obtained end-effector velocities as input to the Jacobian function. Output of this function is necessary joint velocities and predicted joint positions
    PIDcalculation(Q1, Q2, Q3);                      // Ensure smooth motion. Uses new position of joints calculated by Jacobian. This function then converts the position into appropriate PWM out values.

}

void r_movebase()
{

    setpointreadout(Vz_des);                       // Start with obtaining the position of the robot and the desired velocities
    //pc.printf("\r\n Vz_des outside setpoint function = %f\r\n", Vz_des);
    Jacobian(0, 0, Vz_des);                         // Give the obtained end-effector velocities as input to the Jacobian function. Output of this function is necessary joint velocities and predicted joint positions
    PIDcalculation(Q1, Q2, Q3);                      // Ensure smooth motion. Uses new position of joints calculated by Jacobian. This function then converts the position into appropriate PWM out values.

}
//--------------------------------


void maintickerfunction()
{
    count++;
    if (go_switch) {
        r_processStateSwitch();
    }

    if(go_EMG && count%2 == 0) {
        processEMG();
    }
    
    if (go_move) {
            switch(state) {
                case R_HORIZONTAL:
                    r_movehorizontal();
                    break;
                case R_VERTICAL:
                    r_movevertical();
                    break;
                case R_BASE:
                    r_movebase();
                    break;
            }
        }

    /*if(emg1.normalized >=0.2 && emg2.normalized >= 0.2) // PIDcalculation should not happen.
    {
        go_PID = false;
    }
    else{go_PID = true;}*/


}

int main()
{
    pc.baud(9600);
    go_EMG = true;                      // Setting ticker variables
    go_calibration = true;              // Setting the timeout variable
    go_PID = true;
    go_switch = false;
    go_move = true;
    speed1.period(PwmPeriod);
    speed2.period(PwmPeriod);

    calibrationgo.attach(&calibrationGO, 5.0);     // Attach calibration timeout to calibration function
    mainticker.attach(&calibrationEMG, 0.001f);      // Attach ticker to the calibration function
    wait(5.0f);
    mainticker.attach(&maintickerfunction,0.001f);

    while(true) {

        ledstatedef = 1;
        if(emg1.normalized >= 0.7 && emg2.normalized >= 0.7) {
            ledstateswitch = 1;
            ledstatedef = 0;
            go_switch = true;
            go_move = false;

        }

        

        if(count == 100) {
            count = 0;
            pc.printf("emg1 = %f, emg2 = %f\r\n", emg1.normalized, emg2.normalized);
            //pc.printf("Angle = %f, pidTerm = %f ,PID_scaled = %f, Error = %f, setpoint = %f\r\n", angle, pidTerm ,pidTerm_scaled, error1, setpoint);
            //pc.printf("Angle 2 = %f, pidTerm 2 = %f ,PID_scaled 2 = %f, Error 2 = %f, setpoint 2 = %f\r\n", angle2, pidTerm2 ,pidTerm_scaled2, error2,setpoint2);
        }


        wait(0.001f);
    }

}