robot positie error test ding

Dependencies:   MODSERIAL mbed EMG_Input QEI biquadFilter

main.cpp

Committer:
kbruil
Date:
2016-10-31
Revision:
14:d0e5894aa352
Parent:
13:4f426186cd19

File content as of revision 14:d0e5894aa352:

#include "mbed.h"
#include "math.h"
#include "stdio.h"
#include "MODSERIAL.h"
#include "QEI.h"
#include "BiQuad.h"
#include "motor.h"
#include "EMG_input.h"
#include "HIDScope.h"
// Angle limits 215, 345 lower arm, 0, 145 upper arm
// Robot arm x,y limits (limit to table top)


#define M_PI 3.141592653589793238462643383279502884L // Pi

/*
** State variable
*/
enum r_states{R_INIT, R_HORIZONTAL, R_VERTICAL, R_GRIPPER};
r_states state;

/*
** Table surface and robot body limits
*/
const double R_XTABLE_LEFT = -30.0;     // Robot in the middle, table width=60
const double R_XTABLE_RIGHT = 30.0;
const double R_YTABLE_BOTTOM = -18.0;
const double R_YTABLE_TOP = 82.0;
const double R_ROBOT_LEFT = -8.0;     // Robot limited from moving onto itself (20x20 base)
const double R_ROBOT_RIGHT = 8.0;
const double R_ROBOT_BOTTOM = -18.0;
const double R_ROBOT_TOP = 2.0;

/*
** Robot arm angle limits
*/
const double ARM1_ALIMLO = (2*M_PI/360)*125;
const double ARM1_ALIMHI = -(2*M_PI/360)*125;
const double ARM2_ALIMLO = 0;
const double ARM2_ALIMHI = (2*M_PI/360)*145;

/*
** Ticker variables
*/
bool flag_switch;
bool flag_PID;
bool flag_output;
bool flag_EMG;

HIDScope scope(5);

Ticker mainticker;  // Main ticker

/*
** Motor variables
*/
motor motor1(D6, D7, D13, D12, 0.8, 0.2, 0.1, true);
motor motor2(D5, D4, D10, D11, 0.8, 0.2, 0.1, false);

/*
** Gripper variables
*/
PwmOut gripperpwm(D3);      // Gripper pwm port
bool gripperclosed=true;    // Gripper open/close flag

/*
** Setpoint variables
*/
float vx=0;     // Setpoint x speed
float vy=0;     // Setpoint y speed
float ax=0.01;  // Setpoint accelleration x direction
float ay=0.01;  // Setpoint accelleration y diraction
const double maxspeed=3.0f; // Setpoint maximum speed
bool moveleft;  // Flag set to true if setpoint moving left
bool movedown;  // Flag set to true if setpoint moving down

/*
** EMG variables
*/

EMG_input emg1(A0);
EMG_input emg2(A1);

// Struct r_link:
// Defines a robot link (arm or end effector).
struct r_link {
    float length;   // link length
    float x;        // link x position
    float y;        // link y position
    float theta;    // link angle
};
    
MODSERIAL pc(USBTX, USBRX);
DigitalIn switch1(SW3);
DigitalIn switch2(SW2);
DigitalIn switch3(D9);
bool switch3down=false;
DigitalOut ledr(LED_RED);
DigitalOut ledg(LED_GREEN);


r_link arm1;    // Robot upper arm
r_link arm2;    // Robot lower arm
r_link end;     // Robot end effector

