📜 ⬆️ ⬇️

Learning robot control by IR remote


I recently joined the project Robot-Mitya . Thanks a lot to Dmitry DmitryDzz for making such a cool project and helping with the initial launch of the robot, especially with regard to launching an Android application.
The robot could already be controlled via Bluetooth and Wi-Fi (via Android head). And after a while I wanted to control the Mitya remote. On board the staff robot, the Robot already had an IR receiver (after all, it was originally assembled for the IR warhead), so the matter remained with the code. Pretty quickly, we managed to adjust the control using our TV remote control, reading and writing down the “codes” of the remote control keys provided by the IRremote.h library. However, for this I had to prescribe these “codes” in the code, which was not universal: each participant would have to manually read and prescribe the codes separately, and I would have to re-write the data of these consoles in the sketch when changing the console or a small change of commands. And how great it would be to take ANY remote and just start controlling Mitya with it!


Teaching robot remote control commands


And really, why and yes? After all, on board the Arduino 512 bytes of EEPROM, which is enough to save 128 commands. (One “code” takes 4 bytes). I now got only 17 teams. It remains to figure out how to implement learning the robot console. I would like to do this so that it is as simple as possible and desirable even without the participation of the telephone part of the robot (all of a sudden someone will assemble the robot and it will not have an Android phone). In this case, immediately after assembling the robot, it will be possible to fill it with a sketch, immediately control the robot without any changes. This is one of the small victories that will greatly delight the new participant and he will have more motivation to move on (assembling Android and the robot's Windows parts.) - We decided that in the process of joining a new participant it is very important that at each stage a person sees a positive feedback connection from the development, and did not quit halfway, if something does not work. Therefore, it was decided to train the commands from the console exclusively with the help of the console.
In order for the robot to learn that it is time for him to learn, he will wait for pressing any key on the console in Morse code, for example, the letters K (I named my robot after my wife Katyusha), i.e. Long press, then short, long and short again.
After that, the robot enters an interactive learning mode. This means that he starts to perform an action, and you just need to press the button for this action, then the next one and so on until the last action.
In conclusion, after saving all the parameters, Katyusha begins to spin in the dance, inviting him to play.


')

Programming


Add this to our robo_body.ino. sketch robo_body.ino.
All comments in English because The project is planned to be international, but at the beginning of each block I added comments in Russian too :)
All code consists of several blocks:

Loading old teams

When starting up, we load old commands from memory. If they have not been recorded before this, nothing terrible will happen.
 #include <EEPROM.h> ... const int IR_TOTAL_COMMANDS = 17; // Total Number of commands, that can be send by IR control unsigned long IrCommands[IR_TOTAL_COMMANDS]; // All command from remote control // Read all IR commands from EEPROM for(int i=0; i<sizeof(IrCommands); i++) { *((byte*)&IrCommands + i) = EEPROM.read(i); } 


Check keystrokes on the IR remote

We check if the buttons on the remote control were pressed and for how long, after that the command handler is launched.
After pressing any button on the remote, the function irrecv.decode(&results); returns the button code in results.value if the button was just clicked. If the button was pressed and held, the button code is sent, and then IR_COMMAND_SEPARATOR = 0xFFFFFFFF sent at a certain frequency until the button is released. Thus, we define the duration of pressing - fast IrRemoteButtonState = 1 , short IrRemoteButtonState = 2 - one IR_COMMAND_SEPARATOR came after the command, and a long IrRemoteButtonState = 3 - two or more IR_COMMAND_SEPARATOR .
To determine the duration, we will consider fast and short equivalent.

The CheckIrCommands() function is called from the main loop() .

 // Check if any IR remote buttons pressed void CheckIrCommands() { // If we're in programm mode, just check if buttons vere pressed there if(IsInIrProgrammMode) { IrProgrammatorProcess(); return; } if (irrecv.decode(&results)) { // Button was pressed for LONG or SHORT time, but not VERY SHORT ) if(results.value==IR_COMMAND_SEPARATOR) { if(IrRemoteButtonState<3) { // Set the IR remote button state IrRemoteButtonState++; } // Update last pressed button state IrLastCommandsState[0] = IrRemoteButtonState; }else { IrRemoteLastCommand = results.value; IrRemoteButtonState = 1; // Saving last pressed buttons and their state - we will use it to determine when to start IR programmator. for(int i=IR_LAST_COMMANDS_BUFFER_SIZE-1; i>0; i--) { IrLastCommandsState[i] = IrLastCommandsState[i-1]; IrLastCommandsValue[i] = IrLastCommandsValue[i-1]; } IrLastCommandsValue[0] = IrRemoteLastCommand; IrLastCommandsState[0] = 2; // For programmator mode short and very short press of the button is equal. checkIrHit(); // Check if robot was hit by another robot } ProcessIrCommands(); irrecv.resume(); } } 


