Let's Make Robots!

My First Robot

Draft Robot Page: http://letsmakerobots.com/node/12713

My first robot is a Valkyrie CNC using TinHead's example: http://letsmakerobots.com/node/9006


Vector Drawings: http://letsmakerobots.com/files/clone_cnc_vector.pdf (draft)
Measurements will be available for these drawings by December 2009 or if somebody bugs me for it.

Cost will be more than $200 -- The motor drivers were the most expensive bit of it.  One could bring down the cost by hand-building the driver boards, but I sourced the PCBs from BatchPCB.

Plans for this robot: learn about CNC machines and control systems, and machine prototype PCBs for other robots. [...]

Credit where credit is due: My CNC is deeply indebted to TinHead's work on the motor driver (node/6967), driver software, Arduino controller software, and hardware design, along with the discussions on the "Valkyrie" robot page (node/9006).  Driver software is http://github.com/TinHead/Valkyrie-CNC-source-code/tree/master .  Somewhere in the driver and Arduino source code, the RepRap project gets kudos as well.  Debt is also owed to http://buildyourcnc.com for some of the construction tips.  I wouldn't be able to join the wood so well without learning good drilling technique.  Also, ladyada's AVR tutorial ( http://www.ladyada.net/learn/avr/ ) was essential to programming the ATTINY2313 used in the stepper drivers.  The controllers were programmed on a minimalist target board from evilmadscientist.com ( http://www.evilmadscientist.com/article.php/avrtargetboards ). 

Giving back: I'll be posting more detailed plans for my iteration of this machine.  It is TinHead's design, but where there needed to be alterations (e.g. the PCB design needed changes before BatchPCB would accept it) I made changes, and where there were ambiguities, I had to figure them out (for example, the views of the robot from the back, sides, and bottom I wished I could see).  Also, I ground a copper-clad board into my first prototype circuit board (first ever!) while waiting for BatchPCB, and if I wanted to spend many hours grinding copies of that prototype motor driver board, I could have saved easily $70 on manufacturing the boards (toner transfer would have been quicker but I had a new rotary tool to try out).  If the proto driver board works, I'll post that as well.  My plans for this homage to the Valkyrie is to document the building as best I can, so that more pics and plans are available online for folks like me who want to tinker.

Budget example for sourcing components from the USA37.42 KB
clone_CNC_skate_bearings.JPG383.87 KB
clone_CNC_pcb_backlit.JPG350.28 KB
clone_CNC_pcb_front.JPG285.59 KB
clone_CNC_pcb_back.JPG372.71 KB
clone_CNC_copper_front1.JPG347.72 KB
clone_CNC_copper_backlit.JPG336.59 KB
tiny2313_stepper.brd23.75 KB
clone_cnc_600px.jpg125.03 KB
clone_cnc_floating_block.PNG5.74 KB
clone_CNC_1101_front_s.JPG23.07 KB
clone_CNC_1101_back_s.JPG26.48 KB
clone_cnc_captive_nut.png2.44 KB
clone_cnc_1500px.jpg877.57 KB
clone_cnc_vector.pdf99.8 KB
clone_cnc_closeup_sm.jpg102.22 KB
clone_cnc_closeup_1500px.jpg705.01 KB
clone_cnc_I2C_test.PNG7.14 KB
clone_CNC_Dec09_med.jpg319.46 KB
clone_CNC_Dec09_small.jpg122.25 KB

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

I run it as root.  It took me a little bit of time to remember that this morning when I ran a "surfacing" script to flatten the cutting area.  It took about an hour to flatten the 110mm x 165mm cutting area, and I noticed by the end of it that my X and Y axes are of course not orthogonal (since I may have forgotten to design it to be perfectly rectilinear).  I measured a 91° angle at the left front of my machine which means there's 89° between the X and Y axes.  For something built with battery tools I suppose I shouldn't be too disappointed that over the 165mm runout, I have a 3mm error.  I might try to fix it in the code or ignore it, since I have no desire to realign it.  It would have been nice to have <0.3° error.

 I am pleased that in an hour of cutting, nothing broke or burned, and there were no interruptions.  I inserted a delay after each X-axis or Y-axis "send_twi_command()" of 2ms, and the machine moves fast enough at max speed.  Originally the delay was inserted so I could multi-step with a single I2C command, but I dropped that for the X and Y axes since the I2C speed is limited by the stepper driver speed in executing commands and getting ready for the next I2C command.  When I set the delay to zero I got only vibration at the max feedrate, so I keep it at 2ms.  Now I have a mostly flat parallelogram of cutting surface, and a 15kHz ringing in my ears :)

