Introduction: Automated Marble Painting!!!
Objective
Make a robot hand create Swirl Painting based on voice commands. Swirl painting is a kind of art, where in you drop paint droplets in water and upon immersing paper on the surface of this water, the paper would adorn the patterns the paint makes on the surface layer of water. This is widely being used to create design patterns on coffee mugs, guitars, white shirts and so on. A robot being able to do this would really be useful, especially when it is voice oriented. Also, kids would really love this, since it involves playing with water, colourful paints and most importantly the moving robot!!
Materials required
1. OWI robotic arm edge
2. Arduino red board
3. Motor shield (to control the motors of the robot)
4. Male Pin headers (to stack the motor shield above the Arduino)
5. Few jumper wires
6. A 5V-12V external power supply (Ideally 9V)
7. A tub of water
8. Oil paints
9. Sheets of paper (to absorb the pattern)
10. BitVoicer software (Works only on Windows)
Step 1: Assemble the OWI Robotic Arm
The OWI robotic arm is sold as separate components. We had to assemble the different parts.
After assembling the arm completely, we get the above completed output.
As can be seen, it has a gripper which can hold a pen or in our case a paint bottle, which can be squeezed when the gripper is closed as much as possible.
To run the motors of the arm, 4 'D ' batteries are required.
It is available on Amazon: http://www.amazon.com/OWI-OWI-535-Robotic-Arm-Edge/dp/B0017OFRCY
The steps for assembling this can be found at : http://www.robotshop.com/media/files/pdf/owi-535_m...
Step 2: Stacking the Motor Shield on the Arduino
The OWI robotic arm has five degrees of freedom and hence has 5 DC motors. 1 of the motors is responsible for the opening and closing of the gripper, 2 motors for moving left and right and 2 other motors for moving up and down. The Arduino will be handle only 1 motor, and also it won't be able to power the motor completely. In order to make 2 or more motors move simultaneously, we make use of the motor shield.
The motor shield is stacked above the Arduino using the male pin headers. To get a better connection, one side of the pin headers are soldered to the corresponding pins on the motor shield. The other end of the pin headers are plugged into the digital pins of the Arduino board.Also, the motor shield is powered separately using the external power supply. The Arduino is powered from the USB or from the barrel jack.
The motor shield can control 4 motors at a time. Since we did not want all the 5 motors to be running at the same time for any reason, we din't face any problem here.
A sample code for moving the motors is as shown:
#include <Wire.h> #include <Adafruit_MotorShield.h>
Adafruit_DCMotor *myMotor4 = AFMS.getMotor(4);
myMotor4->setSpeed(150); myMotor4->run(FORWARD); delay(3000); myMotor4->setSpeed(0); delay(300);
Step 3: Connecting the Motor Shield and the Robotic Arm
Connect the motors of the robot to the motor shield. We will need the jumper wires for this.
On the Motor Shield, there are 4 slots (M1, M2, M3, M4) to connect the 4 motors. For this project, since we needed only limited movements, we excluded one of the two motors which assists in moving up and down.
Step 4: Coding
Our code performs the following functions:
- The robotic has a fixed starting position. It moves from there to grab a bottle of oil paint from the stand (which is beside it), opens the gripper, catches hold of the bottle and tightens the grip of the bottle.
- From here, it moves to the position of the tub of the water, which is right below the gripper, moves to the rightmost end of the water pool, squeezes the bottle once, moves to the centre, squeezes, moves to the leftmost position and drops another drop of paint.
- Then it goes back to its initial position
- Next it comes back to take another colour paint
- And performs (2) again, but this time drops only 2 drops of paint at points different from where it had dropped the paint previously.
/*References : Adafruit motor Shield DC Motor example called MotorTest
BitVoicer Software example program for Switching LED's. We used the objects from this example just for instantiation */
#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include "utility/Adafruit_PWMServoDriver.h"
#include <BitVoicer11.h>
Adafruit_MotorShield AFMS = Adafruit_MotorShield();
Adafruit_DCMotor *myMotor1 = AFMS.getMotor(1);
Adafruit_DCMotor *myMotor2 = AFMS.getMotor(2);
Adafruit_DCMotor *myMotor3 = AFMS.getMotor(3);
Adafruit_DCMotor *myMotor4 = AFMS.getMotor(4);
BitVoicerSerial bvSerial = BitVoicerSerial();
boolean sampleTest = false;
byte dataType = 0;
void setup() {
Serial.begin(9600);
Serial.println("Adafruit Motorshield v2 - DC Motor test!");
AFMS.begin();
myMotor4->setSpeed(150);
myMotor4->run(FORWARD);
// turn on motor
myMotor4->run(RELEASE);
myMotor2->setSpeed(150);
myMotor2->run(FORWARD);
// turn on motor
myMotor2->run(RELEASE);
bvSerial.setAnalogReference(BV_EXTERNAL);
bvSerial.setAudioInput(0);
Serial.begin(9600);
}
void loop() {
if (sampleTest == true)
{
bvSerial.processAudio(46);
}
if (bvSerial.engineRunning)
{
bvSerial.processAudio(46);
}
}
void serialEvent()
{
dataType = bvSerial.getData();
if (dataType == BV_COMMAND)
sampleTest = bvSerial.cmdData;
if (dataType == BV_STATUS && bvSerial.engineRunning == true)
bvSerial.startStopListening();
if (dataType == BV_STR)
func1();
}
void func1()///3 drops
{
if (bvSerial.strData == "five")
{
myMotor4->run(FORWARD); // comes to pick up colour
myMotor4->setSpeed(150);
delay(3000);
myMotor4->setSpeed(0);
delay(300);
myMotor2->run(BACKWARD);//opening the gripper
myMotor2->setSpeed(150);
delay(380);
myMotor2->setSpeed(0);
delay(200);
myMotor2->run(FORWARD);//closing the gripper
myMotor2->setSpeed(150);
delay(600);
myMotor2->setSpeed(0);
delay(400);
myMotor3->run(BACKWARD); //moves the gripper on top
myMotor3->setSpeed(150);
delay(600);
myMotor3->setSpeed(0);
delay(300);
myMotor4->run(BACKWARD);
myMotor4->setSpeed(150);
delay(4000);
myMotor4->setSpeed(0);
delay(300);
myMotor2->run(FORWARD);//squeezing the gripper
myMotor2->setSpeed(200);
delay(300);
myMotor2->setSpeed(0);
delay(200);
myMotor2->run(BACKWARD);//Unsqueezing
myMotor2->setSpeed(250);
delay(50);
myMotor2->setSpeed(0);
delay(200);
myMotor4->run(FORWARD);
myMotor4->setSpeed(150);
delay(1000);
myMotor4->setSpeed(0);
delay(300);
myMotor2->run(FORWARD);//squeezing the gripper
myMotor2->setSpeed(150);
delay(300);
myMotor2->setSpeed(0);
delay(200);
myMotor2->run(BACKWARD);//Unsqueezing
myMotor2->setSpeed(150);
delay(50);
myMotor2->setSpeed(0);
delay(200);
myMotor4->run(FORWARD);
myMotor4->setSpeed(150);
delay(1000);
myMotor4->setSpeed(0);
delay(300);
myMotor2->run(FORWARD);//squeezing the gripper
myMotor2->setSpeed(150);
delay(300);
myMotor2->setSpeed(0);
delay(200);
myMotor2->run(BACKWARD);//Unsqueezing
myMotor2->setSpeed(150);
delay(50);
myMotor2->setSpeed(0);
delay(200);
myMotor4->run(FORWARD);
myMotor4->setSpeed(150);
delay(2000);
myMotor4->setSpeed(0);
delay(300);
myMotor3->run(FORWARD);
myMotor3->setSpeed(150);
delay(600);
myMotor3->setSpeed(0);
delay(300);
myMotor2->run(BACKWARD);//opening the gripper
myMotor2->setSpeed(150);
delay(400);
myMotor2->setSpeed(0);
delay(400);
myMotor4->run(BACKWARD);//opening the gripper
myMotor4->setSpeed(150);
delay(4000);
myMotor4->setSpeed(0);
delay(400);
func2();
}
else if (bvSerial.strData == "two")
{
two_drops();
}
else
{
bvSerial.startStopListening();
bvSerial.sendToBV("ERROR:" + bvSerial.strData);
bvSerial.startStopListening();
}
}
void func2()///2 drops
{
myMotor4->run(FORWARD); // comes to pick up colour
myMotor4->setSpeed(150);
delay(4000);
myMotor4->setSpeed(0);
delay(300);
myMotor2->run(BACKWARD);//opening the gripper
myMotor2->setSpeed(150);
delay(380);
myMotor2->setSpeed(0);
delay(200);
myMotor2->run(FORWARD);//closing the gripper
myMotor2->setSpeed(150);
delay(600);
myMotor2->setSpeed(0);
delay(400);
myMotor3->run(BACKWARD); //moves the gripper on top
myMotor3->setSpeed(150);
delay(600);
myMotor3->setSpeed(0);
delay(300);
myMotor4->run(BACKWARD);
myMotor4->setSpeed(150);
delay(3500);
myMotor4->setSpeed(0);
delay(300);
myMotor2->run(FORWARD);//squeezing the gripper
myMotor2->setSpeed(150);
delay(300);
myMotor2->setSpeed(0);
delay(200);
myMotor2->run(BACKWARD);//Unsqueezing
myMotor2->setSpeed(150);
delay(50);
myMotor2->setSpeed(0);
delay(200);
myMotor4->run(BACKWARD);
myMotor4->setSpeed(150);
delay(1000);
myMotor4->setSpeed(0);
delay(400);
myMotor4->run(FORWARD);
myMotor4->setSpeed(150);
delay(2000);
myMotor4->setSpeed(0);
delay(400);
myMotor3->run(FORWARD); //moves the gripper on top
myMotor3->setSpeed(150);
delay(600);
myMotor3->setSpeed(0);
delay(300);
myMotor3->run(BACKWARD); //moves the gripper on below
myMotor3->setSpeed(150);
delay(600);
myMotor3->setSpeed(0);
delay(300);
myMotor3->run(FORWARD); //moves the gripper on top
myMotor3->setSpeed(150);
delay(600);
myMotor3->setSpeed(0);
delay(300);
myMotor3->run(BACKWARD); //moves the gripper on below
myMotor3->setSpeed(150);
delay(600);
myMotor3->setSpeed(0);
delay(300);
myMotor3->run(FORWARD); //moves the gripper on top
myMotor3->setSpeed(150);
delay(600);
myMotor3->setSpeed(0);
delay(300);
myMotor3->run(BACKWARD); //moves the gripper on below
myMotor3->setSpeed(150);
delay(600);
myMotor3->setSpeed(0);
delay(300);
myMotor4->run(FORWARD);
myMotor4->setSpeed(150);
delay(1000);
myMotor4->setSpeed(0);
delay(300);
myMotor2->run(FORWARD);//squeezing the gripper
myMotor2->setSpeed(150);
delay(300);
myMotor2->setSpeed(0);
delay(200);
myMotor2->run(BACKWARD);//Unsqueezing
myMotor2->setSpeed(150);
delay(50);
myMotor2->setSpeed(0);
delay(200);
myMotor4->run(FORWARD);
myMotor4->setSpeed(150);
delay(2500);
myMotor4->setSpeed(0);
delay(300);
myMotor3->run(FORWARD);
myMotor3->setSpeed(150);
delay(600);
myMotor3->setSpeed(0);
delay(300);
myMotor2->run(BACKWARD);//opening the gripper
myMotor2->setSpeed(150);
delay(400);
myMotor2->setSpeed(0);
delay(400);
myMotor4->run(BACKWARD);
myMotor4->setSpeed(150);
delay(3000);
myMotor4->setSpeed(0);
delay(400);
}
void two_drops()
{
myMotor4->run(FORWARD); // comes to pick up colour
myMotor4->setSpeed(150);
delay(3000);
myMotor4->setSpeed(0);
delay(300);
myMotor2->run(BACKWARD);//opening the gripper
myMotor2->setSpeed(150);
delay(380);
myMotor2->setSpeed(0);
delay(200);
myMotor2->run(FORWARD);//closing the gripper
myMotor2->setSpeed(150);
delay(600);
myMotor2->setSpeed(0);
delay(400);
myMotor3->run(BACKWARD); //moves the gripper on top
myMotor3->setSpeed(150);
delay(600);
myMotor3->setSpeed(0);
delay(300);
myMotor4->run(BACKWARD);
myMotor4->setSpeed(150);
delay(3500);
myMotor4->setSpeed(0);
delay(300);
myMotor2->run(FORWARD);//squeezing the gripper
myMotor2->setSpeed(150);
delay(300);
myMotor2->setSpeed(0);
delay(200);
myMotor2->run(BACKWARD);//Unsqueezing
myMotor2->setSpeed(150);
delay(50);
myMotor2->setSpeed(0);
delay(200);
myMotor4->run(FORWARD);
myMotor4->setSpeed(150);
delay(1500);
myMotor4->setSpeed(0);
delay(300);
myMotor2->run(FORWARD);//squeezing the gripper
myMotor2->setSpeed(150);
delay(300);
myMotor2->setSpeed(0);
delay(200);
myMotor2->run(BACKWARD);//Unsqueezing
myMotor2->setSpeed(150);
delay(50);
myMotor2->setSpeed(0);
delay(200);
myMotor4->run(FORWARD);
myMotor4->setSpeed(150);
delay(2000);
myMotor4->setSpeed(0);
delay(300);
myMotor3->run(FORWARD);
myMotor3->setSpeed(150);
delay(600);
myMotor3->setSpeed(0);
delay(300);
myMotor2->run(BACKWARD);//opening the gripper
myMotor2->setSpeed(150);
delay(400);
myMotor2->setSpeed(0);
delay(400);
myMotor4->run(BACKWARD);
myMotor4->setSpeed(150);
delay(3000);
myMotor4->setSpeed(0);
delay(400);
}
void brush()
{
myMotor4->run(BACKWARD);
myMotor4->setSpeed(150);
delay(3000);
myMotor4->setSpeed(0);
delay(400);
myMotor2->run(BACKWARD);//opening the gripper
myMotor2->setSpeed(150);
delay(380);
myMotor2->setSpeed(0);
delay(200);
myMotor2->run(FORWARD);//closing the gripper
myMotor2->setSpeed(150);
delay(600);
myMotor2->setSpeed(0);
delay(400);
myMotor4->run(FORWARD);
myMotor4->setSpeed(150);
delay(3000);
myMotor4->setSpeed(0);
delay(400);
myMotor1->run(BACKWARD);
myMotor1->setSpeed(150);
delay(600);
myMotor1->setSpeed(0);
delay(400);
myMotor4->run(BACKWARD);
myMotor4->setSpeed(150);
delay(1000);
myMotor4->setSpeed(0);
delay(400);
myMotor4->run(FORWARD);
myMotor4->setSpeed(150);
delay(2000);
myMotor4->setSpeed(0);
delay(400);
myMotor1->run(FORWARD);
myMotor1->setSpeed(150);
delay(600);
myMotor1->setSpeed(0);
delay(400);
myMotor4->run(BACKWARD);
myMotor4->setSpeed(150);
delay(4000);
myMotor4->setSpeed(0);
delay(400);
myMotor2->run(BACKWARD);//opening the gripper
myMotor2->setSpeed(150);
delay(380);
myMotor2->setSpeed(0);
delay(200);
myMotor4->run(FORWARD);
myMotor4->setSpeed(150);
delay(3000);
myMotor4->setSpeed(0);
delay(400);
}
Step 5: Integrating With BitVoicer
Next step is to download and install the BitVoicer software on any Windows machine.
The code to integrate the Arduino code with BitVoicer is mentioned in the previous step.
After this, we have to had the speech words and what they map to in the BitVoicer window. For this project the speech word is 'MODERN ART' and 'SIMPLE ART'.
The command 'MODERN ART', maps to a function which squeezes 3 sets of paint droplets from the paint bottles. So we get a darker coloured pattern on the paper compared to the 'SIMPLE ART' function.
The command SIMPLE ART', drops only 2 sets of paint droplets.
Step 6: Video of the Working and Final Images
The Video shows the robot performing the marble painting with voice commands. The Marble painting can be seen in the picture which shows the painting with two different colours.
2 Comments
8 years ago on Introduction
Very cool idea!
You need to share some of the art created by this robot! I think a lot of people would love to see some examples of what this has produced. :)
Reply 8 years ago on Introduction
Thank you.. :) I am working on the description still. Will upload pictures of the pattern as well as the video of the bot working...:)