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 BiQuad4th_order biquadFilter MODSERIAL FastPWM
main.cpp
- Committer:
- Mirjam
- Date:
- 2018-10-31
- Revision:
- 19:2d9421b0316a
- Parent:
- 18:f36ac3ee081a
- Child:
- 20:7f1997276ce2
File content as of revision 19:2d9421b0316a:
#include "mbed.h"
#include "MODSERIAL.h"
#include "QEI.h"
#include "FastPWM.h"
#include "math.h"
//#include "HIDScope.h"
#include "BiQuad.h"
#include "BiQuad4.h"
#include "FilterDesign.h"
#include "FilterDesign2.h"
const double pi = 3.14159265359;
// LED's
DigitalOut led_red(LED_RED);
DigitalOut led_green(LED_GREEN);
DigitalOut led_blue(LED_BLUE);
// Buttons
DigitalIn button_clbrt_home(SW2);
DigitalIn button_Demo(D5);
DigitalIn button_Emg(D6);
DigitalIn Fail_button(SW3);
// Modserial
MODSERIAL pc(USBTX, USBRX);
// Encoders
QEI Encoder1(D11, D10, NC, 4200) ; // Encoder motor 1, (pin 1A, pin 1B, index pin(not used), counts/rev)
QEI Encoder2(D9, D8, NC, 4200) ; // Encoder motor 2, (pin 2A, pin 2B, index pin (not used), counts/rev)
// Motors (direction and PWM)
DigitalOut directionM1(D4);
DigitalOut directionM2(D7);
FastPWM motor1_pwm(D5);
FastPWM motor2_pwm(D6);
// Inverse Kinematics
int track;
volatile double U1;
volatile double U2;
DigitalIn directionx(PTC2); //x direction switch button
DigitalIn directiony(PTA2); //y direction switch button
const double r_big = 590.0; //maximum radius of the moving space
const double r_small = 162.0; //minimum radius of the moving space
const double r_top = 250.0; //radius of the top portion of the moving space
double v=1.0; //moving speed of setpoint (dependant on the waiting time)
volatile int sx;//value of the button and store as switch
volatile int sy;//value of the button and store as switch
int dirx = 1; //determine the direction of the setpoint placement
int diry = 1; //determine the direction of the setpoint placement
double q1_diff;
double q2_diff;
double sq = 2.0; //to square numbers
const double x0 = 80.0; //zero x position after homing
const double y0 = 141.0; //zero y position after homing
const double L1 = 250.0; //length of the first link
const double L3 = 350.0; //length of the second link
volatile double setpointx = x0; //sets the begin condition for x to x0
volatile double setpointy = y0; //sets the begin condition for y to y0
// Reference angles of the starting position
double q2_0 = pi + acos((pow(x0,sq)+pow(y0,sq)-pow(L1,sq)-pow(L3,sq))/(2.0*L1*L3));
double q1_0 = atan(y0/x0)+acos((-pow(L3,sq)+pow(L1,sq)+pow(x0,sq)+pow(y0,sq))/(2.0*L1*sqrt(pow(x0,sq)+pow(y0,sq))));
double q2_0_enc = q2_0 + q1_0;
// EMG input en start value of filtered EMG
AnalogIn emg1_raw( A0 );
AnalogIn emg2_raw( A1 );
double emg1_filtered = 0.00;
double emg2_filtered = 0.00;
float threshold_EMG = 0.25; // Threshold on 25 percent of the maximum EMG
// Declare timers and Tickers
Timer timer; // Timer for counting time in this state
Ticker WriteValues; // Ticker to show values of velocity to screen
Ticker StateMachine;
Ticker sample_EMGtoHIDscope; // Ticker to send the EMG signals to screen
//HIDScope scope(4); //Number of channels which needs to be send to the HIDScope
// Set up ProcessStateMachine
enum states {WAITING, MOTOR_ANGLE_CLBRT, EMG_CLBRT, HOMING, WAITING4SIGNAL, MOVE_W_EMG, MOVE_W_DEMO, FAILURE_MODE};
states currentState = WAITING;
bool stateChanged = true;
volatile bool writeVelocityFlag = false;
// Global variables
char c;
int counts1;
int counts2;
float theta1;
float theta2;
float vel_1;
float vel_2;
float theta1_prev = 0.0;
float theta2_prev = 0.0;
float tijd = 0.005;
float time_in_state;
volatile double error1;
volatile double error2;
int need_to_move_1; // Does the robot needs to move in the first direction?
int need_to_move_2; // Does the robot needs to move in the second direction?
double EMG_calibrated_max_1 = 2.00000; // Maximum value of the first EMG signal found in the calibration state.
double EMG_calibrated_max_2 = 2.00000; // Maximum value of the second EMG signal found in the calibration state.
double emg1_cal = 0.00000; //measured value of the first emg
double emg2_cal = 0.00000; //measured value of the second emg
// ----------------------------------------------
// ------- FUNCTIONS ----------------------------
// ----------------------------------------------
float ReadEncoder1() // Read Encoder of motor 1.
{
counts1 = Encoder1.getPulses(); // Counts of outputshaft of motor 1
theta1 = (float(counts1)/4200) * 2*pi; // Angle of outputshaft of motor 1
vel_1 = (theta1 - theta1_prev) / tijd; // Velocity, current angle - previous angle, devided by avarage time between encoder read-outs
theta1_prev = theta1; // Define theta_prev
return vel_1;
}
float ReadEncoder2() // Read encoder of motor 2.
{
counts2 = Encoder2.getPulses(); // Counts of outputshaft of motor 2
theta2 = (float(counts2)/4200) * 2*pi; // Angle of outputshaft of motor 2
vel_2 = (theta2 - theta2_prev) / tijd; // Velocity, current angle - previous angle, devided by avarage time between encoder read-outs
theta2_prev = theta2; // Define theta_prev
return vel_2;
}
void MotorAngleCalibrate() // Function that drives motor 1 and 2.
{
float U1 = -0.2; // Negative, so arm goes backwards.
float U2 = -0.2; // Motor 2 is not taken into account yet.
motor1_pwm.write(fabs(U1)); // Send PWM values to motor
motor2_pwm.write(fabs(U2));
directionM1 = U1 > 0.0f; // Either true or false, determines direction.
directionM2 = U2 > 0.0f;
}
void sample()
{
emg1_filtered = FilterDesign(emg1_raw.read());
emg2_filtered = FilterDesign2(emg2_raw.read());
/**
scope.set(0, emg1_raw.read()); // Raw EMG 1 send to scope 0
scope.set(1, emg1_filtered); // Filtered EMG 1 send to scope 1
scope.set(2, emg2_raw.read()); // Raw EMG 2 send to scope 2
scope.set(3, emg2_filtered); // Filtered EMG 2 send to scope 3
scope.send(); // Send the data to the computer
*/
}
// ---------------------------------------------------
// --------INVERSE-KINEMATICS-------------------------
// ---------------------------------------------------
double makeAngleq1(double x, double y){
double q1 = atan(y/x)+acos((-pow(L3,sq)+pow(L1,sq)+pow(x,sq)+pow(y,sq))/(2.0*L1*sqrt(pow(x,sq)+pow(y,sq)))); //angle of the first joint in the setpoint configuration
q1_diff = -2.0*(q1-q1_0); //the actual amount of radians that the motor has to turn in total to reach the setpoint
return q1_diff;
}
double makeAngleq2(double x, double y){
double q2 = -acos((pow(x,sq)+pow(y,sq)-pow(L1,sq)-pow(L3,sq))/(2.0*L1*L3)); //angle of the second joint in setpoint configuration
double q1 = atan(y/x)+acos((-pow(L3,sq)+pow(L1,sq)+pow(x,sq)+pow(y,sq))/(2.0*L1*sqrt(pow(x,sq)+pow(y,sq)))); //angle of the first joint in the setpoint configuration
double q2_motor = (pi - q2)+q1; //because q2 represents the angle at joint two and not at the motor a calculation has to be done
q2_diff = (2.0*(q2_motor - q2_0_enc))/(2.0*pi); //the actual amount of radians that the motor has to turn in total to reach the setpoint
return -q2_diff;
}
// ---------------------------------------------------
// --------DETERMINE SETPOINT-------------------------
// ---------------------------------------------------
//function that determines the setpoint of the x coordinate
double EMG1On(int s){
if (setpointx < 80.0){ //minimum setpoint
setpointx = setpointx;}
if (setpointy > -66.0 && setpointy < 362.0 && sqrt(pow(setpointx,sq)+pow(setpointy,sq)) > r_big){ //defines the large circle endpoint
setpointx = setpointx;}
if (setpointy > 141.0 && setpointx < 540.0 && sqrt(pow(setpointy-119.2,sq)+pow(setpointx-329.0,sq)) > r_top){ //defines the top circle endpoint
setpointx = setpointx;}
if (setpointx > 80.0 && setpointy > -36.0 && sqrt(pow(setpointx,sq)+pow(setpointy,sq)) < r_small){ //defines the small circle endpoint
setpointx = setpointx;}
if (setpointy > -66.0 && setpointy < -36.0 && setpointx < 157.0){ //kleine stukje
setpointx = setpointx+2;}
else setpointx = setpointx + dirx*s*v;
return setpointx;
}
//function that determines the setpoint of the y coordinate
double EMG2On(int s){
if (setpointy < -66.0) //bottom line
setpointy = setpointy;
if (setpointy > -66.0 && setpointy < 362.0 && sqrt(pow(setpointx,sq)+pow(setpointy,sq)) > r_big){ //defines the large circle endpoint
setpointy = setpointy;}
if (setpointy >= 141.0 && setpointx < 540.0 && sqrt(pow(setpointy-119.2,sq)+pow(setpointx-329.0,sq)) > r_top){ //defines the top circle endpoint
setpointy = setpointy;}
if (setpointx > 80.0 && setpointy > -36.0 && sqrt(pow(setpointx,sq)+pow(setpointy,sq)) < r_small){ //defines the small circle endpoint
setpointy = setpointy;}
else setpointy = setpointy + diry*s*v;
return setpointy;
}
//function to change the moving direction of the setpoint
void ChangeDirectionX(){
dirx = -1*dirx;
}
void ChangeDirectionY(){
diry = -1*diry;
}
// --------------------------------------------------------------------
// ---------------READ-OUT ENCODERS------------------------------------
// --------------------------------------------------------------------
double counts2angle1()
{
counts1 = Encoder1.getPulses(); // Counts of outputshaft of motor 1
theta1 = -(double(counts1)/4200) * 2*pi; // Angle of outputshaft of motor 1
return theta1;
}
double counts2angle2()
{
counts2 = Encoder2.getPulses(); // Counts of outputshaft of motor 2
theta2 = (double(counts2)/4200) * 2*pi; // Angle of outputshaft of motor 2
return theta2;
}
// -----------------------------------------------------------------
// --------------------------- PI controllers ----------------------
// -----------------------------------------------------------------
double PI_controller1(double error1)
{
static double error_integral1 = 0;
// Proportional part
double Kp1 = 3.95; // Kp (proportionele controller, nu nog een random waarde)
double u_p1 = Kp1*error1; // Voltage dat naar de motor gestuurd wordt (volgt uit error en Kp)
// Integral part
double Ki1 = 6.0; // Ki (Integrale deel vd controller, nu nog een random waarde)
double Ts1 = 0.005; // Sample tijd, net zo vaak als de controller wordt aangeroepen (200 Hz, statemachine)
error_integral1 = error_integral1 + error1 * Ts1;
double u_i1 = Ki1 * error_integral1;
// Sum
U1 = u_p1 + u_i1;
// Return
return U1;
}
double PI_controller2(double error2)
{
static double error_integral2 = 0;
// Proportional part
double Kp2 = 3.95; // Kp (proportionele controller, nu nog een random waarde)
double u_p2 = Kp2*error2; // Voltage dat naar de motor gestuurd wordt (volgt uit error en Kp)
// Integral part
double Ki2 = 6.0; // Ki (Integrale deel vd controller, nu nog een random waarde)
double Ts2 = 0.005; // Sample tijd, net zo vaak als de controller wordt aangeroepen (200 Hz, statemachine)
error_integral2 = error_integral2 + error2 * Ts2;
double u_i2 = Ki2 * error_integral2;
// Sum +
U2 = u_p2 + u_i2;
// Return
return U2;
}
// -----------------------------------------------
// ------------ RUN MOTORS -----------------------
// -----------------------------------------------
void motoraansturing()
{
q1_diff = makeAngleq1(setpointx, setpointy);
q2_diff = makeAngleq2(setpointx, setpointy);
theta2 = counts2angle2();
error2 = q2_diff - theta2;
theta1 = counts2angle1();
error1 = q1_diff - theta1; // Setpoint error, te behalen setpoint minus de huidige positie van de as.
U1 = PI_controller1(error1); // Voltage dat naar de motor gestuurd wordt.
U2 = PI_controller2(error2);
motor1_pwm.write(fabs(U1)); // Motor aansturen
directionM1 = U1 > 0.0f; // Richting van de motor bepalen
motor2_pwm.write(fabs(U2));
directionM2 = U2 > 0.0f;
}
// ---------------------------------------------------
// --------STATEMACHINE-------------------------------
// ---------------------------------------------------
void ProcessStateMachine(void)
{
switch (currentState)
{
case WAITING:
// Description:
// In this state we do nothing, and wait for a command
// Actions
led_red = 0; led_green = 0; led_blue = 0; // Colouring the led WHITE
// State transition logic
if (button_clbrt_home == 0)
{
currentState = MOTOR_ANGLE_CLBRT;
stateChanged = true;
pc.printf("Starting Calibration\n\r");
}
else if (Fail_button == 0)
{
currentState = FAILURE_MODE;
stateChanged = true;
}
break;
case MOTOR_ANGLE_CLBRT:
// Description:
// In this state the robot moves with low motor PWM to some
// mechanical limit of motion, in order to calibrate the motors.
// Actions
led_red = 1; led_green = 0; led_blue = 0; // Colouring the led TURQUOISE
timer.start(); //Start timer to get time in the state "MOTOR_ANGLE_CLRBRT"
if (stateChanged)
{
MotorAngleCalibrate(); // Actuate motor 1 and 2.
vel_1 = ReadEncoder1(); // Get velocity of motor 1
vel_2 = ReadEncoder2(); // Get velocity of motor 2
stateChanged = true; // Keep this loop going, until the transition conditions are satisfied.
}
// State transition logic
time_in_state = timer.read(); // Determine if this state has run for long enough.
if(time_in_state > 2.0f && vel_1 < 1.1f && vel_2 < 1.1f)
{
//pc.printf( "Tijd in deze staat = %f \n\r", time_in_state);
//pc.printf( "Tijd tijdens actions loop (Waarde voor bepalen van snelheid)") = %f \n\r", tijd);
pc.printf("Motor calibration has ended. \n\r");
timer.stop(); // Stop timer for this state
timer.reset(); // Reset timer for this state
motor1_pwm.write(fabs(0.0)); // Send PWM values to motor
motor2_pwm.write(fabs(0.0));
Encoder1.reset(); // Reset Encoders when arrived at zero-position
Encoder2.reset();
currentState = EMG_CLBRT; // Switch to next state (EMG_CLRBRT).
pc.printf("EMG calibration \r\n");
stateChanged = true;
}
if (Fail_button == 0)
{
currentState = FAILURE_MODE;
stateChanged = true;
}
break;
case EMG_CLBRT:
// In this state the person whom is connected to the robot needs
// to flex his/her muscles as hard as possible, in order to
// measure the maximum EMG-signal, which can be used to scale
// the EMG-filter.
led_red = 1; led_green = 1; led_blue = 0; // Colouring the led BLUE
for (int i = 0; i <= 10; i++) //10 measuring points
{
if (emg1_cal > EMG_calibrated_max_1){
EMG_calibrated_max_1 = emg1_cal;}
if (emg2_cal > EMG_calibrated_max_2){
EMG_calibrated_max_2 = emg2_cal;}
//pc.printf("EMG1 = %f, EMG2 = %f \r\n",EMG_calibrated_max_1,EMG_calibrated_max_2);
wait(0.5f);
}
currentState = HOMING;
if (Fail_button == 0)
{
currentState = FAILURE_MODE;
stateChanged = true;
}
break;
case HOMING:
// Description:
// Robot moves to the home starting configuration
pc.printf("HOMING \r\n");
led_red = 0; led_green = 1; led_red = 0; // Colouring the led PURPLE
// Requirements to move to the next state:
// If we are in the right location, within some margin, we move to the Waiting for
// signal state.
wait(5.0f); // time_in_this_state > 5.0f
// INSERT MOVEMENT
currentState = WAITING4SIGNAL;
if (Fail_button == 0)
{
currentState = FAILURE_MODE;
stateChanged = true;
}
break;
case WAITING4SIGNAL:
// Description:
// In this state the robot waits for an action to occur.
led_red = 0; led_green = 0; led_blue = 0; // Colouring the led WHITE
// Requirements to move to the next state:
// If a certain button is pressed we move to the corresponding
// state (MOVE_W_DEMO, MOVE_W_EMG or SHUTDOWN)
if (button_clbrt_home == 0)
{
currentState = MOTOR_ANGLE_CLBRT;
stateChanged = true;
pc.printf("Starting Calibration \n\r");
}
else if (button_Demo == 1)
{
currentState = MOVE_W_DEMO;
pc.printf("DEMO \r\n");
wait(1.0f);
}
else if (button_Emg == 1)
{
currentState = MOVE_W_EMG;
pc.printf("EMG \r\n");
wait(1.0f);
}
else if (Fail_button == 0)
{
currentState = FAILURE_MODE;
stateChanged = true;
}
break;
case MOVE_W_DEMO:
// Description:
// In this state the robot follows a preprogrammed shape, e.g.
// a square.
led_red = 1; led_green = 1; led_blue = 0; // Colouring the led GREEN
// Requirements to move to the next state:
// When the home button or the failure button is pressed, we
// will the move to the corresponding state.
// BUILD DEMO MODE
// determinedemoset();
motoraansturing();
if (button_clbrt_home == 0)
{
currentState = HOMING;
stateChanged = true;
pc.printf("Moving home\n\r");
}
else if (Fail_button == 0)
{
currentState = FAILURE_MODE;
stateChanged = true;
}
break;
case MOVE_W_EMG:
// Description:
// In this state the robot will be controlled by use of
// EMG-signals.
led_red = 1; led_green = 0; led_blue = 1; // Colouring the led GREEN
if (emg1_filtered >= (threshold_EMG*EMG_calibrated_max_1)){
need_to_move_1 = 1; // The robot does have to move
}
else {
need_to_move_1 = 0; // If the robot does not have to move
}
if(emg2_filtered >= threshold_EMG*EMG_calibrated_max_2){
need_to_move_2 = 1;
}
else {
need_to_move_2 = 0;
}
setpointx = EMG1On(need_to_move_1); // Determine setpoints
setpointy = EMG2On(need_to_move_2);
motoraansturing();
// Requirements to move to the next state:
// When the home button or the failure button is pressed, we
// will the move to the corresponding state.
if (button_clbrt_home == 0)
{
currentState = MOTOR_ANGLE_CLBRT;
stateChanged = true;
pc.printf("Starting Calibration \n\r");
}
else if (Fail_button == 0)
{
currentState = FAILURE_MODE;
stateChanged = true;
}
break;
case FAILURE_MODE:
// Description:
// This state is reached when the failure button is reached.
// In this state everything is turned off.
led_red = 0; led_green = 1; led_blue = 1; // Colouring the led RED
// Actions
if (stateChanged)
{
motor1_pwm.write(fabs(0.0)); // Stop all motors!
motor2_pwm.write(fabs(0.0));
pc.printf("FAILURE MODE \r\n PLEASE RESTART THE WHOLE ROBOT \r\n (and make sure this does not happen again) \r\n");
stateChanged = false;
}
break;
// State transition logic
// No state transition, you need to restart the robot.
default:
// This state is a default state, this state is reached when
// the program somehow defies all of the other states.
pc.printf("Unknown or unimplemented state reached!!! \n\r");
led_red = 1; led_green = 1; led_blue = 1; // Colouring the led BLACK
for (int n = 0; n < 50; n++) // Making an SOS signal with the RED led
{
for (int i = 0; i < 6; i++)
{
led_red = !led_red;
wait(0.6f);
}
wait(0.4f);
for (int i = 0 ; i < 6; i++)
{
led_red = !led_red;
wait(0.2f);
}
wait(0.4f);
}
}
}
// --------------------------------
// ----- MAIN LOOP ----------------
// --------------------------------
int main()
{
// Switch all LEDs off
led_red = 1;
led_green = 1;
led_blue = 1;
pc.baud(115200);
pc.printf("\r\n _______________ INSERT ROBOT NAME HERE! _______________ \r\n");
wait(0.5f);
pc.printf("WAITING... \r\n");
StateMachine.attach(&ProcessStateMachine, 0.005f); // Run statemachine 200 times per second
sample_EMGtoHIDscope.attach(&sample, 0.02f); // Display EMG values 50 times per second
while (true) {
if (currentState == MOVE_W_EMG){
InterruptIn directionx(PTC2);
directionx.fall(ChangeDirectionX); //change the direction of the setpoint in x direction
InterruptIn directiony(PTA2);
directiony.fall(ChangeDirectionY); //change the direction of the setpoint in y direction
}
}
}