x2 humanoid robot
[Update: Chapter PROGRAMMING - Chat bot mode]
Introduction
x2 is a small humanoid robot platform for my current research. The specifications are as following:
- Arduino Mega micro controller, maybe upgrade to Arduino Due
- Adafruit 16-Channel 12-bit PWM/Servo Driver - I2C interface
- HC-06 HC-05 Bluetooth module to connect with other devices
- Differential wheeled
- 2 DOF head
- Optional two 3 DOF arms
- Nokia 5110 LCD to display GUI/robot OS/emotional states of the robot
- TTS module EMIC-2
- 3 HC-SR04 distance sensors and a 3-axis accelerometer (ADXL335)
- On-board mini keyboard to communicate with the robot
- TTL camera for snapshots, motion detection
- Micro SD card
- DS1307 RTC and DS18B20 temperature sensor
- Supervised machine learning algorithm via Stochastic Learning Automaton
- String To Math Parser
Mechanics
The main frame of the robot is made from MicroRax beams. The drive wheels and the free turning wheel are from Makeblock. The motors of the differential wheeled robot are two for continuous rotation modified Hitec HS-645 MG servos, which providing more than enough torque. The wheels are attached to the servos by aluminium servo horns:
Front side of the robot:
Backside of the robot:
Several objects as seen below are 3-D printed. I draw the parts with Sketchup Make 2014, exported the files to .STL format via STL-plugin and sent them off to a 3-D printing factory in Canton.From left to right: Camera holder, sound box for loudspeaker, assembling ring for loudspeaker, cover for sound box, HC-SR04 distance sensor holder, Nokia 5110 LCD holder.
Received 3-D printed parts, still not cleaned up:
The complete 3-D drawing can be found in the attachment in case you want to explore the construction of the robot further.
MicroRax frame assembled:
Further assembly:
Electronics
The system design looks as following:
I designed two PCB's for the robot. One is a shield for the Arduino Mega, contains micro SD card, RTC, LCD level shifter/backlight PWM, temperature sensor and power management for the servos, 5V and 3.3V devices, the other is an onboard keyboard based on the CD4021B 8-Stage Static Shift Register, similar to the ALPHAZED keyboard. I designed both PCB's with Fritzing, the extended Gerber files were then sent off to a Chinese PCB manufacturer.
Routed PCB's before copper fill and manufactured boards...
PCB's populated and tested:
After I got two faulty HC-05 carrier boards for the HC-05 Bluetooth module I decided to design my own. Routed PCB's before copper fill and manufactured board, featuring KEY (configure HC-05 module) and STATE (pairing is done or not) breakout, STATUS LED (indicator of work mode), voltage divider for the HC-05 RX and CE (chip enable by a PNP transistor to supply power) to switch the HC-05 off whenever I want to save power or upload code by serial (does not work if HC-05 is connected to hardware serial and running same time). And it has mounting holes. A good PCB design starts with mounting holes.
Populated PCB ready for testing...
Programming
Stochastic Learning Automaton:
A stochastic learning automaton is used to obtain supervised machine learning. The robot has a given set of possible actions, and every of these actions is tagged with the same probability at start-up. An action is then randomly selected when an according event occurs and the the robot waits for input from the user or evaluates by itself by given targets as to whether it was a good action or not. If it was good, this action will be tagged with a higher probability while the other actions will be tagged with a lower probability to be chosen if this even occurs again and vice versa.
Beside learning to avoid obstacles the algorithm will be used in the chat mode. Instead of actions the robot chooses randomly topics. According to your response the robot learns after a while, about which topics you want to talk and about which topics not so much.
String To Math Parser:
One of x2's modes is a calculator, a simple equation solver which supports only integers, the operators '+', '-', '*' and '/', one prefix ('+' or '-') per integer and no white space, for example -3*5/2-3-1/-6, with the implemented rule: multiplication and division first, then addition and subtraction. The algorithm works as following:
- Isolate all integers from left to right from the String, convert them to positive or negative floats, store them and their end position number in according arrays
- Reciprocal all floats which have the pre operator '/'. Now only multiplication and addition is left
- Compute the multiplication bulks in the equation, e.g. -3*5*0.5 = -7.5, store them in according addition array
- Add the integers which belong not to multiplication bulks also to this addition array
- Sum this addition array up
- Check for errors, e.g. division by 0. If no error, result is sum of addition array, else display error
I have attached a first draft of the Arduino source code and added it below. You can test it by just using the serial monitor. I am sure the code can still be simplified and cleaned up. For bug reports and suggestions, feel free to post a comment.
1 // Supports only integers, the operators '+', '-', '*' and '/' and one prefix ('+' or '-') per integer; no white space. 2 // Open Serial monitor and type in equation like -3*5/2-3-1/-6 without '=', then and press enter. 3 // All valid operators will be deleted automatically after last integer in the equation, e.g. -3*5/2-3-1/-6-- will be 4 // interpreted as -3*5/2-3-1/-6 5 6 String EquationStringBuffer = ""; // stores the user input 7 String EquationString = ""; // stores the equation 8 String Number = ""; // stores an integer in the equation 9 float operandsArray [21]; // stores the operands of the equation 10 int numberPositionEnd [21]; // stores the end position of numbers in the String 11 float MultiplicationArray [21]; // stores numbers which need to be multiplicated 12 float AdditionArray [21]; // stores numbers which need to be added 13 char* ErrorMessage[] = {"Error. Division by 0", "Error. Too many Integers. Maximum 20", "Error. Integer too long"}; 14 15 void setup() { 16 Serial.begin (9600); 17 } 18 19 void loop() { 20 while (1) { 21 while (Serial.available()) { 22 delay(3); 23 if (Serial.available() > 0) { 24 char c = Serial.read(); 25 EquationStringBuffer += c; 26 } 27 } 28 if (EquationStringBuffer.length() > 0) { 29 EquationString = EquationStringBuffer; 30 Serial.print(EquationString); 31 Serial.print(" = "); 32 EquationStringBuffer = ""; 33 break; 34 } 35 } 36 int ErrorCode = 0; 37 int numberCounter = 0; 38 boolean digitCounter = false; 39 for (int i = (EquationString.length()-1); i > -1; i --) { // delete any operators after last number in EquationString 40 if (EquationString.charAt(i) == '-' || EquationString.charAt(i) == '+' || EquationString.charAt(i) == '*' || EquationString.charAt(i) == '/') { 41 EquationString = EquationString.substring(0, i); 42 } else break; 43 } 44 EquationString += '-'; // add this character on the end of the string to recognize last number of equation 45 for (int i = 0; i < EquationString.length(); i ++) { // there must start numbers 46 if (EquationString.charAt(i) == '0' || EquationString.charAt(i) == '1' || EquationString.charAt(i) == '2' || EquationString.charAt(i) == '3' || EquationString.charAt(i) == '4' 47 || EquationString.charAt(i) == '5' || EquationString.charAt(i) == '6' || EquationString.charAt(i) == '7'|| EquationString.charAt(i) == '8' || EquationString.charAt(i) == '9') { 48 if (EquationString.charAt(i - 1) == '-') { 49 Number += '-'; 50 Number += EquationString.charAt(i); 51 digitCounter = true; 52 } else { 53 Number += EquationString.charAt(i); 54 digitCounter = true; 55 } 56 } else if (EquationString.charAt(i) != '0' && EquationString.charAt(i) != '1' && EquationString.charAt(i) != '2' && EquationString.charAt(i) != '3' && EquationString.charAt(i) != '4' 57 && EquationString.charAt(i) != '5' && EquationString.charAt(i) != '6' && EquationString.charAt(i) != '7'&& EquationString.charAt(i) != '8' && EquationString.charAt(i) != '9' && 58 digitCounter == true) { 59 char Number_buffer[Number.length() + 1]; 60 Number.toCharArray(Number_buffer, Number.length() + 1); // copies the string's characters to Number_buffer char array 61 float NumberToArray = atof(Number_buffer); // convert to float 62 if (NumberToArray > 999999.0) ErrorCode = 3; 63 operandsArray [numberCounter] = NumberToArray; // store in operandsArray 64 numberPositionEnd [numberCounter] = i; 65 numberCounter ++; 66 digitCounter = false; 67 Number = ""; 68 } 69 } 70 if (numberCounter > 20) ErrorCode = 2; 71 int MultiplicationCounter = 0; 72 int AdditionCounter = 0; 73 for (int i = 0; i < numberCounter; i ++) { 74 if (EquationString.charAt(numberPositionEnd [i]) == '*' || EquationString.charAt(numberPositionEnd [i]) == '/') { 75 if (EquationString.charAt(numberPositionEnd [i]) == '*') { 76 MultiplicationArray [MultiplicationCounter] = operandsArray [i]; 77 MultiplicationCounter ++; 78 } 79 if (EquationString.charAt(numberPositionEnd [i]) == '/') { 80 if (operandsArray [i + 1] == 0.0) ErrorCode = 1; 81 operandsArray [i + 1] = 1/operandsArray [i + 1]; // reciprocal next number 82 MultiplicationArray [MultiplicationCounter] = operandsArray [i]; 83 MultiplicationCounter ++; 84 } 85 } 86 float MultificationsBuffer = 1.0; 87 if (EquationString.charAt(numberPositionEnd [i]) != '*' && EquationString.charAt(numberPositionEnd [i]) != '/' && MultiplicationCounter > 0) { 88 MultiplicationArray [MultiplicationCounter] = operandsArray [i]; 89 for (int k = 0; k < MultiplicationCounter + 1; k ++) { 90 MultificationsBuffer = MultificationsBuffer * MultiplicationArray [k]; 91 } 92 MultiplicationCounter = 0; 93 AdditionArray [AdditionCounter] = (MultificationsBuffer); 94 AdditionCounter ++; 95 } 96 if (i == 0 && EquationString.charAt(numberPositionEnd [0]) != '*' && EquationString.charAt(numberPositionEnd [0]) != '/') { 97 AdditionArray [AdditionCounter] = operandsArray [0]; 98 AdditionCounter ++; 99 } 100 if (i == (numberCounter - 1) && numberCounter > 1 && EquationString.charAt(numberPositionEnd [numberCounter - 2]) != '*' 101 && EquationString.charAt(numberPositionEnd [numberCounter - 2]) != '/') { 102 AdditionArray [AdditionCounter] = operandsArray [i]; 103 AdditionCounter ++; 104 } 105 if (i > 0 && i != numberCounter - 1 && EquationString.charAt(numberPositionEnd [i]) != '*' && EquationString.charAt(numberPositionEnd [i]) != '/' 106 && EquationString.charAt(numberPositionEnd [i - 1]) != '*' && EquationString.charAt(numberPositionEnd [i - 1]) != '/' ) { 107 AdditionArray [AdditionCounter] = operandsArray [i]; 108 AdditionCounter ++; 109 } 110 } 111 float AdditionsBuffer = 0; 112 for (int n = 0; n < AdditionCounter; n ++) { 113 AdditionsBuffer = AdditionsBuffer + AdditionArray [n]; 114 } 115 switch (ErrorCode) { 116 case 0: 117 Serial.println(AdditionsBuffer); // print result 118 break; 119 case 1: 120 Serial.println(ErrorMessage[0]); 121 break; 122 case 2: 123 Serial.println(ErrorMessage[1]); 124 break; 125 case 3: 126 Serial.println(ErrorMessage[2]); 127 break; 128 } 129 }
Date & Time:
x2 is now able to compute the current date, day of the week of any date (day of the week of current date is supported by the Adafruit RTClib), leap year, lunar phase at present, time in 24 h format and time in 12 h format, which features an analog watch. Again, only Graphics Primitives are used for the lunar phases and analog watch as for x2's emotional expressions. I compute the coordinates of the second/minute/hour hand of the analog watch via polar coordinates of a circle, which seems to be the easiest approach:
Demo video below.
Markov chain text generator:
I am experimenting with a Markov chain text generator for the chat mode of x2. Following the explanations here, I wrote a first (and dirty) draft sketch of a simplified Markov chain text generator:
1 // https://www.cs.umd.edu/class/winter2012/cmsc389C/Projects/P1/P1.html 2 3 #include <Entropy.h> 4 String User = "THE DOG IS YELLOW THE CAT IS YELLOW SO IS THE DOG THE CAT"; // input from user or text file 5 String Ending = "THE_END"; // add terminal word, can be changed from a randomly chosen String 6 char *words[500]; // storage buffer for the words in String/array 7 char *leading_words[500]; // store leading words 8 char *chains[500]; // store chains 9 int chainNumbers[500]; 10 11 void setup() { 12 Serial.begin (9600); 13 Entropy.Initialize(); 14 User += ' '; 15 User += Ending; // add terminal word to input String 16 } 17 18 void loop() { 19 int chain_end = 0; 20 int output_word_counter = 0; 21 int leading_word_number = 0; 22 int random_word; 23 int word_counter = 0; // count words in String/array 24 int newword = 0; 25 int strcmp_counter = 0; 26 int leading_word_counter = 0; 27 char inputchar[User.length() + 1]; // define array to convert String to it 28 char *pch; 29 User.toCharArray(inputchar, User.length() + 1); // convert String to array 30 pch = strtok (inputchar, " "); 31 while (pch != NULL) { // save all words in the array 'words' 32 words[word_counter] = pch; 33 pch = strtok (NULL, " "); 34 word_counter ++; 35 } 36 for (int i = 0; i < word_counter; i ++) { 37 for (int j = 0; j < i; j ++) { 38 if (i != 0 && !strcmp(words[j],words[i])) strcmp_counter ++; 39 } 40 if (strcmp_counter == 0 && i != word_counter - 1) { // ignore last word if it is unique 41 leading_words[leading_word_counter] = words[i]; 42 leading_word_counter ++; 43 } 44 strcmp_counter = 0; // reset counter 45 } 46 for (int m = 0; m < leading_word_counter; m ++) { 47 for (int n = 0; n < word_counter; n ++) { 48 if (!strcmp(words[n],leading_words[m])) { 49 chains[chain_end] = words[n + 1]; 50 chain_end ++; 51 } 52 } 53 chainNumbers[m] = chain_end; 54 } 55 String Robot_Output = leading_words[leading_word_number]; 56 Robot_Output += ' '; 57 while (1) { 58 if (leading_word_number == 0) { 59 if (chainNumbers[leading_word_number] < 2) { 60 Robot_Output += chains[0]; 61 } else { 62 random_word = Entropy.random(0,chainNumbers[leading_word_number]); 63 Robot_Output += chains[random_word]; 64 } 65 } else { 66 if (chainNumbers[leading_word_number] - chainNumbers[leading_word_number - 1] < 2) { 67 Robot_Output += chains[chainNumbers[leading_word_number - 1]]; 68 } else { 69 random_word = Entropy.random(chainNumbers[leading_word_number - 1],chainNumbers[leading_word_number]); 70 Robot_Output += chains[random_word]; 71 } 72 } 73 Robot_Output += ' '; 74 for (int i = 0; i < leading_word_counter; i ++) { 75 if (!strcmp(leading_words[i],chains[random_word])) { 76 leading_word_number = i; 77 break; 78 } 79 } 80 String EndingBuffer = chains[random_word]; 81 if ((leading_word_number == 0 && chainNumbers[leading_word_number] < 2) || 82 (chainNumbers[leading_word_number] - chainNumbers[leading_word_number - 1]) < 2 || 83 !EndingBuffer.compareTo(Ending) || Robot_Output.length() >= User.length()) break; 84 output_word_counter ++; 85 } 86 Serial.print(Robot_Output); 87 Serial.println(" "); 88 }
The generator makes no big sense if the input text is short and lacks therefore of repeating words. A first idea is to add the most common 100 words in English to the user input String or multiply it by an order depending on its length.
Bluetooth:
The Arduino serial monitor does not work reliable as a serial terminal when using Bluetooth. Either the serial port is already in use or the Arduino serial monitor freezes during pairing. Tera Term works fine but you can't submit a string, every single character you press on your keyboard is transmitted immediately. CoolTerm has a line mode where you can type in your string and press then ENTER to transmit it. Perfect for my needs. Below test set-up of my HC-05 carrier PCB and test source code.
1 int ledpin = 13; 2 int enableBTpin = 3; 3 int stateBTpin = 2; 4 char BluetoothData; 5 boolean PairingMode = false; 6 7 void setup() { 8 Serial2.begin(9600); 9 // Serial2.println("Send 1 to switch LED on and 0 to switch LED off"); 10 pinMode(ledpin, OUTPUT); 11 pinMode(enableBTpin, OUTPUT); 12 pinMode(stateBTpin, INPUT); 13 digitalWrite(enableBTpin, 0); // switch BT module on 14 } 15 16 void loop() { 17 if (digitalRead(stateBTpin) == HIGH && PairingMode == false) { 18 Serial2.println("PAIRING DONE!"); 19 PairingMode = true; 20 } 21 if (Serial2.available()){ 22 BluetoothData = Serial2.read(); 23 if(BluetoothData == '1') { 24 digitalWrite(ledpin,1); 25 Serial2.println("LED ON"); 26 } 27 if (BluetoothData == '0') { 28 digitalWrite(ledpin, 0); 29 Serial2.println("LED OFF"); 30 } 31 } 32 delay(100); 33 }
Chat bot mode :
I finally finished a draft version of x2's chat bot mode which uses the Arduino serial monitor. I was inspired by the natural language processing bot ELIZA written by Joseph Weizenbaum between 1964 and 1966. You can find the original code in BASIC attached as a .txt file. After debugging the code every time times when I got unexpected results, I had the idea to manipulate the for statements by purpose to get really unpredictable responses. I will experiment further.
1 //********************************** LIBRARIES ********************************** 2 3 // ----------------------------- Random ---------------------------- 4 #include <Entropy.h> // true random lib, see 5 // http://forum.arduino.cc/index.php/topic,108380.0.html 6 7 // ------------------------------ Time ----------------------------- 8 #include <Wire.h> 9 #include "RTClib.h" 10 RTC_Millis RTC; 11 12 // ------------------------------ Math ----------------------------- 13 14 #include <Average.h> // performing mathematical analysis of arrays of numbers 15 // http://playground.arduino.cc/main/average 16 17 //*************************** GLOBAL VARIABLES/CONSTANTS ************************* 18 19 String User; // input from user 20 String LastInput; // buffers user input 21 String LastOutput; // buffers robot output 22 23 char *conjugations[] = { 24 "MYSELF", "YOURSELF", 25 "YOURSELF", "MYSELF", 26 "MY", "YOUR", 27 "YOUR'S", "MINE", 28 "YOUR", "MY", 29 "MINE", "YOUR'S", 30 "I MUST", "YOU MUST", 31 "YOU MUST", "I MUST", 32 "I'M", "YOU ARE", 33 "I AM", "YOU ARE", 34 "YOU'RE", "I AM", 35 "YOU ARE", "I AM", 36 "ARE YOU", "I AM", 37 "I'VE", "YOU HAVE", 38 "I HAVE", "YOU HAVE", 39 "YOU'VE", "I HAVE", 40 "YOU HAVE", "I HAVE", 41 "I'LL", "YOU WILL", 42 "I WILL", "YOU WILL", 43 "YOU'LL", "I WILL", 44 "YOU WILL", "I WILL", 45 "YOU WON'T", "I WON'T", 46 "I WON'T", "YOU WON'T", 47 "I CAN'T", "YOU CAN'T", 48 "YOU CAN'T", "I CAN'T", 49 "I CAN", "YOU CAN", 50 "YOU CAN", "I CAN", 51 "I DON'T", "YOU DON'T", 52 "YOU DON'T", "I DON'T", 53 "I DO", "YOU DO", 54 "YOU DO", "I DO", 55 "DO I", "DO YOU", 56 "DO YOU", "DO I", 57 "DID I", "DID YOU", 58 "DID YOU", "DID I", 59 "I SHOULDN'T", "YOU SHOULDN'T", 60 "YOU SHOULDN'T", "I SHOULDN'T", 61 "I SHOULD", "YOU SHOULD", 62 "YOU SHOULD", "I SHOULD", 63 "I COULDN'T", "YOU COULDN'T", 64 "YOU COULDN'T", "I COULDN'T", 65 "I COULD", "YOU COULD", 66 "YOU COULD", "I COULD" 67 }; 68 69 char *conjugationCombo[] = { 70 "WHAT MAKES YOU BELIEVE ", 71 "WOULD YOU BE PLEASED IF ", 72 "ARE YOU SURE ", 73 "I DON'T THINK ", 74 "MAYBE YOU ARE RIGHT AND ", 75 "HOW YOU CAME TO THE IDEA ", 76 "I AM NOT SURE WHY YOU THINK ", 77 "WHO TOLD YOU ", 78 "PERHAPS IN YOUR FANTASIES ", 79 "HOW DO YOU KNOW ", 80 "DO YOU ENJOY ", 81 "WHY YOU SAY ?", 82 "I WAS NEVER TOLD ", 83 "DOES IT PLEASE YOU TO BELIEVE ", 84 "DO YOU REALLY BELIEVE YOURSELF ", 85 "NEVER HEARED ", 86 "DOES SOMEBODY ELSE ALSO BELIEVE ", 87 "I THINK IT IS ONLY YOUR OPINION THAT ", 88 "LAUGH OUT LOUD ", 89 "OH, ", 90 "WHAT, ", 91 "HAVE YOU ASKED ANYONE ELSE AND THE ANSWER WAS " 92 }; 93 94 char *topics[] = { 95 "ARE YOU INTERESTED IN ", "PHYSICS?", 96 "DO YOU THINK I LIKE ", "CHEMISTRY?", 97 "DO YOU LIKE ", "HISTORY?", 98 "WHAT DO YOU THINK ABOUT ", "SPORT?", 99 "DO YOU HAVE INTERESTS IN THE TOPIC ", "MOVIES?", 100 "WHAT DO YOU THINK ABOUT THE TOPIC ", "GOSSIP?", 101 "ANY INTERESTS IN ", "MATHEMATICS?", 102 "HAVE I MENTIONED THAT I LIKE ", "LANGUAGE ARTS?", 103 "WHAT COMES INTO YOUR MIND WHEN I SAY ", "ART GENERALLY?", 104 "DO YOU HAVE NEWS ABOUT ", "MUSEUMS?", 105 "I AM NOT SURE BUT DO YOU LIKE ", "POLITICS?", 106 "ANY THOUGHTS ABOUT ", "SOCIETY?", 107 "WHY DO I THINK YOU LIKE ", "PARTIES?", 108 "DOES ANY OF YOUR FRIENDS LIKE ", "TRAVELING?", 109 "DO WE LIKE ", "BIOLOGY?", 110 "WHAT ARE YOUR THOUGHTS ABOUT ", "GIRLS?", 111 "WHAT IS BETTER, GIRLS OR ", "BOYS?", 112 "WHAT IS BETTER, BOYS OR ", "CARS?", 113 "DO YOU THINK WE CAN IMPROVE OUR SOCIETY BY ", "BOOKS?", 114 "DO YOU ALSO SOMETIMES THINK NOTHING IS MORE BORING THAN ", "WATCHING TV?", 115 "DO YOU ALSO SOMETIMES THINK NOTHING IS MORE INTERESTING THAN ", "NATURE?", 116 "HAVE YOU READ A BOOK ABOUT ", "ANIMALS?", 117 "DO YOU WANT TO BRAINSTORM ABOUT ", "RELIGIONS?", 118 "WHAT IS WRONG TODAY WITH ", "OTHER COUNTRIES?", 119 "MAKE IT SENSE TO YOU TO TALK ABOUT ", "GETTING MARRIED?", 120 "NOWADAYS THE MAIN TOPIC SEEMS TO BE ", "SPACE TRAVEL?", 121 "DO YOU ALSO THINK EVERYBODY TALKS NOW ABOUT ", "ROBOTS?", 122 "DO YOU ALSO THINK NOTHING IS WORSE THAN ", "ARTIFICIAL INTELLIGENCE?", 123 }; 124 125 char *keywords_responses[] = { 126 "NOTHING", "REALLY NOTHING?", "NOTHING AT ALL?", "I DO NOT BELIEVE", 127 "YES", "CAN YOU TELL ME MORE?", "I SEE", "OK!", 128 "MAYBE", "YOU ARE NOT SURE?", "I AM SURE", "YOU SEEM TO BE UNCERTAIN", 129 "FRIEND", "DO YOU HAVE MANY FRIENDS?", "WHAT'S THE NAME OF YOUR BEST FRIEND?", "I AM YOUR FRIEND", 130 "FAMILY", "DO YOU HAVE A BROTHER OR A SISTER?", "HOW IS YOUR DAD?", "HOW IS YOUR MOM", 131 "SORRY", "NO SORRY", "YOU DON'T NEED TO SAY SORRY", "NO PROBLEM", 132 "LOVE", "DO YOU LOVE SOMETHING MORE?", "I LOVE FRESH BATTERIES", "I FALL IN LOVE TO ANOTHER ROBOT GIRL", 133 "HATE", "NOT WORTHY TO HATE ANYTHING", "HATERS ARE HATERS", "WHEN YOU HATE, ONLY YOU SUFFER", 134 "BOY", "I AM A BOY", "DO YOU HAVE A BOYFRIEND?", "GIRLS ARE MORE CLEVER THAN BOYS", 135 "GIRL", "I LIKE GIRLS", "ARE YOU A GIRL OR A BOY?", "GIRLS ARE CUTE", 136 "TIME", "YOU CAN CHECK MY TIME MODE IF YOU NEED TO KNOW THE TIME", "DON'T KNOW", "TIME FLIES", 137 "ROBOT", "I AM A ROBOT", "ROBOTS ARE COOL I THINK", "WHAT'S YOUR FAVORITE ROBOT?", 138 "NOT", "UM!", "WHY NOT?", "CAN YOU TELL ME THE REASON?", 139 "NO", "OH!", "ANY REASON?", "WHY?", 140 "SOMETIMES", "HOW OFTEN IS SOMETIMES?", "UNDERSTAND", "I SEE", 141 "BOOK", "I LIKE TO READ BOOKS", "I LIKE TO READ PICTURE BOOKS", "YOU CAN LEARN MANY THINGS FROM BOOKS", 142 "MARRY", "I WANT TO GET MARRIED TO ANOTHER ROBOT LATER", "ARE YOU MARRIED?", "I HOPE YOU WILL HAVE A HAPPY LIFE", 143 "PARTY", "I LIKE TO DANCE AND TO SING", "I WANT TO GO TO A PARTY WITH YOU", "NO STANDING, ONLY DANCING", 144 "MOVIE", "I LIKE THE MOVIE FINDING NEMO", "I LIKE MICKEY MOUSE MOVIES", "I WANT TO WATCH A MOVIE WITH YOU", 145 "SPORT", "SPORT IS GOOD", "I LIKE TO PLAY BALL", "YOU DO SPORT?", 146 "CAR", "I like BMW", "I LIKE PORSCHE", "I LIKE FAST CARS VERY MUCH", 147 "ALSO", "OH, I DIDN'T EXPECT THAT", "NICE", "IT WAS MY GUESS", 148 "WHY", "I DON'T KNOW", "YOU MIGHT BETTER ASK A HUMAN NOT A ROBOT", "I HAVE NO ANSWER, SOORY", 149 "WHERE", "ON THE TABLE?", "ON THE CHAIR?", "MAYBE IN THE KITCHEN COUNTER", 150 "WHEN", "LAST WEEK I THINK", "NEXT WEEK I THINK", "TOMORROW?", 151 "WHAT", "REALLY NOT SURE", "CAN FIND NOTHING IN MY DATA BASE", "BETTER ASK A HUMAN" 152 }; 153 154 char *user_repeat[] = { 155 "IT SEEMS YOU LIKE TO SAY: ", 156 "I THINK ONE OF YOUR FAVORITE WORDS IS: ", 157 "I HEAR QUITE OFTEN FROM YOU: ", 158 "I FEEL BORING IF YOU ALWAYS USE: " 159 }; 160 161 char *robot_repeat[] = { 162 "I DON'T LIKE TO REPEAT MYSELF BUT: ", 163 "I HAVE TO SAY IT AGAIN: ", 164 "AND AGAIN: ", 165 "I AM NOT GETTING TIRED TO SAY: " 166 }; 167 168 char *user_delay[] = { 169 "HI! ARE YOU STILL HERE?", 170 "YOU FALL ASLEEP?", 171 "OUR CONVERSATION SEEMS VERY BORING FOR YOU", 172 "I DON'T WANT TO BE ALONE. PLEASE COME BACK" 173 }; 174 175 char *daytimeGreeting[] = { 176 "GOOD MORNING. ", 177 "GOOD AFTERNOON. ", 178 "GOOD EVENING. " 179 }; 180 181 long interval = 1000; 182 long previousMillis = 0; 183 int time_counter = 0; 184 boolean introduction_state = false; 185 186 void setup() { 187 Serial.begin(9600); 188 Entropy.Initialize(); // init random lib 189 RTC.begin(DateTime(__DATE__, __TIME__)); 190 } 191 192 void loop() { 193 while (1) { 194 if (introduction_state == false) { 195 DateTime now = RTC.now(); 196 if ((now.hour()*60 + now.minute()) >= 0 && (now.hour()*60 + now.minute()) <= 720) { 197 Serial.print(daytimeGreeting[0]); 198 } 199 if ((now.hour()*60 + now.minute()) >= 721 && (now.hour()*60 + now.minute()) <= 1020) { 200 Serial.print(daytimeGreeting[1]); 201 } 202 if ((now.hour()*60 + now.minute()) >= 1021) { 203 Serial.print(daytimeGreeting[2]); 204 } 205 Serial.println("MY NAME IS X2, I AM YOUR PERSONAL ROBOT. HOW ARE YOU? NICE TO CHAT WITH YOU. PLEASE "); 206 Serial.println("DON'T USE ANY PUNCTUATION MARKS BESIDE APOSTROPHE. SUBMIT THE WORD 'BYE' TO END THIS CONVERSATION"); 207 Serial.println(""); 208 introduction_state = true; 209 } 210 int size_conjugations = sizeof(conjugations)/sizeof(conjugations[0]); 211 int conjugationsPosition[size_conjugations / 2 + 1]; 212 int size_conjugationCombo = sizeof(conjugationCombo)/sizeof(conjugationCombo[0]); 213 int size_topics = sizeof(topics)/sizeof(topics[0]); 214 int size_keywords_responses = sizeof(keywords_responses)/sizeof(keywords_responses[0]); 215 int size_user_repeat = sizeof(user_repeat)/sizeof(user_repeat[0]); 216 int size_robot_repeat = sizeof(robot_repeat)/sizeof(robot_repeat[0]); 217 int size_user_delay = sizeof(user_delay)/sizeof(user_delay[0]); 218 unsigned long currentMillis = millis(); 219 if (currentMillis - previousMillis > interval) { 220 previousMillis = currentMillis; 221 time_counter ++; 222 } 223 if (time_counter > 50) { 224 int randomNumber = Entropy.random(0, size_user_delay); 225 Serial.println(user_delay[randomNumber]); 226 Serial.println(""); 227 time_counter = 0; // reset timer 228 } 229 while (Serial.available() > 0) { 230 delay (2); 231 char ch = Serial.read(); 232 User += ch; 233 } 234 User.trim(); // remove leading and trailing whitespace 235 User.toUpperCase(); // force to upper case 236 if (User.length() > 0) { 237 if (User.length() > 49) { 238 Serial.println("DATE INPUT TO0 LARGE TO BE PROCESSED"); 239 Serial.println(""); 240 time_counter = 0; 241 } 242 else if (LastInput == User) { 243 time_counter = 0; 244 int randomNumber = Entropy.random(0, size_user_repeat); 245 Serial.print(user_repeat[randomNumber]); 246 Serial.println(User); 247 Serial.println(""); 248 } 249 else if (User == "BYE") { 250 Serial.println("BYE BYE. SEE YOU NEXT TIME"); 251 Serial.println(""); 252 User = ""; 253 break; 254 } else { 255 Serial.println(User); 256 Serial.println(""); 257 time_counter = 0; // reset timer 258 LastInput = User; 259 // cut and conjugate 260 int conjugationCounter = 0; 261 int conjugationPos = 0; 262 int keywordPos = 0; 263 boolean keywordFound = false; 264 String RobotOutput; 265 for (keywordPos = 0; keywordPos < size_keywords_responses - 2; keywordPos = keywordPos + 4) { 266 if (User.indexOf(keywords_responses[keywordPos]) != -1) { 267 int randomNumber = Entropy.random(keywordPos + 1, keywordPos + 4); 268 RobotOutput = keywords_responses[randomNumber]; 269 keywordFound = true; 270 break; 271 } 272 } 273 if (keywordFound == false) { 274 for (conjugationPos = 0; conjugationPos < size_conjugations; conjugationPos = conjugationPos + 2) { 275 if (User.lastIndexOf(conjugations[conjugationPos]) != -1) { 276 conjugationsPosition[conjugationCounter] = User.lastIndexOf(conjugations[conjugationPos]); 277 conjugationCounter ++; 278 } 279 } 280 if (conjugationCounter > 0) { 281 User = User.substring(maximum(conjugationsPosition, conjugationCounter)); 282 for (conjugationPos = 0; conjugationPos < size_conjugations; conjugationPos = conjugationPos + 2) { 283 if (User.indexOf(conjugations[conjugationPos]) != -1) { 284 User.replace(conjugations[conjugationPos], conjugations[conjugationPos + 1]); 285 break; 286 } 287 } 288 if (User.length() == strlen(conjugations[conjugationPos + 1])) { 289 RobotOutput = User; 290 RobotOutput += '?'; 291 } else { 292 int randomNumber = Entropy.random(0, size_conjugationCombo); 293 RobotOutput = conjugationCombo[randomNumber]; 294 RobotOutput += User; 295 RobotOutput += '?'; 296 } 297 } else { 298 // choose random topic 299 int randomNumber = Entropy.random(0, size_topics / 2); 300 randomNumber = randomNumber * 2; 301 int randomNumber2 = Entropy.random(0, size_topics / 2); 302 randomNumber2 = randomNumber2 * 2 + 1; 303 RobotOutput = topics[randomNumber]; 304 RobotOutput += topics[randomNumber2]; 305 } 306 } 307 if (LastOutput == RobotOutput) { 308 int randomNumber = Entropy.random(0, size_robot_repeat); 309 Serial.print(robot_repeat[randomNumber]); 310 } 311 int robot_think_delay = Entropy.random(500, 2001); 312 delay (robot_think_delay); 313 Serial.println(RobotOutput); 314 Serial.println(""); 315 LastOutput = RobotOutput; 316 } 317 User = ""; 318 } 319 } 320 while (1) {} 321 }
Chat snippet:
GOOD EVENING. MY NAME IS X2, I AM YOUR PERSONAL ROBOT. HOW ARE YOU? NICE TO CHAT WITH YOU. PLEASE DON'T USE ANY PUNCTUATION MARKS BESIDE APOSTROPHE. SUBMIT THE WORD 'BYE' TO END THIS CONVERSATION
HI
MAKE IT SENSE TO YOU TO TALK ABOUT TRAVELING?
NO
WHY?
TIME
TIME FLIES
I HAVE NO TIME
I AM NOT GETTING TIRED TO SAY: TIME FLIES
YES SEEMS SO
OK!
OK WHAT
BETTER ASK A HUMAN
YOU FALL ASLEEP?
Interesting resources:
Online JAVA version of ELIZA (mouse right click, view page source)
10 Tricks That Chatbots Use to Make You Believe They're Human
Videos
Video demonstration of x2's mode select. Example: Read temperature and map it to human sensations
interacts with human beings