![](/media/cache/profiles/01e5405ccd7d3efa1fd033aa7b7489e9.jpg.50x50_q85.jpg)
Line following bot using MAXREFDES89 and MAX32600MBED
Dependencies: MAX14871_Shield mbed
main.cpp
- Committer:
- j3
- Date:
- 2016-01-06
- Revision:
- 4:eb6d6d25355c
- Parent:
- 3:d268f6e06b7a
- Child:
- 5:c673430c8a32
File content as of revision 4:eb6d6d25355c:
/********************************************************************** * Simple line following bot with PID to demo MAXREFDES89# * PID feedback comes from infrared led sensor from Parallax * https://www.parallax.com/product/28034 * * The following link is a good example for a PID line follower * * http://www.inpharmix.com/jps/PID_Controller_For_Lego_Mindstorms_Robots.html * **********************************************************************/ #include "mbed.h" #include "max14871_shield.h" //comment out following line for normal operation //#define TUNE_PID 1 //state variables for ISR volatile bool run = false; BusOut start_stop_led(D6, D7); //Input to trigger interrupt off of InterruptIn start_stop_button(SW3); //callback fx for ISR void start_stop() { run = !run; start_stop_led = (start_stop_led ^ 3); } int main(void) { //Trigger interrupt on falling edge of SW3 and cal start_stop fx start_stop_button.fall(&start_stop); //state indicator, default red for stop start_stop_led = 2; // D7 on D6 off = red //Connect IR sensor to port 4 BusIn ir_bus(P4_0, P4_1, P4_2, P4_3, P4_4, P4_5, P4_6, P4_7); DigitalOut ir_bus_enable(D3, 1); //active high enable //raw sensor data uint8_t ir_val = 0; //used to measure dt DigitalOut loop_pulse(D8, 0); //MAXREFDES89# object Max14871_Shield motor_shield(D14, D15, true);// Default config //local vars that are more meaningful and easier to use than the class name with scope operator Max14871_Shield::max14871_motor_driver_t r_motor_driver = Max14871_Shield::MD4; Max14871_Shield::max14871_motor_driver_t l_motor_driver = Max14871_Shield::MD3; motor_shield.set_current_regulation_mode(r_motor_driver, Max14871_Shield::RIPPLE_25_EXTERNAL_REF, 2.0); motor_shield.set_current_regulation_mode(l_motor_driver, Max14871_Shield::RIPPLE_25_EXTERNAL_REF, 2.0); int32_t r_duty_cycle = 0; int32_t l_duty_cycle = 0; const int32_t MAX_DUTY_CYCLE = 100; const int32_t MIN_DUTY_CYCLE = -100; const int32_t TARGET_DUTY_CYCLE = 35; /*************************************************** | Control Type | KP | KI | KD | |---------------|--------|------------|------------| | P | 0.5Kc | 0 | 0 | |--------------------------------------------------- | PI | 0.45Kc | 1.2KpdT/Pc | 0 | |---------------|--------|------------|------------| | PD | 0.80Kc | 0 | KpPc/(8dT) | |---------------|--------|------------|------------| | PID | 0.60Kc | 2KpdT/Pc | KpPc/(8dT) | ***************************************************/ //Kc = critical gain, gain with just P control at which system becomes marginally stable //Pc = period of oscillation for previous scenario. //for values below Kc = 10 and Pc was measured at 0.33 //set PID terms to 0 if not used/needed //calculated starting points given in comments below //Ended up decreasing integral term, increasing derivative term and small decrease in proportional term. int32_t kp = 5; //6 int32_t ki = 10; //.0576, divide by 1000 later int32_t kd = 500; //156.25 //initialize vars int32_t current_error = 0; int32_t previous_error = current_error; int32_t integral = 0; int32_t derivative = 0; //raw sensor data scaled to a useable range for error signal // SP - feedback raw sensor ir_val const uint8_t ERROR_SIGNAL_P7 = 0xFE; //b11111110 const uint8_t ERROR_SIGNAL_P6 = 0xFC; //b11111100 const uint8_t ERROR_SIGNAL_P5 = 0xFD; //b11111101 const uint8_t ERROR_SIGNAL_P4 = 0xF9; //b11111001 const uint8_t ERROR_SIGNAL_P3 = 0xFB; //b11111011 const uint8_t ERROR_SIGNAL_P2 = 0xF3; //b11110011 const uint8_t ERROR_SIGNAL_P1 = 0xF7; //b11110111 const uint8_t ERROR_SIGNAL_00 = 0xE7; //b11100111, feedback = SP const uint8_t ERROR_SIGNAL_N1 = 0xEF; //b11101111 const uint8_t ERROR_SIGNAL_N2 = 0xCF; //b11001111 const uint8_t ERROR_SIGNAL_N3 = 0xDF; //b11011111 const uint8_t ERROR_SIGNAL_N4 = 0x9F; //b10011111 const uint8_t ERROR_SIGNAL_N5 = 0xBF; //b10111111 const uint8_t ERROR_SIGNAL_N6 = 0x3F; //b00111111 const uint8_t ERROR_SIGNAL_N7 = 0x7F; //b01111111 //special case error signals, 90 degree turns const uint8_t ERROR_LT0 = 0xE0; //b11100000 const uint8_t ERROR_LT1 = 0xF0; //b11110000 const uint8_t ERROR_LT2 = 0xF8; //b11111000 const uint8_t ERROR_RT0 = 0x07; //b00000111 const uint8_t ERROR_RT1 = 0x0F; //b00001111 const uint8_t ERROR_RT2 = 0x1F; //b00011111 //Lost bot error signal const uint8_t ERROR_OFF_TRACK = 0xFF; //b11111111 #ifdef TUNE_PID char response = 'N'; printf("\nDo you want to change the PID coefficents? 'Y' or 'N': "); scanf("%c", &response); if((response == 'Y') || (response == 'y')) { printf("\nCurrent kp = %d, please enter new kp = ", kp); scanf("%d", &kp); printf("\nCurrent ki = %d, please enter new ki = ", ki); scanf("%d", &ki); printf("\nCurrent kd = %d, please enter new kd = ", kd); scanf("%d", &kd); } printf("\nThe PID coefficents are: "); printf("\nkp = %d", kp); printf("\nki = %d", ki); printf("\nkd = %d\n", kd); #endif//TUNE_PID //loop time is ~1.6ms for(;;) { //wait for start_stop button press while(!run); //mode is set to forward, but duty cycle is still 0 motor_shield.set_operating_mode(l_motor_driver, Max14871_Shield::FORWARD); motor_shield.set_operating_mode(r_motor_driver, Max14871_Shield::FORWARD); while(run) { //measure dt with scope loop_pulse = !loop_pulse; //get raw ir sensor data ir_val = ~(ir_bus.read()); //scale feedback switch(ir_val) { case(ERROR_SIGNAL_P7): current_error = 7; break; case(ERROR_SIGNAL_P6): current_error = 6; break; case(ERROR_SIGNAL_P5): current_error = 5; break; case(ERROR_SIGNAL_P4): current_error = 4; break; case(ERROR_SIGNAL_P3): current_error = 3; break; case(ERROR_SIGNAL_P2): current_error = 2; break; case(ERROR_SIGNAL_P1): current_error = 1; break; case(ERROR_SIGNAL_00): current_error = 0; break; case(ERROR_SIGNAL_N1): current_error = -1; break; case(ERROR_SIGNAL_N2): current_error = -2; break; case(ERROR_SIGNAL_N3): current_error = -3; break; case(ERROR_SIGNAL_N4): current_error = -4; break; case(ERROR_SIGNAL_N5): current_error = -5; break; case(ERROR_SIGNAL_N6): current_error = -6; break; case(ERROR_SIGNAL_N7): current_error = -7; break; case(ERROR_LT0): current_error = 7; break; case(ERROR_LT1): current_error = 7; break; case(ERROR_LT2): current_error = 7; break; case(ERROR_RT0): current_error = -7; break; case(ERROR_RT1): current_error = -7; break; case(ERROR_RT2): current_error = -7; break; case(ERROR_OFF_TRACK): //requires reset motor_shield.set_pwm_duty_cycle(r_motor_driver, 0.0f); motor_shield.set_pwm_duty_cycle(l_motor_driver, 0.0f); while(1); default: current_error = previous_error; break; } /*get integral term, if statement helps w/wind-up from url in file banner If integral wind-up is a problem two common solutions are (1) zero the integral, that is set the variable integral equal to zero, every time the error is zero or the error changes sign. (2) "Dampen" the integral by multiplying the accumulated integral by a factor less than one when a new integral is calculated. */ if(current_error == 0) { integral = 0; } else if(((current_error < 0) && (previous_error > 0)) || ((current_error > 0) && (previous_error < 0))) { integral = 0; } else { integral = (integral + current_error); } //get derivative term derivative = (current_error - previous_error); //save current error for next loop previous_error = current_error; //get new duty cycle for right motor r_duty_cycle = (TARGET_DUTY_CYCLE - (kp*current_error + ((ki*integral)/1000) + kd*derivative)); //clamp to limits if((r_duty_cycle > MAX_DUTY_CYCLE) || (r_duty_cycle < MIN_DUTY_CYCLE)) { if(r_duty_cycle > MAX_DUTY_CYCLE) { r_duty_cycle = MAX_DUTY_CYCLE; } else { r_duty_cycle = MIN_DUTY_CYCLE; } } //get new duty cycle for left motor l_duty_cycle = (TARGET_DUTY_CYCLE + (kp*current_error + ((ki*integral)/1000) + kd*derivative)); //clamp to limits if((l_duty_cycle > MAX_DUTY_CYCLE) || (l_duty_cycle < MIN_DUTY_CYCLE)) { if(l_duty_cycle > MAX_DUTY_CYCLE) { l_duty_cycle = MAX_DUTY_CYCLE; } else { l_duty_cycle = MIN_DUTY_CYCLE; } } //update direction and duty cycle for right motor if(r_duty_cycle < 0) { r_duty_cycle = (r_duty_cycle * -1); motor_shield.set_operating_mode(r_motor_driver, Max14871_Shield::REVERSE); motor_shield.set_pwm_duty_cycle(r_motor_driver, ((float) r_duty_cycle)/100.0f); } else { motor_shield.set_operating_mode(r_motor_driver, Max14871_Shield::FORWARD); motor_shield.set_pwm_duty_cycle(r_motor_driver, ((float) r_duty_cycle)/100.0f); } //update direction and duty cycle for left motor if(l_duty_cycle < 0) { l_duty_cycle = (l_duty_cycle * -1); motor_shield.set_operating_mode(l_motor_driver, Max14871_Shield::REVERSE); motor_shield.set_pwm_duty_cycle(l_motor_driver, ((float) l_duty_cycle)/100.0f); } else { motor_shield.set_operating_mode(l_motor_driver, Max14871_Shield::FORWARD); motor_shield.set_pwm_duty_cycle(l_motor_driver, ((float) l_duty_cycle)/100.0f); } } //shutdown motor_shield.set_operating_mode(l_motor_driver, Max14871_Shield::COAST); motor_shield.set_operating_mode(r_motor_driver, Max14871_Shield::COAST); //reset all data to initial state r_duty_cycle = 0; l_duty_cycle = 0; current_error = 0; previous_error = current_error; integral = 0; derivative = 0; } }