Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: HIDScope MODSERIAL QEI biquadFilter mbed Servo
main.cpp
- Committer:
- SvenD97
- Date:
- 2018-10-29
- Revision:
- 32:2596cc9156ec
- Parent:
- 31:393a7ec1d396
- Child:
- 33:eb77ed8d167c
File content as of revision 32:2596cc9156ec:
#include "mbed.h" #include "MODSERIAL.h" #include "QEI.h" #include "HIDScope.h" #include "BiQuad.h" #include "PID_controller.h" #include "kinematics.h" #include "processing_chain_emg.h" //Define objects MODSERIAL pc(USBTX, USBRX); HIDScope scope(2); // emg inputs AnalogIn emg0( A0 ); AnalogIn emg1( A1 ); // motor ouptuts PwmOut motor1_pwm(D5); DigitalOut motor1_dir(D4); PwmOut motor2_pwm(D7); DigitalOut motor2_dir(D6); // defining encoders QEI motor_1_encoder(D12,D13,NC,32); QEI motor_2_encoder(D10,D11,NC,32); // other objects AnalogIn potmeter1(A2); AnalogIn potmeter2(A3); DigitalIn button(D0); DigitalOut led_R(LED_RED); DigitalOut led_B(LED_BLUE); DigitalOut led_G(LED_GREEN); // tickers and timers Ticker loop_ticker; Timer state_timer; Timer emg_timer; enum States {failure, waiting, calib_emg, calib_enc, operational, demo, homing}; //All possible robot states enum Emg_measures_states {not_in_calib_emg, calib_right_bicep, calib_right_tricep, calib_left_bicep, calib_left_tricep}; // States inside //Global variables/objects States current_state; Emg_measures_states current_emg_calibration_state = not_in_calib_emg; double des_vx, des_vy, x, y, q1, q2, qref1, qref2, e1, e2, u1, u2; //will be set by the motor_controller function double vxmax = 1.0, vymax = 1.0; double right_bicep_max = 0.0, right_tricep_max = 0.0, left_bicep_max= 0.0, left_tricep_max = 0.0; // Variables for emg double raw_emg_0, process_emg_0; double raw_emg_1, process_emg_1; double raw_emg_2, process_emg_2; double raw_emg_3, process_emg_3; // Variables defined for the homing state double q1_homing = 0, q2_homing = 0; double q1_dot_ref_homing, q2_dot_ref_homing; double beta = 5; double k_hom = 2; // For the state calib_enc double q1old; double q2old; // Meaning of process_emg_0 and such // - process_emg_0 is right biceps // - process_emg_1 is right triceps // - process_emg_2 is left biceps // - process_emg_3 is left triceps int counts_per_rotation = 32; bool state_changed = false; const double T = 0.001; // Resolution of the encoder at the output axle double resolution = (2.0f*3.1415926535f/double(counts_per_rotation))*(1.0/131.0); // In radians // Functions void measure_all() { q1 = motor_1_encoder.getPulses()*2.0f*3.1415926535f/counts_per_rotation; //do this here, and not in the encoder interrupt, to reduce computational load q2 = motor_2_encoder.getPulses()*2.0f*3.1415926535f/counts_per_rotation; forwardkinematics_function(q1,q2,x,y); //motor_angle is global, this function ne raw_emg_0 = emg0.read(); //sample analog voltages (all sampling theory applies, you might get aliasing etc.) raw_emg_1 = emg1.read(); processing_chain_emg(raw_emg_0, raw_emg_1, process_emg_0, process_emg_1); // processes the emg signals } void output_all() { motor1_pwm = fabs(u1); motor1_dir = u1 > 0.0f; motor2_pwm = fabs(u2); motor2_dir = u2 > 0.0f; static int output_counter = 0; output_counter++; if (output_counter == 100) {pc.printf("Something something... %f",u1); output_counter = 0;} //Print to screen at 10 Hz with MODSERIAL } void state_machine() { switch(current_state) { //States can be: failure, wait, calib_enc, calib_emg, operational, demo, case waiting: //Nothing useful here, maybe a blinking LED for fun and communication to the user if (button.read()==true) { current_state = calib_enc; //the NEXT loop we will be in calib_enc state state_changed = true; } break; //to avoid falling through to the next state, although this can sometimes be very useful. case calib_enc: if (state_changed==true) { state_timer.reset(); state_timer.start(); state_changed = false; n = 0; led_G = 0; led_B = 1; led_R = 1; u1 = 0.55f; //a low PWM value to move the motors slowly (0.0 to 0.45 don’t do much due to friction) u2 = 0.55f; q1old = 0; q2old = 0; } if (q1-q1old == 0.0 && q2 - q2old < 0.0 && state_timer.read() > 5.0f) { current_state = calib_emg; //the NEXT loop we will be in calib_emg state current_emg_calibration_state = calib_right_bicep; state_changed = true; } q1old = q1; q2old = q2; n++; if (n%1000 == 0) { led_G = !led_G; } break; case calib_emg: //calibrate emg-signals if (state_changed == true){ state_timer.reset(); state_timer.start(); emg_timer.reset(); emg_timer.start(); state_changed = false; } switch(current_emg_calibration_state){ case calib_right_bicep: if(emg_timer < 5.0f){ if (process_emg_0 > right_bicep_max){ right_bicep_max = process_emg_0; } } else if (process_emg_0 < 0.1*right_bicep_max){ current_emg_calibration_state = calib_right_tricep; emg_timer.reset(); emg_timer.start(); } break; case calib_right_tricep: if(emg_timer < 5.0f){ if (process_emg_1 > right_tricep_max){ right_tricep_max = process_emg_1; } } else if (process_emg_1 < 0.1*right_tricep_max){ current_emg_calibration_state = calib_left_bicep; emg_timer.reset(); emg_timer.start(); } break; case calib_left_bicep: if(emg_timer < 5.0f){ if (process_emg_2 > left_bicep_max){ left_bicep_max = process_emg_2; } } else if (process_emg_2 < 0.1*left_bicep_max){ current_emg_calibration_state = calib_left_tricep; emg_timer.reset(); emg_timer.start(); } break; case calib_left_tricep: if(emg_timer < 5.0f){ if (process_emg_3 > left_tricep_max){ left_tricep_max = process_emg_3; } } else if (process_emg_3 < 0.1*left_tricep_max){ current_emg_calibration_state = not_in_calib_emg; current_state = homing; state_changed = true; emg_timer.reset(); } break; default: current_state = failure; state_changed = true; } break; case homing: if (state_changed == true){ state_timer.reset(); state_timer.start(); qref1 = q1; //NOT SURE IF WE NEED THIS. I do not think so, but just to be sure. qref2 = q2; } q1_dot_ref_homing = min(beta, k_hom*(q1 - q1_homing)); q2_dot_ref_homing = min(beta, k_hom*(q2 - q2_homing)); qref1 = q1_dot_ref_homing*T + q1; qref2 = q2_dot_ref_homing*T + q2; // The value of 3.0 and 2*resolution can be changed if (fabs(q1-q1_homing) <= 2*resolution && fabs(q2-q2_homing) <= 2 * resolution ){ if (state_timer > 3.0f){ current_state = operational; state_changed = true; } } else{ state_timer.reset(); } break; case operational: //interpreting emg-signals to move the end effector if (state_changed == true){ state_changed = false; } // here we have to determine the desired velocity based on the processed emg signals and calibration if (process_emg_0 >= 0.16) { des_vx = vxmax; } else if(process_emg_0 >= 0.09) { des_vx = vxmax * 0.66; } else if(process_emg_0 >= 0.02) { des_vx = vxmax * 0.33; } else { des_vx = 0; } if (process_emg_1 >= 0.16) { des_vy = vymax; } else if(process_emg_1 >= 0.09) { des_vy = vymax * 0.66; } else if(process_emg_1 >= 0.02) { des_vy = vymax * 0.33; } else { des_vy = 0; } if (button.read() == true) { current_state = demo; state_changed = true; } // Isn't this going to cause problems as it will switch on and of very quickly? break; case demo: //moving according to a specified trajectory if (state_changed == true){ state_changed = false; } if (button.read() == true) { current_state = operational; state_changed = true; } break; case failure: //no way to get out u1 = 0.0f; u2 = 0.0f; led_R = 0; led_G = 1; led_B = 1; break; } } void motor_controller() { if (current_state >= operational) { // we can (ab)use the fact that an enum is actually an integer, so math/logic rules still apply inversekinematics_function(x,y,T,qref1,qref2,q1,q2,des_vx,des_vy); //many different states can modify your reference position, so just do the inverse kinematics once, here e1 = qref1 - q1; //tracking error (q_ref - q_meas) e2 = qref2 - q2; PID_controller(e1,e2,u1,u2,T); //feedback controller or with possibly fancy controller additions...; pass by reference } //otherwise we just don’t mess with the value of control variable ‘u’ that is set somewhere in the state-machine. } void loop_function() { measure_all(); //measure all signals state_machine(); //Do relevant state dependent things motor_controller(); //Do not put different motor controllers in the states, because every state can re-use the same motor-controller! output_all(); //Output relevant signals, messages, screen outputs, LEDs etc. } int main() { pc.baud(115200); motor1_pwm.period_us(60); motor2_pwm.period_us(60); current_state = waiting; //we start in state ‘waiting’ and current_state can be accessed by all functions u1 = 0.0f; //initial output to motors is 0. u2 = 0.0f; bqc0.add(&bq0high).add(&bq0notch); // filter cascade for emg bqc1.add(&bq1high).add(&bq1notch); // filter cascade for emg loop_ticker.attach(&loop_function, T); //Run the function loop_function 1000 times per second led_R = 1; led_B = 1; led_G = 1; while (true) { } //Do nothing here (timing purposes) }