
mbed motor control with emg
Dependencies: Encoder HIDScope MODSERIAL QEI TextLCD biquadFilter mbed
Fork of 2MotorPID by
main.cpp
- Committer:
- Frimzenner
- Date:
- 2017-11-07
- Revision:
- 4:ca797e7daaf4
- Parent:
- 3:c63c0a23ea21
File content as of revision 4:ca797e7daaf4:
/* * Parts of the code copied from PES lecture slides */ // include all necessary libraries #include "mbed.h" #include "QEI.h" #include "math.h" #include "iostream" #include "BiQuad.h" #include "TextLCD.h" //intialize all pins PwmOut motor1(D5); PwmOut motor2(D6); DigitalOut motor1Dir(D4); // direction of motor 1 (1 is ccw 0 is cw (looking at the shaft from the front)) DigitalOut motor2Dir(D7); // direction of motor 2 (1 is ccw 0 is cw (looking at the shaft from the front)) QEI motor1Encoder (D10,D11, NC, 624,QEI::X4_ENCODING); QEI motor2Encoder (D12,D13, NC, 624,QEI::X4_ENCODING); DigitalIn button1(D8); //button to move cw DigitalIn button2(D9); //button to move ccw //DigitalIn button3(D2); //DigitalIn button4(D3); AnalogIn bicepsEMG(A0); AnalogIn tricepsEMG(A1); AnalogIn carpiFlexEMG(A2); AnalogIn palmarisEMG(A3); DigitalOut bit1(D2); DigitalOut bit2(D14); DigitalOut bit3(D15); //initialize variables const float pi = 3.14159265358979323846; //value for pi double positionIncrement = 20; // increment of angle when button pressed (1 is a whole rotation (360 degrees)) const double motor1KP=7.0; //Proportional gain of motor1 PI control const double motor1KI=3.0; //Integral gain of motor1 PI control const double motor1KD=3.0; // Differential gain of motor1 PID control const double motor2KP=1.3; //Proportional gain of motor2 PI control const double motor2KI=0.5; //Integral gain of motor2 PI control const double motor2KD=1.0; // Differential gain of motor2 PID control const double N=100; //LP filter coefficient const double encoderToMotor= 0.000119047619047619; //proportion of the rotation of the motor to the rotation of the encoder const double controllerTickerTime=0.01; //ticker frequency double motor1ErrorInt=0; //error of motor1 for the integrating part of PI controller double motor1ErrorDif=0; //error of motor1 for the integrating part of PI controller double desiredAngle1 =0; //desired position of motor1 double motor2ErrorInt=0; //error of motor1 for the integrating part of PI controller double motor2ErrorDif=0; //error of motor1 for the integrating part of PI controller double desiredAngle2 =0; //desired position of motor2 //initialize ticker for checking and correcting the angle Ticker myControllerTicker; enum States {off,motorCalib, bicepsCalib, tricepsCalib, carpiCalib, palmarisCalib, demo, EMGControl}; States state=off; bool stateChange=false; Timer flex; double tickerF =0.002; double bicepsMOVAG [20]; double tricepsMOVAG [20]; double carpiFlexMOVAG [20]; double palmarisMOVAG [20]; int MOVAGLength=20; //Notch Filter 50Hz Q of 0.7 double bicepsNotcha0 = 0.7063988100714527; double bicepsNotcha1 = -1.1429772843080923; double bicepsNotcha2 = 0.7063988100714527; double bicepsNotchb1 = -1.1429772843080923; double bicepsNotchb2 = 0.41279762014290533; double tricepsNotcha0 = 0.7063988100714527; double tricepsNotcha1 = -1.1429772843080923; double tricepsNotcha2 = 0.7063988100714527; double tricepsNotchb1 = -1.1429772843080923; double tricepsNotchb2 = 0.41279762014290533; double carpiFlexNotcha0 = 0.7063988100714527; double carpiFlexNotcha1 = -1.1429772843080923; double carpiFlexNotcha2 = 0.7063988100714527; double carpiFlexNotchb1 = -1.1429772843080923; double carpiFlexNotchb2 = 0.41279762014290533; double palmarisNotcha0 = 0.7063988100714527; double palmarisNotcha1 = -1.1429772843080923; double palmarisNotcha2 = 0.7063988100714527; double palmarisNotchb1 = -1.1429772843080923; double palmarisNotchb2 = 0.41279762014290533; //Highpass filters 20Hz cutoff Q of 0.7 double bicepsHigha0 = 0.8370879899975344; double bicepsHigha1 = -1.6741759799950688; double bicepsHigha2 = 0.8370879899975344; double bicepsHighb1 = -1.6474576182593796; double bicepsHighb2 = 0.7008943417307579; double tricepsHigha0 = 0.8370879899975344; double tricepsHigha1 = -1.6741759799950688; double tricepsHigha2 = 0.8370879899975344; double tricepsHighb1 = -1.6474576182593796; double tricepsHighb2 = 0.7008943417307579; double carpiFlexHigha0 = 0.8370879899975344; double carpiFlexHigha1 = -1.6741759799950688; double carpiFlexHigha2 = 0.8370879899975344; double carpiFlexHighb1 = -1.6474576182593796; double carpiFlexHighb2 = 0.7008943417307579; double palmarisHigha0 = 0.8370879899975344; double palmarisHigha1 = -1.6741759799950688; double palmarisHigha2 = 0.8370879899975344; double palmarisHighb1 = -1.6474576182593796; double palmarisHighb2 = 0.7008943417307579; BiQuad bicepsNotch (bicepsNotcha0,bicepsNotcha1,bicepsNotcha2,bicepsNotchb1,bicepsNotchb2); BiQuad tricepsNotch (tricepsNotcha0,tricepsNotcha1,tricepsNotcha2,tricepsNotchb1,tricepsNotchb2); BiQuad carpiFlexNotch (carpiFlexNotcha0,carpiFlexNotcha1,carpiFlexNotcha2,carpiFlexNotchb1,carpiFlexNotchb2); BiQuad palmarisNotch (palmarisNotcha0,palmarisNotcha1,palmarisNotcha2, palmarisNotchb1,palmarisNotchb2); BiQuad bicepsHigh (bicepsHigha0,bicepsHigha1,bicepsHigha2,bicepsHighb1,bicepsHighb2); BiQuad tricepsHigh (tricepsHigha0,tricepsHigha1,tricepsHigha2,tricepsHighb1,tricepsHighb2); BiQuad carpiFlexHigh (carpiFlexHigha0,carpiFlexHigha1,carpiFlexHigha2,carpiFlexHighb1,carpiFlexHighb2); BiQuad palmarisHigh (palmarisHigha0,palmarisHigha1,palmarisHigha2, palmarisHighb1,palmarisHighb2); double bicepsAvg; double tricepsAvg; double carpiFlexAvg; double palmarisAvg; double bicepsMax=0; double tricepsMax=0; double carpiFlexMax=0; double palmarisMax=0; double bicepsMulti=0; double tricepsMulti=0; double carpiFlexMulti=0; double palmarisMulti=0; Ticker filterTicker; const float l1 = 460; //Length of the arm from base to joint 1 (arm1) ENTER MANUALLY [mm] const float l2 = 450; //length of the arm from joint 1 to the end-effector.(arm2) ENTER MANUALLY [mm] float x_des = l1+l2; //(initial)desired x location of the end-effector (ee) float y_des = 0; //(initial) desired y location of the end-effector (ee) float xe, ye, D, phi, q2, beta, alpha, q1; //other variables used in calculating the angle for the motors float radDeg, rad2rot; //q1 is the angle for the base motor, q2 is the angle for the elbow motor, both in [rad] double PIDController(double error, const double Kp, const double Ki, const double Kd, double Ts, const double N,double &intError, double &DifError) { const double a1 = -4/(N*Ts+2); const double a2 = -(N*Ts-2)/(N*Ts+2); const double b0 = (4*Kp + 4*Kd*N + 2*Ki*Ts + 2*Kp*N*Ts + Ki*N*pow(Ts,2))/(2*N*Ts + 4); const double b1 = (Ki*N*pow(Ts,2) - 4*Kp - 4*Kd*N)/(N*Ts + 2); const double b2 = (4*Kp + 4*Kd*N - 2*Ki*Ts - 2*Kp*N*Ts + Ki*N*pow(Ts,2))/(2*N*Ts + 4); double v = error - a1*intError - a2*DifError; double u = b0*v + b1*intError + b2*DifError; DifError = intError; intError = v; return u; } void sendState(States s) { if(s==motorCalib) { bit1=1; bit2=0; bit3=0; } else if(s==bicepsCalib) { bit1=0; bit2=1; bit3=0; } else if(s==tricepsCalib) { bit1=1; bit2=1; bit3=0; } else if(s==carpiCalib) { bit1=0; bit2=0; bit3=1; } else if(s==palmarisCalib) { bit1=1; bit2=0; bit3=1; } else if(s==demo) { bit1=0; bit2=1; bit3=1; } else if(s==EMGControl) { bit1=1; bit2=1; bit3=1; } } void filterBiceps() { double bicepsSignal = bicepsEMG.read(); double bicepsFiltered = bicepsSignal; //Filter out 50Hz from all signals bicepsFiltered = bicepsNotch.step(bicepsFiltered); //Highpass filtering bicepsFiltered = bicepsHigh.step(bicepsFiltered); //Rectification bicepsFiltered = fabs(bicepsFiltered); //caculate moving average for(int i=0; i<MOVAGLength-1; i++) { bicepsMOVAG[i]=bicepsMOVAG[i+1]; } bicepsMOVAG[MOVAGLength-1]=bicepsFiltered; double bicepsSum; for(int i=0; i<MOVAGLength; i++) { bicepsSum+= bicepsMOVAG[i]; } bicepsAvg= bicepsSum/MOVAGLength; } void filterTriceps() { double tricepsSignal = tricepsEMG.read(); double tricepsFiltered = tricepsSignal; //Filter out 50Hz from all signals tricepsFiltered = tricepsNotch.step(tricepsFiltered); //Highpass filtering tricepsFiltered = tricepsHigh.step(tricepsFiltered); //Rectification tricepsFiltered = fabs(tricepsFiltered); //caculate moving average for(int i=0; i<MOVAGLength-1; i++) { tricepsMOVAG[i]=tricepsMOVAG[i+1]; } tricepsMOVAG[MOVAGLength-1]=tricepsFiltered; double tricepsSum; for(int i=0; i<MOVAGLength; i++) { tricepsSum+= tricepsMOVAG[i]; } tricepsAvg= tricepsSum/MOVAGLength; } void filterCarpiFlex() { double carpiFlexSignal = carpiFlexEMG.read(); double carpiFlexFiltered = carpiFlexSignal; //Filter out 50Hz from all signals carpiFlexFiltered = carpiFlexNotch.step(carpiFlexFiltered); //Highpass filtering carpiFlexFiltered = carpiFlexHigh.step(carpiFlexFiltered); //Rectification carpiFlexFiltered = fabs(carpiFlexFiltered); //caculate moving average for(int i=0; i<MOVAGLength-1; i++) { carpiFlexMOVAG[i]=carpiFlexMOVAG[i+1]; } carpiFlexMOVAG[MOVAGLength-1]=carpiFlexFiltered; double carpiFlexSum; for(int i=0; i<MOVAGLength; i++) { carpiFlexSum+= carpiFlexMOVAG[i]; } carpiFlexAvg= carpiFlexSum/MOVAGLength; } void filterPalmaris() { double palmarisSignal = palmarisEMG.read(); double palmarisFiltered = palmarisSignal; //Filter out 50Hz from all signals palmarisFiltered = palmarisNotch.step(palmarisFiltered); //Highpass filtering palmarisFiltered = palmarisHigh.step(palmarisFiltered); //Rectification palmarisFiltered = fabs(palmarisFiltered); //caculate moving average for(int i=0; i<MOVAGLength-1; i++) { palmarisMOVAG[i]=palmarisMOVAG[i+1]; } palmarisMOVAG[MOVAGLength-1]=palmarisFiltered; double palmarisSum; for(int i=0; i<MOVAGLength; i++) { palmarisSum+= palmarisMOVAG[i]; } palmarisAvg= palmarisSum/MOVAGLength; } void filter() { filterBiceps(); filterTriceps(); filterCarpiFlex(); filterPalmaris(); } //Code for motor angles as a function of the length and positions of the arms, for a double revolutional joint arm in 2D plane void motorAngle() { //Function for making sure the arm does not exceed its maximum reach //if it tries to go beyond its max. reach //it will try to reach a point within reach in the same direction as desired. //Works for all 4 quadrants of the (unit) circle xe = x_des; ye = y_des; //while(pow(xe, 2)+pow(ye,2) > pow(l1+l2, 2)){ //when attempted to go to a point out of reach // if (y_des == 0){ //make sure you do not divide by 0 if ye == 0 // xe = x_des - 1; // } else { // xe = x_des - (x_des/y_des)/10; //go to a smaller xe point on the same line // } // if (x_des == 0){ //make sure you do not divide by 0 if xe == 0 // ye = y_des - 1; // } else { // ye = y_des - (y_des/x_des)/10; //go to a smaller ye point on the same line // } // x_des = xe; // y_des = ye; // } // while(pow(xe, 2)+pow(ye, 2) > pow(l1+l2, 2)) { // if (xe > 0) { //right hand plane // if (ye > 0) { //positive x & y QUADRANT 1 // xe = x_des - (x_des/y_des); //go to a smaller xe point on the same line // //(x_des/y_des) can be multiplied or divided to make bigger or smaller steps back if you're trying to exceed max. reach // ye = y_des - (y_des/x_des); //go to a smaller ye point on the same line // } else if (ye < 0) { //positive x, negative y QUADRANT 2 // xe = x_des - (x_des/y_des); //smaller xe // ye = y_des + (y_des/x_des); //greater ye // } else if (ye == 0) { //positive x, y == 0 // xe = x_des - 1; //stay on the x-axis but at a smaller value (within reach) // } // } else if (xe < 0) { //left hand plane // if (ye > 0) { //negative x, positive y QUADRANT 4 // xe = x_des + (x_des/y_des); //greater xe // ye = y_des - (y_des/x_des); //smaller ye // } else if (ye < 0) { //negative x & y QUADRANT 3 // xe = x_des + (x_des/y_des); //greater xe // ye = y_des + (y_des/x_des); //greater ye // } else if (ye ==0) { //negative x, y == 0 // xe = x_des + 1; //stay on the x-axis but at a greater value (within reach) // } // // } else if (xe == 0) { //on the y-axis // if (ye > 0) { //x == 0, positive y // ye = y_des - 1; //stay on the y-axis but at a smaller value (within reach) // } else if (ye < 0) { //x == 0, negative y // ye = y_des + 1; //stay on the y-axis but at a greater value (within reach) // //x == 0 & y == 0 is a state that is unable to be reached, no need to cover that case // } // x_des = xe; // y_des = ye; // } // } //from here on is the code for setting the angles for the motors D = ((pow(l1,2)+pow(l2,2))-pow(xe,2)-pow(ye,2))/(2*l1*l2); //D = cos(phi) phi = atan2(sqrt(1 - pow(D, 2)), D); //angle between arm1 and arm2, from arm1 to arm2 [rad] //Use atan2(sqrt(1 - pow(D, 2)),D) for "elbow down" position (like your right arm) q2 = pi - phi; //angle of arm2 with respect to the orientation of arm1, motor2 [rad] if (-pi/2 > q2) { //Make sure the angle of motor2 doesn’t wreck our setup (max -90 or 90 degrees w.r.t. arm1) q2 = 0; } else if ( q2 > pi/2) { q2 = pi/2; } beta = atan2(ye, xe); //angle between "line from origin to ee" and x-axis [rad] alpha = atan2(l2*sin(q2), l1+l2*cos(q2)); //angle between "line from origin to ee" and arm1 [rad] q1 = beta - alpha; //angle of arm 1 with respect to the x-axis, motor1 [rad] radDeg = 180/pi; rad2rot = radDeg/360; desiredAngle1 = q1 * rad2rot; desiredAngle2 = q2 * rad2rot; } void motorButtonController() { double angle1= encoderToMotor*motor1Encoder.getPulses(); double angleError1 = (desiredAngle1 - angle1)*1.25; //change direction based on error sign if(PIDController( angleError1, motor1KP, motor1KI,motor1KD, controllerTickerTime, N, motor1ErrorInt ,motor1ErrorDif)>0) { motor1Dir=0; } else { motor1Dir =1; } //set motor speed based on PI controller error motor1 = fabs(PIDController( angleError1, motor1KP, motor1KI,motor1KD, controllerTickerTime, N, motor1ErrorInt ,motor1ErrorDif)); double angle2= encoderToMotor*motor2Encoder.getPulses(); double angleError2 = desiredAngle2 - angle2; //change direction based on error sign if(PIDController( angleError2, motor2KP, motor2KI,motor2KD, controllerTickerTime, N, motor2ErrorInt ,motor2ErrorDif)>0) { motor2Dir=0; } else { motor2Dir =1; } //set motor speed based on PI controller error motor2 = fabs(PIDController( angleError2, motor2KP, motor2KI,motor2KD, controllerTickerTime, N, motor2ErrorInt ,motor2ErrorDif)); } int main() { wait(2); xe = x_des; ye = y_des; myControllerTicker.attach(&motorButtonController, controllerTickerTime); bit1=0; bit2=0; bit3=0; wait(5); for(int i=0; i<MOVAGLength; i++) { bicepsMOVAG[i]=0; tricepsMOVAG[i]=0; carpiFlexMOVAG[i]=0; palmarisMOVAG[i]=0; } filterTicker.attach(filter,tickerF); //start up state=bicepsCalib; stateChange=true; while(true) { if(palmarisAvg*palmarisMax > 0.9) { y_des -= positionIncrement; motorAngle(); wait(0.3f); } if(carpiFlexAvg*carpiFlexMax > 0.9) { y_des += positionIncrement; motorAngle(); wait(0.3f); } if(bicepsAvg*bicepsMulti > 0.9) { x_des -= positionIncrement; motorAngle(); wait(0.3f); } if(tricepsAvg*tricepsMulti > 0.9) { x_des += positionIncrement; motorAngle(); wait(0.3f); } if(stateChange) { switch(state) { case motorCalib : stateChange=false; sendState(motorCalib); break; case bicepsCalib : stateChange=false; sendState(bicepsCalib); wait(5); flex.reset(); flex.start(); while(flex.read()<2) { if(bicepsAvg>bicepsMax) { bicepsMax=bicepsAvg; } } bit1 = 0; bit2 = 0; bit3 = 0; wait(5); state=tricepsCalib; stateChange=true; break; case tricepsCalib : stateChange=false; sendState(tricepsCalib); wait(5); flex.reset(); flex.start(); while(flex.read()<2) { if(tricepsAvg>tricepsMax) { tricepsMax=tricepsAvg; } } bit1 = 0; bit2 = 0; bit3 = 0; wait(5); state=carpiCalib; stateChange=true; break; case carpiCalib : stateChange=false; sendState(carpiCalib); wait(5); flex.reset(); flex.start(); while(flex.read()<2) { if(carpiFlexAvg>carpiFlexMax) { carpiFlexMax=carpiFlexAvg; } } bit1 = 0; bit2 = 0; bit3 = 0; wait(5); state=palmarisCalib; stateChange=true; break; case palmarisCalib : stateChange=false; sendState(palmarisCalib); wait(5); flex.reset(); flex.start(); while(flex.read()<2) { if(palmarisAvg>palmarisMax) { palmarisMax=palmarisAvg; } } bit1 = 0; bit2 = 0; bit3 = 0; wait(5); state=EMGControl; stateChange=true; break; case demo : stateChange=false; sendState(demo); break; case EMGControl : stateChange=false; sendState(EMGControl); if(bicepsMax != 0) { bicepsMulti=1/bicepsMax; } if(tricepsMax != 0) { tricepsMulti=1/tricepsMax; } if(carpiFlexMax != 0) { carpiFlexMulti=1/carpiFlexMax; } if(palmarisMax != 0) { palmarisMulti=1/palmarisMax; } break; } } } }