Introduction: The Butter Robot: the Arduino Robot With Existential Crisis
This project is based on the animated series "Rick and Morty". In one of the episodes, Rick makes a robot whose sole purpose is to bring butter. As students from Bruface (Brussels Faculty of Engineering) we have an assignment for the mechatronics project which is to build a robot based on a suggested topic.
The assignment for this project is: Make a robot that only serves butter. It can have an existential crisis. Of course the robot in the episode of Rick and Morty is a quite complex robot and some simplifications need to be made:
Since it's sole purpose is to bring butter, there are more straightforward alternatives. Instead of making the robot look and grab the butter, before it brings it to the right person, the robot can be carrying the butter all the time. The main idea is thus to make a cart that transports the butter to where it needs to be.
Apart from transporting the butter, the robot needs to know where he needs to bring the butter. In the episode, Rick uses his voice to call and command the robot. This requires a expensive voice recognition system and would be to complicated. Instead, everyone at the table gets a button: once this button is activated, the robot can locate this button and move towards it.
To recap, the robot needs to fulfill the following requirements:
- It needs to be safe: it must avoid obstacles and prevent itself from falling of the table;
- The robot needs to be small: the space on the table is limited and no one would want a robot that serves butter but is half the size of the table itself;
- The working of the robot cannot depend on the size or shape of the table, that way it can be used on different tables;
- It needs to bring the butter to the right person at the table.
Step 1: Main Concept
The previous mentioned requirements can be met using different techniques. The decisions about the main design that were made are explained in this step. Details about how these ideas are implemented can be found in the following steps.
To accomplish his duty, the robot needs to move until the destination is reached. Considering the application of the robot it is straightforward that using wheels instead of a "walking" motion is better to make it move. Since a table is a flat surface and the robot won't reach very high speeds, two actuated wheels and one caster ball is the simplest and most easy to control solution. The actuated wheels need to be powered by two motors. The motors need to have a big torque but they don't need to reach a high speed, that's why continuous servo motors will be used. Another advantage of servo motors is the simplicity of use with an Arduino.
The detection of obstacles can be done using an ultrasonic sensor that measures the distance, attached to a servo motor to choose the direction of the measurement. The edges can be detected using LDR sensors. Using LDR sensors will require the construction of a device that contains both a led light and a LDR sensor. A LDR sensor measures the reflected light and can be seen as some kind of distance sensor. The same principle exists with infrared light. There exist some infrared proximity sensors which have a digital output: close or not close. This is exactly what the robot needs to detect the edges. By combining 2 edge sensors placed like two insect antennas and one actuated ultrasonic sensor, the robot should be able to avoid obstacles and edges.
The button detection can also be accomplished by using IR sensors and leds. The advantage of IR is that it is invisible which makes the use of it non disturbing for the people at the table. Lasers could be used as well, but then the light would be visible and also dangerous when someone points the laser into another persons eye. Also, the user would need to target the sensors on the robot with only a thin laser beam, which would be quite annoying. By equipping the robot with two IR sensors and constructing the button with an IR led, the robot knows which direction he needs to go by following the intensity of the IR-light. When there is no button the robot can turn around until one of the leds captures the signal from one of the buttons.
The butter is put in a compartment on the top of the robot. This compartment can consist of a box and a actuated lid to open the box. To open the lid and move the ultrasonic sensor to scan and detect the obstacles we need two motors and for this purpose, non continuous servo motors are more adapted because the motors need to go at a certain position and maintain that postion.
An extra feature of the project was to interact with the external environment with a robot voice. A buzzer is simple and adapted for this purpose but it can't be used at anytime because the curent draw is high.
The main difficulties of the project relies on the coding, as the mechanical part is pretty straightforward.
Many cases need to be taken into account to avoid the robot being stuck or doing something unwanted.
The main problems we need to solve are losing the IR signal because of an obstacle and stop when it arrives at the button!
Step 2: Materials
Mechanical parts
- 3D printer and Laser cutting machine
- PLA will be used for 3D printing but you can also use ABS
- A plate of 3 mm birch plywood will be used for laser cutting as it gives the possibility to make modifications later on easily, Plexiglas can also be used but it is more difficult to modify it once it's laser cut without destroying it
- Bolts, nuts, washers
- Most of the components are held together using M3 buttonhead bolts, washers and nuts, but some of them require M2 or M4 bolts set. The length of the bolts are in the range 8-12 mm.
- PCB spacers, 25 mm and 15 mm
- 2 Servo motors with compatible wheels
- Some thick metal wire around 1-2 mm in diameter
Electronic parts
- Microcontroller
- 1 arduino UNO board
- Servo motors
- 2 Large servo motors: Feetech continuous 6Kg 360 degrees
- 2 micro servo motors: Feetech FS90
- Sensors
- 1 Ultrasonic sensor
- 2 IR proximity sensors
- 2 IR photodiodes
- Batteries
- 1 9V battery holder + battery
- 1 4AA battery holder + batteries
- 1 9V battery box + battery
- Additional components
- Some jumping wires, wires and soldering plates
- Some resistors
- 1 IR LED
- 3 switches
- 1 buzzer
- 1 button
- 1 Arduino to 9V battery connector
Step 3: Testing the Electronics
Creation of the button:
The button is simply made by a switch, an infrared LED, and a 220 Ohm resistor in series, powered by a 9V battery. This is put in a 9V battery pack for a compact and clean design.
Creation of the infrared receiver modules:
These modules are made with through hole soldering boards, which will later be attached with screws to the robot. The circuits for these modules are depicted in the general schematics. The principle is to measure the intensity of the infrared light. To improve the measurements, collimators (made with shrink tubes) can be used to focus on a certain direction of interest.
Different requirements of the project need to be accomplished using electronic devices. The number of devices should be limited in order to keep a relative low complexity. This step contains the wiring schematics and each code to test all the parts separately:
- Continuous Servo motors;
- Ultrasonic sensor;
- Non continuous Servo motors;
- Buzzer;
- IR button direction detection;
- Edge detection by proximity sensors;
These codes can help understanding the components in the beginning, but it is also very useful for debugging at later stages. If a certain problem occurs, the bug can be detected more easily by testing all the components separately.
Attachments
Step 4: 3D Printed and Laser Cut Pieces Design
Laser Cut pieces
The assembly is made of three main horizontal plates held together by PCB spacers to get an open design providing easy access to the electronics if needed.
Those plates need to have the necessary holes cut in order to screw the spacers and other components for the final assembly. Mainly, all three plates have holes at the same location for the spacers, and specific holes for the electronics fixed respectively on each plate. Notice that the middle plate does have a hole for passing wires in the middle.
Smaller pieces are cut to the dimensions of the large servo to fix them to the assembly.
3D Printed pieces
In addition to laser cutting, some pieces will need to be 3D printed:
- The support for the ultrasonic sensor, which links it to one micro servo motor arm
- The support for the castor wheel and the two IR edge sensors. The particular design of the kind of box shaped ends of the piece for the IR sensors act as a screen to avoid interferences between the button emiting IR signal and the IR sensors which need to focus on what's happening on the ground only
- The support for the micro servo motor opening the lid
- And finally the lid itself, made of two pieces to have a larger operating angle by avoiding collision with the micro servo motor opening the lid:
- The bottom one which will be fixed to the top plate
- And the top which is linked to the bottom by a hinge, and actuated by the servo using a thick metal wire. We decided to add a little bit of personality to the robot by giving it a head.
Once all the pieces are designed and the files exported in the correct format for the machines used, the pieces can actually be made. Be aware that 3D printing takes a lot of time, especially with the dimensions of the top piece of the lid. You might need one or two days to print all the pieces. Laser cutting however is just a matter of minutes.
All the SOLIDWORKS-files can be found in the zipped folder.
Attachments
Step 5: Assembly and Wiring
The assembly will be a mix of wiring and screwing the components together, starting from the bottom to the top.
Bottom plate
The bottom plate is assembled with the 4AA battery pack, the servo motors, the printed part (attaching the ball caster underneath the plate), the two edge sensors, and 6 male-femal spacers.
Middle plate
Next, the middle plate can be mounted, compressing the servo motors between the two plates. This plate can then be fixed by putting another set of spacers on top of it. Some cables can be passed through the center hole.
The ultrasonic module can be attached to a non-continuous servo, which is fixed on the middle plate with the Arduino, the 9V battery pack (powering the arduino), and the two infrared receiver modules at the front of the robot. These modules are made with through hole soldering boards and attached with screws to the plate. The circuits for these modules are depicted in the general schematics.
Top plate
At this part of the assembly, the switches are not fixed but the robot can already do everything except actions requiring the lid, thus it allows us to make some test to correct the treshold, to adapt the code of the movement and to have an easy access to the ports of the arduino.
When all of this is achieved, the top plate can be fixed with spacers. The last components which are the two switches, the button, the servo, the buzzer and the lid system can be finally fixed to the top plate to finish the assembly.
The last thing to test and correct is the angle of the servo to correctly open the lid.
The threshold of the edge sensors have to be adapted with the included potentiometer (by using a flat screwdriver) for different table surfaces. A white table should have a lower threshold than a brown table for example. Also the height of the sensors will influence the needed threshold.
At the end of this step,the assembly is finish and the last remaining part is the missing codes.
Step 6: Coding: Putting Everything Together
All the necessary code to make the robot work are in the zipped file that can be downloaded. The most important one is the "main" code that includes the setup and functional loop of the robot. Most of the other functions are written in sub-files (also in the zipped folder). These sub-files should be saved in the same folder (which is named "main") as the main script before uploading it to the Arduino
First the general speed of the robot is defined together with the variable "remind". This "remind" is a value that remembers in which direction the robot was turning. If "remind = 1", the robot was/is turning left, if "remind = 2", the robot was/is turning right.
int speed = 9 ; // General speed of the robot int remind = 1; // Initial direction
In the setup of the robot, the different sub-files of the program are initialized. In these sub-files, the basic functions on the control of the motors, sensors, ... are written. By initializing them in the setup, the functions that are describes in each of these files can be used in the main loop.
By activating the function r2D2(), the robot will make a noise like the R2D2 robot from the Star Wars movie franchise when it starts up. Here the r2D2() function is disabled to prevent the buzzer from drawing too much current.
//Setup @ reset//---------------- void setup(){ initialize_IR_sensors(); initialize_obstacles_and_edges(); initialize_movement(); initialize_lid(); initialize_buzzer(); // r2D2(); int remind = 1; // initial direction Starter(remind); }
The Starter(remind) function is first called upon in the setup. This function makes the robot turn around and look for the IR signal of one of the buttons. Once it has found the button, the program will exit the Starter function by changing the variable 'cond' to false.
During the rotation of the robot it needs to be aware of its environment: it has to detect edges and obstacles. This is checked every time before it continues to turn around. Once the robot detects an obstacle or an edge, the protocol to avoid these obstacles or edges will be executed. These protocols will be explained later in this step. The Starter function has one variable which is the remind variable that was discussed before. By giving the remind value to the Starter function, the robot knows in which direction it needs to turn in order to look for the button.
//Starter Loop: Turn around and search for the button//---------------------------------------------------- void Starter(int remind) { if (isedgeleft() || isedgeright()) { // Detect the edges edgeDetected(remind); } else { bool cond = true; while (cond == true) { if (buttonleft() == false && buttonright() == false && isButtonDetected() == true) { cond = false; } else { if (remind == 1) { // We were turning left if (isobstacleleft()) { stopspeed(); avoid_obstacle(remind); } else if (isedgeleft() || isedgeright()) { // Detect the edges edgeDetected(remind); } else { turnleft(speed); } } else if (remind == 2) { if (isobstacleright()) { stopspeed(); avoid_obstacle(remind); } else if (isedgeleft() || isedgeright()) { // Detect the edges edgeDetected(remind); } else { turnright(speed); } } } } } }
If the robot finds the button, then the first Starter loop is exited and the main, functional loop of the robot begins. This main loop is quite complex since every time, the robot needs to detect whether or not there is an obstacle or an edge in front of it.
The main idea is that the robot follows the button by finding it and losing it every time. By using two IR sensors, we can distinguish three situations:
- the difference between the IR light detected by the left and right sensor is bigger than a certain threshold, and there is a button.
- the difference in IR light is smaller than the threshold, and there is a button in front of the robot.
the difference in IR light is smaller than the threshold, and there is NO button in front of the robot.
The way the track routine works is as follows: when the button is detected, the robot moves towards the button by turning in the same direction it was turning (using the remind variable) and in the same time move a little forward. If the robot turns too far, the button will be lost again, and at this point the robot remembers he needs to turn in the other direction. This is also done while moving forward a little bit. By doing this the robot is constantly turning left and turning right, but in the mean time still advancing towards the button. Every time the robot finds the button, it just keeps on turning until it has lost it in which case it starts moving in the other direction.
Notice the difference in functions that are used in the Starter loop and the main loop: the Starter loop uses "turnleft()" or "turnright()", while the main loop uses "moveleft()" and "moveright()". The moveleft/right functions do not only make the robot turn but also make him move forward at the same time.
/* Functional loop ---------------------------- Here, there is only the track routine */ int lost = 0; // If lost = 0 the button is found, if lost = 1 the button is lost void loop() { if (isedgeleft() || isedgeright()) { <p>if (!isobstacle()) { moveforward(speed); delay(5); } else { avoid_obstacle(remind); } else {if (remind == 1 && lost == 1) { // We were turning left<br> stopspeed(); if (!isobstacleright()) { moveright(speed); // Turn around to find the button } else { avoid_obstacle(remind); } remind = 2; } else if (remind == 2 && lost == 1) { stopspeed(); if (!isobstacleleft()) { moveleft(speed); //We were turning right } else { avoid_obstacle(remind); } remind = 1; }else if (lost == 0) {<br> if (remind == 1) { // We were turning left if (!isobstacleleft()) { moveleft(speed); //We were turning right } else { stopspeed(); avoid_obstacle(remind); } // } else if (remind == 2) { if (!isobstacleright()) { moveright(speed); // Turn around to find the button } else { stopspeed(); avoid_obstacle(remind); } } } delay(10); lost = 0; } } //} }
Now, a little explanation of the two most complex routines is given:
Avoid edges
The protocol to avoid edges is defined in a function called "edgeDetection()" which is written in the "movement" sub-file. This protocol relies on the fact that the robot should only encounter an edge when it has reached its destination: the button. Once the robot detects an edge, the first thing it does is move back a little bit to be at a safe ditance from the edge. Once this is done, the robot waits for 2 seconds. If someone presses the button at the front of the robot in those two seconds, the robot knows it has reached the person who wants the butter and opens the butter compartment and presents the butter. At this point, someone can take butter from the robot. After a few seconds the robot will get tired of waiting and will just close the butter lid. Once the lid is closed the robot will execute the Starter loop to look for another button. If it happens that the robot encounters an edge before reaching its destination and the button on the front of the robot is not pressed, the robot will not open the butter lid and will immediately execute the Starter loop.
Avoid obstacles
The avoid_obstacle() function is also situated in the "movement" sub-file. The hard part about avoiding obstacles is the fact that the robot has a pretty big blind spot. The ultrasonic sensor is placed at the front of the robot, which means it can detect obstacles, but does not know when he is passed it. To solve this, the following principle is used: Once the robot encounters an obstacle, it uses the reming variable to turn in the other direction. This way the robot avoid hitting the obstacle. The robot keeps turning until the ultrasonic sensor does not detect the obstacle anymore. During the time the robot is turning, a counter is increased until the obstacle is not detected anymore. This counter gives then an approximation of the length of the obstacle. By moving then forward and at the same time decreasing the counter the obstacle can be avoided. Once the counter reaches 0, the Starter function can be used again to relocate the button. Of course the robot does the Starter function by turning in the direction it remembered it was going before he encountered the obstacle (again using the remind variable).
Now that you fully understand the code, you can start using it!
Be sure to adapt the thresholds to your environment (IR reflection is higher on white tables for example) and to adapt the different parameters to your needs. Also, great attention should be given to the powering of the different modules. It is of main importance that the servo motors are not powered by the Arduino 5V port, since they take a lot of current (this could damage the microcontroller). If the same power source is used for the sensors as the one to power the servos, some measurement problems could be encountered.