Sets the Kp, Ki and Kd values of the PID controller for the encoder motors
Dependencies: FastPWM HIDScope_motor_ff MODSERIAL QEI mbed
Fork of Encoder by
main.cpp
- Committer:
- sjoerdbarts
- Date:
- 2016-10-31
- Revision:
- 10:65f63a0a6b3c
- Parent:
- 9:278d25dc0ef3
File content as of revision 10:65f63a0a6b3c:
#include "mbed.h" #include "FastPWM.h" #include "HIDScope.h" #include "QEI.h" #include "BiQuad.h" #define SERIAL_BAUD 115200 // baud rate for serial communication Serial pc(USBTX,USBRX); // Setup Pins // Note: Pin D10 and D11 for encoder, D4-D7 for motor controller // Potmeter 1 gives the reference position x AnalogIn pot1(A3); // Potmeter 2 gives the reference position y AnalogIn pot2(A4); // Setup Buttons InterruptIn button1(PTB9); // button 1 InterruptIn button2(PTA1); // button 2 InterruptIn button3(PTC6); // SW2 InterruptIn button4(PTA4); // SW3 // volatile bool button1_value = false; // Set motor Pinouts DigitalOut motor1_dir(D4); FastPWM motor1_pwm(D5); DigitalOut motor2_dir(D7); FastPWM motor2_pwm(D6); // Set LED pins DigitalOut led(LED_RED); // Set HID scope HIDScope scope(6); // Set encoder QEI m1_EncoderCW(D10,D11,NC,32); QEI m1_EncoderCCW(D11,D10,NC,32); QEI m2_EncoderCW(D13,D12,NC,32); QEI m2_EncoderCCW(D12,D13,NC,32); volatile const int COUNTS_PER_REV = 8400; volatile const float DEGREES_PER_PULSE = 8400 / 360; volatile const float CONTROLLER_TS = 0.01; // 1000 Hz // Set initial Kp and Ki volatile double Kp = 0.01; // last known good Kp: 0.01 volatile double Ki = 0.0; // last known good Ki: 0.0025 volatile double Kd = 0.0; // last known good Kd: 0.0 volatile double Ts = 0.01; volatile double N = 100; // Memory values for controllers double m1_v1 = 0, m1_v2 = 0; double m2_v1 = 0, m2_v2 = 0; // Set default mode volatile int Mode = 0; // Variabelen voor MotorHoekBerekenen volatile double x = 0.0; // beginpositie x en y volatile double y = 305.5; volatile const double pi = 3.14159265359; volatile double Theta1Gear = 60.0; // Beginpositie op 60 graden volatile double Theta2Gear = 60.0; volatile double Theta1 = Theta1Gear*42/12; // Beginpositie van MotorHoek volatile double Theta2 = Theta2Gear*42/12; // frequentie in Hz waarmee theta wordt uigereken // Functions for angle calculation double Calc_Prev_x () { double Prev_x = x; //pc.printf("prev x = %f\r\n", Prev_x); return Prev_x; } // vorige y opslaan double Calc_Prev_y () { double Prev_y = y; //pc.printf("prev y = %f\r\n", Prev_y); return Prev_y; } // vorige Theta1Gear opslaan double Calc_Prev_Theta1_Gear () { double Prev_Theta1_Gear = Theta1Gear; return Prev_Theta1_Gear; } //vorige Theta2Gear opslaan double Calc_Prev_Theta2_Gear () { double Prev_Theta2_Gear = Theta2Gear; return Prev_Theta2_Gear; } void CalcXY() { /* double Step = 5/Fr_Speed_Theta ; //10 mm per seconde afleggen if (BicepsLeft==true && BicepsRight==true && Leg==true && Stepper_State==false) { Stepper_State = true; // we zijn aan het flipper dus geen andere dingen doen FunctieFlipper() ; } else if (BicepsLeft==true && BicepsRight==false && Leg==false && Stepper_State==false) { x = x - Step; } else if (BicepsLeft==false && BicepsRight==true && Leg==false && Stepper_State==false) { x = x + Step; // naar Right bewegen } else if (BicepsLeft==true && BicepsRight==true && Leg==false && Stepper_State==false) { y = y + Step; // naar voren bewegen } else if (BicepsLeft==false && BicepsRight==true && Leg==true && Stepper_State==false) { y = y - Step; // naar achter bewegen } else if (BicepsLeft==false && BicepsRight==false && Leg==false || Stepper_State==true) { } */ x = (pot1.read()-0.5f)*400.0f; // x is een waarde tussen de -200 en 200 y = 50.0f+(pot2.read()*256.0f); // Grenswaardes LET OP: ARMEN MISSCHIEN GEBLOKKEERD DOOR BALK AAN DE BINNENKANT if (x > 200) { x = 200; } else if (x < -200) { x = -200; } if (y > 306) { y = 306; } else if (y < 50) { y = 50; // GOKJE, UITPROBEREN } //pc.printf("x = %f, y = %f\r\n", x, y); //scope.set(2,x); //scope.set(3,y); } // diagonaal berekenen voor linker arm double CalcDia1 (double x, double y) { double a = 50.0; // de afstand van gekozen assenstelsel tot de armas (assenstelsel precies in het midden) KEERTJE NAMETEN double BV1 = sqrt(pow((a+x),2) + pow(y,2)); // diagonaal (afstand van armas tot locatie) berekenen double Dia1 = pow(BV1,2)/(2*BV1); // berekenen van de afstand oorsprong tot diagonaal //pc.printf("dia = %f, x = %f, y= %f\r\n", Dia1, x, y); return Dia1; } // diagonaal berekenen voor rechter arm double CalcDia2 (double x, double y) { double a = 50.0; double BV2 = sqrt(pow((x-a),2) + pow(y,2)); // zelfde nog een keer doen maar nu voor de rechter arm double Dia2 = pow(BV2,2)/(2*BV2); //pc.printf("dia = %f, x = %f, y= %f\r\n", Dia2, x, y); return Dia2; } // calculate Theta1 void CalcTheta1 (double Dia1) { double a = 50.0; double Bar = 200.0; // lengte van de armen // Hoek berekenen van het grote tandwiel (gear) if (x > -a) { Theta1Gear = pi - atan(y/(x+a)) - acos(Dia1/Bar); } else if (x > -a) { Theta1Gear = pi - (pi + atan(y/(x+a))) - acos(Dia1/Bar); } else { // als x=-a Theta1Gear = 0.5*pi - acos(Dia1/Bar); } Theta1Gear = Theta1Gear*180.0/pi; // veranderen van radialen naar graden // omrekenen van grote tandwiel naar motortandwiel Theta1 = Theta1Gear*42.0/12.0; // grote tandwiel heeft 42 tanden. Motortandwiel heeft er 12. // pc.printf("thetaMotor = %f, thetaGear = %f\r\n", Theta1, Theta1Gear); } void CalcTheta2 (double Dia2) { double a = 50.0; double Bar = 200.0; // lengte van de armen double Prev_Theta2_Gear = Theta2Gear; // Hoek berekenen van het grote tandwiel (gear) if (x < a) { Theta2Gear = pi + atan(y/(x-a)) - acos(Dia2/Bar); } else if (x > a) { Theta2Gear = pi - (pi - atan(y/(x-a))) - acos(Dia2/Bar); } else { // als x=a Theta2Gear = 0.5*pi - acos(Dia2/Bar); } Theta2Gear = Theta2Gear*180/pi; // veranderen van radialen naar graden // omrekenen van grote tandwiel naar motortandwiel Theta2 = Theta2Gear*42.0/12.0; // grote tandwiel heeft 42 tanden. Motortandwiel heeft er 12. // pc.printf("thetaMotor = %f, thetaGear = %f\r\n", Theta2, Theta2Gear); } // als een van de hoeken te groot wordt, zet dan alles terug naar de vorige positie void AngleLimits (double Prev_Theta1_Gear, double Prev_Theta2_Gear, double Prev_x, double Prev_y) { double MaxThetaGear = 100.0; // de hoek voordat arm het opstakel raakt (max hoek is 107.62 tussen arm en opstakel, maken er 100 van voor veiligheid) if (Theta1Gear >= MaxThetaGear || Theta2Gear >= MaxThetaGear) { Theta1Gear = Prev_Theta1_Gear; Theta2Gear = Prev_Theta2_Gear; x = Prev_x; y = Prev_y; Theta1 = Theta1Gear*42.0/12.0; Theta2 = Theta2Gear*42.0/12.0; } // pc.printf("thetaMotor = %f, thetaGear = %f\r\n", Theta2, Theta2Gear); } void CalculationsForTheta () { double Prev_x = Calc_Prev_x (); double Prev_y = Calc_Prev_y (); double Prev_Theta1_Gear = Calc_Prev_Theta1_Gear (); double Prev_Theta2_Gear = Calc_Prev_Theta2_Gear (); CalcXY(); double Dia1 = CalcDia1 (x, y); double Dia2 = CalcDia2 (x, y); CalcTheta1 (Dia1); CalcTheta2 (Dia2); AngleLimits (Prev_Theta1_Gear, Prev_Theta2_Gear, Prev_x, Prev_y); // laatste check } void Set_Mode() { Mode++; if (Mode == 3) { Mode = 0; } pc.printf("\r 0: Kp \r\n"); pc.printf("\r 1: Ki \r\n"); pc.printf("\r 2: Kd \r\n"); pc.printf("\r MODE = %i \r\n",Mode); } void increase() { switch(Mode) { case 0: Kp = Kp + 0.01f; break; case 1: Ki = Ki + 0.00001f; break; case 2: Kd = Kd + 0.01f; break; default: pc.printf("\r swtich error \r\n"); } pc.printf("\r Kp: %f, Ki: %f, Kd: %f \r\n",Kp,Ki,Kd); } void decrease() { switch(Mode) { case 0: if (Kp <= 0.0f) { Kp = 0.0f; break; } Kp = Kp - 0.01f; break; case 1: if (Ki == 0.0f) { Ki = 0.0f; break; } Ki = Ki - 0.00001f; break; case 2: if (Kd == 0.0f) { Kd = 0.0f; break; } Kd = Kd - 0.01f; break; default: pc.printf("\r swtich error \r\n"); } pc.printf("\r Kp: %f, Ki: %f, Kd: %f \r\n",Kp,Ki,Kd); } double m1_GetPosition() { int countsCW = m1_EncoderCW.getPulses(); int countsCCW= m1_EncoderCCW.getPulses(); int net_counts=countsCW-countsCCW; double Position=(net_counts*360.0)/COUNTS_PER_REV; return Position; } double m2_GetPosition() { int countsCW = m2_EncoderCW.getPulses(); int countsCCW= m2_EncoderCCW.getPulses(); int net_counts=countsCW-countsCCW; double Position=(net_counts*360.0)/COUNTS_PER_REV; return Position; } double m1_GetPosition_cal() { int countsCW = m1_EncoderCW.getPulses(); int countsCCW= m1_EncoderCCW.getPulses(); int net_counts=countsCW-countsCCW; double Position=(net_counts*360.0)/COUNTS_PER_REV+210.0f; // calibrated position is 210 degrees return Position; } double m2_GetPosition_cal() { int countsCW = m2_EncoderCW.getPulses(); int countsCCW= m2_EncoderCCW.getPulses(); int net_counts=countsCW-countsCCW; double Position=(net_counts*360.0)/COUNTS_PER_REV+210.0f; // calibrated position is 210 degrees return Position; } double m1_PID(double error, const double Kp, const double Ki, const double Kd, const double Ts, const double N, double &m1_v1, double &m1_v2) { double a1 = -4/(N*Ts+2), a2 = -1*(N*Ts - 2)/(N*Ts+2), b0 = (4*Kp + 4*Kd*N + 2*Ki*Ts + 2*Kp*N*Ts + Ki*N*pow(Ts,2))/(2*N*Ts + 4), b1 = (Ki*N*pow(Ts,2) - 4*Kp - 4*Kd*N)/(N*Ts + 2), b2 = (4*Kp + 4*Kd*N - 2*Ki*Ts - 2*Kp*N*Ts + Ki*N*pow(Ts,2))/(2*N*Ts + 4); double v = error - a1*m1_v1 - a2*m1_v2; double u = b0*v + b1*m1_v1 + b2*m1_v2; m1_v2 = m1_v1; m1_v1 = v; return u; } double m2_PID(double error, const double Kp, const double Ki, const double Kd, const double Ts, const double N, double &m2_v1, double &m2_v2) { double a1 = -4/(N*Ts+2), a2 = -1*(N*Ts - 2)/(N*Ts+2), b0 = (4*Kp + 4*Kd*N + 2*Ki*Ts + 2*Kp*N*Ts + Ki*N*pow(Ts,2))/(2*N*Ts + 4), b1 = (Ki*N*pow(Ts,2) - 4*Kp - 4*Kd*N)/(N*Ts + 2), b2 = (4*Kp + 4*Kd*N - 2*Ki*Ts - 2*Kp*N*Ts + Ki*N*pow(Ts,2))/(2*N*Ts + 4); double v = error - a1*m1_v1 - a2*m1_v2; double u = b0*v + b1*m1_v1 + b2*m1_v2; m1_v2 = m1_v1; m1_v1 = v; return u; } void SetMotor(int motor_number, double MotorValue) { // Given -1<=motorValue<=1, this sets the PWM and direction // bits for motor 1. Positive value makes motor rotating // clockwise. motorValues outside range are truncated to // within range if (motor_number == 1) { if (MotorValue >=0) { motor1_dir=0; } else { motor1_dir=1; } if (fabs(MotorValue)>1){ motor1_pwm.write(1); } else { motor1_pwm.write(abs(MotorValue)); } } else { if (MotorValue >=0) { motor2_dir=0; } else { motor2_dir=1; } if (fabs(MotorValue)>1){ motor2_pwm.write(1); } else { motor2_pwm.write(abs(MotorValue)); } } } void BlinkLed(){ led = not led; } void Controller_motor() { // calculate the reference position CalculationsForTheta(); // get the actual position double m1_Position = m1_GetPosition_cal(); double m2_Position = m2_GetPosition_cal(); // Set position scopes scope.set(0,x); scope.set(1,y); scope.set(2,Theta1); scope.set(3,Theta2); /* scope.set(0,Theta1); scope.set(1,m1_Position); scope.set(3,Theta2); scope.set(4,m2_Position); */ //scope.set(2,m1_Position); //scope.set(4,m2_Position); // calc the error double m1_error = Theta1 - m1_Position; double m2_error = Theta2 - m2_Position; //scope.set(2,m1_error); //scope.set(6,m2_error); // calc motorvalues for controller; double m1_MotorValue = m1_PID(m1_error, Kp, Ki, Kd, Ts, N, m1_v1, m1_v2); double m2_MotorValue = m2_PID(m2_error, Kp, Ki, Kd, Ts, N, m2_v1, m2_v2); scope.set(4,m1_MotorValue); scope.set(5,m2_MotorValue); // Set the motorvalues SetMotor(1, m1_MotorValue); SetMotor(2, m2_MotorValue); // Set motorvalues for scope // Send data to HIDScope scope.send(); } void button1_switch() { button1_value = not button1_value; } void PotControl() { double Motor1_Value = (pot1.read() - 0.5f)/2.0f; double Motor2_Value = (pot2.read() - 0.5f)/2.0f; //pc.printf("\r\n Motor value 1: %f \r\n",Motor1_Value); //pc.printf("\r\n Motor value 2: %f \r\n",Motor2_Value); double m1_Position = m1_GetPosition(); double m2_Position = m2_GetPosition(); scope.set(0, Motor1_Value); scope.set(1, m1_Position); scope.set(2, Motor2_Value); scope.set(3, m2_Position); scope.send(); // Write the motors SetMotor(1, Motor1_Value); SetMotor(2, Motor2_Value); } /* void CalculateSpeed() { countsCW = EncoderCW.getPulses(); countsCCW= EncoderCCW.getPulses(); net_counts=countsCW-countsCCW; degrees=(net_counts*360.0)/counts_per_rev; curr_degrees = degrees; speed = (curr_degrees-prev_degrees)/T_CalculateSpeed; prev_degrees = curr_degrees; //scope.set(0, degrees); scope.set(0, speed); double speed_filtered = bqc.step(speed); scope.set(1,speed_filtered); scope.send(); } */ int main(){ // Set baud connection with PC pc.baud(SERIAL_BAUD); pc.printf("\r\n ***THERMONUCLEAR WARFARE COMMENCES*** \r\n"); // Setup Blinking LED led = 1; Ticker TickerBlinkLed; TickerBlinkLed.attach(BlinkLed,0.5); // Set motor PWM speeds motor1_pwm.period(1.0/1000); motor2_pwm.period(1.0/1000); // Setup Interruptin.fall button1.fall(button1_switch); button2.fall(Set_Mode); button3.fall(increase); button4.fall(decrease); // Setup motor control Ticker PIDControllerTicker; Ticker PotControlTicker; pc.printf("\r\n ***************** \r\n"); pc.printf("\r\n Press button SW3 to start positioning the arms using PotControl\r\n"); pc.printf("\r\n Make sure both potentiometers are positioned halfway! \r\n"); pc.printf("\r\n ***************** \r\n"); while (button1_value == false){} PotControlTicker.attach(&PotControl,0.01f); pc.printf("\r\n ***************** \r\n"); pc.printf("\r\n When done positioning, press button SW3 to detach Potcontrol"); pc.printf("\r\n ***************** \r\n"); while (button1_value == true){} // Detach potcontrol PotControlTicker.detach(); // Set motors to 0 motor2_pwm.write(0); motor2_pwm.write(0); // Reset the encoders to set the 0 position m1_EncoderCW.reset(); m1_EncoderCCW.reset(); m2_EncoderCW.reset(); m2_EncoderCCW.reset(); pc.printf("\r\n ***************** \r\n"); pc.printf("\r\n When done positioning, press button SW3 to start potmeter PID control"); pc.printf("\r\n Make sure both potentiometers are positioned halfway!!! \r\n"); pc.printf("\r\n ***************** \r\n"); while (button1_value == false){} PIDControllerTicker.attach(&Controller_motor, CONTROLLER_TS); // 100 Hz while(true){} }