Printer ink is the most expensive commodity in the world (by weight, or volume.. apparently). I’m always running out of it just when I need to print out a boarding pass, or some other essential printed document. So wouldn’t it be great if you could print things out using a biro?
Here is my version 0 attempt at such a device in action at Lift’12. It actually has a lot of similarities to this drawing automaton, although I only realised that after I’d finished and the video blogger Nicolas Charbonnier pointed it out to me! Nicolas did a video interview with me at Lift’12 where I had the chance to show off my creation a little. You can watch the video (which includes a fair bit of discussion about my day job at CERN) here.
Basic idea: A robot arm with two drive servos and one more to move a pen (or pencil) up and down onto standard A5 paper. Plugs into a computer for the power and the drawing data. Draws following the mouse or via a crude black & white filter on a JPEG image.
Ingredients:
Arduino (I used my trusty old duemilanove), USB lead to connect it to a computer, three Futaba S3003 servos (or equivalent), a few bits of wire, and some stationary from Muji (one A5 file box, two aluminium rulers, one wooden ruler and one acryllic one to be precise). A biro (one with four colours if you are feeling fancy!). Software: Processing and some jpg’s (+a bit of patience)!
Building it was fairly straight foreward, with the most complicated part making a cam to lift the pen up/down. I did this by drilling 1/2 of an acryllic ruler so that the pen can pass through it, with the pen attached using a single thin screw to an extremity of a servo horn. Fixing the acrylic ruler to the up/down servo didn’t work very well, hence the blue ‘LIFT’ tape in the photos.
Once assembled it should look like this – obviously you can modify the hardware build as you wish.
You can see the coins I added (wrapped in masking tape) as a counterweight on the left. It helped to resolve some issues with the lack of co-planar motion in the two arm segments.
After trying to write my own code I gave up and used the excellent Firmata library for the arduino/processing link (my own code was doing strange things…). The computer communicates serially to the plotter, sending positions for each of the three servos.
When I was writing the initial processing sketch, I controlled the arm directly for the first few passes. This was disasterous as I managed to snap teeth off two out of my three servos (originally they were super-micro lightweight ones… after the damage I decided to upgrade to the S3003’s). This lead me to develop my own ’emulator’ for the arm, so that I could solve the mathematical and physical constraints without breaking anything else. You can see an early demo of the arm here.
Here is the processing source code:
import processing.serial.*;
import cc.arduino.*;
Arduino arduino;
int countstart = 0;
int servo1Pin = 9; // Control pin for servo motor
int servo2Pin = 10; // Control pin for servo motor
int servo3Pin = 11; // Control pin for servo motor
int armposn;// = 0;
int digitposn;// = 0;
//int penposn;//= 0;
int penstate = 0;int xcent = 300;
int ycent = 300;
//int targetX= 400;
//int targetY= 200;
int line1 = 130;
int line2 = 130;
int minimumlength = 40;
color black = color(0);
PImage img;
PImage edgeImg ;void setup(){
float[][] kernel = { { -1, -1, -1 },
{ -1, 9, -1 },
{ -1, -1, -1 } };size (600, 600);
background(255);img = loadImage(“ben.jpg”); // Load the original image
img.loadPixels();
edgeImg = createImage(img.width, img.height, RGB);// Loop through every pixel in the image.
for (int y = 1; y < img.height-1; y++) { // Skip top and bottom edges
for (int x = 1; x < img.width-1; x++) { // Skip left and right edges
float sum = 0; // Kernel sum for this pixel
for (int ky = -1; ky <= 1; ky++) {
for (int kx = -1; kx <= 1; kx++) {
// Calculate the adjacent pixel for this kernel point
int pos = (y + ky)*img.width + (x + kx);
// Image is grayscale, red/green/blue are identical
float val = red(img.pixels[pos]);
// Multiply adjacent pixels based on the kernel values
sum += kernel[ky+1][kx+1] * val;
}
}
// For this pixel in the new image, set the gray value
// based on the sum from the kernel
edgeImg.pixels[y*img.width + x] = color(sum);
}
}
// State that there are changes to edgeImg.pixels[]
edgeImg.updatePixels();arduino = new Arduino(this, Arduino.list()[1]);
arduino.pinMode(servo1Pin, Arduino.OUTPUT);
arduino.pinMode(servo2Pin, Arduino.OUTPUT);
arduino.pinMode(servo3Pin, Arduino.OUTPUT);arduino.analogWrite(servo1Pin, 70);
arduino.analogWrite(servo2Pin, 170);
arduino.analogWrite(servo3Pin, 50); // the servo moves to the horizontal location of the mouse// note – we are setting a digital pin to output
background(255);}
void draw()
{
if (countstart == 1){
makepic();
arduino.analogWrite(servo1Pin, 70);
arduino.analogWrite(servo2Pin, 170);
arduino.analogWrite(servo3Pin, 50);
}
countstart = countstart +1;
}//void draw(){
void makepic(){
background(255);
//fill(50);
stroke(0);
//rect(350+30,320-10,130,130);
image(img, 0, 0, 130, 130); // Displays the image from point (0,0)image(img, 130, 0, 130, 130); // Draw the new image
filter(POSTERIZE,4);
filter(THRESHOLD);
colorMode(HSB);for (int xcycle = 130; xcycle < 259; xcycle = xcycle+2)
{
for (int ycycle = 0; ycycle < 129; ycycle++)
{
if (get(xcycle,ycycle)<-2)
{
set(xcycle+350-130+30,ycycle+320-10,black);
markpaper(xcycle+350-130+30,ycycle+320-10,110);//changed pen variable 110
delay(20);
markpaper(xcycle+350-130+30,ycycle+320-10,170);//comment this line and the one further below out to draw lines not dots
//delay(20);
}
else
{
set(xcycle,ycycle,0);
markpaper(xcycle+350-130+30,ycycle+320-10,170);
//delay(50);
}
}
for (int ycycle = 129; ycycle > 0; ycycle–)
{
if (get(xcycle+1,ycycle)<-2)
{
set(xcycle+350-130+1+30,ycycle+320-10,black);
markpaper(xcycle+350-130+1+30,ycycle+320-10,110);
delay(20);
markpaper(xcycle+350-130+1+30,ycycle+320-10,170);//likewise comment this line out for lines not dots on your paper as well as the one further up}
else
{
set(xcycle+1,ycycle,0);
markpaper(xcycle+350-130+1+30,ycycle+320-10,170);
//delay(50);
}
}
}}
void markpaper(int targetX, int targetY, int penposn){
//print(“xposn” + targetX);
//println(” yposn” + targetY);
float angle1 = atan2((targetX – xcent), (targetY – ycent));
float sectorlength = sqrt(sq(targetX – xcent)+sq(targetY – ycent));//if it’s out of reach, shorten the length in the same direction
if (sectorlength > (line1+line2))
{
sectorlength = line1+line2;
targetX = int(xcent + sin(angle1)*sectorlength);
targetY = int(ycent + cos(angle1)*sectorlength);
}if (sectorlength < minimumlength)
{
sectorlength = minimumlength;
targetX = int(xcent + sin(angle1)*sectorlength);
targetY = int(ycent + cos(angle1)*sectorlength);
}float internangle = acos((sq(sectorlength)-sq(line2)-sq(line1))/(2*line1*line2));
float sendangle1st = (angle1+(internangle/2));
if (degrees(sendangle1st) < 0 )
{
sendangle1st = radians(0);
}
if (degrees(sendangle1st) > 180 )
{
sendangle1st = radians(180);
}int line1X = int(xcent+ sin(sendangle1st)*line1);
int line1Y = int(ycent+ cos(sendangle1st)*line1);
int line2X = line1X + int(sin(((angle1+(internangle/2) – internangle)))*line2);
int line2Y = line1Y + int(cos(((angle1+(internangle/2) – internangle)))*line2);
stroke(0,9);
line(xcent, ycent, line1X, line1Y);
line(line1X, line1Y, line2X-1, line2Y-1);
// if (penposn == 150)
// {
// set(line2X,line2Y,black);
// println(“TEXT”);
// }
// set(mouseX,mouseY,black);//if it’s too close set the minimum threshold in the same direction
int sendangle1 = round(degrees(sendangle1st));
int sendangle2 = round(degrees((radians(180) – internangle)-radians(35)));
//println(“Send1>”+ sendangle1 + ” Send2 >” + sendangle2);
digitposn = sendangle2;
armposn = sendangle1;arduino.analogWrite(servo1Pin, digitposn);
arduino.analogWrite(servo2Pin, penposn);
arduino.analogWrite(servo3Pin, armposn);
if (penstate != penposn)
{
delay(300);
}
else
{
delay(1);
}// the servo moves to the horizontal location of the mouse
penstate = penposn;
}//end of source\\
I should add that this code is very rough, and contains hard-coded boundary limits for the drawing area which are specific to the physical configuration of my hardware (and painstakingly obtained by moving the arm and looking at where the pen is). This code is also set to draw dots rather than lines, as people generally indicated a preference for this kind of output.
Performance is ‘interesting’. I’ve set it to draw an approx 160×160 matrix, one pixel at a time. Depending upon the number of dark pixels this can take up to 1.5 hours per image. If you want to draw lines that go between all the connected pixels the time per image comes down to <20 minutes. This is mostly a function of delays added in the code to cope with mechanical oscillations in the two arms and the pen.
Things that could be improved:
- The code is very ropey! Like my flat, it would benefit from a tidy and the hoovering up of any stray variables.
- The drawing area is still a bit sub-optimal. The arm can cover almost 100% of the space of an A5 sheet, however I’m only using about 65% for drawing at the moment. The boundary conditions for the angles would need to be carefully adjusted for this.
- Worst of all the image processing is very crude (simple cut for black and white). This is mostly because I’ve been lazy (it WORKS, right?) and wish to avoid re-inventing the wheel when there are so many excellent image processing suites out there. If I have time it would be great to code something to vectorise JPG’s properly rather than my current ‘quick and dirty’ approach.
- Colour! My pen has Red, Green, Blue and Black ink.. mostly I use black or blue for clarity, but it would be very interesting to come up with a multi-pass colour image reproduction system.
- Good projects are never quite finished 😉
A picture of Ben Bashford drawn from a JPG photo, live on the stage of Lift’12.
I hope you liked my creation – please feel free to comment, suggest and contribute. I’d like to thank Amy Shen for being my first drawing subject and for helping me to figure out the maths of converting an XY co-ordinate space into a two polar variables.
[…] James Devine made this automatic pen printer device as his hobby. It took him about 3 days of work, programming the Arduino, the motors, putting stuff together, now it can print any black and white picture automatically. It reminds me of the totally amazing 1810 Henri Maillardet Automaton. James Devine works as a electrical engineer at CERN, standby to work on electrical systems for all the experiments at the Large Hadron Collider. James Devine released the source code and info about his robot drawing arm at his blog. […]
[…] James Devine made this automatic pen printer device as his hobby. It took him about 3 days of work, programming the Arduino, the motors, putting stuff together, now it can print any black and white picture automatically. It reminds me of the totally amazing 1810 Henri Maillardet Automaton. James Devine works as a electrical engineer at CERN, standby to work on electrical systems for all the experiments at the Large Hadron Collider. James Devine released the source code and info about his robot drawing arm at his blog. […]
hai friend this is swamy i have seen ur project and it was quite impressive and i am new with aurduino and i didn’t know any thing about processing i searched in net and downloaded the processing and i dont understand what to do with the processing weather i should load it to my controller or i should load it in my pc can u please send my the detailed description what to do and how to do
THANKING YOU
HOPE FOR THE SPEED REPLY
REGARDS,
V V L SWAMY
MY EMAIL ID : swamy.vonumu@gmail.com
Processing goes on the PC! It send the commands via serial over USB to the arduino. The enviornments for processing and arduino look similar as the arduino IDE is basically a sub-set of processing. Good luck!
When trying to upload code I get error message “unexpected char:’/'” in the line for (int ycycle = 129; ycycle>0; ycycle-)
Thanks
I think it might be the – ? The code worked fine in my example… perhaps you’ve had a copy/paste error with the cursor floating somewhere. I would suggest you try to re-paste it.
Hi , I have build your robot as you have describe below , but when tryng to upload the code , I get error message I’ don’t know why , please have you install some libraries on processing ?
Samson, I had another go with Version 2 of my arm recently and there’s a problem with the firmata libraries I used. Apparently they haven’t been updated for Processing 2.0. I think the most recent version works with Processing 1.5 only – though I haven’t had time to get my installation working properly yet. Good luck!
Hai,
Can I have the code for the arduino please
Kind Regards
Erwin
Here’s the Processing code: https://gist.github.com/pingud98/8140322 the Arduino is just running standard Firmata with the three servos http://arduino.cc/en/reference/firmata
Best of luck!
Hai ,
Thanks for the fast reply
I have one more question please :
I get an error in Processing “cc.does not exist” where can I find the library
Is this oke :
Servo1 = Shoulder
Servo2 = Elbow
Servo3 = Up/down
Glad you help me thanks
Kind Regards
Erwin
Hi Erwin, I think you’re missing the arduino library from processing (line import cc.arduino etc.) you should go to the processing site and download it. As for which servo does which thing, I think you’re correct – you can also check out this other script I wrote, that gives manual control over the arm using the mouse. Should help you to debug the hardware –
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Miniarmcablibration.pde
hosted with ❤ by GitHub
Hi, pingu98
When I run your code in processing 1.5.1,
I get the following errors and warning. can you suggest a possible course of action.
Stable Library
=========================================
Native lib Version = RXTX-2.2pre2
Java lib Version = RXTX-2.1-7
WARNING: RXTX Version mismatch
Jar version = RXTX-2.1-7
native lib Version = RXTX-2.2pre2
Exception in thread “Animation Thread” java.lang.ArrayIndexOutOfBoundsException: 1
at project.setup(project.java:87)
at processing.core.PApplet.handleDraw(Unknown Source)
at processing.core.PApplet.run(Unknown Source)
at java.lang.Thread.run(Thread.java:662)
Kind regards and thanks,
Mayur Bille.
Hello Mayur, Did you take the code from Github? If you copied and pasted it there might be non-unicode characters. Otherwise it sounds like you have an issue with your Java installation on your computer as well, if you google around there’s probably a way to make the two libraries match up (possibly updating your OS Java). You might also want to check out line 87 of your code, to see if there is anything suspicious, assuming that project.java:87 refers to a line… that isn’t already changed due to compilation in Java. Best of luck and let me know how you get on! You could also try the Arduino/Firmata SERVO example, which I used as inspiration – and if your system is working correctly should enable you to get something moving! James
Hai James
Sorry to bother you so much
I have all versions (2.2.1 / 1.5.1 / 3.0a5) of processing dowload and tried and I always get the same error
“Cc.arduino doe not exist” and “no library fond”
Dowloaded , unpack , and start
I have searched the Internet for “cc.arduino” but nothing found
I have tried on two computers, one vista, and the other windows 8.1, no result
hardware:
http://www.thingiverse.com/thing:384850
This is about my setup, see site above
I installed the pencil to a servo and I use the Arduino Mega 2560
The pin I use are 8,9,10 and this I change in the program
What do I wrong?????
Kind Regards
Erwin
Nice hardware! You need the Arduino/Firmata library which you can find here: http://playground.arduino.cc/Interfacing/Processing make sure you follow the instructions regarding installing it. It’s been a while but I think you had to put it into the correct directory (which you might have to create) and then restart processing. Should work fine in versions 1.5 and 2, provided Firmata isn’t broken… (which it has been from time to time). Best of luck and please let me know how you get on!
James
Hai ,
Hai
Thanks for the explanation I’m going to watch it quietly
Between () Standard Version Processor 1.5.1, is STANDARD ( you can see that upper right )
version 2.2.1 = JAVA
mvg
Erwin
Hai James,
There is already movement in the robot arm , thank you
I think I have now installed the labrary good (Labraries)
I used you control prog
1: arduino = new Arduino(this, Arduino.list()[1]); the 1 I changed to 0 and then the prog works
2: I go to the left with the mouse, go to the robot arm to the right
when I use move the mouse to upsite the robot arm go under
Can this solved in the software ?????
May I also ask for the “James.jpg” file, I can not find him
Thanks for helping my on way
Kind regards
Erwin
Hi pingu98,
I have made the robotic arm using three servos and arduino uno.
My arduino runs standard firmata.
i used your processing code, but when i run the code in processing after about a second the serial communication stops. and the arduino resets itself and the RxTx leds switches off. I have installed the arduino library correctly. the processing sketch gives no errors but arduino stops getting data. Can you help and tell me exactly where i am going wrong.
Hi Mayur,
Sounds like you have a hardware communication issue, if the Arduino is resetting. It could also be an illegal code sent from the PC, or a short circuit somewhere. I would suggest you run a serial Rx/Tx routine with your computer over a few minutes to check the stability of the connection.
When you’ve got that stable, try running the code from this gist, which I used to calibrate my own arm.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Miniarmcablibration.pde
hosted with ❤ by GitHub
The arm should move following your mouse pointer once you configure it correctly. When it works okay, then try moving back to the original processing sketch. I assume you already got the non-unicode characters out if you did a copy/paste from my blog.
Best of luck!
James
hello sir,i m making this project as my 7th sem project…i hav followed all your instructions but when the 3 servo motor are connected to the external power supply of 5v ,they are not working..please do reply asap.
Check the data line to the servos. If no PWM is provided to the servo’s then they won’t move. You can test it with a simple PWM signal generated from an arduino PWM output, also worth double checking the power – both the wiring and the servo by running it off the Arduino 5V supply as a test. Best of luck!
hallo sir and congratulations for your great build.
I am trying to build the robotic-arm but i have this output. (photo ending)
https://drive.google.com/file/d/0B13HDerpRZ5WaVJOczJoNG1lOUk/view?usp=sharing
photo of processing output end image (it seems the down right image is wrong)
https://drive.google.com/file/d/0B13HDerpRZ5WS1ZWV3FjdS1LaFE/view?usp=sharing
i also get a warning but the processing sketch runs
https://drive.google.com/file/d/0B13HDerpRZ5WekJpaHB4YXFlYmc/view?usp=sharing
The setup i am usingProcessing 1.5 + arduino IDE 1.05. r2 + micro servos 9g
black+white pic “sq.jpg” 180X180 px +win 8.1 64bit
can you please help me sort the problem?
Hello Dimitrios!
Good job on building the arm. I think you need to fix the rigidity of the pen mount, as the ‘preview’ image comes out fine. Don’t worry about the error, it’s to do with the version of Java you’re using – are you by chance using a Mac? If you change the settings in the script then you can get a clearer preview by turning off the path of the arm, you might also find that the geometry needs to be modified in the script to reflect the arm length and zero positions that you have constructed. I’m so happy to see someone’s used my code 🙂 Thanks for sharing, especially the video!
🙂 it’s not a great build is a quick one. No, i am not using a Mac, Do you think that the pen down is ok @ that angle? Should it draw that way? as you see the final result? *** Which are the geometry length of ams and zero position in the processing sketch? p.s. sorry for the double post bellow.
do you think that the output for that square https://drive.google.com/file/d/0B13HDerpRZ5WaVJOczJoNG1lOUk/view?usp=sharing was ok, but needed the geometry details and servo (pen down adjustment)?
the image i tested to print was https://drive.google.com/file/d/0B13HDerpRZ5WS1ZWV3FjdS1LaFE/view?usp=sharing and the result from the robotic arm is
https://drive.google.com/file/d/0B13HDerpRZ5WaVJOczJoNG1lOUk/view?usp=sharing
I’ve seen all photos and videos of pingu98 and I have to say I just got the same result
This is my setup http://www.thingiverse.com/thing:1165504, I’ve posted a picture and I wonder, why is there this double picture, and how does that black spot over there
May I ask if you’ve already solved the problem and possibly how
Thank you in advance
and some video from my copy build https://drive.google.com/file/d/0B13HDerpRZ5WWXdsdjhRcVJ1eTg/view?usp=sharing
hallo sir and congratulations for your great build.
I am trying to build the robotic-arm but i have this output. (photo ending)
https://drive.google.com/file/d/0B13HDerpRZ5WaVJOczJoNG1lOUk/view?usp=sharing
photo of processing output end image (it seems the down right image is wrong)
https://drive.google.com/file/d/0B13HDerpRZ5WS1ZWV3FjdS1LaFE/view?usp=sharing
i also get a warning but the processing sketch runs
https://drive.google.com/file/d/0B13HDerpRZ5WekJpaHB4YXFlYmc/view?usp=sharing
The setup i am usingProcessing 1.5 + arduino IDE 1.05. r2 + micro servos 9g
black+white pic “sq.jpg” 180X180 px +win 8.1 64bit
can you please help me sort the problem?
and some video of the process
https://drive.google.com/file/d/0B13HDerpRZ5WWXdsdjhRcVJ1eTg/view?usp=sharing
Processing is awesome! for the callibration the following processing sketch can be used: http://users.sch.gr/dimpapadop/arkas.html (in greek but you’ll understand)
int sx,sy,ex,ey,hx,hy,hxo,hyo;
int armLength,ua,la;
float uad, lad;
void setup(){
size(500,500);
background(255, 224, 150);
sx = width/2;
sy = height/2;
armLength = int(width/5);
}
void draw(){
fill(255);
rect(0,0,width,height);
upperArm();
}
void upperArm(){
int dx = mouseX – sx;
int dy = mouseY – sy;
float distance = sqrt(dx*dx+dy*dy);
int a = armLength;
int b = armLength;
float c = min(distance, a + b);
float B = acos((b*b-a*a-c*c)/(-2*a*c));
float C = acos((c*c-a*a-b*b)/(-2*a*b));
float D = atan2(dy,dx);
float E = D + B + PI + C;
ex = int((cos(E) * a)) + sx;
ey = int((sin(E) * a)) + sy;
print(“UpperArm Angle= “+degrees(E)+” “);
hx = int((cos(D+B) * b)) + ex;
hy = int((sin(D+B) * b)) + ey;
println(“LowerArm Angle= “+degrees((D+B)));
stroke(255,0,0,100);
fill(240,0,0,200);
ellipse(sx,sy,10,10);
ellipse(ex,ey,8,8);
ellipse(hx,hy,6,6);
stroke(0);
line(sx,sy,ex,ey);
line(ex,ey,hx,hy);
}
Thanks for sharing your calibration code! I’m sure it will be useful to others in the future as well 🙂 I hope you’ve got your arm drawing correctly now.
yes, i have. I just need to help me with the arm length – geometry. Are the 130 values? mine are 100mm=10cm shall i go with 100px? (so to be able not to draw outside… 🙂 the image comes ok now! Thank you again
I’ve seen all your photos and videos and I have to say I just got the same result
This is my setup http://www.thingiverse.com/thing:1165504, I’ve posted a picture and I wonder, why is there this double picture, and how does that black spot over there
May I ask if you’ve already solved the problem and possibly how
Thank you in advance
Hallo Dimitrios,
I’ve seen all your photos and videos and I have to say I just got the same result
This is my setup http://www.thingiverse.com/thing:1165504, I’ve posted a picture and I wonder, why is there this double picture, and how does that black spot over there
May I ask if you’ve already solved the problem and possibly how
Thank you in advance
I’ve seen all your photos and videos and I have to say I just got the same result
This is my setup http://www.thingiverse.com/thing:1165504, I’ve posted a picture and I wonder, why is there this double picture, and how does that black spot over there
May I ask if you’ve already solved the problem and possibly how
Thank you in advance
[…] The Robot Arm (with Arduin0) […]
[…] The Robot Arm (with Arduin0) […]