Bachelor Assignment for delayed teleoperating systems

Dependencies:   EthernetInterface FastPWM mbed-rtos mbed MODSERIAL

main.cpp

Committer:
darth_bachious
Date:
2018-05-15
Revision:
6:ccbbf4c77d35
Parent:
5:4d5b077b3fe6
Child:
7:984f363f5e87

File content as of revision 6:ccbbf4c77d35:

#include "mbed.h"
#include "EthernetInterface.h"
#include "rtos.h"
#include <vector>
#include "FastPWM.h"
#include <cmath>

//Master or Slave? 1=Master, 0=Slave
static const int identity = 1;

//network config
static const char* master_ip = "192.168.1.101";
static const char* slave_ip = "192.168.1.102";
static const char* MASK = "255.255.255.0";
static const char* GATEWAY = "192.168.1.1";
static const int port = 865;

//declaration of interfaces
DigitalOut led(LED_GREEN); 
DigitalOut led2(LED_RED);
EthernetInterface eth;  //network 
Serial pc(USBTX, USBRX);//create PC interface
UDPSocket socket;       //socket to receive data on
Endpoint client;        //The virtual other side, not to send actual information to
Endpoint counterpart;   //The actual other side, this is where the information should go to
InterruptIn Button1(SW2);
InterruptIn Button2(SW3);
Ticker controllerloop;
Ticker mainloop;
DigitalOut debug(D11);

//Motor interfaces and variables
InterruptIn EncoderA(D2);
DigitalIn  EncoderB(D3);
DigitalOut M1_DIR(D4);
FastPWM M1_pwm(D5);
AnalogIn measuredForce(A5);

//Low pass filter filter coeffs, 2nd order, 50 Hz
double b[3] = {0.020083365564211, 0.020083365564211*2, 0.020083365564211};
double a[3] = {1.00000000000000, -1.561018075, 0.64135153805};

//variables related to networking
char data[30]= {""};
int size;
int counter = 1;
int counter_received = 1;
char * var;
char output[30] = {""};
float input = 0.0;

//measured variables
float angle = 0.0;
int encoderPos = 0;
int prev_encoderPos = 0;
float encoder_vel = 0.0;
float motor_vel = 0.0;
float force = 0.0;
float control_torque = 0.0;
float force_offset = 0.5;

//Reference, used in admittance
float ref_angle = 0.0;
float ref_vel = 0.0;
float ref_acc = 0.0;
const float virtualMass = 0.005;
const float virtualDamping = 0.05;
const float virtualStiffness = 5;
const float arm = 0.07;

//Controller required variables 
bool controller_check = 0;
const float looptime  = 1.0/50; //50Hz, for the controller
const float ADDMlooptime = 1.0/5000; //5KHz, for the admittance controller 
enum States {stateCalibration, stateHoming, stateOperation};
int currentState = stateCalibration;
int transparancy = 1; // 1=Pos-Pos, 2=Pos-Force, 3=Force-Pos
int passivity =0; // 0=off, 1=on
int received_transparancy = 1;
int received_passivity = 0;

//Controller parameters
const float Ktl = 1; //high level controller parameters
const float Dtl=0.1;
const float Kp = 5; //low level controller parameters
const float Dp = 0.05;
float u = 0.0;      //serves as input variable for the motor;

//passivity layer constant
const float beta =  0.15;
const float Hd = 0.5;
const float alpha = 10;

//passivity layer variables
float tank = 0.0;
float prev_ref_angle = 0.0;
float package_out = 0.0;
float received_package = 0.0;
float prev_torque = 0.0;


//Constants
const float PI = 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679;
const float RadsPerCount = (2 * PI)/(1024*10);
const float FORCESENSORGAIN = 15.134;
const int MAX_ENCODER_LEFT = 1333;
const int MAX_ENCODER_RIGHT = -1453;
const float WORKSPACEBOUND = PI/6;

const int frequency_pwm = 20000;

