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: mbed QEI HIDScope biquadFilter MODSERIAL FastPWM
Diff: main.cpp
- Revision:
- 22:afd521069446
- Parent:
- 1:b862262a9d14
--- a/main.cpp Wed Sep 04 15:30:13 2019 +0000
+++ b/main.cpp Mon Oct 21 08:20:32 2019 +0000
@@ -1,23 +1,396 @@
+/*
+To-do:
+ Add reference generator
+ fully implement schmitt trigger
+ Homing
+ Turning the magnet on/off
+ Inverse kinematics
+ Gravity compensation
+ PID values
+ General program layout
+ better names for EMG input
+*/
+
#include "mbed.h"
-//#include "HIDScope.h"
-//#include "QEI.h"
#include "MODSERIAL.h"
-//#include "BiQuad.h"
-//#include "FastPWM.h"
+#include "FastPWM.h"
+#include "QEI.h"
+#include "HIDScope.h"
+#include "BiQuad.h"
+#define PI 3.14159265
+
+Serial pc(USBTX, USBRX); //connect to pc
+HIDScope scope(1); //HIDScope instance
+DigitalOut motor1_direction(D4); //rotation motor 1 on shield (always D6)
+FastPWM motor1_pwm(D5); //pwm 1 on shield (always D7)
+DigitalOut motor2_direction(D7); //rotation motor 2 on shield (always D4)
+FastPWM motor2_pwm(D6); //pwm 2 on shield (always D5)
+Ticker loop_ticker; //used in main()
+Ticker scope_ticker;
+AnalogIn Pot1(A1); //pot 1 on biorobotics shield
+AnalogIn Pot2(A0); //pot 2 on biorobotics shield
+InterruptIn but1(D10); //debounced button on biorobotics shield
+InterruptIn but2(D9); //debounced button on biorobotics shield
+AnalogIn EMG1(A2);
+AnalogIn EMG2(A3);
+
+void check_failure();
+
+QEI enc1 (D11, D12, NC, 8400, QEI::X4_ENCODING); //encoder 1 gebruiken
+QEI enc2 (D1, D2, NC, 8400, QEI::X4_ENCODING); //encoder 2 gebruiken
+
+BiQuad bq1 (0.881889334678067, -1.76377866935613, 0.8818893346780671, -1.77069673005903, 0.797707797506027);
+BiQuad bq2 (0.000198358203463849, 0.000396716406927699, 0.000198358203463849, -1.96262073248799, 0.963423352820821);
+
+//variables
+enum States {s_idle, s_cali_EMG, s_cali_enc, s_moving_magnet_off, s_moving_magnet_on, s_homing, s_failure};
+States state; //using the States enum
+struct actuator_state {
+ float duty_cycle1; //pwm of 1st motor
+ float duty_cycle2; //pwm of 2nd motor
+ int dir1; //direction of 1st motor
+ int dir2; //direction of 2nd motor
+ bool magnet; //state of the magnet
+} actuator;
+
+struct EMG_params {
+ float max; //params of the emg, tbd during calibration
+ float min;
+} EMG_values;
+
+struct PID {
+ float P;
+ float I;
+ float D;
+ float I_counter;
+};
+PID PID1;
+PID PID2;
+
+float dt = 0.001;
+float theta;
+float L;
+int enc1_zero = 0;//the zero position of the encoders, to be determined from the
+int enc2_zero = 0;//encoder calibration
+int EMG1_filtered;
+int EMG2_filtered;
+int enc1_value;
+int enc2_value;
+float error1 = 0.0;
+float error2 = 0.0;
+float last_error1 = 0.0;
+float last_error2 = 0.0;
+float action1;
+float action2;
+bool state_changed = false; //used to see if the state is "starting"
+volatile bool but1_pressed = false;
+volatile bool but2_pressed = false;
+volatile bool failure_occurred = false;
+float pot_1; //used to keep track of the potentiometer values
+float pot_2;
+bool EMG_has_been_calibrated;
+bool button1_pressed;
+bool button2_pressed;
+const int EMG_cali_amount = 1000;
+float past_EMG_values[EMG_cali_amount];
+int past_EMG_count = 0;
+
+void do_nothing()
+
+/*
+ Idle state. Used in the beginning, before the calibration states.
+*/
+{
+ if (button1_pressed) {
+ state_changed = true;
+ state = s_cali_enc;
+ button1_pressed = false;
+ }
+}
+
+void failure()
+/*
+ Failure mode. This should execute when button 2 is pressed during operation.
+*/
+{
+ if (state_changed) {
+ pc.printf("Something went wrong!\r\n");
+ state_changed = false;
+ }
+}
+
+void cali_EMG()
+/*
+ Calibration of the EMG. Values determined during calibration should be
+ added to the EMG_params instance.
+*/
+{
+ if (state_changed) {
+ pc.printf("Started EMG calibration\r\n");
+ state_changed = false;
+ }
+ if (past_EMG_count < EMG_cali_amount) {
+ past_EMG_values[past_EMG_count] = EMG1_filtered;
+ past_EMG_count++;
+ }
+ else { //calibration is has concluded
+ float sum = 0.0;
+ for(int i = 0; i<EMG_cali_amount; i++) {
+ sum += past_EMG_values[i];
+ }
+ float mean = sum/(float)EMG_cali_amount;
+ EMG_values.min = mean;
+ //calibration done, moving to cali_enc
+ pc.printf("Calibration of the EMG is done, lower bound = %f", mean);
+ EMG_has_been_calibrated = true;
+ state_changed = true;
+ state = s_cali_enc;
+ }
+}
+
+void cali_enc()
+/*
+ Calibration of the encoder. The encoder should be moved to the lowest
+ position for the linear stage and the horizontal postition for the
+ rotating stage.
+*/
+{
+ if (state_changed) {
+ pc.printf("Started encoder calibration\r\n");
+ state_changed = false;
+ }
+ if (button1_pressed) {
+ pc.printf("Encoder has been calibrated");
+ enc1_zero = enc1_value;
+ enc2_zero = enc2_value;
+ button1_pressed = false;
+ state = s_moving_magnet_off;
+ state_changed = true;
+
+ }
+}
+
+void moving_magnet_off()
+/*
+ Moving with the magnet disabled. This is the part from the home position
+ towards the storage of chips.
+*/
+{
+ if (state_changed) {
+ pc.printf("Moving without magnet\r\n");
+ state_changed = false;
+ }
+}
-DigitalOut led(LED_RED);
+void moving_magnet_on()
+/*
+ Moving with the magnet enabled. This is the part of the movement from the
+ chip holder to the top of the playing board.
+*/
+{
+ if (state_changed) {
+ pc.printf("Moving with magnet\r\n");
+ state_changed = false;
+ }
+ return;
+}
+void homing()
+/*
+ Dropping the chip and moving towards the rest position.
+*/
+{
+ if (state_changed) {
+ pc.printf("Started homing");
+ state_changed = false;
+ }
+ return;
+}
+
+void measure_signals()
+{
+ //only one emg input, reference and plus
+ float EMG1_raw = EMG1.read();
+ float EMG2_raw = EMG2.read();
+ float filter_value1 = fabs(bq2.step(fabs(bq1.step(EMG1_raw - EMG2_raw))));
+
+ if (filter_value1 > EMG_values.max) {
+ EMG_values.max = filter_value1;
+ }
+ if (EMG_has_been_calibrated) {
+ EMG1_filtered = (filter_value1-EMG_values.min)/(EMG_values.max-EMG_values.min);
+ }
+ else {
+ EMG1_filtered = filter_value1;
+ }
+
+ enc1_value = enc1.getPulses();
+ enc2_value = enc2.getPulses();
+ enc1_value -= enc1_zero;
+ enc2_value -= enc2_zero;
+ theta = (float)(enc1_value)/(float)(8400*2*PI);
+ L = (float)(enc2_value)/(float)(8400*2*PI);
+
+
+}
+
+void sample()
+{
+ /* Set the sampled emg values in channel 0 (the first channel) and 1 (the second channel) in the 'HIDScope' instance named 'scope' */
+ float emg0_value = EMG1.read();
+ float emg1_value = EMG2.read();
+
+ //double filter_value = bqc.step(emg1_value);
+ float filter_value = fabs(bq2.step(fabs(bq1.step(emg0_value - emg1_value))));
+ if (filter_value > EMG_values.max) {
+ EMG_values.max = filter_value;
+ }
+ if (EMG_values.min > filter_value) {
+ EMG_values.min = filter_value;
+ }
+
+ filter_value = filter_value-EMG_values.min;
+ filter_value = filter_value/(EMG_values.max-EMG_values.min);
+
+ scope.set(0, EMG1.read() );
+ scope.set(1, EMG2.read() );
+ scope.set(2, filter_value);
+ /* Repeat the step above if required for more channels of required (channel 0 up to 5 = 6 channels)
+ * Ensure that enough channels are available (HIDScope scope( 2 ))
+ * Finally, send all channels to the PC at once */
+ scope.send();
+ /* To indicate that the function is working, the LED is toggled */
+}
-MODSERIAL pc(USBTX, USBRX);
+void motor_controller() {
+ float error1, error2;
+ //P part of the controller
+ float P_action1 = PID1.P * error1;
+ float P_action2 = PID2.P * error2;
+
+ //I part
+ PID1.I_counter += error1;
+ PID2.I_counter += error2;
+ float I_action1 = PID1.I_counter * PID1.I;
+ float I_action2 = PID2.I_counter * PID2.I;
+
+ //D part
+ float velocity_estimate_1 = (error1-last_error1)/dt; //estimate of the time derivative of the error
+ float velocity_estimate_2 = (error2-last_error2)/dt;
+ float D_action1 = velocity_estimate_1 * PID1.D;
+ float D_action2 = velocity_estimate_2 * PID2.D;
+
+ action1 = P_action1 + I_action1 + D_action1;
+ action2 = P_action2 + I_action2 + D_action2;
+
+ last_error1 = error1;
+ last_error2 = error2;
+}
+
+void output()
+{
+ motor1_direction = actuator.dir1;
+ motor2_direction = actuator.dir2;
+ motor1_pwm.write(actuator.duty_cycle1);
+ motor2_pwm.write(actuator.duty_cycle2);
+
+ scope.set(0, EMG1_filtered);
+}
+
+void state_machine()
+{
+ check_failure(); //check for an error in the last loop before state machine
+ //run current state
+ switch (state) {
+ case s_idle:
+ do_nothing();
+ break;
+ case s_failure:
+ failure();
+ break;
+ case s_cali_EMG:
+ cali_EMG();
+ break;
+ case s_cali_enc:
+ cali_enc();
+ break;
+ case s_moving_magnet_on:
+ moving_magnet_on();
+ break;
+ case s_moving_magnet_off:
+ moving_magnet_off();
+ break;
+ case s_homing:
+ homing();
+ break;
+ }
+}
+
+void main_loop()
+{
+ measure_signals();
+ state_machine();
+ motor_controller();
+ output();
+}
+
+//Helper functions, not directly called by the main_loop functions or
+//state machines
+void check_failure()
+{
+ //state = s_failure;
+ //state_changed = true;
+}
+
+void but1_interrupt()
+{
+ if(but2.read()) {//both buttons are pressed
+ failure_occurred = true;
+ }
+ but1_pressed = true;
+ pc.printf("Button 1 pressed \n\r");
+}
+
+void but2_interrupt()
+{
+ if(but1.read()) {//both buttons are pressed
+ failure_occurred = true;
+ }
+ but2_pressed = true;
+ pc.printf("Button 2 pressed \n\r");
+}
+
+int schmitt_trigger(float i)
+{
+ int speed;
+ speed = -1; //default value, this means the state should not change
+ if (i > 0/14 && i < 2/14) {speed = 0;}
+ if (i > 3/14 && i < 5/14) {speed = 1;}
+ if (i > 6/14 && i < 8/14) {speed = 2;}
+ if (i > 9/14 && i < 11/14) {speed = 3;}
+ if (i > 12/14 && i < 14/14) {speed = 4;}
+ return speed;
+}
int main()
{
pc.baud(115200);
- pc.printf("\r\nStarting...\r\n\r\n");
-
+ pc.printf("Executing main()... \r\n");
+ state = s_idle;
+
+ motor2_pwm.period(1/160000); // 1/frequency van waarop hij draait
+ motor1_pwm.period(1/160000); // 1/frequency van waarop hij draait
+
+ actuator.dir1 = 0;
+ actuator.dir2 = 0;
+
+ actuator.magnet = false;
+
+ but1.fall(&but1_interrupt);
+ but2.fall(&but2_interrupt);
+ scope_ticker.attach(&scope, &HIDScope::send, 0.02);
+ loop_ticker.attach(&main_loop, dt); //main loop at 1kHz
+ pc.printf("Main_loop is running\n\r");
while (true) {
-
- led = !led;
-
- wait_ms(500);
+ wait(0.1f);
}
-}
+}
\ No newline at end of file