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.
main.cpp
- Committer:
- janusboandersen
- Date:
- 2018-12-05
- Revision:
- 24:7fa7383e1b07
- Parent:
- 23:e1e6411e5221
- Child:
- 25:17e9e410795c
File content as of revision 24:7fa7383e1b07:
#include "mbed.h" #include "m3pi.h" m3pi m3pi; //declare a new car object //Filename settings #define FILESYS "robot" //name of the virtual file system on the LPC1768 #define FILENAME "/robot/data.txt" //filename to write backups to //Team calibrated parameters for car and battery #define LOW_BATTERY_VOLTAGE 4.75 //measured voltage where charging is required #define CHARGE_TIME 20 //in seconds. 1200 seconds = 20 minutes #define CROSSROAD_REFL_VAL 350 //sensor reading on PC0 and PC4 at crossroad //Pit control #define ENTER_PIT 1 //direction constant to mark driven forward out of pit #define LEAVE_PIT -1 //direction constant to mark backing out of pit #define SPEED_INSIDE_PIT 0.2 //speed to drive inside the pit #define PIT_TRANSPORT_TIME 1 //seconds to drive pit stretch at interal pit spd //Car sensors and LEDs #define PC0 0 //location of PC0 reflectance sensor #define PC4 4 //location of PC4 reflectance sensor #define CALIBRATED_SENS_VALS 0x87 //command to 3pi to get calibrated values //Various motor speeds #define MAX 1.0 #define MIN 0 #define OPTIMAL_SPEED 0.8 //Current targeted optimal speed #define PIT_SPEED 0.5 //Speed on a pit lap to avoid overshooting the pit #define DECELLERATION 0.1 //Rate of decelleration to avoid bumping //Calibrated values for performing 90 degree turns #define TURN_SPEED 0.3 #define TURN_TIME 0.22 // PID terms #define P_TERM 1 #define D_TERM 8 #define I_TERM 0 //Forward declare our type for storing car performance date //It can now be used as a type by the name performance_data typedef struct pd { float wh; int pitstops; } performance_data; //Function to write car performance data to persistent file void write_to_file(performance_data data) { //Define file system LocalFileSystem local(FILESYS); //Open a file (or create a new) with write access //"w": write "r":read "a":append (add @end) //"w": will overwrite any existing data, so we just store one line. FILE *fp = fopen(FILENAME, "w"); //write performance data to the file fprintf(fp, "%f %d", data.wh, data.pitstops); //close the file and release the memory when done. fclose(fp); } //Function to read car performance data from persistent file performance_data read_from_file() { //Declare the variable which will ultimately be returned performance_data data; //Define file system on mbed LPC1768 LocalFileSystem local(FILESYS); //Try to open the file as read (it might not exist) FILE *fp = fopen(FILENAME, "r"); //Test whether the file exists if (fp != NULL) { //file exists, so read from it //Read stored data from the file and put in struct // &(struct.member) and &struct.member yield same address fscanf(fp, "%f %d", &(data.wh), &(data.pitstops) ); //close file and release mem. fclose(fp); } else { //file doesn't exist, so just return zeros (no performance yet) data.wh = 0.0; data.pitstops = 0; } //return the data object to the caller return data; } //This function tests the car's battery state, and returs true if it needs //to charge. Otherwise false is returned. bool battery_low() { //Get battery level from the 3pi robot float voltage = m3pi.battery(); //return battery state result return (voltage <= LOW_BATTERY_VOLTAGE) ? true : false; } //Get the values from 3pi read from reflectance sensors PC0-PC4 //The function returns in the array that is provided as function argument void sensor_all(int arr[]) { m3pi.putc(CALIBRATED_SENS_VALS); // request 3pi for calibrated values //Pick up the received data for(int n = PC0; n <= PC4; n++) { // loop through all sensors char lowbyte = m3pi.getc(); //LSB: we receive the data "little endian" char hibyte = m3pi.getc(); //get the MSB arr[n] = ((lowbyte + (hibyte << 8))); // make 2 byte int (8 bits / byte) } } //Function to check whether car is just at the pit bool car_at_pit_crossroad(int sensor_array[]) { //Read the sensor values sensor_all(sensor_array); //Check the PC0 and PC4 sensors vs thresholds if (sensor_array[PC0] > CROSSROAD_REFL_VAL && sensor_array[PC4] > CROSSROAD_REFL_VAL) { //Car is at the crossroad return true; } else { //Car is NOT at the crossroad return false; } } //Turn the car 90 degrees left void turn_left (void) { m3pi.left(TURN_SPEED); wait(TURN_TIME); } //Turn the car 90 degrees right void turn_right (void) { m3pi.right (TURN_SPEED); wait(TURN_TIME); } //Safely navigate the car from the pit crossroad and all the way to the charger. //direction = 1 to enter pit... direction = -1 to leave the pit. //takes control all the way from track to pit OR reverse. //Error handling: If a wrong code is passed, the function does nothing quietly. void pit_navigate(int direction) { //Car must enter pit forward if (direction == ENTER_PIT) { m3pi.stop(); turn_right(); m3pi.stop(); m3pi.forward(SPEED_INSIDE_PIT); wait(PIT_TRANSPORT_TIME); m3pi.stop(); } //Car must leave pit backward if (direction == LEAVE_PIT) { m3pi.backward(SPEED_INSIDE_PIT); wait(PIT_TRANSPORT_TIME); m3pi.stop(); turn_left(); m3pi.stop(); } } //TO DO: IMPLEMENT A WH CALCULATION /* Begin the charging by calling when parked in pit * Charges for a called number of seconds (int charge_time) * Return the amount of Wh charged during each charging session * Function keeps control as long as charging */ float charge(int charge_time) { float wh_this_session = 0; for (int i = 0; i < charge_time; i++) { // PERFORM SOME WEIRD CALCULATION HERE wait(1.0); } //end for //return the amount of Wh charged return wh_this_session; } //display values from the performance data struct in the display void show_car_data(performance_data data) { /* Format: xxx.xxWh 0000xxPs Formats are specified according to guesstimates with the following calcs Wh = u * i * t = 6 V * 0.5 A * 1/3h * 20 times = 20 Wh Bilforbrug: ca. 800 mA -> over 20 timer -> 16000 mAh -> 16 Ah 16 Ah * 5 V = 80 Wh. -> go for something between 20 - 80 Wh, with two decimals */ //clear screen m3pi.cls(); //print Wh in first line m3pi.locate(0,0); printf("%06.2fWh", data.wh); //6 wide in total, 2 after comma, pad with zero //print Pitstops in second line m3pi.locate(0,1); printf("%06dPs", data.pitstops); //6 wide in total, padded with zeros } int main() { /* Define all needed variables in this section */ performance_data car_data; //Saves the car's Wh and pitstops data int laps = 0; //experimantal lap counter float right; //right motor speed float left; //left motor speed float current_pos_of_line = 0.0; //for PID float previous_pos_of_line = 0.0; //for PID float derivative,proportional,integral = 0; //for PID float power; //differential speed float speed = OPTIMAL_SPEED; //our optimal speed setting int sensor_values[5]; //array for reflectance sensor values bool battery_low_flag = false; //goes true when battery needs charging bool pit_crossroad_flag = false; //goes true when car is at the pit crossrd bool button_pressed_flag = false; //goes true when the button has been prs //define the button: user push button on p21 DigitalIn button(p21); button.mode(PullUp); //1 if not pressed, 0 if pressed /* When the car starts, read previous progress * if only connected to USB, do not perform this restore process */ // If robot is on this returns a value, otherwise it's just connected USB if (m3pi.battery() > 0) { car_data = read_from_file(); //restore data from file } //Info to user m3pi.locate(0,1); m3pi.printf("Line PID"); //Calibrate before starting <- might be moved somewhere else m3pi.sensor_auto_calibrate(); //Begin main event loop while (1) { /* Check sensors and update sensor-based flags * - might not be every loop: wrap this in a timer of sorts */ battery_low_flag = battery_low(); pit_crossroad_flag = car_at_pit_crossroad(sensor_values); //anden frekvens? //debounce the button if (button == 0) { button_pressed_flag = true; } /* switch if nothing else: line following */ //if the button is pressed, show some stats on the display if ( button_pressed_flag ) { //call the function to print car data show_car_data(car_data); //reset the flag button_pressed_flag = false; } //if low_batt: decellerate car (slow down) and continue to pit if ( battery_low_flag ) { speed = (speed > PIT_SPEED) ? speed-DECELLERATION : PIT_SPEED; } //if @pitcrossrd, not going to charge: increment internal lap counter if ( pit_crossroad_flag ) { laps++; //might count multiple times if the loop is too fast } //if low_batt && atPitIntersection: Stop && execute pit navigation program if ( battery_low_flag && pit_crossroad_flag ) { //We are at the pit, and the battery is low //Enter the pit pit_navigate(ENTER_PIT); //Call charging function //When done, it will return the amount of Wh charged car_data.wh += charge(CHARGE_TIME); //increment the number of pitstops car_data.pitstops += 1; //backup data to persistent file write_to_file(car_data); //Leave the pit pit_navigate(LEAVE_PIT); //Reset pit-related flags battery_low_flag = false; pit_crossroad_flag = false; } /* if all_sensors == 1000 (lifted from track): show Wh and pitstops on display stop motors, reset PID errors, wait 20 secs, recalibrate sensors if all_sensors < 90 (driven off track): stop and ask for help? Flash LEDs? turn back and continue? Reset PID errors Periodically (not necessarily every lap - maybe every 2-5th lap): Save to file -> possibly only after pit Check battery voltage -> every couple of minutes Check light sensors for drift -> recalibrate (optional) */ /* Separate functions pit_execution (only when connected to charger - voltage > V_CHARGER_MIN charge and wait for signal from the external charging circuit during charging: integrate v(t) dt, add to cumulative sum when fully charged (or signal goes high): Calculate delta_Wh ( integrate{ v(t) dt} * u * 1/3600 ) Calculate total_Wh AfterPitAdmin: Calibrate sensors show_results_on_display check_sensors_for_errors */ // Get the position of the line. current_pos_of_line = m3pi.line_position(); proportional = current_pos_of_line; // Compute the derivative derivative = current_pos_of_line - previous_pos_of_line; // Compute the integral integral += proportional; // Remember the last position. previous_pos_of_line = current_pos_of_line; // Compute the power power = (proportional * (P_TERM) ) + (integral*(I_TERM)) + (derivative*(D_TERM)) ; // Compute new speeds right = speed+power; left = speed-power; // limit checks if (right < MIN) right = MIN; else if (right > MAX) right = MAX; if (left < MIN) left = MIN; else if (left > MAX) left = MAX; // set speed m3pi.left_motor(left); m3pi.right_motor(right); } //end while } //end main