/*************************************************** ________ ___ _________ ________ _____ |\ __ \|\ \|\___ ___\\ ____\ / __ \ \ \ \|\ /\ \ \|___ \ \_\ \ \___|_|\/_|\ \ \ \ __ \ \ \ \ \ \ \ \_____ \|/ \ \ \ \ \ \|\ \ \ \ \ \ \ \|____|\ \ \ \ \ \ \_______\ \__\ \ \__\ ____\_\ \ \ \__\ \|_______|\|__| \|__| |\_________\ \|__| \|_________| Name: BitS1 v2 Author: Prof. Kamau Jannis Kampmann Timo Altan IFK TH Köln Last Modified: 22.12.2023 backward_factor and search_factor needs revisit Fall 8.1 & 8.2 if and else conditions adapted MOTOR_IN1 & 2 changed to PIN7 MOTOR_IN3 & 4 changed to PIN8 check_voltage() changed from quantity sensitive to time sensitive (2000ms) 21.08.2023 Switch from libraries "DeviceDriverSet_xxx0" to "thk-motor-driver-v2.0" & "MPU6050_getdata" to "thk_imu-v2.0" 08 September 2022 improvement outline / format 15 Juli 2022 Recoding all functions **************************************************/ #define Key_pin 2 #define RGBLed_pin 4 #define Echo_pin 12 #define Trig_pin 13 #define OpticSensorR A0 #define OpticSensorM A1 #define OpticSensorL A2 #define Voltage_pin A3 #define NUM_LEDS 60 const int MOTOR_COUNT = 1; const int MOTOR_STBY = 3; const int MOTOR_PWM_A = 5; const int MOTOR_IN1 = 7; const int MOTOR_IN2 = 7; const int MOTOR_PWM_B = 6; const int MOTOR_IN3 = 8; const int MOTOR_IN4 = 8; thk_MotorDriverTB6612FNG motorR(MOTOR_COUNT, MOTOR_STBY, MOTOR_PWM_A, MOTOR_IN1, MOTOR_IN2, 0, 0, 0); thk_MotorDriverTB6612FNG motorL(MOTOR_COUNT, MOTOR_STBY, MOTOR_PWM_B, MOTOR_IN3, MOTOR_IN4, 0, 0, 0); const int INTERRUPT_PIN = 0; // Wenn kein Interrupt Pin angeschlossen ist, dann Interrupt Pin gleich 0 setzen. thk_IMU imu(INTERRUPT_PIN); extern unsigned long start_time = 0; char commandline_input = 'E'; char last_direction = 'D'; int commandline_output = 3; int modecounter; int obstacle_velocity = 0; int no_reflection = 950; int divisor = 1000; int rest = divisor / 2; int voltagetimer = 0; boolean mode_changed = false; float voltage_low = 7.2; float voltage_min = 7.0; float yaw = 0; float pitch = 0; float roll = 0; float backward_factor = 3.0; //Neuer backward_factor: Muss getestet werden float search_factor = 120000; //Neuer search_facotr: Muss getestet werden float search_time = 0; unsigned long time_drive = 0; float line_factor = 1.2; CRGB leds[NUM_LEDS]; extern float turn_factor; //Übergabe: Geschwindigkeitsreduktion bei Kurvenfahrt extern const float distance_one; //Übergabe: Muss größer als distance_two sein extern const float distance_two; //Übergabe: Muss größer als 0 sein extern int acceleration_increment; //Übergabe: Beschleunigung für Obstacle Modus extern int total_velocity; //Übergabe: Geschwindigkeit; Werte von 0 bis 255 zulässig extern int white_surface_reflection; //Übergabe: Wert muss für jeden Untergrund angepasst werden; Hoher Wert = Hohe reflection enum direction //Definieren der möglichen Fahrtrichtungen { Forward, Left, Right, Leftforward, Rightforward, Backward, Leftbackward, Rightbackward, Coast, Break }; enum modi //Definieren des Datentyps "modi" { line_follow_mode, obstacle_mode, standby_mode }; modi current_mode; //Variable "current_mode" vom Datentyp "modi" wird deklariert class Robotcar_functions { public: //************************************************************************************************************* //*****************************************/* Car Initialization */******************************************** //************************************************************************************************************* void initialise() { FastLED.addLeds<NEOPIXEL, RGBLed_pin>(leds, NUM_LEDS); FastLED.setBrightness(25); current_mode = standby_mode; Serial.begin(19200); pinMode(Echo_pin, INPUT); pinMode(Trig_pin, OUTPUT); pinMode(OpticSensorL, INPUT); pinMode(OpticSensorM, INPUT); pinMode(OpticSensorR, INPUT); pinMode(Key_pin, INPUT); pinMode(Voltage_pin, INPUT); pinMode(4, OUTPUT); imu.init(); motorR.init(); motorL.init(); if (total_velocity > 255) { total_velocity = 255; } if(turn_factor == 0) { turn_factor = 1; } if(turn_factor < 0) { turn_factor = turn_factor * -1.0; } search_time = search_factor*1/((float)total_velocity); } //************************************************************************************************************* //*****************************************/* Distance function */********************************************* //************************************************************************************************************* int get_distance() { unsigned long duration = 0; int distance = 0; digitalWrite(Trig_pin, LOW); delayMicroseconds(2); digitalWrite(Trig_pin, HIGH); delayMicroseconds(10); digitalWrite(Trig_pin, LOW); duration = pulseIn(Echo_pin, HIGH); distance = duration / 58; if ( distance > 150 ) { distance = 150; } return distance; } //************************************************************************************************************* //*****************************************/* Brightness function */******************************************* //************************************************************************************************************* int get_brightness(char LMR) { switch (LMR) { case 'L': return analogRead(OpticSensorL); break; case 'M': return analogRead(OpticSensorM); break; case 'R': return analogRead(OpticSensorR); break; default: return 0; break; } } //************************************************************************************************************* //*****************************************/* Serial Data Output */******************************************** //************************************************************************************************************* void serial_sensor_data_output() { if (current_mode == standby_mode) { switch (commandline_input) { case 'D': commandline_output = 0; break; case 'H': commandline_output = 1; break; case 'W': commandline_output = 2; break; case 'E': commandline_output = 3; Serial.println(""); Serial.println("Ausgabe der Sensorendaten!"); Serial.println("---------------------------------------------"); Serial.println("Eingabe 'D' für die Distanz!"); Serial.println("Eingabe 'H' für die Helligkeit!"); Serial.println("Eingabe 'W' für die Winkel!"); Serial.println("Eingabe 'E' um die Ausgabe zu Stoppen!"); Serial.println(""); Serial.flush(); break; default: break; } switch (commandline_output) { case 0: Serial.print("Distanz: "); Serial.print(get_distance()); Serial.println(""); break; case 1: Serial.print("Links: "); Serial.print(get_brightness('L')); Serial.print("\t"); Serial.print("Mitte: "); Serial.print(get_brightness('M')); Serial.print("\t"); Serial.print("Rechts: "); Serial.print(get_brightness('R')); Serial.println(""); break; case 2: Serial.print("Gieren: "); Serial.print(imu.get_z_rot()); Serial.print("\t"); Serial.print("Nicken: "); Serial.print(imu.get_y_rot()); Serial.print("\t"); Serial.print("Wanken: "); Serial.print(imu.get_x_rot()); Serial.println(""); break; default: break; } commandline_input = Serial.read(); } } //************************************************************************************************************* //*****************************************/* Car Drive Functions */******************************************* //************************************************************************************************************* void drive ( direction a , int velocity) { digitalWrite(3, HIGH); switch (a) { case Forward: motorR.drive(velocity, 1); motorL.drive(velocity, 1); break; case Left: motorR.drive(velocity, 1); // 1 = Vorwärts motorL.drive(velocity, 0); // 0 = Rückwärts break; case Right: motorR.drive(velocity, 0); motorL.drive(velocity, 1); break; case Leftforward: motorR.drive(velocity, 1); motorL.drive(0,1); break; case Rightforward: motorR.drive(0,1); motorL.drive(velocity, 1); break; case Backward: motorR.drive(velocity, 0); motorL.drive(velocity, 0); leds[0] = CRGB::Yellow; FastLED.show(); break; case Leftbackward: motorR.drive(velocity, 0); motorL.drive(0,0); break; case Rightbackward: motorR.drive(0,0); motorL.drive(velocity, 0); break; case Coast: digitalWrite(MOTOR_STBY, LOW); break; case Break: motorR.drive(0, 0); motorL.drive(0, 0); break; default: break; } return; } //************************************************************************************************************* //****************************************/* Function Mode Select */******************************************* //************************************************************************************************************* void mode_selection() { if (digitalRead(Key_pin) == 0) { if (!mode_changed) { mode_changed = true; modecounter++; if (modecounter > 3) { modecounter = 0; } switch (modecounter) { case 0: current_mode = standby_mode; break; case 1: if (check_voltage() >= 1) { current_mode = line_follow_mode; } start_time = millis(); break; case 2: current_mode = standby_mode; break; case 3: if (check_voltage() >= 1) { current_mode = obstacle_mode; } start_time = millis(); break; default: current_mode = standby_mode; break; } } } else { mode_changed = false; } } //************************************************************************************************************* //*************************************/* Check Mode / Battery Level */**************************************** //************************************************************************************************************* void check_standby() { if (current_mode == standby_mode) { drive(Break, 0); if (check_voltage() == 2) { leds[0] = CRGB::White; FastLED.show(); } else if (check_voltage() == 1) { if ((millis() % divisor) < rest) { leds[0] = CRGB::White; FastLED.show(); } else { leds[0] = CRGB::Red; FastLED.show(); } } obstacle_velocity = 0; start_time = millis(); last_direction = 'D'; } } int check_voltage() //gibt 0 wieder wenn Spannug zu niedrig, 1 wenn Spannung niedrig, 2 wenn Spannung gut { float voltage = analogRead(Voltage_pin) * 0.0375; voltage = voltage + (voltage * 0.08); if (voltage > voltage_min) //Wenn die gemessene Spannung größer ist, als die mindest Spannung... { voltagetimer = millis(); //Speichere die Systemzeit auf die Variable voltagetimer return 2; } else if((millis()-voltagetimer)>=2000) //Überprüfe, ob 2 Sekunden lang die Spannung zu niedrig ist { if(voltage > voltage_low){ return 1; } else { return 0; } } else { return 2; } } //************************************************************************************************************* //***************************************/* Car on the ground? */********************************************** //************************************************************************************************************* boolean car_on_ground() { if ((get_brightness('L') > no_reflection) && (get_brightness('M') > no_reflection) && (get_brightness('R') > no_reflection)) { current_mode = standby_mode; return false; } else { return true; } } //************************************************************************************************************* //*************************************/* Obstacle Avoidance */************************************************ //************************************************************************************************************* void obstacle_avoidance() { if (current_mode == obstacle_mode && check_voltage() >= 1) { if (check_voltage() == 2) { leds[0] = CRGB::Blue; FastLED.show(); } else { if ((millis() % divisor) < rest) { leds[0] = CRGB::Blue; FastLED.show(); } else { leds[0] = CRGB::Red; FastLED.show(); } } if ((millis() - start_time) > 5000) { float distance = 0; distance = get_distance(); if ( distance > distance_one) { obstacle_velocity = obstacle_velocity + acceleration_increment; if (obstacle_velocity > total_velocity) { obstacle_velocity = total_velocity; } drive(Forward, obstacle_velocity); } else if ((distance < distance_one) && (distance > distance_two)) { obstacle_velocity = total_velocity * (distance / distance_one); drive(Forward, obstacle_velocity); } else if (distance < distance_two) { drive(Break, 0); current_mode = standby_mode; } } } } //************************************************************************************************************* //****************************************/* Car Line Follow */************************************************ //************************************************************************************************************* void line_follow() { int distance; if (current_mode == line_follow_mode && check_voltage() >= 1) { if (check_voltage() == 2) { leds[0] = CRGB::Green; FastLED.show(); } else { if ((millis() % divisor) < rest) { leds[0] = CRGB::Green; FastLED.show(); } else { leds[0] = CRGB::Red; FastLED.show(); } } if ((millis() - start_time) > 5000) { distance = get_distance(); if ((car_on_ground() == false) || (distance < 5)) { current_mode = standby_mode; return; } int optic_Robotcar_functions_object[3]; optic_Robotcar_functions_object[0] = get_brightness('L'); optic_Robotcar_functions_object[1] = get_brightness('M'); optic_Robotcar_functions_object[2] = get_brightness('R'); int turn_velocity = total_velocity / turn_factor; //lokale Variable zur berechnung der kurvengeschwindigkeit wird deklariert if (turn_velocity > 255) { turn_velocity = 255; } if ((optic_Robotcar_functions_object[0] >= white_surface_reflection) && (optic_Robotcar_functions_object[1] >= white_surface_reflection) && (optic_Robotcar_functions_object[2] >= white_surface_reflection)) // Fall 1 { drive(Forward, (total_velocity / line_factor)); last_direction = 'F'; time_drive = millis(); } else if ((optic_Robotcar_functions_object[0] > white_surface_reflection) && (optic_Robotcar_functions_object[1] < white_surface_reflection) && (optic_Robotcar_functions_object[2] < white_surface_reflection)) // Fall 2 { drive(Left, turn_velocity); last_direction = 'L'; time_drive = millis(); } else if ((optic_Robotcar_functions_object[0] < white_surface_reflection) && (optic_Robotcar_functions_object[1] < white_surface_reflection) && (optic_Robotcar_functions_object[2] > white_surface_reflection)) // Fall 3 { drive(Right, turn_velocity); last_direction = 'R'; time_drive = millis(); } else if ((optic_Robotcar_functions_object[0] > white_surface_reflection) && (optic_Robotcar_functions_object[1] > white_surface_reflection) && (optic_Robotcar_functions_object[2] < white_surface_reflection)) // Fall 4 { drive(Leftforward, turn_velocity); last_direction = 'L'; time_drive = millis(); } else if ((optic_Robotcar_functions_object[0] < white_surface_reflection) && (optic_Robotcar_functions_object[1] > white_surface_reflection) && (optic_Robotcar_functions_object[2] > white_surface_reflection)) // Fall 5 { drive(Rightforward, turn_velocity); last_direction = 'R'; time_drive = millis(); } else if ((optic_Robotcar_functions_object[0] < white_surface_reflection) && (optic_Robotcar_functions_object[1] > white_surface_reflection) && (optic_Robotcar_functions_object[2] < white_surface_reflection)) // Fall 6 { drive(Forward, turn_velocity); last_direction = 'F'; time_drive = millis(); } else if ((optic_Robotcar_functions_object[0] > white_surface_reflection) && (optic_Robotcar_functions_object[1] < white_surface_reflection) && (optic_Robotcar_functions_object[2] > white_surface_reflection)) // Fall 7 { drive(Forward, turn_velocity); last_direction = 'F'; time_drive = millis(); } else if ((optic_Robotcar_functions_object[0] < white_surface_reflection) && (optic_Robotcar_functions_object[1] < white_surface_reflection) && (optic_Robotcar_functions_object[2] < white_surface_reflection)) // Fall 8 { if ((last_direction == 'L')) // Fall 8.1 (Fahrzeug sucht die Linie mit zeitlicher Begrenzung) { if (millis() - time_drive < search_time) { drive(Left, turn_velocity); } else if(millis() - time_drive < 2*search_time) { drive(Right, turn_velocity); } else { drive(Break, 0); current_mode = standby_mode; } } else if ((last_direction == 'R')) // Fall 8.2 (Fahrzeug sucht die Linie mit zeitlicher Begrenzung) { if (millis() - time_drive < search_time) { drive(Right, turn_velocity); } else if(millis() - time_drive < 2*search_time) { drive(Left, turn_velocity); } else { drive(Break, 0); current_mode = standby_mode; } } else if (last_direction == 'F') // Fall 8.3 (Fahrzeug fährt Rückwärts bis wieder eine Linie erkannt wird) { drive(Break, 0); delay(2000); while ((get_brightness('L') < white_surface_reflection) && (get_brightness('M') < white_surface_reflection) && (get_brightness('R') < white_surface_reflection)) { drive(Backward, (total_velocity / backward_factor)); } time_drive = millis(); } else if (last_direction == 'D') // Fall 8.4 (Fahrzeug wird zurück in den Stand-By versetzt, da es nicht auf die Linie gesetzt wurde) { current_mode = standby_mode; return; } } } } } };