
presentatie versie met potmeters enabled
Dependencies: Encoder HIDScope mbed
Diff: main.cpp
- Revision:
- 5:ad4ae6b65474
- Parent:
- 4:9684b6f8b63c
- Child:
- 6:30afff8ae34a
--- a/main.cpp Wed Oct 28 13:34:22 2015 +0000 +++ b/main.cpp Thu Oct 29 10:33:03 2015 +0000 @@ -1,19 +1,17 @@ #include "mbed.h" -#include "MODSERIAL.h" #include "encoder.h" #include "HIDScope.h" #include "math.h" -Serial pc(USBTX,USBRX); // MODSERIAL output HIDScope scope(4); // definieerd het aantal kanalen van de scope // Define Tickers and control frequencies -Ticker controller1, controller2, main_filter, cartesian, send; +Ticker controller1, controller2, main_filter, cartesian; // Go flag variables belonging to Tickers volatile bool motor1_go = false, motor2_go = false, emg_go = false, cart_go = false; // Frequency control - double controlfreq = 50 ; // controlloops frequentie (Hz) + double controlfreq = 200 ; // controlloops frequentie (Hz) double controlstep = 1/controlfreq; // timestep derived from controlfreq double EMG_freq = 200; @@ -47,9 +45,9 @@ bool reset = false; // axis control - DigitalIn switch_xy_button(D0); + DigitalIn program_button(D0); // init values - bool switch_xy = false; + bool program = false; // LED outputs on bioshield DigitalOut led_right(D2); // LED outputs on dev-board @@ -62,9 +60,9 @@ ////////////////////////// GLOBAL VARIABLES /////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////// -double t = 0; // used for circle movement - -double sw1 = 0; + bool switch_xy = false; // bool for switching + double sw1 = 0; // counter for switching axes + double t_switch = 0.6; // seconds for switching // physical constants const double L = 36; // lenght of arms const double pi = 3.1415926535897; // pi @@ -144,7 +142,7 @@ double emg_gain2 = 7; double cal_time = 2.5; // amount of time calibration should take (seconds) -double cal_samples = controlfreq*cal_time; // amount of samples that is used (dependent on the frequency of the filter) +double cal_samples = EMG_freq*cal_time; // amount of samples that is used (dependent on the frequency of the filter) double normalize_emg_value = 1; // set the desired value to calibrate the signal to (eg max signal = 1) // FILTER VARIABLES @@ -198,7 +196,7 @@ // counter double mt1 = 0; // time -double T1 = 4, T2 = T1 + 2, T3 = T2 + 6, T4 = T3 + 6, T5 = T4 + 2, T6 = T5 + 4; +double T1 = 4, T2 = T1 + 2, T3 = T2 + 6, T4 = T3 + 1, T5 = T4 + 6, T6 = T5 + 4, T7 = T6 + 4, T8 = T7 + 1; // x,y coordinates //double x1 = 55, y1 = -15; //double x2 = 55, y2 = 15; @@ -209,9 +207,11 @@ double x1 = 50, y1 = -37; double x2 = 60, y2 = -37; double x3 = 60, y3 = 30; -double x4 = 60, y4 = -37; -double x5 = 50, y5 = -37 ; -double x6 = 50, y6 = 0; +double x4 = 60, y4 = 30; +double x5 = 60, y5 = -37; +double x6 = 50, y6 = -37 ; +double x7 = 50, y7 = 0; +double x8 = 50, y8 = 0; //////////////////////////////////////////////////////////////// /////////////////// START OF SIDE FUNCTIONS //////////////////// @@ -307,11 +307,11 @@ // function that limits the angles that can be used in the motor reference signal double angle_limits(double phi, double limlow, double limhigh) { - if(phi < limlow) + if(phi < limlow) // determine if the reference is lower than the minimum angle { - phi = limlow; + phi = limlow; // if lower, set the lower limit as reference. } - if(phi > limhigh) + if(phi > limhigh) // determine if the reference is higher than the maximum angle { phi = limhigh; } @@ -323,7 +323,7 @@ double adapt_signal(double input) { // add threshold value for outputs - if(input < input_threshold){input = 0;} + if(input < input_threshold){input = 0;} // set the input as zero if the signal is lower than the threshold // return the input to a value between 0 and 1 (otherwise you will get jumps in input) input = (input-input_threshold) * (1/(1-input_threshold)); @@ -336,49 +336,74 @@ return input; } -// send stuff to putty to check things -void mod_send() + +void switch_axes (double input1,double input2) { - pc.printf("xx = %f, yy = %f, phi1 = %f, phi2 = %f \n",xx,yy,phi_one,phi_two); + if(input1 > input_threshold && input2 > input_threshold) // when both signals are above the threshold, add one to global counter + { + sw1++; + } + if(sw1 == t_switch*controlfreq) // if global counter > s*freq flip the bool. + { + switch_xy = !switch_xy; + led_right.write(!led_right.read()); // turn on led when switched + sw1 = 0; + } + if(input1 < input_threshold || input2 < input_threshold) // if one becomes lower than the threshold, set the global variable to zero + { + sw1 = 0; + } } +// this function allows the xx and yy variables to follow a figure determined by a set of coordinates +// this function is very simple, can be (possibly) improved by writing a single loop. void square_move() { if (mt1 > 0 && mt1 < T1*controlfreq) // horizontal movement from (65,-20) -> (55,-20) { - xx = x6 + (x1-x6)*(mt1/(T1*controlfreq)); - yy = y6 + (y1-y6)*(mt1/(T1*controlfreq)); + xx = x8 + (x1-x8)*(mt1/(T1*controlfreq)); + yy = y8 + (y1-y8)*(mt1/(T1*controlfreq)); } - else if (mt1 >= T1*controlfreq && mt1 < T2*controlfreq) // vertical movement (55,-20) -> (55,20) + else if (mt1 >= T1*controlfreq && mt1 < T2*controlfreq) { xx = x1 + (x2-x1)*(mt1-T1*controlfreq)/(T2*controlfreq-T1*controlfreq) ; yy = y1 + (y2-y1)*(mt1-T1*controlfreq)/(T2*controlfreq-T1*controlfreq) ; } - else if (mt1 >= T2*controlfreq && mt1 < T3*controlfreq) // horizontal movement (55,20) -> (65,20) + else if (mt1 >= T2*controlfreq && mt1 < T3*controlfreq) { xx = x2 + (x3-x2)*(mt1-T2*controlfreq)/(T3*controlfreq-T2*controlfreq) ; yy = y2 + (y3-y2)*(mt1-T2*controlfreq)/(T3*controlfreq-T2*controlfreq); } - else if (mt1 >= T3*controlfreq && mt1 < T4*controlfreq) // vertical movement (65,20) -> (65,-20) + else if (mt1 >= T3*controlfreq && mt1 < T4*controlfreq) { xx = x3 + (x4-x3)*(mt1-T3*controlfreq)/(T4*controlfreq-T3*controlfreq) ; yy = y3 + (y4-y3)*(mt1-T3*controlfreq)/(T4*controlfreq-T3*controlfreq) ; } - else if (mt1 >= T4*controlfreq && mt1 < T5*controlfreq) // vertical movement (65,20) -> (65,-20) + else if (mt1 >= T4*controlfreq && mt1 < T5*controlfreq) { xx = x4 + (x5-x4)*(mt1-T4*controlfreq)/(T5*controlfreq-T4*controlfreq) ; yy = y4 + (y5-y4)*(mt1-T4*controlfreq)/(T5*controlfreq-T4*controlfreq) ; } - else if (mt1 >= T5*controlfreq && mt1 < T6*controlfreq) // vertical movement (65,20) -> (65,-20) + else if (mt1 >= T5*controlfreq && mt1 < T6*controlfreq) { xx = x5 + (x6-x5)*(mt1-T5*controlfreq)/(T6*controlfreq-T5*controlfreq) ; yy = y5 + (y6-y5)*(mt1-T5*controlfreq)/(T6*controlfreq-T5*controlfreq) ; } - else if (mt1 >= T6*controlfreq) + else if (mt1 >= T6*controlfreq && mt1 < T7*controlfreq) + { + xx = x6 + (x7-x6)*(mt1-T6*controlfreq)/(T7*controlfreq-T6*controlfreq) ; + yy = y6 + (y7-y6)*(mt1-T6*controlfreq)/(T7*controlfreq-T6*controlfreq) ; + } + else if (mt1 >= T7*controlfreq && mt1 < T8*controlfreq) + { + xx = x7 + (x8-x7)*(mt1-T7*controlfreq)/(T8*controlfreq-T7*controlfreq) ; + yy = y7 + (y8-y7)*(mt1-T7*controlfreq)/(T8*controlfreq-T7*controlfreq) ; + } + else if (mt1 >= T8*controlfreq) { mt1 = 0; - xx = x6; - yy = y6; + xx = x8; + yy = y8; } mt1++; } @@ -390,7 +415,7 @@ // function that updates the values of the filtered emg-signal void EMG_filter() { -// filteren van EMG signaal 1 (A0) eerst notch(2 biquads), dan highpass, rectify(abs()), lowpass +// filtering of EMG signal 1 (A0) first notch(2 biquads), then highpass, rectify(abs()), lowpass double u1 = EMG_in.read(); double y1 = biquadfilter( u1, f1_v1, f1_v2,dennotch50biq1_2, dennotch50biq1_3,numnotch50biq1_1,numnotch50biq1_2,numnotch50biq1_3); double y2 = biquadfilter( y1, f2_v1, f2_v2,dennotch50biq2_2, dennotch50biq2_3,numnotch50biq2_1,numnotch50biq2_2,numnotch50biq2_3); @@ -398,10 +423,10 @@ double y4 = abs(y3); double y5 = biquadfilter( y4, f4_v1, f4_v2, denlow5_2,denlow5_3,numlow5_1, numlow5_2, numlow5_3); // update global variables - output1 = y5; - output1_amp = y5*emg_gain1; // update global variable + output1 = y5; // output for calibration + output1_amp = y5*emg_gain1; // output for control loop -// filteren van EMG signaal 2 (A2), zelfde proces als signaal 1 +// filtering of EMG signal 2 (A2) same as before double u1t = EMG_int.read(); double y1t = biquadfilter( u1t, f1_v1t, f1_v2t,dennotch50biq1_2, dennotch50biq1_3,numnotch50biq1_1,numnotch50biq1_2,numnotch50biq1_3); double y2t = biquadfilter( y1t, f2_v1t, f2_v2t,dennotch50biq2_2, dennotch50biq2_3,numnotch50biq2_1,numnotch50biq2_2,numnotch50biq2_3); @@ -411,12 +436,11 @@ // update global variables output2 = y5t; output2_amp = y5t*emg_gain2; - //scope.set(0,output1_amp); - //scope.set(1,output2_amp); - //scope.set(2,u1); - //scope.set(3,u1t); - //scope.send(); - } + + scope.set(0,output1_amp); + scope.set(1,output2_amp); + scope.send(); +} // function that updates the required motor angles from the current filtered emg @@ -430,59 +454,37 @@ //xy_input1 = potright.read(); //xy_input2 = potleft.read(); + // add a threshold to the signals and limit to [0,1] xy_input1 = adapt_signal(xy_input1); xy_input2 = adapt_signal(xy_input2); - if(xy_input1 > input_threshold && xy_input2 > input_threshold) - { - sw1++; - } - if(sw1 == 30) - { - switch_xy = !switch_xy; - led_right.write(!led_right.read()); // turn on led when switched - sw1 = 0; - } - if(xy_input1 < input_threshold || xy_input2 < input_threshold) - { - sw1 = 0; - } - + // function that when both muscles are above a threshold for 3/5s switches the axes + switch_axes(xy_input1,xy_input2); + double xy_main_input = xy_input1 - xy_input2 ; // subtract inputs to create a signal that can go from -1 to 1 //scope.set(0,xy_main_input); // limit the output between -1 and 1 (signal is not supposed to be able to go above but last check) if(xy_main_input>1) {xy_main_input = 1;} if(xy_main_input<-1) {xy_main_input = -1;} - - - // calculate the y limits belonging to that particular x coordinate and update global variables + + // calculate the y limits belonging to that particular x coordinate and update global variables y_min = - sqrt(5184 - pow(xx,2)); if(y_min<y_min_max){y_min = y_min_max;} // make sure the arm cannot hit the table (may later be removed) y_max = sqrt(5184 - pow(xx,2)); - if(switch_xy == false) // use the signal to change the x-reference - { - xx = reference_f(xy_main_input,c_reference_x,x_min,x_max,Vmax_x); // change the global x-reference - } - if(switch_xy == true) // use the signal to change the y-reference - { - yy = reference_f(xy_main_input,c_reference_y,y_min,y_max,Vmax_y); // change the y-reference - } - square_move(); - // check the y-reference (otherwise if x is controlled after y has been controlled, the circle is not followed). + // use the signal to change the x-reference + if(switch_xy == false){xx = reference_f(xy_main_input,c_reference_x,x_min,x_max,Vmax_x);} + // use the signal to change the y-reference + if(switch_xy == true){yy = reference_f(xy_main_input,c_reference_y,y_min,y_max,Vmax_y);} + + // start the pre-programmed movement if a button has been pressed + if(program){square_move();} + + // check the y-reference (otherwise if x is controlled after y has been controlled, the limits are not followed). if(yy < y_min){yy = y_min;} if(yy > y_max){yy = y_max;} - scope.set(0,xx); - scope.set(1,yy); - scope.send(); - - // let the arm make a circle (testing) - // xx = 60 + 5*cos(t); - // yy = 5*sin(t); - // t = t + 0.01; - // x-y to arm-angles math double r = sqrt(pow(xx,2)+pow(yy,2)); // vector naar end effector double alfa = acos((2*pow(L,2)-pow(r,2))/(2*pow(L,2))); // alfa is de hoek tussen upper en lower arm @@ -492,7 +494,8 @@ // convert arm-angles to motor angles( (x transmission) and offset (+ offset) to account for reset position) double phi1 = 4*(theta_one) + phi_one_offset; - double phi2 = 4*(theta_one+theta_two) + phi_two_offset; // math assumes angle relative to first arm. motor does not change relative orientation, so angle wrt horizontal position is needed. + // math assumes angle relative to first arm. motor does not change relative orientation, so angle wrt horizontal position is needed. + double phi2 = 4*(theta_one+theta_two) + phi_two_offset; phi2 = -phi2; // reverse angle because of transmission. // check the angles and apply the limits @@ -506,7 +509,6 @@ // write into global variables phi_one = phi1; phi_two = phi2; - // if the reset button has been pressed, continiously write the start position into the global variables to reset the arm if(reset == true) { @@ -530,14 +532,10 @@ rc1++; reference1 = phi_one_curr + ((double) rc1/start_loops)*(reference1-phi_one_curr); } - double rads1 = get_radians(motor1_enc.getPosition()); // determine the position of the motor double error1 = (reference1 - rads1); // determine the error (reference - position) double m_output1 = PID(error1, m1_Kp, m1_Ki, m1_Kd, controlstep, m1_err_int, m1_prev_err); - // check the real angles and stop if exceeded (NaN protection) // doesnt work because start angle is lower so output stays 0 - // if(rads1 < (limlow1 - 0.05)){m_output1 = 0;} - // if(rads1 > (limhigh1 + 0.05)){m_output1 = 0;} - m_output1 = outputlimiter(m_output1,1); // relimit the output between -1 and 1 for safety + m_output1 = outputlimiter(m_output1,1); // relimit the output between -1 and 1 for safety if(m_output1 > 0) { // uses the calculated output to determine the direction of the motor motor1_rich.write(0); motor1_aan.write(m_output1); @@ -559,14 +557,10 @@ { rc2++; reference2 = phi_two_curr + ((double) rc2/start_loops)*(reference2-phi_two_curr); - } - + } double rads2 = get_radians(motor2_enc.getPosition()); // determine the position of the motor double error2 = (reference2 - rads2); // determine the error (reference - position) double m_output2 = PID(error2, m2_Kp, m2_Ki, m2_Kd, controlstep, m2_err_int, m2_prev_err); - // check the real angles and stop if exceeded - // if(rads2 < limlow2 - 0.05){m_output2 = 0;} - // if(rads2 > limhigh2 + 0.05){m_output2 = 0;} m_output2 = outputlimiter(m_output2,1); // final output limit (not really needed, is for safety) if(m_output2 > 0) { // uses the calculated output to determine the direction of the motor motor2_rich.write(0); @@ -591,13 +585,11 @@ if(input1>max1){max1 = input1;} // take max input double input2 = output2; if(input2>max2){max2 = input2;} // take max input - wait(controlstep); // !! has to run at same interval as filter in main loop !! otherwise a 'different' signal will be used for calibration + wait(EMG_step); // !! has to run at same interval as filter in main loop !! otherwise a 'different' signal will be used for calibration } emg_gain1 = normalize_emg_value/max1; // normalize the amplification so that the maximum signal hits the desired one emg_gain2 = normalize_emg_value/max2; - pc.printf("gain1 = %f, gain2 = %f",emg_gain1,emg_gain2); // print the calculated gains to putty - -} + } ////////////////////////////////////////////////////////////////// //////////// DEFINE GO-FLAG FUNCTIONS /////////////////////////// //////////////////////////////////////////////////////////////// @@ -610,12 +602,10 @@ int main() { - pc.baud(115200); main_filter.attach(&EMG_activate, EMG_step); cartesian.attach(&angle_activate, controlstep); controller1.attach(&motor1_activate, controlstep); // call a go-flag controller2.attach(&motor2_activate, controlstep); - send.attach(&mod_send, 1); // send data (only global variables) to putty while(true) { // button press functions @@ -632,13 +622,14 @@ wait(0.2); while(buttonrechts.read() == 0); } - // reverse buttons - if(switch_xy_button.read() == 0) + // start pre-programmed movement + if(program_button.read() == 0) { - switch_xy = !switch_xy; - led_right.write(!led_right.read()); // turn on led when switched to y control + program = !program; + rc1 = 0; + rc2 = 0; wait(0.2); - while(switch_xy_button.read() == 0); + while(program_button.read() == 0); } if(reset_button.read() == 0) { @@ -649,7 +640,6 @@ rc2 = 0; wait(0.2); while(reset_button.read() == 0); - } ////////////////////////////////////////////////// // Main Control stuff and options