/*
** =============================================================================
** robot_applySetpointLimits
** =============================================================================
** Function:
** Limits the cartesian coordinates to table, length of robot and prevents
** the robot arm crossing it's own base. 
** Input:
** float x, float y: position in cartesian coordinates
** Output:
** float x, float y: limited position in cartesian coordinates
** =============================================================================
*/
void robot_applySetpointLimits (float& x, float& y){
    float z;
    float angle;
    
    // Limit x,y to table top size
    if (x<R_XTABLE_LEFT) { x=R_XTABLE_LEFT; vx=0;}
    else if (x>R_XTABLE_RIGHT) { x=R_XTABLE_RIGHT; vx=0;}
    if (y<R_YTABLE_BOTTOM) { y=R_YTABLE_BOTTOM; vy=0;}
    else if (y>R_YTABLE_TOP) { y=R_YTABLE_TOP; vy=0; }
    
    // Limit x,y to maximum reach of robot arm
    z = sqrt(pow(x,2)+pow(y,2));
    angle = atan2(y,x);
    if (z>(arm1.length+arm2.length)){
        z = arm1.length+arm2.length;
        x = z*cos(angle);
        y = z*sin(angle);
    }
    
    // Prevent x,y from entering robot body
    if (end.x > R_ROBOT_LEFT && end.x < R_ROBOT_RIGHT && end.y >= R_ROBOT_TOP && y < R_ROBOT_TOP) {
        y = R_ROBOT_TOP;
    }
    if (end.x <= R_ROBOT_LEFT && x > R_ROBOT_LEFT && y < R_ROBOT_TOP) {
        x = R_ROBOT_LEFT;
    }
    if (end.x >= R_ROBOT_RIGHT && x < R_ROBOT_RIGHT && y < R_ROBOT_TOP) {
        x = R_ROBOT_RIGHT;
    }
}

/*
** =============================================================================
** robot_applyAngleLimits
** =============================================================================
** Function:
** Limits the angles of the robot's arms to prevent them from moving into 
** themselves
** Input: -
** Output: -
** =============================================================================
*/
void robot_applyAngleLimits(){
    if (arm1.theta>ARM1_ALIMLO){    // Limit angle arm1
        arm1.theta = ARM1_ALIMLO;
    }
    else if (arm1.theta<ARM1_ALIMHI){
        arm1.theta = ARM1_ALIMHI;
    }

    if (arm2.theta>ARM2_ALIMHI){    // Limit angle arm 2
        arm2.theta = ARM2_ALIMHI;
    } else if (arm2.theta < ARM2_ALIMLO) {
        arm2.theta = ARM2_ALIMLO;
    }
}

/*
** =============================================================================
** robot_setSetpoint
** =============================================================================
** Function:
** Transforms cartesian coordinates into rotadions of the joints of the robot's 
** arms.
** Input:
** float x, float y: setpoint position in cartesian coordinates
** =============================================================================
**       (x,y)
**        |\
**        | \
**        |b2\
**     z2 |   \arm2.length
**        |    \
**  z     |_ _ _\
**        |     /
**        |    /
**     z1 |   /arm1.length
**        |b1/
**        | /
**        |/
** =============================================================================
*/
void robot_setSetpoint (float x,float y){
    float z;        // length z of vector (x,y)
    float z1;       // part z1
    float z2;       // part z2

    float angle;    // angle of vector (x,y), !pointing up (x==0) the angle is defined as 0!
    float b1,b2;    // angle b1 and b2 (see drawing)
    
    robot_applySetpointLimits(x,y); // Apply limits
    
    z = sqrt(pow(x,2)+pow(y,2));    // Calculate vector (x,y) length
    
    // Calculate vector (x,y) angle
    angle = atan2(y,x);                 // Calculate (x,y) angle using atan 2
    if (angle<0.5*M_PI && angle>0){     // Rotate result of atan2 90 degrees counter clockwise, so up pointing vector (x==0, y>0) has a zero degree rotation instead of 90 degrees.

        angle = angle - 0.5*M_PI;
    }
    else if (angle<M_PI && angle>0.5*M_PI){
        angle = angle - 0.5*M_PI;
    }
    else if (angle<0 && angle>-0.5*M_PI){
        angle = angle - 0.5 * M_PI;
    }
    else{
        angle = angle + 1.5*M_PI;
    }  
    if (y==0 && x<0) { angle = 0.5*M_PI; }  // Special case correction: y=0, x < 0 Normally this returns PI, now it  must return PI/2
    if (y==0 && x>0) { angle = -0.5*M_PI; }  // Special case correction: y=0, x > 0 Normally this returns -PI, now it  must return -PI/2

    z1 = (pow(arm1.length,2)-pow(arm2.length,2)+pow(z,2))/(2*z);    // Calculate z1
    z2 = z-z1;                                                      // Calculate z2
    // Roundoff errors sometimes make z1 and z2 slightly higher than the arm lengths, this causes erratic behaviour in the acos function,
    // so it is countered here by an extra check and fix.
    if (z1>arm1.length) {z1 = arm1.length;}
    else if (z1<-arm1.length) { z1 = -arm1.length;}
    if (z2>arm2.length) {z2 = arm2.length;}
    else if (z2<-arm2.length) { z2 = -arm2.length;}
    
    b1 = acos(z1/arm1.length);      // Calculate angle b1
    b2 = acos(z2/arm2.length);      // Calculate angle b2
    
    arm1.theta = angle - b1;        // Calculate new angle arm 1
    arm2.theta = b1 + b2;           // Calculate new angle arm 2
    robot_applyAngleLimits();
        
    arm2.x = arm1.length*-sin(arm1.theta);  // Calculate new x position arm 2
    arm2.y = arm1.length*cos(arm1.theta);   // Calculate new y position arm 2
    
    end.x = arm2.length*(-sin(arm1.theta+arm2.theta)) + arm2.x; // Calculate new x position end effector
    end.y = arm2.length*(cos(arm1.theta+arm2.theta)) + arm2.y;  // Calculate new y position end effector
}

