diff options
author | lonkaars <loek@pipeframe.xyz> | 2022-06-08 08:33:37 +0200 |
---|---|---|
committer | lonkaars <loek@pipeframe.xyz> | 2022-06-08 08:33:37 +0200 |
commit | 932f46a1f0b7e3ed99bbfc901dad80e2636cd9e4 (patch) | |
tree | 961829c1ac511c78650c16802ab814bd1267c1b8 | |
parent | 519bad1fca5b774564d68c8c2441523c5038c57f (diff) | |
parent | 1545ba9f1591a21ad2ecef2f0ef2414ae36c667e (diff) |
Merge branch 'nonailla-master'
-rw-r--r-- | robot/mode_grid.c | 818 | ||||
-rw-r--r-- | robot/mode_grid.h | 83 |
2 files changed, 899 insertions, 2 deletions
diff --git a/robot/mode_grid.c b/robot/mode_grid.c index 8526499..23e539e 100644 --- a/robot/mode_grid.c +++ b/robot/mode_grid.c @@ -1,3 +1,817 @@ -#include "mode_grid.h" +/* + * 3pi-linefollower-pid - demo code for the Pololu 3pi Robot + * + * This code will follow a black line on a white background, using a + * PID-based algorithm. + * + * http://www.pololu.com/docs/0J21 + * http://www.pololu.com + * http://forum.pololu.com + * + */ + +// The 3pi include file must be at the beginning of any program that +// uses the Pololu AVR library and 3pi. +#include <pololu/3pi.h> + +// This include file allows data to be stored in program space. The +// ATmega168 has 16k of program space compared to 1k of RAM, so large +// pieces of static data should be stored in program space. +#include <avr/pgmspace.h> + +unsigned int sensors[5]; // an array to hold sensor values +unsigned int position; +int orderNumber; +int transition; +int chargedStatus; + +int mazeStatus; + + +enum section{ + mazeMode, + gridMode, + chargeMode +} parcourMode; + +enum orientation{ + North, + East, + South, + West +} direction; + + +typedef struct { + int x; + int y; +} coordinates; + +coordinates order[4]; +coordinates location; +coordinates destination; + +int Detection; +char xLocation; +char yLocation; + + +// Introductory messages. The "PROGMEM" identifier causes the data to +// go into program space. +const char welcome_line1[] PROGMEM = " Pololu"; +const char welcome_line2[] PROGMEM = "3\xf7 Robot"; +const char demo_name_line1[] PROGMEM = "PID Line"; +const char demo_name_line2[] PROGMEM = "follower"; + +// A couple of simple tunes, stored in program space. +const char welcome[] PROGMEM = ">g32>>c32"; +const char go[] PROGMEM = "L16 cdegreg4"; + +// Data for generating the characters used in load_custom_characters +// and display_readings. By reading levels[] starting at various +// offsets, we can generate all of the 7 extra characters needed for a +// bargraph. This is also stored in program space. +const char levels[] PROGMEM = { + 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b11111, + 0b11111, + 0b11111, + 0b11111, + 0b11111, + 0b11111, + 0b11111 +}; + +void grid(); + +// This function loads custom characters into the LCD. Up to 8 +// characters can be loaded; we use them for 7 levels of a bar graph. +void load_custom_characters() +{ + lcd_load_custom_character(levels+0,0); // no offset, e.g. one bar + lcd_load_custom_character(levels+1,1); // two bars + lcd_load_custom_character(levels+2,2); // etc... + lcd_load_custom_character(levels+3,3); + lcd_load_custom_character(levels+4,4); + lcd_load_custom_character(levels+5,5); + lcd_load_custom_character(levels+6,6); + clear(); // the LCD must be cleared for the characters to take effect +} + +// This function displays the sensor readings using a bar graph. +void display_readings(const unsigned int *calibrated_values) +{ + unsigned char i; + + for(i=0;i<5;i++) { + // Initialize the array of characters that we will use for the + // graph. Using the space, an extra copy of the one-bar + // character, and character 255 (a full black box), we get 10 + // characters in the array. + const char display_characters[10] = {' ',0,0,1,2,3,4,5,6,255}; + + // The variable c will have values from 0 to 9, since + // calibrated values are in the range of 0 to 1000, and + // 1000/101 is 9 with integer math. + char c = display_characters[calibrated_values[i]/101]; + + // Display the bar graph character. + print_character(c); + } +} + +// Initializes the 3pi, displays a welcome message, calibrates, and +// plays the initial music. +void initialize() +{ + unsigned int counter; // used as a simple timer + + + // This must be called at the beginning of 3pi code, to set up the + // sensors. We use a value of 2000 for the timeout, which + // corresponds to 2000*0.4 us = 0.8 ms on our 20 MHz processor. + pololu_3pi_init(2000); + load_custom_characters(); // load the custom characters + + // Play welcome music and display a message + print_from_program_space(welcome_line1); + lcd_goto_xy(0,1); + print_from_program_space(welcome_line2); + play_from_program_space(welcome); + delay_ms(1000); + + clear(); + print_from_program_space(demo_name_line1); + lcd_goto_xy(0,1); + print_from_program_space(demo_name_line2); + delay_ms(1000); + + // Display battery voltage and wait for button press + while(!button_is_pressed(BUTTON_B)) + { + int bat = read_battery_millivolts(); + + clear(); + print_long(bat); + print("mV"); + lcd_goto_xy(0,1); + print("Press B"); + + delay_ms(100); + } + + // Always wait for the button to be released so that 3pi doesn't + // start moving until your hand is away from it. + wait_for_button_release(BUTTON_B); + delay_ms(1000); + + // Auto-calibration: turn right and left while calibrating the + // sensors. + for(counter=0;counter<80;counter++) + { + if(counter < 20 || counter >= 60) + set_motors(40,-40); + else + set_motors(-40,40); + + // This function records a set of sensor readings and keeps + // track of the minimum and maximum values encountered. The + // IR_EMITTERS_ON argument means that the IR LEDs will be + // turned on during the reading, which is usually what you + // want. + calibrate_line_sensors(IR_EMITTERS_ON); + + // Since our counter runs to 80, the total delay will be + // 80*20 = 1600 ms. + delay_ms(20); + } + set_motors(0,0); + + // Display calibrated values as a bar graph. + while(!button_is_pressed(BUTTON_B)) + { + // Read the sensor values and get the position measurement. + unsigned int position = read_line(sensors,IR_EMITTERS_ON); + + // Display the position measurement, which will go from 0 + // (when the leftmost sensor is over the line) to 4000 (when + // the rightmost sensor is over the line) on the 3pi, along + // with a bar graph of the sensor readings. This allows you + // to make sure the robot is ready to go. + clear(); + print_long(position); + lcd_goto_xy(0,1); + display_readings(sensors); + + delay_ms(100); + } + wait_for_button_release(BUTTON_B); + + clear(); + + print("Go!"); + + // Play music and wait for it to finish before we start driving. + play_from_program_space(go); + while(is_playing()); +} + +void full_rotation(){ + set_motors(0,0); + delay_ms(500); + set_motors(60,-60); + delay_ms(540); + set_motors(0,0); + delay_ms(100); + set_motors(10,10); + delay(500); + set_motors(0,0); + position = read_line(sensors,IR_EMITTERS_ON); + delay_ms(500); +} + +void half_rotation_left(){ + set_motors(0,0); + set_motors(50,50); + delay_ms(150); + set_motors(-30,30); + delay_ms(600); + set_motors(0,0); + position = read_line(sensors,IR_EMITTERS_ON); + delay_ms(500); + +} + +void crossway_detection(){ + half_rotation_left(); +} + +void cross_walk(){ + while(sensors[0] < 100 && sensors[1] <100 && sensors[2] < 100 && sensors[3] < 100 && sensors[4] < 100){ + set_motors(15,15); + delay(300); + position = read_line(sensors,IR_EMITTERS_ON); + if(sensors[2] > 100 || sensors[3] > 100 || sensors[1] > 100){ + set_motors(0,0); + clear(); + print("WALK"); + transition++; + if(transition == 3){ + set_motors(40,40); + delay(600); + transition = 0; + parcourMode = gridMode ; + } + } + + else{ + transition = 0; + full_rotation(); + } + } +} + +void charge_cross_walk(){ + while(sensors[0] < 100 && sensors[1] <100 && sensors[2] < 100 && sensors[3] < 100 && sensors[4] < 100){ + set_motors(15,15); + delay(500); + position = read_line(sensors,IR_EMITTERS_ON); + if(sensors[2] > 100 || sensors[3] > 100 || sensors[1] > 100){ + set_motors(0,0); + clear(); + print("WALK"); + transition++; + if(transition == 3){ + set_motors(40,40); + delay(600); + set_motors(0,0); + + transition =0; + mazeStatus = 1; + parcourMode = mazeMode; + break; + } + } + else{ + transition = 0; + full_rotation(); + } + } +} + +void grid_rotation_full(){ + set_motors(60,-60); + delay_ms(540); + set_motors(10,10); + position = read_line(sensors,IR_EMITTERS_ON); +} + +void grid_rotation_left(){ + set_motors(-30,30); + delay_ms(600); + set_motors(10,10); + position = read_line(sensors,IR_EMITTERS_ON); +} + +void grid_rotation_right(){ + set_motors(30,-30); + delay_ms(600); + set_motors(10,10); + position = read_line(sensors,IR_EMITTERS_ON); +} + +void grid_crossway_detection(){ + set_motors(50,50); + delay_ms(150); + set_motors(10,10); + position = read_line(sensors,IR_EMITTERS_ON); +} + +void gridFollowLine(){ + unsigned int last_proportional=0; + long integral=0; + + // This is the "main loop" - it will run forever. + while(1) + { + // Get the position of the line. Note that we *must* provide + // the "sensors" argument to read_line() here, even though we + // are not interested in the individual sensor readings. + position = read_line(sensors,IR_EMITTERS_ON); + + // The "proportional" term should be 0 when we are on the line. + int proportional = ((int)position) - 2000; + + // Compute the derivative (change) and integral (sum) of the + // position. + int derivative = proportional - last_proportional; + integral += proportional; + + // Remember the last position. + last_proportional = proportional; + + // Compute the difference between the two motor power settings, + // m1 - m2. If this is a positive number the robot will turn + // to the right. If it is a negative number, the robot will + // turn to the left, and the magnitude of the number determines + // the sharpness of the turn. + int power_difference = proportional/20 + integral/10000 + derivative*3/2; + + // Compute the actual motor settings. We never set either motor + // to a negative value. + const int max = 60; + if(power_difference > max) + power_difference = max; + if(power_difference < -max) + power_difference = -max; + + if(sensors[0] >= 500 && sensors[1] >= 250 && sensors[2] >= 500 && sensors[3] >= 250 &&sensors[4] >= 500){ + break; + } + else if (sensors[0] >= 500 && sensors[1] >= 200 && sensors[4] < 100){ + break; + } + else if(sensors[4] >= 500 && sensors[3] >= 200 && sensors[0] < 100){ //for the south and west borders of the grid + break; + } + else if(sensors[4] >= 500 && sensors[3] >= 200 && sensors[2] <100 && sensors[0] < 100){ //sharp right corners + break; + } + + else{ + if(power_difference < 0 && (sensors[2] > 100 || sensors[3] > 100 || sensors[1] > 100) ){ + set_motors(max+power_difference, max);} + else if( power_difference > 0 && ( sensors[2] > 100 || sensors[3] > 100 || sensors[1] > 100)){ + set_motors(max, max-power_difference);} + } + } +} + +void shortDrive(){ + set_motors(50,50); + delay(150); + set_motors(0,0); +} + +void orderOne(){ + order[0].x = 1; + order[0].y = 4; +} + +void orderTwo(){ + order[1].x = 3; + order[1].y = 2; +} + +void orderThree(){ + order[2].x = 1; + order[2].y = 4; +} + +void orderFour(){ + order[3].x = 0; + order[3].y = 0; +} + + +void beginLocation(){ + location.x = 4; + location.y = 0; + direction = West; +} + +void endDestination(){ + destination.x = 4; + destination.y = 4 ; +} + +void turn_North() +{ + switch (direction) + { + case North: + break; + + case East: + grid_rotation_left(); + break; + + case South: + grid_rotation_full(); + break; + + case West: + grid_rotation_right(); + break; + } + + direction = North; +} + +void turn_West() +{ + switch (direction) + { + case West: + break; + + case North: + grid_rotation_left(); + break; + + case East: + grid_rotation_full(); + break; + + case South: + grid_rotation_right();; + break; + } + + direction = West; +} + +void turn_South() +{ + switch (direction) + { + case South: + break; + + case West: + grid_rotation_left(); + break; + + case North: + grid_rotation_full(); + break; + + case East: + grid_rotation_right();; + break; + } + + direction = South; +} + +void turn_East() +{ + switch (direction) + { + case East: + break; + + case South: + grid_rotation_left(); + break; + + case West: + grid_rotation_full(); + break; + + case North: + grid_rotation_right();; + break; + } + + direction = East; +} + +void locationMessage(){ + clear(); + print_long(location.x); + print(","); + print_long(location.y); + delay(200); +} + +void arrivedMessage(){ + if (location.x == destination.x && location.y == destination.y){ + clear(); + print("ORDER "); + print_long(orderNumber); + play_frequency(400,500,7); + delay(500); + } +} + +void goToX(){ + if (location.x != destination.x ){ + while(location.x != destination.x ){ + if (location.x > destination.x){ + turn_West(); + gridFollowLine(); + grid_crossway_detection(); + location.x--; + locationMessage(); + } + + else if(location.x < destination.x){ + turn_East(); + gridFollowLine(); + grid_crossway_detection(); + location.x++; + locationMessage(); + } + } + } +} + +void goToY(){ + if (location.y != destination.y ){ + while(location.y != destination.y ){ + if (location.y > destination.y){ + turn_South(); + gridFollowLine(); + grid_crossway_detection(); + location.y--; + locationMessage(); + } + + else if(location.y < destination.y){ + turn_North(); + gridFollowLine(); + grid_crossway_detection(); + location.y++; + locationMessage(); + } + } + } +} + +void home(){ + set_motors(0,0); + delay_ms(150); + clear(); + print("CHARGING"); + set_motors(30,30); + delay_ms(600); + set_motors(0,0); + play_frequency(300,500,7); + delay_ms(600); + position = read_line(sensors,IR_EMITTERS_ON); + chargedStatus = 1; + clear(); + delay_ms(2000); +} + +void charge(){ + unsigned int last_proportional=0; + long integral=0; + //initialize(); + chargedStatus = 0; + clear(); + print("CHARGE"); + + while(1){ + // Get the position of the line. Note that we *must* provide + // the "sensors" argument to read_line() here, even though we + // are not interested in the individual sensor readings. + position = read_line(sensors,IR_EMITTERS_ON); + + // The "proportional" term should be 0 when we are on the line. + int proportional = ((int)position) - 2000; + + // Compute the derivative (change) and integral (sum) of the + // position. + int derivative = proportional - last_proportional; + integral += proportional; + + // Remember the last position. + last_proportional = proportional; + + // Compute the difference between the two motor power settings, + // m1 - m2. If this is a positive number the robot will turn + // to the right. If it is a negative number, the robot will + // turn to the left, and the magnitude of the number determines + // the sharpness of the turn. + int power_difference = proportional/20 + integral/10000 + derivative*3/2; + + // Compute the actual motor settings. We never set either motor + // to a negative value. + + const int max = 60; + if(power_difference > max) + power_difference = max; + if(power_difference < -max) + power_difference = -max; + + + if(sensors[0] < 100 && sensors[1] <100 && sensors[2] < 100 && sensors[3] < 100 && sensors[4] < 100){ + charge_cross_walk(); + if (parcourMode == mazeMode){ + break; + } + } + + else if((sensors[0] >= 500 && sensors[1] >= 500 && sensors[2] >= 500 && sensors[3] >= 500 &&sensors[4] >= 500) && chargedStatus == 0){ + home(); + delay(200); + full_rotation(); + shortDrive(); + } + else if (sensors[0] >= 500 && sensors[1] >= 200 && sensors[4] < 100){ + clear(); + half_rotation_left(); + } + + else if(sensors[0] >= 500 && sensors[1] >= 250 && sensors[2] >= 500 && sensors[3] >= 250 &&sensors[4] >= 500){ + clear(); + crossway_detection(); + } + else{ + if(power_difference < 0 && (sensors[2] > 100 || sensors[3] > 100 || sensors[1] > 100) ){ + set_motors(max+power_difference, max);} + else if( power_difference > 0 && ( sensors[2] > 100 || sensors[3] > 100 || sensors[1] > 100)){ + set_motors(max, max-power_difference);} + } + } +} + +void grid() { + + set_motors(0,0); + clear(); + print("GRID"); + delay(500); + + //coordinates of the orders + orderOne(); + orderTwo(); + orderThree(); + orderFour(); + + beginLocation(); + + for (int i = 0; i < 4; i++ ){ + orderNumber = i+1; + + destination.x = order[i].x; + destination.y = order[i].y; + + locationMessage(); + delay(1000); + goToX(); + goToY(); + arrivedMessage(); + } + //go to the end of the grid, to transition to charge station + endDestination(); + + locationMessage(); + delay(1000); + goToY(); + goToX(); + turn_East(); //this was uncommented (6.3) + parcourMode = chargeMode; +} + + + +// This is the main function, where the code starts. All C programs +// must have a main() function defined somewhere. +void maze() +{ + + unsigned int last_proportional=0; + long integral=0; + + // set up the 3pi + if (mazeStatus == 0){ + initialize(); + } + clear(); + print("MAZE"); + + + transition = 0; + + // This is the "main loop" - it will run forever. + while(1) + { + // Get the position of the line. Note that we *must* provide + // the "sensors" argument to read_line() here, even though we + // are not interested in the individual sensor readings. + position = read_line(sensors,IR_EMITTERS_ON); + + // The "proportional" term should be 0 when we are on the line. + int proportional = ((int)position) - 2000; + + // Compute the derivative (change) and integral (sum) of the + // position. + int derivative = proportional - last_proportional; + integral += proportional; + + // Remember the last position. + last_proportional = proportional; + + // Compute the difference between the two motor power settings, + // m1 - m2. If this is a positive number the robot will turn + // to the right. If it is a negative number, the robot will + // turn to the left, and the magnitude of the number determines + // the sharpness of the turn. + int power_difference = proportional/20 + integral/10000 + derivative*3/2; + + // Compute the actual motor settings. We never set either motor + // to a negative value. + + const int max = 60; + if(power_difference > max) + power_difference = max; + if(power_difference < -max) + power_difference = -max; + + if(sensors[0] < 100 && sensors[1] <100 && sensors[2] < 100 && sensors[3] < 100 && sensors[4] < 100){ + cross_walk(); + if (parcourMode == gridMode){ + break; + } + //full_rotation(); + } + else if(sensors[0] >= 500 && sensors[1] >= 250 && sensors[2] >= 500 && sensors[3] >= 250 &&sensors[4] >= 500){ + crossway_detection(); + } + else if (sensors[0] >= 500 && sensors[1] >= 200 && sensors[4] < 100){ + half_rotation_left(); + } + else{ + if(power_difference < 0 && (sensors[2] > 100 || sensors[3] > 100 || sensors[1] > 100) ) + set_motors(max+power_difference, max); + else if( power_difference > 0 && ( sensors[2] > 100 || sensors[3] > 100 || sensors[1] > 100) ) + set_motors(max, max-power_difference); + } + + } + +} + +void mode(){ + while(1){ + if(parcourMode == mazeMode){ + maze(); + } + else if(parcourMode == gridMode){ + grid(); + } + else if(parcourMode == chargeMode){ + charge(); + } + } +} + + +int main(){ + chargedStatus = 0; + mazeStatus = 0; + + parcourMode = mazeMode; + + mode(); +} -void w2_mode_grid() {} diff --git a/robot/mode_grid.h b/robot/mode_grid.h index 55093ad..b13cc4c 100644 --- a/robot/mode_grid.h +++ b/robot/mode_grid.h @@ -7,4 +7,87 @@ * * processes orders from the order buffer */ + +enum orientation{ + North, + East, + South, + West +} direction; + +typedef struct { + int x; + int y; +} coordinates; + +coordinates order[4]; +coordinates location; +coordinates destination; + +char xLocation; +char yLocation; + +//LINE 25-68 CAN BE TAKEN AWAY WHEN USED WITH MODE_MAZE + +// The 3pi include file must be at the beginning of any program that +// uses the Pololu AVR library and 3pi. +#include <pololu/3pi.h> + +// This include file allows data to be stored in program space. The +// ATmega168 has 16k of program space compared to 1k of RAM, so large +// pieces of static data should be stored in program space. +#include <avr/pgmspace.h> + +unsigned int sensors[5]; // an array to hold sensor values +unsigned int position; +// Introductory messages. The "PROGMEM" identifier causes the data to +// go into program space. +const char welcome_line1[] PROGMEM = " Pololu"; +const char welcome_line2[] PROGMEM = "3\xf7 Robot"; +const char demo_name_line1[] PROGMEM = "PID Line"; +const char demo_name_line2[] PROGMEM = "follower"; + +// A couple of simple tunes, stored in program space. +const char welcome[] PROGMEM = ">g32>>c32"; +const char go[] PROGMEM = "L16 cdegreg4"; + +// Data for generating the characters used in load_custom_characters +// and display_readings. By reading levels[] starting at various +// offsets, we can generate all of the 7 extra characters needed for a +// bargraph. This is also stored in program space. +const char levels[] PROGMEM = { + 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b11111, + 0b11111, + 0b11111, + 0b11111, + 0b11111, + 0b11111, + 0b11111 +}; + void w2_mode_grid(); +void initialize(); +void full_rotation(); +void grid_rotation_left(); +void grid_rotation_right(); +void grid_crossway_detection(); +void gridFollowLine(); +void orderOne(); +void orderTwo(); +void orderThree(); +void orderFour(); +void beginLocation(); +void turn_North(); +void turn_West(); +void turn_South(); +void turn_East(); +void locationMessage(); +void arrivedMessage(); + |