So we’re on stage III of the rebuild at Null Space Labs (http://032.la) . What we’re aiming for is :-
- Rotation servo in the head
- Consistent speeds
- Addition of more checks
Adding more checks was straightforward, added code to determine if the machine picked up a part, with the small compressor we were using it’d start to loose vacuum so if it failed to pick up a part, the delay became longer. This allowed the the compressor to catch up a little, as well as alert the user that something was amiss. Next it checks to see if the part fell off during transport to the board. The machine has a vacuum sensor that knows if the top is being blocked. This lets the machine controller (Arduino) know a part is probably there.
The machine won’t move if the head is down, and the limit switches were put on interrupts. tool changers are tested and put down if they’re up for some reason.
The interrupt based limit switches are nice, anytime a change of the signal is recognised, either in limit or coming out of limit the routine is called and it reads the status of the pin again, and sets the out of home condition, so that machine controller knows the machine should be rehomed before continuing. The code is simple and looks like this in the setup()
attachInterrupt(5, x1Limit, CHANGE );
void x1Limit( void )
homed = false;
xLimit1 = digitalRead( XL1 );
attachInterrupt takes the pin number , the function to call and type of signal to look for, HIGH, LOW, and CHANGE, i use CHANGE so that the xLimit1 variable is always set properly, and rather than rely on software tracking I read the state back in.
I’ll probably change the digital read to actually just read the PORTx value directly, you want to minimise time spent in an ISR (interrupt service routine ) as much as possible. 5 is the pin that the XL1 limit switch is connected to on the shield, and homed/xLimit1 are volatile global variables that have the condition, they are set as volatile since they can be changed at unexpected times, either by the hardware directly or the ISR. volatile lets the compiler know not to cache the value or other trickery. This is often the case with things like memory mapped hardware clocks where you read a specific memory location to read a clock, so you’d do something like (clockVal would be set to the specific memory location by the linker or other directive)
const volatile unsigned long clockVal;
We’re not using const for the homed/xLimit1, since we are writing to it via software, a hardware clock changes outside of our code.
Someone knocked one of the feeders slightly off (probably me) as it was being worked on, I didn’t notice and moved the head.
It caught the feeder pin ( bottom left) and bent badly enough that it wouldn’t go back down. So i pulled it apart and used a vice with cut outs and gently squeezed it back into shape, then mmca used his micro lathe to make it straight again. Luckily these are easy to make from scratch.
The servo pulse speeds are more of a pain, the Arduino has some fairly inconsistent timing and some of the functions you’d expect to be fast, aren’t, the delayMicroseconds has a lot of skew, digitalWrite takes a long long time. Once you know about them, they’re easy to deal with i replaced them with bitSet/bitClear and the __builtin_avr_delay_cycles feature in avr-gcc. We captured the output of the servos from the Juki PC with a scope and my logic analyser. That allowed us to see the ramp up and down times to get the head moving at a decent clip. There is still some ripple in the software PWM though, so we’ve converted most of it over to just AVR asm/C and setting the ports individually,the Arduino does what it is meant to do really well and that is be easy to use, but what’s uncommon about it is that it easily lets you drop in code that bypasses or override the built in libraries, in my experience that’s rare in other simplified dev kit software, its typically all or nothing.
Changing the digitalWrite is straightforward. Determine which port the pin you want to set is on and then use bitClear or bitSet instead.
digitalWrite(YCCW,HIGH) becomes #define YCCW_HIGH bitSet(PORTF,3); YCCW is defined as A3, which on the mega is on PORTF, bit 3. digitalWrite LOW is bitClear(PORTF,3) going even further the PORTF can be manipulated directly with PORTF |= (1<<3) to set it, then you can combine them together with PORTF |= (1<<3)|(1<<2) etc to clear PORTF &= ~( (1<<3) | (1<<2 ) ) also there is a _BV() macro that does the (1<<n) shift. so
PORTF |= _BV( 3 )
Using |= and &= should output the closest ASM instructions that you can could in C.
Once we started to investigate the timing from the Arduino that is when I noticed the problems, even with interrupts disabled we were seeing inconsistent results. So I wrote some test code and hooked up the output to the LA and set about trying to make a micro second accurate delay. specs showed a 2uS rise , 5uS pulse and 2uS fall for the initial step pulse, the stepper IO controller is HIGH delay LOW delay HIGH . for the first delay about 8uS works well.
We captured the data with the logic analyser and pulled the data into Excel which is great for this sort of thing.
A quick couple of macros and copy/paste and we’ve got differences in Column F , you can see the length of the pulse in F3 and F4 has the off time length, which gives us our full duty cycle for that pulse.
In H its just the lengths of the off times since the on time is constant. Column I has the differences.
One of the next things excel can do is trend lines, I did this by hand and then arko showed it to me, I’d seen the equation solver before but not this.
First create a line chart with the off times (the part of the duty cycle that’s after the pulse)
Click on the data points in the line to select the line. then use the trendline option
Set the options like so :-
Which gives you this trend line (in black ) and the algorithm for it. I opted to use a table lookup since it has to be super fast for the Arduino.
Skipping back a little after the last blog post we did an 0603 repeatability test, used the layerOne speaker badge as a base, I exported the centroids from eagle for one side and ran test code. As well as just have the machine draw lines of 0603’s. with a logo-esq list of commands.
The left side has a little wobble, partly pickup, partly the slightly bent head ( we’re lucky that a generous person sent us a new head and feeders you rock Steven ! ) the right side is a similar test but its placing the 0603s on top of each other, that actually worked pretty well a few of them fell over. But given the bent head, no centering or machine vision its working better than we can hand place them. This picture is the version that just places the parts in lines. I also think our stop is a little aggressive in this test, and this is before we really got into the ramping.
The ramping is simply having the PWM have a longer off time, pulsing the servo, decreasing the off time, pulse it again and do that for N pulses until the motors are moving fast enough to get to the top speed, that’s what the Excel sheet is for. I’ll go into it in more detail in another post.
Someone from Russian TV decided to film it, Central TV i believe. Unfortunately all the airlines are now tight so i can’t remotely shoot air or disconnect hoses at him anymore.
Adding a camera
This is a Cognex 4100 machine vision camera, machine vision cameras are generally low noise, stable image sources. This one can determine angles, and so on and then send them out via Ethernet or serial. The 4100 has been dropped by Cognex it seems and the software needed to run it is an old version, which has a lot of compatibility errors with Windows 7, its been something I’ve seen before where the menu’s lockup for a long time. But we figured our way around it by using Windows XP inside 7 with its builtin Virtual PC and got it to see the rotation. Think RoboRealm built into a camera.
Inside the camera, TI DSP and a Xilinx FPGA, very nice.
We’re also using some webcams , a couple of them are microscopes, one is from think geek and it’s a wireless HView camera, the other was an eBay special pen microscope usb2. Oddly finding a 12mm drill bit to make a hole for the camera wasn’t so easy.
Adding the camera needs two pins for the servo CW/CCW, three pins for the camera, TX/RX and trigger. The shield has three pins left!, Though I did cheat and use two of the pins as a via since I didn’t’ really want to deal with the LPKF via rivets even though its only a few of them. We do really only need RX though. The trigger just tells the camera to only operate while, or after the trigger is set. The final board will not use the cheats of course, but before I remill a new one we have to
This is our test bed software Pickobear it allows us to test OpenCV (though we have a Cognex camera), the eagle script output CSV is imported into Pickobear and then the machine knows how to place it.
One of the problems with test software is you often leave it in a state where it was testing something specific, and you forgot to remove it , case in point, I had one extra YCW pulse that caused a problem when we were doing a tool change test. After 4 changes it’d refuse to put down the changer, we checked air pressure, offsets, tool head distance and all that, eventually did test code that moved home, then to 0,0, 1000,1000, back to 0,0 and saw it was one Y off each time. A quick check of the firmware and the extra pulse was spotted. But it did allow us to get the numbers needed to exactly place the adjustment for the pickup head.
Next we’re testing out different cameras, I milled out a quick holder from PCB material and the epic blue painters tape. This is a nice’ish USB 2.0 camera that’s setup as a microscope, it came with a nice metal holder and was about $80 from eBay. M4 nuts and bolts seem to be rare around us, never mind the M2.5’s we were looking for locally last week. So zip tie to the rescue temporarily, it does allow a little yaw though but it is ok for testing the software until the bracket arrives.
To make this I measured the hole sizes, the distance apart and picked a distance for the camera, then guessed the size. I then added two drill holes with the hole command, set the size to 4mm for the larger 12mm hole I used the milling layer, draw a circle with the circle command, and set the width (right click circle and choose properties or type change width ) to a very small number, if you use 0.0 as the width eagle will fill in the whole circle and doesn’t cut anything, a width of 0.0 works for milling wires, but not circles. Then milled it out on our AccurateCNC 560H.
Now we fit the new holder, has a nice set screw to hold the camera in place.
Next is adding camera movement and offsetting the board. I did this by adding a mouse click even to the custom control, it then determines the center of the image and a difference calculation, then depending on the direction it needs to go it moves that amount, I still have to calibrate what the relationship of the camera image is to actual axis steps, but its close enough to use right now.
cvLine(img1, xyF, xyT, CV_RGB(0,0,200),2);
Camera setup is straightforward
int numDevices = VI.listDevices();
m_camera = DeviceID;
img1 = cvCreateImage(cvSize(VI.getWidth(m_camera),VI.getHeight(m_camera) ),IPL_DEPTH_8U,3);
img2 = cvCreateImage(cvSize(VI.getWidth(m_camera),VI.getHeight(m_camera) ),IPL_DEPTH_8U,3);
There is a nice simple class I’ve used for rendering OpenGL into an MFC custom control for a while.
Head servo rotation
The rotation head is the next thing we’ll get fully operational. We decided to upgrade the 24V power supply since the little servo can draw more amps than it could safely handle. All electronics has suitable 24V 6.5A deals.
$26 at the time of writing.
A custom CNC’d bracket was designed by mmca and laid out in solidworks by arko, the motor, pulley etc are all added. This allows us to switch from the simple 90o rotation to full a 360o’s so we can place the part turn 360’s and moonwalk away from it. There is some play in the rotation shaft because of the set screw, so that is being remade so there is no backlash., even though its a tiny amount and the software can correct it. The machine is able to do more than 90o in steps of 90o by simply changing which side the feeder is mounted too, but we want 45o’s for those gangsta leans.
The top bracket/reed switch isn’t mounted or screwed down, that is why its tilted.
New standoffs added and rest of head mounted.
So here is a short video showing the setting of the PCB offset, I home the machine, pick the part I want to use a registration. Then move to the location where it really is and press the OFFSET button., then I move around to other components just to check its working, again the blue lines are OpenCV doing stuff.
Motor Drive Upgrade
The Y motor driver and motor aren’t that great, so we changed it out for this controller
First removing all the old wiring and converting it to the style connectors, it’s a shame to do this as the wiring is just so nice compared to modern equipment. Its not often you see this level of attention anymore.
New controller in place of the old one, it fits pretty well its longer than the old one but luckily there was lots of room, we just have to extend the AC lines a little.
Removing the plastic conduit covers and rerouting the wiring.
Almost there, I’m heading out early tonight and we apparently don’t have crimpers at NSL so rather than botch it, I’ll pick up some tomorrow or bring mine in from home
A new motor was fitted too.
Close up of the belts and cogs etc.
New vexta motor
Machine powered up and homed ok ! sweet..
I’m not sure how detailed or long to make each of these posts, since from reading the comments and emails, its obvious a lot of people skim or don’t read the text and look at the pictures, but I want to make sure we give enough details ( plus the wiki/svn ) so that people can recreate it. We are at the point we can pick and place our boards, surpassing the original software in some ways, a few things are left to be done to make it 100% of the original features sets, mostly to do with feeders. But we’re waaay beyond what it could do in some ways.