/*
** =============================================================================
** robot_init
** =============================================================================
** Function:
** Initialise robot parameters
** =============================================================================
*/
void robot_init() {
    // Set pwm motor periods
 //   gripperpwm.period_ms(20);
    
    pc.baud(115200);    // Set serial communication speed

    // Initialise robot segments
    state = R_HORIZONTAL;
    // Init arm1 (upper arm) first link
    arm1.length = 28.0f;
    arm1.x = 0;
    arm1.y = 0;
    arm1.theta = 0;

    // Init arm2 (lower arm) second link
    arm2.length = 35.0f;
    arm2.x = 0; 
    arm2.y = arm1.length;
    arm2.theta = 0;

    // Init end (end effector) third/last link
    end.length = 5.0f;
    end.x = 0; 
    end.y = arm1.length+arm2.length;
    end.theta = 0;    
}

/*
** =============================================================================
** r_moveHorizontal
** =============================================================================
** Function:
** Moves setpoint left or right depending on which switch is active.
** =============================================================================
*/
void r_moveHorizontal(){
    if (flag_switch){
        if (!switch1.read() || emg1.read()){
            moveleft = true;
            vx = (vx<-maxspeed)?-maxspeed:(vx-ax);
        }
        else {
            vx = moveleft?0:vx;
        }
        if (!switch2.read() || emg2.read()){
            moveleft = false;
            vx = (vx>maxspeed)?maxspeed:(vx+ax);
        }
        else {
            vx = !moveleft?0:vx;
        }
        robot_setSetpoint(end.x+vx, end.y);
    }
}

/*
** =============================================================================
** r_moveVertical
** =============================================================================
** Function: 
** Moves setpoint up or down depending on which switch is active.
** =============================================================================
*/
void r_moveVertical(){
    if (flag_switch){
        if (!switch1.read() || emg1.read()){
            movedown = true;
            vy = (vy<-maxspeed)?-maxspeed:(vy-ay);
        }
        else {
            vy = movedown?0:vy;
        }
        if (!switch2.read() || emg2.read()){
            movedown = false;
            vy = (vy>maxspeed)?maxspeed:(vy+ay);
        }
        else {
            vy = !movedown?0:vy;
        }
        robot_setSetpoint(end.x, end.y+vy);
    }
}

/*
** =============================================================================
** r_moveGripper
** =============================================================================
** Function: 
** Opens and closes the gripper depending on which switch is active.
** =============================================================================
*/
void r_moveGripper(){
    if(flag_switch){
        if((!switch1.read() || emg1.read()) && !gripperclosed){
            gripperpwm.pulsewidth_us(1035);     // Close gripper
            gripperclosed = true;
        } else if((!switch2.read() || emg2.read()) && gripperclosed){
            gripperpwm.pulsewidth_us(1500);     // Open gripper
            gripperclosed = false;
        }
    }
}