Command definition

The ProcessIrCommands() function determines which of the known keys has been pressed and executes the corresponding command.
If the command is unknown, then we check for pressing the same button in Morse code (Long-Short-Long-Short) to start the learning mode.

 void ProcessIrCommands() { if(IrRemoteLastCommand==0) return; // The signal was not good enough to get the signal data // Check for known commands and executing them for(int i=0; i<IR_TOTAL_COMMANDS; i++) { if(IrCommands[i]==IrRemoteLastCommand) { executeIrCommand(i); return; } } // We recieved unknown command. Let's check if we need to enter in programmator mode. (Same button should be pressed in the following order: LONG->SHORT->LONG->SHORT if((IrLastCommandsValue[0]==IrLastCommandsValue[1])&&(IrLastCommandsValue[0]==IrLastCommandsValue[2])&&(IrLastCommandsValue[0]==IrLastCommandsValue[3]) &&(IrLastCommandsState[0]==2)&&(IrLastCommandsState[1]==3)&&(IrLastCommandsState[2]==2)&&(IrLastCommandsState[1]==3)) { // Starting Programmator mode. IrServoStep = IR_SERVO_STEP_PROGRAM_MODE; // Maximun step for Programm mode, so that user will notice servo movement IsInIrProgrammMode = true; IrProgrammatorStep = 0; executeIrCommand(0); // Executing command and waiting for user to press the button to save it. } } 


Command execution

 void executeIrCommand(int cmd) { switch(cmd) { case 0: // move forward moveMotor( "G", IR_MOVE_SPEED ); break; case 1: // move backwards moveMotor( "G", -IR_MOVE_SPEED ); break; case 2: // turn left moveMotor( "L", -IR_MOVE_SPEED ); moveMotor( "R", IR_MOVE_SPEED ); break; case 3: // turn right moveMotor( "L", IR_MOVE_SPEED ); moveMotor( "R", -IR_MOVE_SPEED ); break; case 4: //stop moveMotor( "G", 0 ); break; case 5: // Move Horizontal Head Left moveHead( "H", servoHeadHorizontal.read()-IrServoStep ); break; case 6: // Move Horizontal Head Right moveHead( "H", servoHeadHorizontal.read()+IrServoStep ); break; case 7: // Move Vertical Head Up moveHead( "V", servoHeadVertical.read()-IrServoStep ); break; case 8: // Move Vertical Head Down moveHead( "V", servoHeadVertical.read()+IrServoStep ); break; case 9: //no noSwinger.startSwing(90, 1, 400, 2.5, 60, 0.75, true); break; case 10: //yes yesSwinger.startSwing(60, 1, 400, 2.5, 30, 0.8, true); break; case 11: //tail tailSwinger.startSwing(90, 1, 250, 6, 70, 0.9, true); break; case 12: // Mood executeAction("M", 0x0102, true ); break; case 13: // Mood executeAction("M", 0x0103, true ); break; case 14: // Mood executeAction("M", 0x0104, true ); break; case 15: // Mood executeAction("M", 0x0105, true ); break; case 16: // Mood executeAction("M", 0x0106, true ); break; } } 


Remembering commands

We start the action of the robot and wait until the user presses the button on the IR remote control. After pressing the button, we launch the next action, etc.
After receiving the final command, we save everything into the EEPROM's memory and start Katyusha's rotation in a circle, as an indication that she is ready to play with the console.

 void IrProgrammatorProcess() { if (irrecv.decode(&results)) { if((results.value!=IR_COMMAND_SEPARATOR)&&(results.value!=0)&&(results.value!=IrLastCommandsValue[0])) { IrCommands[IrProgrammatorStep]=results.value; if(IrProgrammatorStep==IR_TOTAL_COMMANDS-1) // Was it the last command? { // Save all IR commands to EEPROM for(int i=0; i<sizeof(IrCommands); i++) { EEPROM.write(i, *((byte*)&IrCommands + i) ); } IsInIrProgrammMode = false; IrServoStep = IR_SERVO_STEP_DEFAULT; executeIrCommand(2); // Tell the user that we finished programm mode }else { executeIrCommand(++IrProgrammatorStep); } } irrecv.resume(); } } 


Full code in SVN: http://code.google.com/p/robot-mitya/source/checkout

Source: https://habr.com/ru/post/168197/


All Articles