dsa

Dependencies:   MatrixMath Matrix ExperimentServer QEI_pmw MotorShield

main.cpp

Committer:
sehwan
Date:
2020-11-18
Revision:
30:1dd3ef6acde6
Parent:
29:8b4fd3d36882

File content as of revision 30:1dd3ef6acde6:

#include "mbed.h"
#include "rtos.h"
#include "EthernetInterface.h"
#include "ExperimentServer.h"
#include "QEI.h"
#include "BezierCurve.h"
#include "MotorShield.h" 
#include "HardwareSetup.h"
#include "Matrix.h"
#include "MatrixMath.h"

#define PULSE_TO_RAD (2.0f*3.14159f / 1200.0f)

#define size_torqueTraj 20
#define size_jointTraj 20
#define size_inputs 14+9*size_torqueTraj
#define size_outputs 30

// Initializations
Serial pc(USBTX, USBRX);    // USB Serial Terminal
ExperimentServer server;    // Object that lets us communicate with MATLAB
Timer t;                    // Timer to measure elapsed time of experiment

// Leg motors:
QEI encoderA(PE_9,PE_11, NC, 1200, QEI::X4_ENCODING);  // MOTOR A ENCODER (no index, 1200 counts/rev, Quadrature encoding)
QEI encoderB(PA_5, PB_3, NC, 1200, QEI::X4_ENCODING);  // MOTOR B ENCODER (no index, 1200 counts/rev, Quadrature encoding)

// Arm motor:
QEI encoderC(PC_6, PC_7, NC, 1200, QEI::X4_ENCODING);  // MOTOR C ENCODER (no index, 1200 counts/rev, Quadrature encoding)

// Extra
QEI encoderD(PD_12, PD_13, NC, 1200, QEI::X4_ENCODING);// MOTOR D ENCODER (no index, 1200 counts/rev, Quadrature encoding)

MotorShield motorShield(12000); //initialize the motor shield with a period of 12000 ticks or ~20kHZ
Ticker currentLoop;

// Variables for q1 (hip)
float current1;
float current_des1 = 0;
float prev_current_des1 = 0;
float current_int1 = 0;
float angle1;
float velocity1;
float duty_cycle1;
float angle1_init;

// Variables for q2 (knee)
float current2;
float current_des2 = 0;
float prev_current_des2 = 0;
float current_int2 = 0;
float angle2;
float velocity2;
float duty_cycle2;
float angle2_init;

// Variables for q3 (arm)
float current3;
float current_des3 = 0;
float prev_current_des3 = 0;
float current_int3 = 0;
float angle3;
float velocity3;
float duty_cycle3;
float angle3_init;

// Fixed kinematic parameters
const float l_OA=.011; 
const float l_OB=.042; 
const float l_AC=.096; 
const float l_DE=.091;
const float m1 =.0393 + .2;
const float m2 =.0368; 
const float m3 = .00783;
const float m4 = .0155;
const float I1 = 0.0000251;  //25.1 * 10^-6;
const float I2 = 0.0000535;  //53.5 * 10^-6;
const float I3 = 0.00000925; //9.25 * 10^-6;
const float I4 = 0.0000222;  //22.176 * 10^-6;
const float l_O_m1=0.032;
const float l_B_m2=0.0344; 
const float l_A_m3=0.0622;
const float l_C_m4=0.0610;
const float N = 18.75;
const float Ir = 0.0035/pow(N,2);

// Design parameters
const float m_body = 0.186+0.211;
const float l_body = 0.04;
const float l_arm = 0.10;
const float l_cm_arm = 0.8*l_arm;
const float m_arm = 0.1;
const float I_arm = 0.00064; // calculated with I = ml^2, not from CAD

// Timing parameters
float current_control_period_us = 200.0f;     // 5kHz current control loop
float impedance_control_period_us = 10000.0f;  // 100 Hz impedance control loop
float start_period, traj_period, end_period;

// Control parameters
float current_Kp = 4.0f;         
float current_Ki = 0.4f;           
float current_int_max = 3.0f;   
float K_q1;
float K_q2;
float K_q3;
float D_qd1;
float D_qd2;
float D_qd3;
int control_method = 0; // defaults to using just ff torque    
float duty_max;      

// Model parameters
float supply_voltage = 12;     // motor supply voltage
float R = 2.0f;                // motor resistance
float k_t = 0.18f;             // motor torque constant
float nu = 0.0005;             // motor viscous friction