//Time delay related variables
float timedelay = 0.04; //SECONDS
int delaysteps = timedelay/looptime;
std::vector<float> delayArrayINPUT(max(delaysteps,1),0.0);
std::vector<float> delayArrayMODE(max(delaysteps,1),0.0);
std::vector<float> delayArrayPASS(max(delaysteps,1),0.0);
std::vector<float> delayArrayENERGY(max(delaysteps,1),0.0);
Timer t;

//FUNCTIONS START HERE

void encoderFunctionA()
    {
        if ((bool)EncoderA == (bool)EncoderB) { //Increment or decrement encoder position during interrupt
            encoderPos++;
        } else {
            encoderPos--;
        }
    }
double velocityFilter(float x)
{
    double y = 0.0; //Output
    static double y_1 = 0.0; //Output last loop
    static double y_2 = 0.0; //Output 2 loops ago
    static double x_1 = 0.0; //Input last loop
    static double x_2 = 0.0; //Input two loops ago

    //Finite difference equation for 2nd order Butterworth low-pass
    y = -a[1]*y_1 - a[2]*y_2 + x*b[0] + x_1*b[1] + x_2*b[2];
    y_2 = y_1;
    y_1 = y;
    x_2 = x_1;
    x_1 = x;
    return (float)y;
}

void inet_eth(){
    if(identity==1)
        {
        eth.init(master_ip, MASK,GATEWAY);
        eth.connect();
    
        socket.bind(port);
        counterpart.set_address(slave_ip,port);
        }
    else if(identity==0)
        {
        eth.init(slave_ip, MASK,GATEWAY);
        eth.connect();
    
        socket.bind(port);
        counterpart.set_address(master_ip,port);   
        }

    }

void inet_USB(){
    pc.baud(115200);
    }
    

void end_eth(){
    socket.close();
    eth.disconnect();
    }

void controllertrigger()
    {
        controller_check = 1;
    }
    
    
float update_delay(std::vector<float>&array, float new_value)
    {
    float return_value = array[1];
    for (int i=0; i<array.size()-1; ++i) 
    {
        array[i]=array[i+1];
    }
    array.back() = new_value;
    return return_value;
    }
    
void limit(float &x, float lower, float upper)
{
    if (x > upper)
        x = upper;
    if (x < lower)
        x = lower; 
}

void motor_update(float PWM) //angle required to safeguard it from crashing into its stops
{
    limit(PWM,-1.0f,1.0f);
    if(PWM >= 0.0f)
    {
        M1_DIR = false;
        M1_pwm = PWM;
    } else {
        M1_DIR = true;
        M1_pwm = -PWM;    
    }
}

void sensorUpdate()
{
    angle = encoderPos * RadsPerCount;
    encoder_vel = (encoderPos - prev_encoderPos)/ADDMlooptime; //careful, this function should be called every 1/5000 seconds
    motor_vel = velocityFilter(encoder_vel * RadsPerCount);
    prev_encoderPos = encoderPos;
    force = -FORCESENSORGAIN*2.0f*(measuredForce - force_offset); //Measured force
}    
 
void doCalibration()
{
    u = 0.09;
    motor_update(u);
    led2=0;
    led=1;
    //switching states
    if((abs(motor_vel)<0.001f)&& t.read()>3.0f)
    {
        encoderPos = MAX_ENCODER_LEFT;
        ref_angle = encoderPos * RadsPerCount;
        currentState = stateHoming;
        t.stop();
    }    
}

void doHoming()
{
    led2=0;
    led=0;
    ref_vel = -0.2;
    if(ref_angle > 0.0f)
    {
        ref_angle += ref_vel*ADDMlooptime; //careful, this function should be called every 1/50 seconds
    }
    else
    {
        ref_angle = 0.0;
        ref_vel = 0.0;
    }
    u = Kp*(ref_angle - angle) + Dp*(ref_vel - motor_vel);
    motor_update(u);
    
    //switching states
    if ((abs(encoderPos)<20)&&abs((ref_vel - motor_vel)<0.02f))
    {
        currentState = stateOperation;
        force_offset = measuredForce;
        motor_update(0.0);
        led2=1;
        led=1;
    }
        
}