/*
** =============================================================================
** r_processStateSwitch
** =============================================================================
** Function: 
** Processes presses of the state switch.
** This switch switches between control states of the robot.
** States are horizontal movement, vertical movement and gripper open/close.
** =============================================================================
*/
void r_processStateSwitch(){
    if(flag_switch){
        if(switch3.read()){
            switch3down = false;
        }
        else {
            if (switch3down==false){
                switch3down = true;
                switch(state){
                    case R_HORIZONTAL:
                        state = R_VERTICAL;
                        break;
                    case R_VERTICAL:
                        state = R_GRIPPER;
                        break;
                    case R_GRIPPER:
                        state = R_HORIZONTAL;
                        break;
                    
                }
            }
        }
    }    
}

/*
** =============================================================================
** r_switchFlagReset
** =============================================================================
** Function:
** Resets the switch ticker flag.
** Call near the end of the main loop, after all switches have been read.
** =============================================================================
*/
void r_switchFlagReset(){
    if (flag_switch){
        flag_switch = false;
    }
}


/*
** =============================================================================
** r_doPID
** =============================================================================
** Function: 
** Runs the PID controller using the setpoint and sets the motorspeeds.
** =============================================================================
*/
void r_doPID(){
    double dt = 1/50.0; // PID time step

    if (flag_PID){
        flag_PID = false;
        motor1.PID(arm1.theta, dt);
        motor2.PID(arm2.theta, dt);
    }     
}

/*
** =============================================================================
** r_outputToMatlab
** =============================================================================
** Function: 
** Outputs data to the serial interface to be read in matlab (or putty).
** =============================================================================
*/
void r_outputToMatlab(){
    if (flag_output){
        flag_output = false;    
        printf("Angle1\n\r");
        printf("%f\n\r", arm1.theta);
        printf("Angle2\n\r");
        printf("%f\n\r", arm2.theta);
        printf("Pos1\n\r");
        printf("%f\n\r", motor1.getPosition());
        printf("Pos2\n\r");
        printf("%f\n\r", motor2.getPosition());
        
        printf("x: %f | y: %f\n\r", end.x, end.y);
    }
}

/*
** =============================================================================
** r_processEMG
** =============================================================================
** Function: 
** Processes the EMG signals at ticker frequency
** =============================================================================
*/
void r_processEMG(){
    if(flag_EMG){
        flag_EMG = false;
        emg1.tick();
        emg2.tick();
        scope.set(0, emg1.discrete);
        scope.set(1, emg1.read()?0.5:0);
        scope.set(2, emg2.discrete);
        scope.set(3, emg2.read()?0.5:0);

    }
}
/*
** =============================================================================
** maintickerfunction
** =============================================================================
** Function: 
** Ticker call back. Sets flags at appropriate timer intervals for
** reading switches, calculating PID, outputting information via serial and
** reading EMG signals.
** =============================================================================
*/
void maintickerfunction (){
    static int cnt=0;
    if (cnt%20 == 0){   // Read and react to switches 50 times per second
        flag_switch = true;
    }
    if (cnt%20 == 0){   // Run PID controller 50 times per second
        flag_PID = true;
    }
    if (cnt%100 == 0){  // Output data to serial bus 10 times per second
        flag_output = true;
    }
    flag_EMG = true;
    cnt++;
}

/*
** =============================================================================
** main
** =============================================================================
** Function:
** Contains the main loop with state machine.
** =============================================================================
*/
int main(){
    // Initialise main ticker
    mainticker.attach(&maintickerfunction,0.001f);
    robot_init();
    while(true){
        unsigned int t1 = us_ticker_read();
        switch(state){
            case R_INIT:
                break;
            case R_HORIZONTAL:
                r_moveHorizontal();
                break;
            case R_VERTICAL:
                r_moveVertical();
                break;
            case R_GRIPPER:
                r_moveGripper();
                break;
        }
        r_processEMG();

        if (flag_switch){
            scope.send();

        }
        r_processStateSwitch();
        r_switchFlagReset();
        r_doPID();
        t1 = us_ticker_read()-t1;
        scope.set(4, (float)t1);
        r_outputToMatlab();
    }
}