Robot control

Dependencies:   HIDScope MODSERIAL QEI biquadFilter mbed

main.cpp

Committer:
Luuk
Date:
2017-10-31
Revision:
7:75d2244d7745
Parent:
6:acc2669ab20c

File content as of revision 7:75d2244d7745:

#include "mbed.h"
#include "QEI.h"
#include "BiQuad.h"
#include "MODSERIAL.h"
//#include "HIDScope.h"

Serial pc (USBTX,USBRX);
//HIDScope scope(2);

PwmOut Motor1pwm(D6);            //motor 1 velocity control
DigitalOut Motor1Dir(D7);        //motor 1 direction
PwmOut Motor2Vel(D5);            //motor 2 velocity control
DigitalOut Motor2pwm(D4);        //motor 2 direction
DigitalOut led1(D2);
AnalogIn emg1(A0);
AnalogIn emg2(A1);
AnalogIn emg3(A2);
DigitalOut Button(D3);
DigitalOut ledb(LED_BLUE);
DigitalOut ledg(LED_GREEN);
DigitalOut ledr(LED_RED);

QEI encoder1(D10,D11,NC,16);      //define encoder pins
QEI encoder2(D8,D9,NC,16);

BiQuad bq1 ( 0.9150 , -1.8299 , 0.9150 , 1.0000 , -1.8227 , 0.8372); //Highpass
BiQuad bq2 ( 0.0001 ,   0.0002  ,  0.0001  ,  1.0000 ,  -1.9733  ,  0.9737); //Lowpass

Ticker motors_tick;
Ticker emg_tick;

float Angle1,Angle2;              //real angles of the motor
float XPos, YPos;                 //position of the end-effector
float XSet = 42, YSet = 0;                 //desired position of the end-effector
float L = 30.0;                  //length of the arms of the robot
const float Ts = 0.01;            //to control the tickers
const float KV = 1;               //velocity constant, to scale the desired velocity of the end-effector
const float P = 0.1;

const float pi = 3.14159265359,PulsesPerRev = 16.0,GearRatio = 131.25*3;   //to convert pulses to radians 
const float Angle1_0 = pi/2 ,Angle2_0 = 0;    //initial angle of the motors

//variables and constants for the PID
const float KP = 200, KI = 0.1, KD = 6, N = 100;
float M1_v1 = 0, M1_v2 = 0, M2_v1 = 0, M2_v2 = 0;

volatile double a = 0, b = 0, c = 0;      //variables to store the highest emg signal to normalize the data
double Y1, Y2;                // Y1 and Y2 are taken to conform Y to be used to change the set position X is also used to change the set position
volatile double Y, X;          

volatile bool ReturnHP = false;       //to keep calling HomePosition until getting to the setpoint
volatile bool calibration = true;     //to call FirstMovement the first loop, when the program is run
int counter = 0;                      //to count the time of inactivity

double emg1filt()
{
    double xtemp=(bq2.step(fabs(bq1.step(emg1))));
    if (xtemp>a)
        a=xtemp;
         
    X = xtemp/a;
    return X;
}

double emg2filt()
{
    double y1temp = (bq2.step(fabs(bq1.step(emg1))));
    if (y1temp>b)
        b=y1temp;
        
    Y1 = y1temp/b;
    return Y1;
}

int emg3filt()
{
    double y2temp = (bq2.step(fabs(bq1.step(emg1))));
    if (y2temp>c)
    {
        c=y2temp;
    }    
    Y2 = y2temp/c;
    if(Y2 < 0.3)
        Y2 = 1;
    else
        Y2 = -1;
    return Y2;
}

void callemgfilt()
{
    X = emg1filt();
    Y = emg2filt()/**emg3filt()*/;  // emg2filt gives the amplitude of Y and emg3filt the sign    
}

// PID controller (Tustin approximation)
float PID(float err, const float KP, const float KI, const float KD,const float Ts, const float N, float &v1, float &v2)
{
    const float a1 = -4/(N*Ts+2);
    const float a2 = -(N*Ts-2)/(N*Ts+2);
    const float b0 = (4*KP+4*KD*N+2*KI*Ts+2*KP*N*Ts+KI*N*pow(Ts,2))/(2*N*Ts+4);
    const float b1 = (KI*N*pow(Ts,2)-4*KP-4*KD*N)/(N*Ts+2);
    const float b2 = (4*KP+4*KD*N-2*KI*Ts-2*KP*N*Ts+2*KI*N*pow(Ts,2))/(2*N*Ts+4);
    
    float v = err-a1*v1-a2*v2;
    float u = abs(b0*v+b1*v1+b2*v2);
    v2 = v1; v1 = v;
    return u;
}

void positionxy(float &Angle1,float &Angle2,float &XPos,float &YPos)
{ 
    Angle1 = -(encoder1.getPulses()/(2*PulsesPerRev*GearRatio))*2*pi + Angle1_0;   //angles of the arms driven by the motors in radians
    Angle2 = -(encoder2.getPulses()/(2*PulsesPerRev*GearRatio))*2*pi + Angle2_0;
    
    XPos = L*cos(Angle1)+L*cos(Angle2);    // calculate the position of the end-effector
    YPos = L*sin(Angle1)+L*sin(Angle2); 
    //pc.printf("Angle1: %f Angle2: %f\r\n",Angle1,Angle2);
}