// Current control interrupt function
void CurrentLoop()
{
    // This loop sets the motor voltage commands using PI current controllers with feedforward terms.
    //use the motor shield as follows:
    //motorShield.motorAWrite(DUTY CYCLE, DIRECTION), DIRECTION = 0 is forward, DIRECTION =1 is backwards.
    
    //***************************************************************************
    //HIP MOTOR
    current1 = -(((float(motorShield.readCurrentA())/65536.0f)*30.0f)-15.0f);           // measure current
    velocity1 = encoderA.getVelocity() * PULSE_TO_RAD;                                  // measure velocity        
    float err_c1 = current_des1 - current1;                                             // current errror
    current_int1 += err_c1;                                                             // integrate error
    current_int1 = fmaxf( fminf(current_int1, current_int_max), -current_int_max);      // anti-windup
    float ff1 = R*current_des1 + k_t*velocity1;                                         // feedforward terms
    duty_cycle1 = (ff1 + current_Kp*err_c1 + current_Ki*current_int1)/supply_voltage;   // PI current controller
    
    float absDuty1 = abs(duty_cycle1);
    if (absDuty1 > duty_max) {
        duty_cycle1 *= duty_max / absDuty1;
        absDuty1 = duty_max;
    }    
    if (duty_cycle1 < 0) { // backwards
        motorShield.motorAWrite(absDuty1, 1);
    } else { // forwards
        motorShield.motorAWrite(absDuty1, 0);
    }             
    prev_current_des1 = current_des1; 
    
    //****************************************************************************
    //KNEE MOTOR
    current2     = -(((float(motorShield.readCurrentB())/65536.0f)*30.0f)-15.0f);       // measure current
    velocity2 = encoderB.getVelocity() * PULSE_TO_RAD;                                  // measure velocity  
    float err_c2 = current_des2 - current2;                                             // current error
    current_int2 += err_c2;                                                             // integrate error
    current_int2 = fmaxf( fminf(current_int2, current_int_max), -current_int_max);      // anti-windup   
    float ff2 = R*current_des2 + k_t*velocity2;                                         // feedforward terms
    duty_cycle2 = (ff2 + current_Kp*err_c2 + current_Ki*current_int2)/supply_voltage;   // PI current controller
    
    float absDuty2 = abs(duty_cycle2);
    if (absDuty2 > duty_max) {
        duty_cycle2 *= duty_max / absDuty2;
        absDuty2 = duty_max;
    }    
    if (duty_cycle2 < 0) { // backwards
        motorShield.motorBWrite(absDuty2, 1);
    } else { // forwards
        motorShield.motorBWrite(absDuty2, 0);
    }             
    prev_current_des2 = current_des2; 
    
    //***************************************************************************
    //ARM MOTOR
    current3     = -(((float(motorShield.readCurrentC())/65536.0f)*30.0f)-15.0f);       // measure current
    velocity3 = encoderC.getVelocity() * PULSE_TO_RAD;                                  // measure velocity  
    float err_c3 = current_des3 - current3;                                             // current error
    current_int3 += err_c3;                                                             // integrate error
    current_int3 = fmaxf( fminf(current_int3, current_int_max), -current_int_max);      // anti-windup   
    float ff3 = R*current_des3 + k_t*velocity3;                                         // feedforward terms
    duty_cycle3 = (ff3 + current_Kp*err_c3 + current_Ki*current_int3)/supply_voltage;   // PI current controller
    
    float absDuty3 = abs(duty_cycle3);
    if (absDuty3 > duty_max) {
        duty_cycle3 *= duty_max / absDuty3;
        absDuty3 = duty_max;
    }    
    if (duty_cycle3 < 0) { // backwards
        motorShield.motorCWrite(absDuty3, 1);
    } else { // forwards
        motorShield.motorCWrite(absDuty3, 0);
    }             
    prev_current_des3 = current_des3; 
}