I never really got to test the runout on the full length, my guess is that the situation is similar or probably worse for me :)

It is very good news that you have not experienced  lookups, this means that my problem is localized. It is either noise from the Dremel on the I2C lines or the Zaxis driver which has been having some weird issues.I think I'm going your way this time, having all controllers in  a box.

Today new parts arrive, so I can start working on building the new controllers, the prototype seems to work well. 

I modified the Arduino code, setup the delay to 5 microseconds. Now the feedrate setup in the configuration really does controll the step delay, for my machine a value of 2000 seems to work well. Of course the cutting feed rate is much lower, once I have the new drivers I can start destroy some more router bits, I'm estimating a cutting feedrate of around 500 mm/min should be achievable with wood.

Not sure about multiple steps per virtual step, first because the resolution of a step is pretty coarse as it is, second because you have no idea if you actually loosing any steps while moving (that being said you have no idea from the software point of view even when going single step, but at least you can hear it skipping). 

This drifting might be caused by the above, my code on the tiny is assuming the stepper is doing one step/halfstep at one time, I can only imagine that when doing multiple step the step count could be messed up, no idea if or how.  

I had a new look at the arduino code and I found the speed limiting problem. In the do_step function I have added a delay of 5 milliseconds after the step is done.

 This was done in early stages when I was doing multiple things at the same time and I had issues with the torque of the motors, never went back to rethink it.

I checked with the original code yesterday and the delay used there was of 5 microseconds. That is so because the original controller used a l297 + l298 to control the motors. The l297 expects an active low pulse on its clock pin to generate a step. The pulse has to be at least 0.5 us long to count so they gone the safe lane and put a wait of 5 us. That is the reason for that delay there.  

So I think it is safe to remove the delay in my code now, the feedrate alone should decide how fast the motors should go. I'm going to test this in the evening. 

One issue I had back then is that different feed rates produced different sizes in the output for example moving 10 mm at 100 mm/min worked great but at 500mm/min it moved only 2-3 mm. That was strange, I do not really remember what the cause was and how i fixed it :( I was doing too many things at the same time (testing PWM on the motors, killing the drivers after stepping, etc, etc).

About the heat in the motors it is caused by the current in the coils AFAIK. The problem is that I found no nice way to to control the current in the coils and keep the voltage high ... My motors get pretty hot too, touchable but not holdable, I estimate around 80 degrees C ... which by some is safe by others is not so safe, I really need measure that some time.

Thank you for the help on this -- I am all but counting the hours until I can dive into the code this weekend.  I think understand the reservations about reducing the resolution of the motors by running multiple steps per I2C command.  I think as long as the step size remains constant in the "parameters.h" file, the code can keep track -- the problem may be if the code expects instant steps, while the driver is running at a slower programmed speed.   If the ATTINY2313 gets an I2C command while it's busy, it may just ignore it.  Previously it was reasonable for the code to expect steps that happened instantly because the driver was commanded one half-step at a time.  I think I'll find that in the routine "calculate_feedrate_delay" I should scale down the feedrate to have a minimum delay for a quarter-turn of the stepper (or whatever fraction I use).  I don't think I'll know whether I've fixed things until I make the changes and either can draw letters with my Inkscape GCODE or end up drawing gibberish.

Once I get it working, I think I may end up using a few speeds on the driver -- and they may have to be the same size/number of steps.   For fast feedrate, I would want a fast turn of the lead screw.  For cutting PCB, I would want a slow turn of the lead screw (even the quarter-steps should be slower).  I think I have 250 more I2C commands available to me (the first six are forward/back, enable/disable, motor test 1 / motor test 2), so there should be room for fast and slow move rates.  I prefer to do this in the driver code, because the I2C interface likely cannot step the motor as fast as it is capable of going (nor as fast as I want it to go).

Of course I haven't spent enough time with the code to know whether my ideas are good or bad :)  43 hours until Saturday morning!


After playing around with the machine today, I am almost sure the problem was that I was sending commands while the stepper was busy.  There may be a queue in the I2C code that can store a certain number of received bytes of data, and if so, I was overflowing it.  A symptom was that the machine was not moving linearly with commanded distance: so if I told it to go from X0 to X5, it would be for example half the size of a move from X0 to X20.  That also made it very difficult to determine the scale of the machine in terms of GCODE steps to millimeters.  The steps are now accurate and I think the problem is solved.  

Single-step mode is also working well -- I think I will like it better than my fractional-rotation mode.  Now that it is drawing to scale, I'll be figuring out the variable feedrate, which I'll need for cutting.


I abandoned my multi-step code for all axes (X,Y,Z) -- it is still on the motor driver, but I do not use it.  The machine works much better in single-step mode.

I do not think we got a bottle neck here, in early tests I banged on the bus with no delay at all just to find out the stepper could not keep up with the commands :) That was with a single slave but it should be just in the range of a few hundred  microseconds with more of them ... so it should be safe.

