
fancy lampje
Dependencies: mbed QEI HIDScope biquadFilter MODSERIAL FXOS8700Q FastPWM
main.cpp
- Committer:
- MatthewMaat
- Date:
- 2019-11-04
- Revision:
- 29:78419e287e62
- Parent:
- 28:5aece9593681
File content as of revision 29:78419e287e62:
#include "mbed.h" #include "HIDScope.h" #include "QEI.h" #include "MODSERIAL.h" #include "BiQuad.h" #include "FastPWM.h" #include <iostream> //Inputs and outputs MODSERIAL pc(USBTX, USBRX); QEI motor2_pos (D8, D9, NC, 32); QEI motor1_pos (D12, D13, NC, 32); AnalogIn ain2(A2); AnalogIn ain1(A3); DigitalOut dir2(D4); DigitalOut dir1(D7); //D4,D7 direction of motors 2,1 on board, D5,D6- PWM of motors 2,1 on board PwmOut motor2_pwm(D5); PwmOut motor1_pwm(D6); AnalogIn emg0( A0 ); AnalogIn emg1( A1 ); Ticker ticktick; Timer state_time; Timeout EMG_peakx; Timeout turnx; Timeout EMG_peaky; Timeout turny; Ticker sample_timer; HIDScope scope( 6); DigitalOut ledred(LED_RED); DigitalOut ledblue(LED_BLUE); DigitalOut ledgreen(LED_GREEN); InterruptIn err(SW2); InterruptIn button(SW3); InterruptIn left_button(D3); InterruptIn right_button(D2); //variables volatile float P0; volatile float P1; volatile float EMG_min0=1; volatile float EMG_max0=0; volatile float EMG_min1=1; volatile float EMG_max1=0; volatile bool ignore_peaksx=false; volatile bool ignore_turnx=true; volatile bool ignore_peaksy=false; volatile bool ignore_turny=true; enum states{Waiting,Position_calibration,EMG_calibration,Homing,Operating,Demo,Failure}; states currentState=Waiting; volatile bool motor1_calibrated=false; volatile bool motorx_on=false; volatile float signx=0.075; volatile bool motory_on=false; volatile float signy=0.075; volatile bool demo_done=false; volatile float x_ref=0.175; volatile float y_ref=0.175; static float v_refx; //reference speeds static float v_refy; static float T = 0.002; //measuring period static float L = 0.35; //distance between the motor axles, has to be checked volatile float theta1_old = 0.0; //feedback variables volatile float theta2_old = 0.0; float error_1; float error_2; volatile float filtered_error_derivativex; volatile float filtered_error_derivativey; //PID controller variables bool q = true; float error_prev; volatile float Kp = 24.6; volatile float Ki = 28.3; volatile float Kd = 0.1; float u; float u_p; float u_i; float u_d; float error_integral1 = 0.0; float error_integral2 = 0.0; float u_1; float u_2; const float angle2_offset=asin(0.2); const float angle1_offset=atan(2.2/32.8); const double pi=3.1415926535897932384626; volatile float theta1; volatile float theta2; volatile float theta1_ref; volatile float theta2_ref; void read_emg()//read and filter EMG { //EMG signal 0 static int count0=0; static float RMS_value0=0; static float HighPass_value0=0; count0+=1; static float RMS0[150]; static float HighPass0[30]; static BiQuad Notch0(0.9695f,-1.5695f,0.9695f,-1.5695f,0.9391f); static BiQuad Notch1(0.9695f,-1.5695f,0.9695f,-1.5695f,0.9391f); float I0; float If0; //signal 1 static int count1=0; static float RMS_value1=0; static float HighPass_value1=0; count1+=1; static float RMS1[150]; static float HighPass1[30]; float I1; float If1; I0=emg0.read(); //read signal double notched0=Notch0.step(I0); HighPass_value0+=(notched0-HighPass0[count0%30])/30.0; HighPass0[count0%30]=notched0; If0=pow(I0-HighPass_value0,2.0f); // Highpass-filtered value squared RMS_value0+=(If0-RMS0[count0%150])/150.0; RMS0[count0%150]=If0; /* Set the sampled emg values in channel 0 (the first channel) and 1 (the second channel) in the 'HIDScope' instance named 'scope' */ P0=sqrt(RMS_value0); I1=emg1.read(); //read signal double notched1=Notch1.step(I1); HighPass_value1+=(notched1-HighPass1[count1%30])/30.0; HighPass1[count1%30]=notched1; If1=pow(I1-HighPass_value1,2.0f); // Highpass-filtered value squared RMS_value1+=(If1-RMS1[count1%150])/150.0; RMS1[count1%150]=If1; /* Set the sampled emg values in channel 0 (the first channel) and 1 (the second channel) in the 'HIDScope' instance named 'scope' */ P1=sqrt(RMS_value1); /* 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 */ /* To indicate that the function is working, the LED is toggled */ ledred=1; ledgreen=0; ledblue=1; } void get_angles(void) //calculate angles from encoder output { float pulses1=motor1_pos.getPulses(); float pulses2=motor2_pos.getPulses(); theta1=angle1_offset+pulses1*17.0/16.0*2*pi/131.0/32.0; theta2=angle2_offset+pulses2*17.0/16.0*2*pi/131.0/32.0; } void pos_cal(void) // main function for calibrating the motors { float t=state_time.read(); static int pos_time_counter=0; static int last_ticks=10000; float pulses; pos_time_counter+=1; if(!motor1_calibrated&&t>1.0f) { dir1=1; //??? motor1_pwm.write(0.7f); pulses=motor1_pos.getPulses(); if(pos_time_counter%500==0&&fabs(pulses-last_ticks)<1) { ledblue=!ledblue; motor1_pos.reset(); last_ticks=10000; } else if(pos_time_counter%500==0) { last_ticks=motor1_pos.getPulses(); } } else if(!motor1_calibrated) { motor2_pwm.write(0.7f); dir2=1; } else if(t>1.0f) { motor1_pwm.write(0.0f); dir2=1; //??? motor2_pwm.write(0.7f); pulses=motor2_pos.getPulses(); if(pos_time_counter%500==0&&fabs(pulses-last_ticks)<1) { ledblue=!ledblue; motor2_pos.reset(); motor2_pwm.write(0.0f); } else if(pos_time_counter%500==0) { last_ticks=motor2_pos.getPulses(); } } } void record_min_max(void)// main function for calibrating EMG { float t=state_time.read(); if(t>0.4) { if(P0<EMG_min0) { EMG_min0=P0; } else if(P0>EMG_max0) { EMG_max0=P0; } if(P1<EMG_min1) { EMG_min1=P1; } else if(P1>EMG_max1) { EMG_max1=P1; } } } //timed functions for EMG control void unignore_peaksx(void) { ignore_peaksx=false; } void start_ignore_turnx(void) { ignore_turnx=true; } void unignore_peaksy(void) { ignore_peaksy=false; } void start_ignore_turny(void) { ignore_turny=true; } void set_v_refxy(void)//set reference velocities based on EMG input { float Q0; Q0=2.0f*(P0-(EMG_min0+EMG_max0)/2.0f)/(EMG_max0-EMG_min0); float Q1; Q1=2.0f*(P1-(EMG_min1+EMG_max1)/2.0f)/(EMG_max1-EMG_min1); if (Q0>-0.2f && !ignore_peaksx) { if (motorx_on) { v_refx=0; EMG_peakx.attach(unignore_peaksx,0.8); turnx.attach(start_ignore_turnx,1); ignore_turnx=false; ignore_peaksx=true; motorx_on=false; } else if(ignore_turnx) { v_refx=1.0*signx; EMG_peakx.attach(unignore_peaksx,0.8); turnx.attach(start_ignore_turnx,1); ignore_turnx=false; ignore_peaksx=true; motorx_on=true; } else { signx=-1.0*signx; v_refx=1.0*signx; EMG_peakx.attach(unignore_peaksx,0.8); ignore_peaksx=true; motorx_on=true; } } if (Q1>-0.2f && !ignore_peaksy) { if (motory_on) { v_refy=0; EMG_peaky.attach(unignore_peaksy,0.8); turny.attach(start_ignore_turny,1); ignore_turny=false; ignore_peaksy=true; motory_on=false; } else if(ignore_turny) { v_refy=1.0*signy; EMG_peaky.attach(unignore_peaksy,0.8); turny.attach(start_ignore_turny,1); ignore_turny=false; ignore_peaksy=true; motory_on=true; } else { signy=-1.0*signy; v_refy=1.0*signy; EMG_peaky.attach(unignore_peaksy,0.8); ignore_peaksy=true; motory_on=true; } } float x_pos=0.35*tan(theta2)/(tan(theta1)+tan(theta2)); float y_pos=tan(theta1)*x_pos; if(x_pos>=0.35&&v_refx>0) { v_refx=0; } else if(x_pos<=0.0&&v_refx<0) { v_refx=0; } if(y_pos>=0.45&&v_refy>0) { v_refy=0; } else if(y_pos<=0.07&&v_refy<0) { v_refy=0; } } void left_fake_emg(void)// optional- set refence x direction based on pressing a button { if (!ignore_peaksx) { if (motorx_on) { v_refx=0; EMG_peakx.attach(unignore_peaksx,0.8); turnx.attach(start_ignore_turnx,1); ignore_turnx=false; ignore_peaksx=true; motorx_on=false; } else if(ignore_turnx) { v_refx=1.0*signx; EMG_peakx.attach(unignore_peaksx,0.8); turnx.attach(start_ignore_turnx,1); ignore_turnx=false; ignore_peaksx=true; motorx_on=true; } else { signx=-1.0*signx; v_refx=1.0*signx; EMG_peakx.attach(unignore_peaksx,0.8); ignore_peaksx=true; motorx_on=true; } } } void right_fake_emg(void)// optional- set refence y direction based on pressing a button { if (!ignore_peaksy) { if (motory_on) { v_refy=0; EMG_peaky.attach(unignore_peaksy,0.8); turny.attach(start_ignore_turny,1); ignore_turny=false; ignore_peaksy=true; motory_on=false; } else if(ignore_turny) { v_refy=1.0*signy; EMG_peaky.attach(unignore_peaksy,0.8); turny.attach(start_ignore_turny,1); ignore_turny=false; ignore_peaksy=true; motory_on=true; } else { signy=-1.0*signy; v_refy=1.0*signy; EMG_peaky.attach(unignore_peaksy,0.8); ignore_peaksy=true; motory_on=true; } } } void set_ref_fake_emg(void) // optional- set refence x/y velocity based on pressing a button { float x_pos=0.35*tan(theta2)/(tan(theta1)+tan(theta2)); float y_pos=tan(theta1)*x_pos; if(x_pos>=0.35&&v_refx>0) { v_refx=0; } else if(x_pos<=0.0&&v_refx<0) { v_refx=0; } if(y_pos>=0.45&&v_refy>0) { v_refy=0; } else if(y_pos<=0.07&&v_refy<0) { v_refy=0; } } void error(void){ //calculate the errors on the motor angles based on input reference velocities or positions float x_pos=0.35*tan(theta2)/(tan(theta1)+tan(theta2)); float y_pos=tan(theta1)*x_pos; float r1=sqrt(pow(x_pos,2)+pow(y_pos,2)); float r2=sqrt(pow(0.35-x_pos,2)+pow(y_pos,2)); float J11=-r1*cos(theta2)/sin(theta1+theta2); float J21=r1*sin(theta2)/sin(theta1+theta2); float J12=r2*cos(theta1)/sin(theta1+theta2); float J22=r2*sin(theta1)/sin(theta1+theta2); float a=J22/(J11*J22-J12*J21); float b=-J12/(J11*J22-J12*J21); float c=-J21/(J11*J22-J12*J21); float d=J11/(J11*J22-J12*J21); /* float dvd=L * (tan(theta2) * pow(tan(theta1),2) + tan(theta1) * pow(tan(theta2),2));//+ is aangepast float a = ( -pow(cos(theta1),2) * pow((tan(theta1)+tan(theta2)),2) * pow(tan(theta1),2) ) / dvd; // inverse matrix components of differential x and differential y float b = ( pow(cos(theta1),2) * pow((tan(theta1)+tan(theta2)),2) * tan(theta1) ) / dvd; float c = ( pow(cos(theta2),2) * pow((tan(theta1)+tan(theta2)),2) * pow(tan(theta2),2) ) / dvd; float d = ( pow(cos(theta2),2) * pow((tan(theta1)+tan(theta2)),2) * pow(tan(theta2),2) ) / dvd; */ float theta1_dot = a*v_refx + b*v_refy; float theta2_dot = c*v_refx + d*v_refy; if(currentState==Operating||currentState==Demo) { theta1_ref = theta1_old+theta1_dot*T; theta2_ref = theta2_old+theta2_dot*T; if(theta1_ref<theta1-0.2) { theta1_ref=theta1-0.2; } else if(theta1_ref>theta1+0.2) { theta1_ref=theta1+0.2; } if(theta2_ref<theta2-0.2) { theta2_ref=theta2-0.2; } else if(theta2_ref>theta2+0.2) { theta2_ref=theta2+0.2; } if(theta1_ref<0.1f) { theta1_ref=0.1f; } else if(theta1_ref>1.8f) { theta1_ref=1.8f; } if(theta2_ref<0.1f) { theta2_ref=0.1f; } else if(theta2_ref>1.8f) { theta2_ref=1.8f; } } else if(currentState==Homing) { theta1_ref=pi/4.0f; theta2_ref=pi/4.0f; } /* else if(currentState==Demo) { theta1_ref=atan(y_ref/x_ref); theta2_ref=atan(y_ref/(0.35f-x_ref)); } */ error_1 = theta1 - theta1_ref; error_2 = theta2 - theta2_ref; theta1_old = theta1_ref; theta2_old = theta2_ref; } float PID1(float err){//Calculate controller output for motor 1 based on error 1 //P action u_p = Kp * err; float x_pos=0.35*tan(theta2)/(tan(theta1)+tan(theta2)); float y_pos=tan(theta1)*x_pos; float In1=0.25f*0.49/3.0f+0.2f*(pow(x_pos,2)+pow(y_pos,2)); float r1=0.1+sqrt(pow(x_pos,2)+pow(y_pos,2)); //I action error_integral1 = error_integral1 + err * T; u_i = Ki * error_integral1; //D action static float error_prevx=0; static float error_derivativex; static BiQuad LowPassFilterx(0.1311f,0.2622f,0.1311f,-0.7478,0.2722f); error_derivativex = (err - error_prevx)/T; filtered_error_derivativex = LowPassFilterx.step(error_derivativex); u_d = Kd * filtered_error_derivativex; error_prevx = err; u = r1/3.0f*(u_p + u_i+u_d); if(filtered_error_derivativex>0.3) { u=0; } return u; } float PID2(float err){//Calculate controller output for motor 2 based on error 2 //P action u_p = Kp * err; float x_pos=0.35*tan(theta2)/(tan(theta1)+tan(theta2)); float y_pos=tan(theta1)*x_pos; float In2=0.25f*0.49/3.0f+0.2f*(pow(0.35-x_pos,2)+pow(y_pos,2)); float r2=0.1+sqrt(pow(0.35f-x_pos,2)+pow(y_pos,2)); //I action error_integral2 = error_integral2 + err * T; u_i = Ki * error_integral2; //D action static float error_prevy=0; static float error_derivativey; static BiQuad LowPassFiltery(0.1311f,0.2622f,0.1311f,-0.7478,0.2722f); error_derivativey = (err - error_prevy)/T; filtered_error_derivativey = LowPassFiltery.step(error_derivativey); u_d = Kd * filtered_error_derivativey; error_prevy = err; u = r2/3.0f*(u_p + u_i+u_d); return u; } void set_refxy(void)//set reference velocities in the demo mode { float t=state_time.read(); int tfloor=floor(t)/1; float x_pos=0.35*tan(theta2)/(tan(theta1)+tan(theta2)); float y_pos=tan(theta1)*x_pos; static int demo_subpart=1; float v_demo=0.08; /* Rectangle if(tfloor%12==0||tfloor%12==1||tfloor%12==2) { x_ref=0.025+0.1*(t-tfloor+tfloor%12); y_ref=0.2; } else if(tfloor%12==3||tfloor%12==4||tfloor%12==5) { x_ref=0.325; y_ref=0.2+0.05*(t-tfloor+tfloor%12-3); } else if(tfloor%12==6||tfloor%12==7||tfloor%12==8) { x_ref=0.325-0.1*(t-tfloor+tfloor%12-6); y_ref=0.35; } else { x_ref=0.025; y_ref=0.35-0.05*(t-tfloor+tfloor%12-9); } */ /* Circle x_ref=0.175f+0.15*cos(t/2.5f); y_ref=0.275f+0.15*sin(t/2.5f); */ if(tfloor<5) { theta1_old=pi/4; theta2_old=pi/4; } else if(tfloor<12) { x_ref=0.025+(x_pos-0.025)*(12-t)/7.0f; y_ref=0.1+(y_pos-0.1)*(12-t)/7.0f; theta1_old=atan(y_ref/x_ref); theta2_old=atan(y_ref/(0.35f-x_ref)); } else if(demo_subpart==1&&y_pos<0.4) { v_refx=0; v_refy=v_demo; } else if(demo_subpart==2&&x_pos<0.075) { v_refx=v_demo; v_refy=0; } else if(demo_subpart==3&&y_pos>0.05) { v_refx=0; v_refy=-v_demo; } else if(demo_subpart==4&&x_pos<0.125) { v_refx=v_demo; v_refy=0; } else if(demo_subpart==5&&y_pos<0.05) { v_refx=0; v_refy=v_demo; } else if(demo_subpart==6&&x_pos<0.175) { v_refx=v_demo; v_refy=0; } else if(demo_subpart==7&&y_pos>0.05) { v_refx=0; v_refy=-v_demo; } else if(demo_subpart==8&&x_pos<0.225) { v_refx=v_demo; v_refy=0; } else if(demo_subpart==9&&y_pos<0.4) { v_refx=0; v_refy=v_demo; } else if(demo_subpart==10&&x_pos<0.275) { v_refx=v_demo; v_refy=0; } else if(demo_subpart==11&&y_pos>0.05) { v_refx=0; v_refy=-v_demo; } else if(demo_subpart==12&&x_pos<0.325) { v_refx=v_demo; v_refy=0; } else if(demo_subpart==13&&y_pos<0.4) { v_refx=0; v_refy=v_demo; } else if(demo_subpart==14) { demo_done=true; wait(10); demo_done=false; demo_subpart=1; state_time.reset(); } else { demo_subpart+=1; } //float asdfgh=x_ref; //x_ref=y_ref-0.075; //y_ref=asdfgh+0.125; } void setPWM_controller(void){//set motor PWM values based on controller output error(); u_1 = PID1(error_1); u_2 = PID2(error_2); if(u_1 < 0.0f){ dir1 = 0; }else{ dir1 = 1; } motor1_pwm.write(min(0.5f+0.5f*fabs(u_1),1.0f)); if(u_2 < 0.0f){ dir2 = 0; }else{ dir2 = 1; } motor2_pwm.write(min(0.5f+0.5f*fabs(u_2),1.0f)); } void sample()// main funtion loop { get_angles(); scope.set(0,theta1); scope.set(1,theta2); scope.set(2,v_refx); scope.set(3,v_refy); scope.set(4,theta1_ref); scope.set(5,theta2_ref); scope.send(); switch(currentState) { case Waiting: ledred=0; ledgreen=0; ledblue=1; break; case Position_calibration: ledred=1; ledgreen=1; ledblue=0; pos_cal(); break; case EMG_calibration: ledred=1; ledgreen=0; ledblue=0; read_emg(); record_min_max(); break; case Homing: setPWM_controller(); ledred=0; ledgreen=1; ledblue=0; break; case Operating: read_emg(); set_v_refxy(); set_ref_fake_emg(); setPWM_controller(); ledred=1; ledgreen=0; ledblue=1; break; case Demo: if(!demo_done) { set_refxy(); setPWM_controller(); } else { motor1_pwm.write(0.0f); motor2_pwm.write(0.0f); } ledred=0; ledgreen=0; ledblue=0; break; case Failure: ledred=0; ledgreen=1; ledblue=1; motor1_pwm.write(0.0); motor2_pwm.write(0.0); break; } //Preventing the machine from breaking } void error_occur()//executed when error button is pressed { currentState=Failure; } void return_home()//main function for homing state { theta1_ref=pi/4.0f; theta2_ref=pi/4.0f; theta1_old=pi/4.0f; theta2_old=pi/4.0f; v_refx=0; EMG_peakx.attach(unignore_peaksx,0.8); ignore_peaksx=true; motorx_on=false; v_refy=0; EMG_peaky.attach(unignore_peaksy,0.8); ignore_peaksy=true; motory_on=false; } void button_press(void) //Press button to change state { state_time.reset(); switch(currentState) { case Waiting: currentState=Position_calibration; wait(1); break; case Position_calibration: if(!motor1_calibrated) { motor1_calibrated=true; state_time.reset(); dir1=!dir1; motor1_pwm.write(0.0f); } else { currentState=EMG_calibration; motor2_pwm.write(0.0f); motor1_pwm.write(0.0f); } wait(1); break; case EMG_calibration: currentState=Homing; wait(1); break; case Homing: currentState=Operating; wait(1); break; case Operating: currentState=Demo; wait(1); break; case Demo: currentState=Operating; wait(1); break; } } int main() { pc.baud(115200); pc.printf("Starting..."); ledred=0; sample_timer.attach(&sample, 0.002); err.fall(error_occur); button.fall(button_press); left_button.fall(left_fake_emg); left_button.rise(left_fake_emg); right_button.fall(return_home); right_button.rise(return_home); int frequency_pwm=21000; motor1_pwm.period(1.0/frequency_pwm); motor2_pwm.period(1.0/frequency_pwm); state_time.start(); while (true) { wait(10); } }