While napping, I had this dream about a crazy way to make a clock face (I often think of these things). It reminded me of this drawing bot ‘hektor’ that I had read about, and I wondered how hard it would be to reproduce. With some encouragement from my friend, an Arduino clone, pieces from a VEX robotics kit and some Python, I have some sort of start. Sample code after the break. Note: I plan to fix up the code for this one.
This part goes on the Arduino. It requires the SimpleMessageSystem library.
#include <SimpleMessageSystem.h> /****************************************************************************************************** * Window washer drawing bot * * by Matt Mets * Created 19 Feb. 2008 * * This is a little driver routine to move around two continuous rotation servo motors, * for the purpose of moving a pen platform to draw on a whiteboard * * Servo control code thanks to: * Servo control from an analog input * http://itp.nyu.edu/physcomp/Labs/Servo * by Tom Igoe * additions by Carlyn Maw * Created 28 Jan. 2006 * Updated 7 Jun. 2006 * * The minimum (minPulse) and maxiumum (maxPuluse) values * will be different depending on your specific servo motor. * Ideally, it should be between 1 and 2 milliseconds, but in practice, * 0.5 - 2.5 milliseconds works well for me. * Try different values to see what numbers are best for you. * * This program uses the millis() function to keep track of when the servo was * last pulsed. millis() produces an overflow error (i.e. generates a number * that's too big to fit in a long variable) after about 5 days. if you're * making a program that has to run for more than 5 days, you may need to * account for this. ******************************************************************************************************/ /***** Variable Definitions ***************************************************************************/ int lMotorPin = 2; // Motor on left side int rMotorPin = 3; // Motor on right side int statusPin = 13; // Status LED // Adjust these to fit your servo int minPulse = 500; // Minimum servo position (us) int maxPulse = 2200; // Maximum servo position (us) int refreshTime = 20; // the time needed in between pulses (ms) /***** Functions ************************************************************************************/ void setup() { pinMode(lMotorPin, OUTPUT); pinMode(rMotorPin, OUTPUT); pinMode(statusPin, OUTPUT); Serial.begin(9600); } /* Move the motors at given speed for a duration of time * lMotorVal - speed of left motor (128 = stop) * rMotorVal - speed of right motor (128 = stop) * time - time to move for (100 ms intervals) */ void doMoveCommand(int lMotorVal, int rMotorVal, int time) { int position; long startTime; long lastPulse = 0; // the time in milliseconds of the last pulse long lMotorDelay, rMotorDelay; lMotorDelay = (long)lMotorVal*4*(maxPulse - minPulse)/1024 + minPulse; rMotorDelay = (long)rMotorVal*4*(maxPulse - minPulse)/1024 + minPulse; Serial.println(lMotorDelay); Serial.println(rMotorDelay); startTime = millis(); while(millis() - startTime < time*100) { // pulse the servo again if rhe refresh time (20 ms) have passed: if (millis() - lastPulse >= refreshTime) { digitalWrite(lMotorPin, HIGH); // Turn the motor on delayMicroseconds(lMotorDelay); // Length of the pulse sets the motor position digitalWrite(lMotorPin, LOW); // Turn the motor off digitalWrite(rMotorPin, HIGH); // Turn the motor on delayMicroseconds(rMotorDelay); // Length of the pulse sets the motor position digitalWrite(rMotorPin, LOW); // Turn the motor off lastPulse = millis(); // save the time of the last pulse } } } void loop() { char firstChar; digitalWrite(statusPin, HIGH); Serial.println("test!"); while(1) { if (messageBuild()) { // Checks to see if the message is complete firstChar = messageGetChar(); // Gets the first word as a character if (firstChar = 'm') { int lMotorVal = messageGetInt(); // Gets the next word an integer int rMotorVal = messageGetInt(); // Gets the next word an integer int time = messageGetInt(); doMoveCommand(lMotorVal, rMotorVal, time); } } digitalWrite(statusPin, LOW); delay(100); digitalWrite(statusPin, HIGH); delay(100); } } |
This is the Python demo driver, to be run on the PC. Warning: The whole thing is a hack…
#!/usr/bin/env python import serial from time import sleep lL = 205 # move left motor counterclockwise lH = 180 # hold left motor position lR = 105 # move left motor clockwise rL = 180 # move right motor counterclockwise rH = 150 # hold right motor position rR = 90 # move right motor clockwise class whiteBoardBot(): def __init__(self, port, baud=9600): self.serial = serial.Serial(port,baud) def makeMove(self, left, right, time): message = 'm %(left)d %(right)d %(time)d\r' % locals() self.serial.write(message) def moveDirection(self, direction): if direction == 'l': bot.makeMove(lL, rL, 8) elif direction == 'r': bot.makeMove(lR, rR, 6) elif direction == 'u': bot.makeMove(lL, rR, 7) elif direction == 'd': bot.makeMove(lR, rL, 9) sleep(1.2) bot = whiteBoardBot("/dev/ttyUSB0") moves = 'r','r','r','u','l','u','l','d','l','d' for i in range (1,4): for move in moves: bot.moveDirection(move) |
You know what’d be really cool? If you were controlling magnets behind the board and the marker was floating mysteriously in front of the board. It’d be a cool seance trick!
I love what you’ve done on this site. The things you are making are incredible. I wish I had time. My Arduino is still blinking 12:00…
great minds think very similarly
http://www.flickr.com/photos/ladyada/2281230002/
I love the way this device does positioning. It places the pen at the intersection of two circles whose radii are decided by the length of the cable.
This is a breath of fresh air when compared to the ubiquitous orthogonal lead-screw design.
Thanks for posting this.
How accurate are the servos? they seem handy for home robotics but it looks like this project relies on the speed of the servos being stable and similar. Have you considered shaft encoders or stepper motors?
Thanks!
@Ric- have you seen the SanDraw bot? http://www.notepad.org/SanDraw/
@Andrew- They are probably not very accurate, especially the continuous rotation ones. I was planning to add some shaft encoders, however my friend gave me a couple of steppers today so I might try those instead.
Have you tried using a counterbalance similar to the kind used with cameras? Basically, just hang a washer from the bottom of the stylus component. It may help smooth out the drawing.
Hi Johntron- I’ve got a somewhat heavy flywheel attached to the bottom, is that the sort of thing you are referring to?
The counter weight is not the problem (keep the one you have). It’s your platform under the stylus. It’s too wide. You need the connection of the string to be closer to the pen from both sides. For down and dirty testing, pull out the duct tape, tape your weight to the pen, tape the 4 connection points of string to the pen (2 near tip, 2 far from tip). That will steady it out quite a bit.
Hi David,
Thanks for the suggestion! I will keep that in mind for the next version. I more or less gave up on this design because the servo motors weren’t able to respond quickly enough to make drawing repeatable. I got some steppers and drivers, but haven’t gotten back to it :-/.
Pingback: del.icio.us links-- sketchbook
Pingback: DrawBot Resources and Links | MakerBlock
Pingback: DrawBot – The Assembly, Part III | MakerBlock