By my (poor) calculations I should be able to reach about 600 RPM running with 1 microsencond  steps with the new design of the driver ... eyeball testing reveals the stepper does turn really fast  and with a very usable torque. That must still be tested on the Valkyrie ... hmm maybe this wk I'll get some time to actually try it.

BTW you are welcome ;) I always meant to have this project as collaborative as possible ... you know more pairs of eyes better odds for success. 

My GMT+2 clock says it's time for me to go to bed ... have fun

It looks great and it also seems to work alright too! Now I need to restore operation to my Valkyrie ... need more diodes damn it ... and fix the tool holder too.

How hot do you think your motors get?

Also while testing did you encounter any lockups (at the electronics/software side I mean)?


Today, cutting copper-clad board, the machine locked up a few times.  If I were cutting easily it would not lock up, but when it started cutting deeper (because I did a bad job of surfacing my table), the machine would lock up.  It happened pretty often.  During one memorable occasion, the I2C Rx LEDs kept blinking off and on, all at once for all the motor drivers, even though there shouldn't have been commands (the Perl script was waiting for an "ok"), and certainly not at the frequency the blinking was happening.  I thought it might be that the drivers kept getting a "reset" signal somehow, or lost voltage below the ATTINY's threshold.  I'll probably see more freezes, but I'm hoping they will not happen when I'm doing everything right.

The motors got pretty toasty when I was running the stages back and forth last week -- they were too hot to keep your hand on, but not too hot to touch.  During the short sequences I recorded on video, they stayed cool.  I'll be learning more about that as I spend more time running the machine.

I encountered some lockups during testing but I need more time on the machine before I can identify whether it is the same I2C error.  I've had wires loose on occasion, loss of communication due to using lowercase letters (g00 != G00) and other errors that I can attribute to me.  Once I try running longer scripts I'll probably encounter the I2C/power issue if it is a problem.  Right now, my biggest problem is that I need to control my feed rate (deep cuts need slower feed rate), and I really don't know why my paths generated with Inkscape gcode are not what I see on the screen (I'll have to see what parameters.h values I changed on the Arduino code).  I'm sure I'll figure it out in testing -- it's a lot more fun than trying to configure an FTDI USB serial port in my Red Hat Enterprise Linux variant.

 Update: My machine is behaving strangely -- I'm not sure whether it is a parsing error or a communication error, but it is losing its place.  I suspect the problem can be fixed in the Arduino code.  I tried drawing square inward spirals -- an example path would start at X10Y-10 and move to X-10Y-10, then X-10Y+10, then X10Y10, X10Y-8, X-8Y-8, X-8Y8, et cetera.  Along the way, the paths drift in the -X+Y direction so that when I recenter, or try to go back to X10Y-10, the new position is offset by about 10-25% of the spiral size.  The drifts get worse as I go further along the spiral.  I was using an automated GCODE example, so I may try it using a hand-typed spiral with integers rather than automatically Inkscape-generated floats (e.g. "G00 X10.00002 Y7.898899").  I have a few ideas about what may be wrong -- my driver takes a few steps per commanded move, so the Arduino code may be losing track of its position.  Or perhaps the string parser is getting confused, or my motor driver is being ordered to move less than it is capable of moving, so it rotates a quarter turn, which is more than the Arduino thinks it moved.  Debugging will be fun.