void doOperation()
{
    ref_acc = (force*arm + control_torque- virtualDamping*ref_vel)/virtualMass;
    ref_vel += ref_acc*ADDMlooptime;
    ref_angle += ref_vel*ADDMlooptime;
    if(ref_angle > WORKSPACEBOUND)
    {
        ref_vel = 0.0;
        ref_acc = 0.0;
        ref_angle = WORKSPACEBOUND;
    } else if(ref_angle < -WORKSPACEBOUND)
    {
        ref_vel = 0.0;
        ref_acc = 0.0;
        ref_angle = -WORKSPACEBOUND;
    }
    u = Kp*(ref_angle - angle) + Dp*(ref_vel - motor_vel);       
    motor_update(u);
    
    //no switching states for now
}

void loopfunction()
{
    sensorUpdate();
    switch(currentState)
    {
        case stateCalibration:
        doCalibration();
        break;
        case stateHoming:
        doHoming();
        break;
        case stateOperation:
        doOperation();
        break;
    }
}

void updateTransparency()
{
    transparancy++;
    if(transparancy>3)
        transparancy = 1;
}

void updatePassivity()
{
    passivity++;
    if(passivity>1)
        passivity = 0;    
}

float passivityLayer(float Ftl, float E_in)
{
    tank = tank + E_in - prev_torque*(ref_angle-prev_ref_angle);
    if(tank>0.0f)
    {
        package_out = tank*beta;
        tank = tank - package_out;
    } else
        package_out = 0.0;
    
    float FMAX1 = 0.0;
    if(tank>0.0f)
    {
        float FMAX1 = abs(Ftl);
    }
    float FMAX2 = abs(tank/(ref_vel*looptime));
    
    float FMAX3 = 0.15;
    
    prev_ref_angle = ref_angle;  
    
    float Ftlc = 0.0;
    
    if((tank<Hd)&&(identity==1))
    {
        Ftlc = - alpha*(Hd-tank)*ref_vel;
    }    
    //pc.printf("Ftlc: %f\r\n",Ftlc);
    //pc.printf("tank: %f\r\n",tank);
    if(Ftl>=0.0f)
    {
        prev_torque = min(min(abs(Ftl),FMAX1),min(FMAX2,FMAX3))+Ftlc;        
        return min(min(abs(Ftl),FMAX1),min(FMAX2,FMAX3))+Ftlc; //min() only takes to arguments, so nested min()'s
    }
    else
    {
        prev_torque = -min(min(abs(Ftl),FMAX1),min(FMAX2,FMAX3))-Ftlc;
        return -min(min(abs(Ftl),FMAX1),min(FMAX2,FMAX3))-Ftlc;   
    }
      
}

void receiveUDP(void const *argument){
    while(true)
    {
        size = socket.receiveFrom(client, data, sizeof(data));
        if(size > 0)
        {
            data[size] = '\0';
            pc.printf("data:%s\r\n",data);
            if(size>5) //first check, an minimum amount of data must have arrived
                {
                var = strtok(data,"; ");
                if(counter_received < atof(var)) //second check, data must be newer
                    {
                    counter_received = atof(var);
                    var = strtok(NULL,"; ");
                    received_transparancy = atof(var);
                    var = strtok(NULL,"; ");
                    received_passivity = atof(var);
                    var = strtok(NULL,"; ");
                    input = atof(var);
                    var = strtok(NULL,"; ");
                    received_package += atof(var);
                    }
            }
        }
    }
}

osThreadDef(receiveUDP, osPriorityNormal, DEFAULT_STACK_SIZE);

