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@15:7d3b138446fa, 2018-10-25 (annotated)
- Committer:
- SvenD97
- Date:
- Thu Oct 25 08:51:32 2018 +0000
- Branch:
- bla
- Revision:
- 15:7d3b138446fa
- Parent:
- 14:4744cc6c90f4
- Child:
- 16:0280a604cf7e
Included motor encoder;
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
MaikOvermars | 0:4cb1de41d049 | 1 | #include "mbed.h" |
MaikOvermars | 0:4cb1de41d049 | 2 | #include "MODSERIAL.h" |
MaikOvermars | 0:4cb1de41d049 | 3 | #include "QEI.h" |
MaikOvermars | 0:4cb1de41d049 | 4 | #include "HIDScope.h" |
MaikOvermars | 0:4cb1de41d049 | 5 | #include "BiQuad.h" |
MaikOvermars | 0:4cb1de41d049 | 6 | #include "PID_controller.h" |
MaikOvermars | 0:4cb1de41d049 | 7 | #include "kinematics.h" |
MaikOvermars | 0:4cb1de41d049 | 8 | |
MaikOvermars | 0:4cb1de41d049 | 9 | //Define objects |
MaikOvermars | 0:4cb1de41d049 | 10 | MODSERIAL pc(USBTX, USBRX); |
MaikOvermars | 0:4cb1de41d049 | 11 | HIDScope scope(2); |
MaikOvermars | 0:4cb1de41d049 | 12 | |
MaikOvermars | 10:7339dca7d604 | 13 | // emg inputs |
MaikOvermars | 0:4cb1de41d049 | 14 | AnalogIn emg0( A0 ); |
MaikOvermars | 0:4cb1de41d049 | 15 | AnalogIn emg1( A1 ); |
MaikOvermars | 0:4cb1de41d049 | 16 | |
MaikOvermars | 10:7339dca7d604 | 17 | // motor ouptuts |
MaikOvermars | 0:4cb1de41d049 | 18 | PwmOut motor1_pwm(D5); |
MaikOvermars | 0:4cb1de41d049 | 19 | DigitalOut motor1_dir(D4); |
MaikOvermars | 0:4cb1de41d049 | 20 | PwmOut motor2_pwm(D7); |
MaikOvermars | 0:4cb1de41d049 | 21 | DigitalOut motor2_dir(D6); |
MaikOvermars | 0:4cb1de41d049 | 22 | |
SvenD97 | 14:4744cc6c90f4 | 23 | // defining encoders |
SvenD97 | 15:7d3b138446fa | 24 | QEI motor_1_encoder(D12,D13,32); |
SvenD97 | 15:7d3b138446fa | 25 | QEI motor_2_encoder(D10,D11,32); |
SvenD97 | 14:4744cc6c90f4 | 26 | |
MaikOvermars | 0:4cb1de41d049 | 27 | AnalogIn potmeter1(A2); |
MaikOvermars | 0:4cb1de41d049 | 28 | AnalogIn potmeter2(A3); |
MaikOvermars | 0:4cb1de41d049 | 29 | DigitalIn button(D0); |
MaikOvermars | 0:4cb1de41d049 | 30 | |
SvenD97 | 14:4744cc6c90f4 | 31 | // tickers and timers |
MaikOvermars | 0:4cb1de41d049 | 32 | Ticker Sample; |
MaikOvermars | 0:4cb1de41d049 | 33 | Timer state_timer; |
MaikOvermars | 0:4cb1de41d049 | 34 | |
MaikOvermars | 0:4cb1de41d049 | 35 | enum States {failure, waiting, calib_enc, calib_emg, operational, demo}; //All possible robot states |
MaikOvermars | 0:4cb1de41d049 | 36 | |
MaikOvermars | 0:4cb1de41d049 | 37 | //Global variables/objects |
MaikOvermars | 0:4cb1de41d049 | 38 | States current_state; |
MaikOvermars | 0:4cb1de41d049 | 39 | Ticker loop_ticker; //The Ticker object that will ensure perfect timing of our looping code |
SvenD97 | 14:4744cc6c90f4 | 40 | double x,y, qref1, qref2 ,e, e1, e2, u1, u2, emg_signal_raw_0, processed_emg_0, emg_signal_raw_1, processed_emg_1, robot_end_point, reference_end_point, motor_angle, q1, q2, motor_1_counts, motor_2_counts; //will be set by the motor_controller function |
MaikOvermars | 0:4cb1de41d049 | 41 | int counts_per_rotation = 32; |
MaikOvermars | 0:4cb1de41d049 | 42 | bool state_changed = false; |
MaikOvermars | 10:7339dca7d604 | 43 | double samplingfreq = 1000; |
SvenD97 | 5:0dd66c757f24 | 44 | double x; // Making the position (x,y) of the end effector global |
SvenD97 | 5:0dd66c757f24 | 45 | double y; |
SvenD97 | 8:bba05e863b68 | 46 | const double T = 0.001; |
MaikOvermars | 0:4cb1de41d049 | 47 | |
MaikOvermars | 0:4cb1de41d049 | 48 | float processing_chain_emg(int num) { |
MaikOvermars | 0:4cb1de41d049 | 49 | return 6.0; |
MaikOvermars | 0:4cb1de41d049 | 50 | } |
MaikOvermars | 0:4cb1de41d049 | 51 | |
MaikOvermars | 0:4cb1de41d049 | 52 | void measure_all() |
MaikOvermars | 0:4cb1de41d049 | 53 | { |
SvenD97 | 14:4744cc6c90f4 | 54 | motor_1_counts = motor_1_encoder.getPulses(); |
SvenD97 | 14:4744cc6c90f4 | 55 | motor_2_counts = motor_2_encoder.getPulses(); |
SvenD97 | 14:4744cc6c90f4 | 56 | q1 = motor_1_counts*2.0f*3.1415926535f/counts_per_rotation; //do this here, and not in the encoder interrupt, to reduce computational load |
SvenD97 | 14:4744cc6c90f4 | 57 | q2 = motor_2_counts*2.0f*3.1415926535f/counts_per_rotation; |
SvenD97 | 14:4744cc6c90f4 | 58 | forwardkinematics_function(q1,q2,x,y); //motor_angle is global, this function ne |
MaikOvermars | 0:4cb1de41d049 | 59 | emg_signal_raw_0 = emg0.read(); //sample analog voltages (all sampling theory applies, you might get aliasing etc.) |
MaikOvermars | 0:4cb1de41d049 | 60 | emg_signal_raw_1 = emg1.read(); |
MaikOvermars | 0:4cb1de41d049 | 61 | processed_emg_0 = processing_chain_emg(0); // some function ‘float my_emg_processing_chain()’ that returns a float. The raw emg is global |
MaikOvermars | 0:4cb1de41d049 | 62 | processed_emg_1 = processing_chain_emg(1); |
MaikOvermars | 0:4cb1de41d049 | 63 | } |
MaikOvermars | 0:4cb1de41d049 | 64 | |
MaikOvermars | 0:4cb1de41d049 | 65 | void output_all() { |
MaikOvermars | 0:4cb1de41d049 | 66 | motor1_pwm = fabs(u1); |
MaikOvermars | 0:4cb1de41d049 | 67 | motor1_dir = u1 > 0.5f; |
MaikOvermars | 0:4cb1de41d049 | 68 | motor2_pwm = fabs(u2); |
MaikOvermars | 0:4cb1de41d049 | 69 | motor2_dir = u2 > 0.5f; |
MaikOvermars | 0:4cb1de41d049 | 70 | static int output_counter = 0; |
MaikOvermars | 0:4cb1de41d049 | 71 | output_counter++; |
MaikOvermars | 0:4cb1de41d049 | 72 | if (output_counter == 100) {pc.printf("Something something... %f",u1); output_counter = 0;} //Print to screen at 10 Hz with MODSERIAL |
MaikOvermars | 0:4cb1de41d049 | 73 | } |
MaikOvermars | 0:4cb1de41d049 | 74 | |
MaikOvermars | 0:4cb1de41d049 | 75 | void state_machine() { |
MaikOvermars | 0:4cb1de41d049 | 76 | switch(current_state) { //States can be: failure, wait, calib_enc, calib_emg, operational, demo, |
MaikOvermars | 0:4cb1de41d049 | 77 | case waiting: //Nothing useful here, maybe a blinking LED for fun and communication to the user |
MaikOvermars | 0:4cb1de41d049 | 78 | if (button.read()==true) |
MaikOvermars | 0:4cb1de41d049 | 79 | { |
MaikOvermars | 0:4cb1de41d049 | 80 | current_state = calib_enc; //the NEXT loop we will be in calib_enc state |
SvenD97 | 9:8e1112874c12 | 81 | // no state_changed action? |
MaikOvermars | 0:4cb1de41d049 | 82 | } |
MaikOvermars | 0:4cb1de41d049 | 83 | break; //to avoid falling through to the next state, although this can sometimes be very useful. |
MaikOvermars | 0:4cb1de41d049 | 84 | |
MaikOvermars | 0:4cb1de41d049 | 85 | case calib_enc: |
MaikOvermars | 0:4cb1de41d049 | 86 | if (state_changed==true) |
MaikOvermars | 0:4cb1de41d049 | 87 | { |
MaikOvermars | 0:4cb1de41d049 | 88 | state_timer.reset(); |
MaikOvermars | 0:4cb1de41d049 | 89 | state_timer.start(); |
MaikOvermars | 0:4cb1de41d049 | 90 | state_changed = false; |
MaikOvermars | 0:4cb1de41d049 | 91 | } |
MaikOvermars | 0:4cb1de41d049 | 92 | u1 = 0.6f; //a low PWM value to move the motors slowly (0.0 to 0.45 don’t do much due to friction) |
MaikOvermars | 0:4cb1de41d049 | 93 | // fabs(motor1.velocity()) < 0.1f && |
MaikOvermars | 0:4cb1de41d049 | 94 | if (state_timer.read() > 5.0f) { |
MaikOvermars | 0:4cb1de41d049 | 95 | current_state = calib_emg; //the NEXT loop we will be in calib_emg state |
MaikOvermars | 0:4cb1de41d049 | 96 | state_changed = true; |
MaikOvermars | 0:4cb1de41d049 | 97 | } |
MaikOvermars | 0:4cb1de41d049 | 98 | break; |
MaikOvermars | 0:4cb1de41d049 | 99 | |
MaikOvermars | 0:4cb1de41d049 | 100 | case calib_emg: //calibrate emg-signals |
MaikOvermars | 0:4cb1de41d049 | 101 | |
MaikOvermars | 0:4cb1de41d049 | 102 | break; |
MaikOvermars | 0:4cb1de41d049 | 103 | |
MaikOvermars | 0:4cb1de41d049 | 104 | case operational: //interpreting emg-signals to move the end effector |
MaikOvermars | 0:4cb1de41d049 | 105 | if (state_changed==true) { int x = 5; } |
MaikOvermars | 0:4cb1de41d049 | 106 | // example |
MaikOvermars | 0:4cb1de41d049 | 107 | reference_end_point = robot_end_point + processed_emg_0; |
MaikOvermars | 0:4cb1de41d049 | 108 | if (button.read() == true) { current_state = demo; } |
MaikOvermars | 0:4cb1de41d049 | 109 | |
MaikOvermars | 0:4cb1de41d049 | 110 | break; |
MaikOvermars | 0:4cb1de41d049 | 111 | |
MaikOvermars | 0:4cb1de41d049 | 112 | case demo: //moving according to a specified trajectory |
MaikOvermars | 0:4cb1de41d049 | 113 | |
MaikOvermars | 0:4cb1de41d049 | 114 | if (button.read() == true) { current_state = demo; } |
MaikOvermars | 0:4cb1de41d049 | 115 | |
MaikOvermars | 0:4cb1de41d049 | 116 | break; |
MaikOvermars | 0:4cb1de41d049 | 117 | |
MaikOvermars | 0:4cb1de41d049 | 118 | case failure: //no way to get out |
MaikOvermars | 0:4cb1de41d049 | 119 | u1 = 0.0f; |
MaikOvermars | 0:4cb1de41d049 | 120 | break; |
MaikOvermars | 0:4cb1de41d049 | 121 | } |
MaikOvermars | 0:4cb1de41d049 | 122 | } |
MaikOvermars | 0:4cb1de41d049 | 123 | |
MaikOvermars | 0:4cb1de41d049 | 124 | void motor_controller() |
MaikOvermars | 0:4cb1de41d049 | 125 | { |
MaikOvermars | 0:4cb1de41d049 | 126 | if (current_state >= operational) { // we can (ab)use the fact that an enum is actually an integer, so math/logic rules still apply |
MaikOvermars | 10:7339dca7d604 | 127 | q_ref += inversekinematics_function(reference_end_point)/samplingfreq; //many different states can modify your reference position, so just do the inverse kinematics once, here |
MaikOvermars | 10:7339dca7d604 | 128 | e1 = q_ref - motor_angle; //tracking error (q_ref - q_meas) |
MaikOvermars | 10:7339dca7d604 | 129 | e2 = q_ref - motor_angle; |
MaikOvermars | 10:7339dca7d604 | 130 | PID_controller(e1,e2,u1,u2); //feedback controller or with possibly fancy controller additions...; pass by reference |
MaikOvermars | 0:4cb1de41d049 | 131 | } //otherwise we just don’t mess with the value of control variable ‘u’ that is set somewhere in the state-machine. |
MaikOvermars | 0:4cb1de41d049 | 132 | } |
MaikOvermars | 0:4cb1de41d049 | 133 | |
MaikOvermars | 0:4cb1de41d049 | 134 | |
MaikOvermars | 0:4cb1de41d049 | 135 | void loop_function() { |
MaikOvermars | 0:4cb1de41d049 | 136 | measure_all(); //measure all signals |
MaikOvermars | 0:4cb1de41d049 | 137 | state_machine(); //Do relevant state dependent things |
MaikOvermars | 0:4cb1de41d049 | 138 | motor_controller(); //Do not put different motor controllers in the states, because every state can re-use the same motor-controller! |
MaikOvermars | 0:4cb1de41d049 | 139 | output_all(); //Output relevant signals, messages, screen outputs, LEDs etc. |
MaikOvermars | 0:4cb1de41d049 | 140 | } |
MaikOvermars | 0:4cb1de41d049 | 141 | |
MaikOvermars | 0:4cb1de41d049 | 142 | |
MaikOvermars | 0:4cb1de41d049 | 143 | int main() |
MaikOvermars | 0:4cb1de41d049 | 144 | { |
MaikOvermars | 0:4cb1de41d049 | 145 | pc.baud(115200); |
MaikOvermars | 0:4cb1de41d049 | 146 | motor1_pwm.period_us(60); |
MaikOvermars | 0:4cb1de41d049 | 147 | motor2_pwm.period_us(60); |
MaikOvermars | 0:4cb1de41d049 | 148 | current_state = waiting; //we start in state ‘waiting’ and current_state can be accessed by all functions |
MaikOvermars | 0:4cb1de41d049 | 149 | u1 = 0.0f; //initial output to motors is 0. |
MaikOvermars | 10:7339dca7d604 | 150 | u2 = 0.0f; |
MaikOvermars | 10:7339dca7d604 | 151 | loop_ticker.attach(&loop_function, 1/samplingfreq); //Run the function loop_function 1000 times per second |
MaikOvermars | 0:4cb1de41d049 | 152 | |
MaikOvermars | 0:4cb1de41d049 | 153 | while (true) { } //Do nothing here (timing purposes) |
MaikOvermars | 0:4cb1de41d049 | 154 | } |