Robot Drawing Arm

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.Image

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 ;)

Image

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.

About these ads
Tagged , , , , , , , , , , , , , , , ,

18 thoughts on “Robot Drawing Arm

  1. […] 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. […]

  2. […] 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. […]

  3. v v l swamy says:

    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

    • pingu98 says:

      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!

  4. Gerhard says:

    When trying to upload code I get error message “unexpected char:’/'” in the line for (int ycycle = 129; ycycle>0; ycycle-)

    Thanks

    • pingu98 says:

      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.

  5. samson says:

    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 ?

    • pingu98 says:

      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!

  6. Erwin says:

    Hai,

    Can I have the code for the arduino please

    Kind Regards
    Erwin

  7. Erwin says:

    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

    • pingu98 says:

      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 –

  8. Mayur Bille says:

    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.

    • pingu98 says:

      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

  9. Erwin says:

    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

    • pingu98 says:

      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

  10. Erwin says:

    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

  11. Erwin says:

    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

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 836 other followers

%d bloggers like this: