Sunday, May 26, 2013

Ultimaker style XY gantry




I've been on and off experimenting with HBot designs, there are some videos of a couple of my prototypes on YouTube here and here if your interested. The issue that arises is that the belt geometry introduces a racking force that if the design is not extremely rigid causes crosstalk in the motion as the carriage moves across the bar in the H.

The Ultimaker design has many of the same advantages, notably static motors resulting in low moving mass,  the above video was a simple X/Y stage I put together so I could better understand the pros and cons.

The obvious con with the Ultimaker design is that the outside travel rods also rotate, so you can'y use LM8UU style bearings there. I'm using 1 inch long bronze bushings on 5/16 drill rod. Again I can't use 8mm rods because an 8mm precision rod won't fit in the 8mm hole of the 608 bearings I'm using to support the rod. The rods for the cross piece are 8mm and on them I'm using LM8UUL bearings since they do not rotate.

There is surprisingly little play in the design, the coupled belts remove almost all of slop.
The design is however extremely sensitive to alignment, towards the end of the video above you can hear one of the bushings vibrating, that's caused by the cross piece being very slightly askew resulting in increased static friction on the bushing. I fixed it just after shooting the video by loosening two of the pulleys and adjusting the alignment.

I have a couple of other issues in this prototype, the printed corner pieces aren't really rigid enough and it needs something to stop the 7/16th's rod moving in the 608 bearings, but it's pretty promising.

I have some parts here for 4th HBot prototype, my intent is to use the CoreXY mechanics this time, it looks like it resolves the issue with unwanted racking forces. So it should make an interesting comparison. I just need to make some time and finish the design.


Saturday, May 4, 2013

ARM motion controller Part 2

Ok so we don't want to solve a quadratic equation every time we make a step.

We want all of the moving axis to be move in a synchronized way, and be able to implement acceleration on top of that with a minimal additional cost.

The idea here is to decouple the acceleration from the linear motion, if we pick a fixed step rate and quantize all of the axis motion to that rate, then we could just treat the motion itself as linear and then change that rate independently to implement acceleration.

The nice thing about this is that acceleration calculation  is completely decoupled from the step interrupt, this is nice because the acceleration calculation still involves a divide which isn't cheap on the microcontroller.


The requirement of all the axis being synchronized is now also easy, the problem become analogous to a simple line drawing algorithm, it just happens to be in N dimensions which as we'll see later doesn't actually make it any more complicated.


So what should we select as our initial "fixed step rate"?

It must be high enough so that you are not required to do more than one step on any given axis per interrupt.
We could pick some arbitrarily high number, but this would result in interrupts where no steps are emitted and that would limit our maximum speed. And starve the rest of the system of CPU resources.
Instead it's better to pick the longest axis in our multidimensional line, compute a step rate based on than and it's initial velocity, such that it would output exactly one step per interrupt.
We then just need to determine if for any given step on that axis we need to output a step on the other axis as well.

There are a couple of ways to approach this, we could implement a DDA with fixed or floating point math, though this would accumulate error over the length of the line because of the limited number of bits in the representation, or we could just use the Bresenham line algorithm which uses only integer math, and therefore introduces no error in addition to the necessary quantization.

I'll quickly derive the bits of the Bresenham algorithm we need here, but Wikipedia has a pretty nice description if you're interested, though it only discusses two dimensions extending it to n is trivial.

The basic DDA is something like this for a stepper driver

// Primary axis is trivially the longest one 
select a primary axis P

// Initialize
foreach axis A
    Ad = (Aend -Astart)/(Pend-Pstart)
    Apos = Astart+0.5;   //Center of the "pixel"

// Steps
foreach step in P
    foreach axis A
        Aposnew = Apos + Ad
        if (Aposnew != Apos)
            output step for this axis
        Apos = Aposnew

It's a relatively easy to realize you don't actually need to track positions, just the fractional portions of them and you end up with.


// Primary axis is trivially the longest one 
select a primary axis P

// Initialize
foreach axis A
    Ad = abs((Aend -Astart)/(Pend-Pstart))
    Aerror = 0.5;

// Steps
foreach step in P
    foreach axis A
        Aerror += Ad
        if (Aerror >= 1)
            output step for this axis
            Aerror -= 1;

Looking at the above algoritm the only error we introduce is computing Ad where we have to divide by (Pend-Pstart), so all Bresenham does is multiply everything through by that and you end up with.


// Primary axis is trivially the longest one 
select a primary axis P

// Initialize
Plength = abs(Pend-Pstart)
foreach axis A
    Ad = abs(Aend -Astart)
    Aerror = Plength >> 1;

// Steps
foreach step in P
    foreach axis A
        Aerror += Ad
        if (Aerror >= Plength)
            output step for this axis
            Aerror -= Plength

Now we can do everything with integers, no multiplies, our only divide is a divide by two which can be done as a trivial shift (the bottom bit doesn't really matter). All dropping the bottom bit in the divide by two above does is offset the concept of the pixel center very slightly. It would in fact be entirely "correct" to pick any pixel center, but picking 0.5. 0.5 results im more natural looking lines. If its something that you think is significant you can multiply everything through by two.


What's our accuracy with the new approach?
Since we're quantizing steps in the none primary axis to steps on the primary axis, we are accurate to ~1/2 a step.
In practice I think this ends up being more accurate that the original implementations, because of rounding error introduced in the original when converting velocities to frequencies.