int main (void)
{
    // Object for torque profile
    //BezierCurve tauDes_bez(3,size_torqueTraj-1);
    
    // Object for joint position profile
    //BezierCurve qDes_bez(3,size_jointTraj-1);
    
    // Object for joint velocity profile
    //BezierCurve qdDes_bez(3,size_jointTraj-1);
    
    // Link the terminal with our server and start it up
    server.attachTerminal(pc);
    server.init();
    
    // Continually get input from MATLAB and run experiments
    float input_params[size_inputs];
    pc.printf("%f",input_params[0]);
    
    while(1) {
        // If there are new inputs, this code will run
        if (server.getParams(input_params,size_inputs)) {   
              
            // Get inputs from MATLAB          
            start_period                = input_params[0];    // First buffer time, before trajectory
            end_period                  = input_params[1];    // Second buffer time, after trajectory
            traj_period                 = input_params[2];    // Total trajectory time
            
            angle1_init                 = input_params[3];    // Initial angle for q1 (hip, rad)
            angle2_init                 = input_params[4];    // Initial angle for q2 (knee, rad)
            angle3_init                 = input_params[5];    // Initial angle for q3 (arm, rad)
            
            K_q1                        = input_params[6];    // Joint space stiffness for hip (N/rad)
            K_q2                        = input_params[7];    // Joint space stiffness for knee (N/rad)
            K_q3                        = input_params[8];    // Joint space stiffness for arm (N/rad)
            D_qd1                        = input_params[9];   // Joint space damping for arm (Ns/rad)
            D_qd2                        = input_params[10];   // Joint space damping for knee (Ns/rad)
            D_qd3                        = input_params[11];  // Joint space damping for hip (Ns/rad)
            
            control_method              = int(input_params[12]);    // Controller choices: feedfwd torque = 0, PD control = 1, both = 2
            
            duty_max                    = input_params[13];   // Maximum duty factor
            
            //**************************************************************
            // LOADING OPTIMIZED PROFILES AND GENERATING BEZIER TRAJECTORIES
            
            int last_index = 14; // index to track where profiles begin and end
            
            // Load torque profile:
            float torque_profile[3*(size_torqueTraj)];
            for(int i = 0; i < 3*(size_torqueTraj); i++) {
              torque_profile[i] = input_params[last_index+i]; 
            }
            //tauDes_bez.setPoints(torque_profile);
            last_index = last_index + 3*(size_torqueTraj);
            
            
            // Load joint angle profile:
            float q_profile[3*(size_jointTraj)];
            for(int i = 0; i < 3*(size_jointTraj); i++) {
              q_profile[i] = input_params[last_index+i];    
            }
            // qDes_bez.setPoints(q_profile);
            last_index = last_index + 3*(size_jointTraj);
            
            
            // Load joint velocity profile:
            float qd_profile[3*(size_jointTraj)];
            for(int i = 0; i < 3*(size_jointTraj); i++) {
              qd_profile[i] = input_params[last_index+i];    
            }
            //qdDes_bez.setPoints(qd_profile);
            
            
            // Attach current loop interrupt
            currentLoop.attach_us(CurrentLoop,current_control_period_us);
                        
            // Setup experiment
            t.reset();
            t.start();
            encoderA.reset();
            encoderB.reset();
            encoderC.reset();
            encoderD.reset();

            motorShield.motorAWrite(0, 0); //turn motor A off
            motorShield.motorBWrite(0, 0); //turn motor B off
            motorShield.motorCWrite(0, 0); //turn motor C off

            // Run experiment
            
            int iter = 0;

            while( t.read() < start_period + traj_period + end_period) { 
                 
                // Read encoders to get motor states
                angle1 = encoderA.getPulses() *PULSE_TO_RAD + angle1_init;       
                velocity1 = encoderA.getVelocity() * PULSE_TO_RAD;
                 
                angle2 = encoderB.getPulses() * PULSE_TO_RAD + angle2_init;       
                velocity2 = encoderB.getVelocity() * PULSE_TO_RAD; 
                
                angle3 = encoderC.getPulses() * PULSE_TO_RAD + angle3_init;       
                velocity3 = encoderC.getVelocity() * PULSE_TO_RAD;           
                
                const float th1 = angle1;
                const float th2 = angle2;
                const float th3 = angle3;
                const float dth1= velocity1;
                const float dth2= velocity2;
                const float dth3 = velocity3;

                // Set gains based on buffer and traj times, then calculate desired x,y from Bezier trajectory at current time if necessary
                float teff  = 0;
                float vMult = 0;
                if( t < start_period) {
                        K_q1 = 0.5; 
                        K_q2 = 0.5; 
                        K_q3 = 0.5; 
                        D_qd1 = 0.01;  
                        D_qd2 = 0.01;  
                        D_qd3 = 0.01;
                }
                else if (t < start_period + traj_period)
                {
                    K_q1 = input_params[7];  // Hip stiffness N/rad
                    K_q2 = input_params[8];  // Knee stiffness N/rad
                    K_q3 = input_params[9];  // Arm stiffness N/rad
                    D_qd1 = input_params[9];  // Hip damping N/(rad/s)
                    D_qd2 = input_params[10]; // Knee damping N/(rad/s)
                    D_qd3 = input_params[11]; // Arm damping N/(rad/s)
                    teff = (t-start_period);
                    vMult = 1;
                }
                else
                {
                    teff = traj_period;
                    vMult = 0;
                }
                
                // desired values
                float tau_des[3], q_des[3], qd_des[3];
                for (int i = 0; i < 3; i++){
                    if (t < start_period){
                        tau_des[i] = 0;
                        q_des[i] = q_profile[i];
                        qd_des[i] = 0;
                    } else if (t < start_period + traj_period){
                        tau_des[i] = torque_profile[3*iter+i];
                        q_des[i] = q_profile[3*iter+i];
                        qd_des[i] = qd_profile[3*iter+i];}
                    else{
                        tau_des[i] = 0;
                        q_des[i] = 0;
                        qd_des[i] = 0;
                        }
                }
                
                
                //tauDes_bez.evaluate(teff/traj_period,tau_des);
                //qDes_bez.evaluate(teff/traj_period,q_des);
                //qdDes_bez.evaluateDerivative(teff/traj_period,qd_des); // get qdDes from derivative of Bezier of qDes
                //qdDes_bez.evaluate(teff/traj_period,qd_des); // alternatively, get qdDes directly from optimized profile. Potential error?
                
                // From old code -> not sure why velocities need to be scaled wrt traj. time. Don't think it's needed.
                // qd_des[0]/=traj_period;
                // qd_des[1]/=traj_period;
                // qd_des[2]/=traj_period;
                
                qd_des[0]*=vMult; // ensures zero velocity when moving to starting configuration
                qd_des[1]*=vMult;
                qd_des[2]*=vMult;
                
                
                // Calculate the forward kinematics (position and velocity)
                float xFoot =   sin(th1)*(l_DE - l_OA + l_OB) + l_AC*sin(th1 + th2) + l_OA*sin(th1);
                float yFoot = - cos(th1)*(l_DE - l_OA + l_OB) - l_AC*cos(th1 + th2) - l_OA*cos(th1);
                float xArm  =   l_arm*sin(th3); // assuming th3 defined relative to line coincident with body pointing down
                float yArm  =   l_body - cos(th3);
                float dxFoot =  dth1*(cos(th1)*(l_DE - l_OA + l_OB) + l_AC*cos(th1 + th2) + l_OA*cos(th1)) + dth2*l_AC*cos(th1 + th2);
                float dyFoot =  dth1*(sin(th1)*(l_DE - l_OA + l_OB) + l_AC*sin(th1 + th2) + l_OA*sin(th1)) + dth2*l_AC*sin(th1 + th2); 
                float dxArm =   dth3*l_arm*cos(th3);
                float dyArm =   dth3*sin(th3);
                
                // Calculate the desired forward kinematics
                float xFootDes =   sin(q_des[0])*(l_DE - l_OA + l_OB) + l_AC*sin(q_des[0] + q_des[1]) + l_OA*sin(q_des[0]);
                float yFootDes = - cos(q_des[0])*(l_DE - l_OA + l_OB) - l_AC*cos(q_des[0] + q_des[1]) - l_OA*cos(q_des[0]);
                float xArmDes  =   l_arm*sin(q_des[2]); // assuming th3 defined relative to line coincident with body pointing down
                float yArmDes  =   l_body - cos(q_des[2]);
                float dxFootDes =  qd_des[0]*(cos(q_des[0])*(l_DE - l_OA + l_OB) + l_AC*cos(q_des[0] + q_des[1]) + l_OA*cos(q_des[0])) + qd_des[1]*l_AC*cos(q_des[0] + q_des[1]);
                float dyFootDes =  qd_des[0]*(sin(q_des[0])*(l_DE - l_OA + l_OB) + l_AC*sin(q_des[0] + q_des[1]) + l_OA*sin(q_des[0])) + qd_des[1]*l_AC*sin(q_des[0] + q_des[1]); 
                float dxArmDes =   qd_des[2]*l_arm*cos(q_des[2]);
                float dyArmDes =   qd_des[2]*sin(q_des[2]);
                
                
                // Calculate error variables
                float e_th1 = q_des[0] - th1;
                float e_th2 = q_des[1] - th2;
                float e_th3 = q_des[2] - th3;
                float de_th1 = qd_des[0] - dth1;
                float de_th2 = qd_des[1] - dth2;
                float de_th3 = qd_des[2] - dth3;

                // Set desired currents             
                float current_ff[3], current_PD[3];
                for(int i = 0; i < 3; i++){                                                  // set feedforward currents
                    current_ff[i] = tau_des[i]/k_t;
                    }
                    
                current_PD[0] = (K_q1*(q_des[0] - th1)+D_qd1*(qd_des[0]-dth1))/k_t;          // set PD currents
                current_PD[1] = (K_q2*(q_des[1] - th2)+D_qd2*(qd_des[1]-dth2))/k_t;
                current_PD[2] = (K_q3*(q_des[2] - th3)+D_qd3*(qd_des[2]-dth3))/k_t;
                
                if( t < start_period) {
                    control_method = 1;}
                else{
                    control_method = int(input_params[12]); 
                }
                
                
                // **************
                // CONTROL CHOICE      (there may be issues with setting current_des inside a switch statement with the current loop interrupt, make sure to check)
                switch(control_method){
                    case 0:                   // feedforward torque only
                        pc.printf("TIME: %f \n FFWD CURRENT \n",t.read());
                        current_des1 = current_ff[0];
                        current_des2 = current_ff[1];
                        current_des3 = current_ff[2];
                        break;
                        
                    case 1:                   // Joint PD control only
                        pc.printf("TIME: %f \n PD CURRENT \n",t.read());
                        current_des1 = current_PD[0];
                        current_des2 = current_PD[1];
                        current_des3 = current_PD[2];
                        break;
                    
                    case 2:                   // both combined
                        pc.printf("TIME: %f \n BOTH CURRENT \n",t.read());
                        current_des1 = current_ff[0] + current_PD[0];
                        current_des2 = current_ff[1] + current_PD[1];
                        current_des3 = current_ff[2] + current_PD[2];
                        break;
                    
                    default:
                        pc.printf("Invalid control method selector.\n");
                        exit(-100);
                        break;
                }   

                // Form output to send to MATLAB     
                float output_data[size_outputs];
                // current time
                output_data[0] = t.read();
                // motor 1 state
                output_data[1] = angle1;
                output_data[2] = velocity1;  
                output_data[3] = current1;
                output_data[4] = current_des1;
                output_data[5] = duty_cycle1;
                // motor 2 state
                output_data[6] = angle2;
                output_data[7] = velocity2;
                output_data[8] = current2;
                output_data[9] = current_des2;
                output_data[10]= duty_cycle2;
                // motor 3 state
                output_data[11] = angle3;
                output_data[12] = velocity3;
                output_data[13] = current3;
                output_data[14] = current_des3;
                output_data[15]= duty_cycle3;
                // foot and arm state
                output_data[16] = xFoot;
                output_data[17] = yFoot;
                output_data[18] = dxFoot;
                output_data[19] = dyFoot;
                output_data[20] = xArm;
                output_data[21] = yArm;
                output_data[22] = dxArm;
                output_data[23] = dyArm;

                output_data[24] = q_des[0];
                output_data[25] = q_des[1];
                output_data[26] = q_des[2];
                output_data[27] = qd_des[0];
                output_data[28] = qd_des[1];
                output_data[29] = qd_des[2];
                
                // can add calculations for more outputs as needed, currently not outputting desired position of arm and foot
                
                // Send data to MATLAB
                server.sendData(output_data,size_outputs);

                wait_us(impedance_control_period_us);   
                if((t > start_period)&&(t<start_period+traj_period)){    
                    iter++;}
            }
            
            // Cleanup after experiment
            server.setExperimentComplete();
            currentLoop.detach();
            motorShield.motorAWrite(0, 0); //turn motor A off
            motorShield.motorBWrite(0, 0); //turn motor B off
            motorShield.motorCWrite(0, 0); //turn motor B off
        
        } // end if
        
    } // end while
    
} // end main