int Direction(float err)
{   //choose the direction of the motor, using the difference between the set angle and the actual angle.
    int a;
    if(err >= 0) 
        a = 1;
    else
        a = 0;
    return a;
}

void MoveMotors(float XSet, float YSet)    // move the motors using the inverse kinematic equations of the robot
{   
    float VX = KV*(XSet - XPos);     //set the desired velocity, proportional to the difference between the set point and the end-effector position.
    float VY = KV*(YSet-YPos);
    
    float W1 = (VX*(XPos-L*cos(Angle1))+VY*(YPos-L*sin(Angle1)))/(L*YPos*cos(Angle1)-L*XPos*sin(Angle1)); // calculate the needed angular velocity to achieve the desired velocity
    float W2 = (-VX*XPos-VY*YPos)/(L*YPos*cos(Angle1)-L*XPos*sin(Angle1));
    
    float Angle1Set = Angle1 + W1*Ts;   //calculate the set angle, angle at which the motor should be after 1 period with the calculated angular velocity
    float Angle2Set = Angle2 + W2*Ts;
    
    Motor1pwm = PID(Angle1Set-Angle1,KP,KI,KD,Ts,N,M1_v1,M1_v2)*P;;
    Motor1Dir = Direction(Angle1Set-Angle1);;
    Motor2Vel = PID(Angle2Set-Angle2,KP,KI,KD,Ts,N,M2_v1,M2_v2)*P;;
    Motor2pwm = !Direction(Angle2Set-Angle2);
    pc.printf("Angle1: %f Angle2: %f XPos: %f YPos: %f \r\n", Angle1, Angle2, XPos, YPos );
    
}

void FirstMovement()//function to get the end-effector to the workspace (with safety margin) to go from there to the home position
{
    ledr = 0;
    XSet = 30;
    YSet = 0;
    positionxy(Angle1,Angle2,XPos,YPos); 
    MoveMotors(XSet,YSet);
    
    if(pow(XSet-XPos,2)+pow(YSet-YPos,2) < pow(0.2,2))
    {//when the motor is close to the set angle(inside the safe workspace) go to the home position
        ledr = 1;
        calibration = false;
        ReturnHP = true;
    }
}

void GoToSet()
{   
    ledg = 0;
    ledb = 1;
    XSet = 42+X*10;  // keep increasing the set point, depending on the input
    //YSet += Y*0.1;
    //XSet = 46;
    YSet = 0;
    MoveMotors(XSet,YSet);  
      
}

void HomePosition()  //Go back to the initial position
{  
    ledb = 0;
    ledg = 1;
    const float  AllowedError = 0.5;  
    const float X0 = 25, X1 = 21.85, X2 = 31.94, slope = -17.34/6.6;  //values of special points in the workspace
    
    positionxy(Angle1,Angle2,XPos,YPos);
    if(sqrt(pow(XPos-XSet,2)+pow(YPos-YSet,2))<AllowedError)  //to call the function until the end effector is at the initial position
    {
        ReturnHP = false;
    }
    else
    {
        ReturnHP = true; 
    }
    //The following if-else statement is to choose a set point so that the end-effector does not go out of the work space while returning 
    if(XPos<X1)
    {
        XSet = X0;
        YSet = 0;
    }
    else if(XPos < X2 && (XPos < slope*YPos+X0 || XPos < slope*YPos+X0 ))
    {
        XSet = XPos;
        YSet = 0;
    }
    else if(XPos >= X2 && (XPos < slope*YPos+X0 || XPos < slope*YPos+X0))
    {
        XSet = X2;
        YSet = 0;
    }
    else
    {
        XSet = X0;
        YSet = 0;
    }
    MoveMotors(XSet,YSet);
}

void CallFuncMovement()   //decide which function has to be used
{   

    X = emg1.read(); 
    Y = emg2.read();                   
    positionxy(Angle1,Angle2,XPos,YPos);     //calculate the position of the end point
    if (Button == 1)
    {  //stop motors
        Motor1pwm = 0;
        Motor2pwm = 0;
    }
    else if(calibration)
    {
        FirstMovement();  
    }
    else if(ReturnHP)
    {   //when InitialPosition is called, keep calling until the end-effector gets there;
        HomePosition();
        counter = 0;
    }
    else if (sqrt(pow(XPos-300,2) + pow(YPos,2)) > 280 || sqrt(pow(XPos,2) + pow(YPos,2)) > 570 || sqrt(pow(XPos,2) + pow(YPos-300,2)) > 320 || sqrt(pow(XPos,2) + pow(YPos+300,2)) > 320 ) 
    { // limit so that the robot does not break. 
        HomePosition();
        counter = 0;    
    }
    else if (counter >= 200)
    {  //after 2 seconds of inactivity the robot goes back to the initial position
        HomePosition();
        counter = 0;
    }
    else if (X > 0 || Y != 0)
    {
        GoToSet(); 
        counter = 0; 
    }
    else 
    {
        counter++;
    } 
}

main()
{
    ledb = 1;
    ledg = 1;
    pc.baud(115200);
    //emg_tick.attach(&callemgfilt,0.001);
    motors_tick.attach(&CallFuncMovement,Ts);
    while (true)
    { /*keep the program running*/}
}