int main(){
    inet_eth();
    inet_USB();
    
    osThreadCreate(osThread(receiveUDP), NULL);
    led2=1;
    led=1;
    
   //Set all interrupt requests to 2nd place priority
    for(int n = 0; n < 86; n++) {
        NVIC_SetPriority((IRQn)n,1);
    }
    //Set out motor encoder interrupt to 1st place priority
    NVIC_SetPriority(PORTB_IRQn,0);
    
    controllerloop.attach(&controllertrigger,looptime);
    mainloop.attach(&loopfunction,ADDMlooptime);
    
    M1_pwm.period(1.0/frequency_pwm);
    EncoderA.rise(&encoderFunctionA);
    
    Button1.fall(&updateTransparency);
    Button2.fall(&updatePassivity);
    
    t.start();
    
    while(true){
        if(controller_check==1){
            debug = 1;
            float received_input = update_delay(delayArrayINPUT,input);
            float current_transparancy = update_delay(delayArrayMODE,received_transparancy);
            float current_passivity = update_delay(delayArrayPASS,received_passivity);
            float package_in = update_delay(delayArrayENERGY,received_package);
            received_package = 0; //IMPORTANT, WILL EXPLODE OTHERWISE
            
            if(identity==0)
            {
                transparancy = current_transparancy;
                passivity = current_passivity;
                if(transparancy==1)
                {
                    float torque_tlc = Ktl*(received_input-ref_angle) - Dtl*ref_vel;
                    if(current_passivity==1)
                        control_torque = passivityLayer(torque_tlc,package_in);
                    else
                        control_torque = torque_tlc;
                    sprintf(output,"%i;%i;%i;%f;%f",counter,transparancy,passivity,ref_angle,package_out);                   
                }
                else if(transparancy==2)
                {
                    float torque_tlc = Ktl*(received_input-ref_angle) - Dtl*ref_vel;
                    if(current_passivity==1)
                        control_torque = passivityLayer(torque_tlc,package_in);
                    else
                        control_torque = torque_tlc;                 
                    sprintf(output,"%i;%i;%i;%f;%f",counter,transparancy,passivity,force,package_out); 
                }
                else if(transparancy==3)
                {
                    float torque_tlc = received_input*arm;
                    if(current_passivity==1)
                        control_torque = passivityLayer(torque_tlc,package_in);
                    else
                        control_torque = torque_tlc;                  
                    sprintf(output,"%i;%i;%i;%f;%f",counter,transparancy,passivity,ref_angle,package_out);
                }   
            }
            else if(identity == 1)
            {
                if(transparancy==1)
                {
                    float torque_tlc = Ktl*(received_input-ref_angle) - Dtl*ref_vel;
                    if(current_passivity==1)
                        control_torque = passivityLayer(torque_tlc,package_in);
                    else
                        control_torque = torque_tlc;                    
                    sprintf(output,"%i;%i;%i;%f;%f",counter,transparancy,passivity,ref_angle,package_out);
                }
                else if(transparancy==2)
                {
                    float torque_tlc = received_input*arm;
                    if(current_passivity==1)
                        control_torque = passivityLayer(torque_tlc,package_in);
                    else
                        control_torque = torque_tlc;                   
                    sprintf(output,"%i;%i;%i;%f;%f",counter,transparancy,passivity,ref_angle,package_out);
                }
                else if(transparancy==3)
                {
                    float torque_tlc = Ktl*(received_input-ref_angle) - Dtl*ref_vel;
                    if(current_passivity==1)
                        control_torque = passivityLayer(torque_tlc,package_in);
                    else
                        control_torque = torque_tlc;                    
                    sprintf(output,"%i;%i;%i;%f;%f",counter,transparancy,passivity,force,package_out);
                }
            }
            
            socket.sendTo(counterpart, output, sizeof(output));
            counter ++;
            led=!led;
            if(current_passivity==1)
                led2=0;
            else
                led2=1;   
            controller_check = 0;
            debug = 0;
            }
        osDelay(0.1); //this might be the most ugliest piece of code that somehow still works. BK
        }  
}