Week 10 - Machine Design

Contents

  1. Testing Gestalt
  2. Delta Kinematics on pyGestalt
  3. Result - Foot Massage Machine

Testing Gestalt

Making Fabnet

I followed Getting Started With Gestalt Nodes, and successfully ran single node test. It does not have to fuse or program the Gestalt nodes again.

And then testing with three nodes

But, the winter is coming again… After this video, these three nodes cannot blink anymore…

In debug time, we found that the people from Fablab Leon faced same problem. I tried to fuse and program these three Gestalt nodes with Arduino.

Error messages showed up.

avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_getsync() attempt 1 of 10: not in sync: resp=0x00

I was going to check the wires, but the programmer still cannot recognize ATmega328P. So we removed the default crystal to burn them again.

It cannot work either.

However we have 4 Gestalt nodes and 3 of them cannot work, so we fused the rest of workable one which can blink and identify by computer.

Boom! It worked!

Which means 3 of them might be totally burned with their ATmega328P and not the problem with their crystals.

Delta Kinematics on pyGestalt

While debugging, I tried to write forward and reverse kinematics of hair milling (tattoo) machine. Though it looks like delta machine, it’s not parallel mechanism at all. We have to control the tool pointer and the position at the same time. So I modified the Python code in machines.py and followed Delta Robot Kinematics by Steve Graves

class kinematics():


    # Bottom               length = D
    #         /\ A          
    #        /  \           ^ x
    #       /    \          |
    #      /      \         |
    #     /   o    \        o ---- > 
    #    /          \             y  
    #   /            \
    #   --------------
    #  C              B

    #---Inherited from matrix class but not real matrix----
    class deltaForwardTransform(matrix):
        '''Customized transform from axes input vector to delta hair-milling coordinate'''
        def __init__(self, L, r, rh, Lt, D):
            self.L = L #length of rod
            self.r = r #radius of iron ball
            self.rh = rh #diameter of your head
            self.Lt = Lt #length of tool-to-ball 
            self.D = D #side length of bottom triangle

            self.Ax = math.sqrt(3)/3 * D
            self.Ay = 0

            self.Bx = -math.sqrt(3)/6 * D
            self.By = 1/2 * D

            self.Cx = -math.sqrt(3)/6 * D
            self.Cy = -1/2 * D

        def transform(self, inputVector):
            if len(inputVector) != 3: #check to make sure that vectors match
                notice(self, 'vector length mismatch')
                return False

            Az = inputVector[0]
            Bz = inputVector[1]
            Cz = inputVector[2]

            P1 = numpy.array([self.Ax, self.Ay, Az])
            P2 = numpy.array([self.Bx, self.By, Bz])
            P3 = numpy.array([self.Cx, self.Cy, Cz])

            ex = (P2 - P1)/(numpy.linalg.norm(P2 - P1))
            i = numpy.dot(ex, P3 - P1)
            ey = (P3 - P1 - i*ex)/(numpy.linalg.norm(P3 - P1 - i*ex))
            ez = numpy.cross(ex,ey)
            d = numpy.linalg.norm(P2 - P1)
            j = numpy.dot(ey, P3 - P1)

            xr = (pow((self.L+self.r),2) - pow((self.L+self.r),2) + pow(d,2))/(2*d)
            yr = ((pow((self.L+self.r),2) - pow((self.L+self.r),2) + pow(i,2) + pow(j,2))/(2*j)) - ((i/j)*x)
            zr = numpy.sqrt(pow((self.L+self.r),2) - pow(x,2) - pow(y,2))

            scale = self.rh / ( self.rh + self.Lt + self.r ) #scaling from iron ball to tool position

            x = scale * xr
            y = scale * yr
            z = scale * zr

            return [x,y]


    #---Inherited from matrix class but not real matrix----
    class deltaReverseTransform(matrix):
        '''Customized transform from delta hair-milling coordinate to axes output vector'''
        def __init__(self, L, r, rh, Lt, D):
            self.L = L #length of rod
            self.r = r #radius of iron ball
            self.rh = rh #radius of your head
            self.Lt = Lt #length of tool
            self.D = D #side length of bottom triangle

            self.Ax = math.sqrt(3)/3 * D
            self.Ay = 0

            self.Bx = -math.sqrt(3)/6 * D
            self.By = 1/2 * D

            self.Cx = -math.sqrt(3)/6 * D
            self.Cy = -1/2 * D

        def transform(self, inputVector):
            # Only x,y allowed within inputVector
            if len(inputVector) != 2: #check to make sure that vectors match
                notice(self, 'vector length mismatch')
                return False

            x = inputVector[0]
            y = inputVector[1]
            z = math.sqrt(r**2 - x**2 - y**2)

            scale = ( self.rh + self.Lt + self.r ) / self.rh #scaling from tool position to iron ball position

            xr = scale * x
            yr = scale * y
            zr = scale * z

            Az = zr - math.sqrt( (self.L+self.r)**2 - (xr-self.Ax)**2 - (yr-self.Ay)**2 )
            Bz = zr - math.sqrt( (self.L+self.r)**2 - (xr-self.Bx)**2 - (yr-self.By)**2 )
            Cz = zr - math.sqrt( (self.L+self.r)**2 - (xr-self.Cx)**2 - (yr-self.Cy)**2 )

            return [Az, Bz, Cz]



    # Testing......
    class hair_milling_delta(transform):
        def __init__(self, L, r, Lt, D):
            self.forwardMatrix = kinematics.deltaForwardTransform(L, r, rh, Lt, D)
            self.reverseMatrix = kinematics.deltaReverseTransform(L, r, rh, Lt, D)

And added these to main file hair-milling.py so that we can input raw data of 2D shape and the kinematics will map to ball coordinate (your head).

def initKinematics(self):
        self.xAxis = elements.elementChain.forward([elements.microstep.forward(4), elements.stepper.forward(1.8), elements.leadscrew.forward(8), elements.invert.forward(True)])
        self.yAxis = elements.elementChain.forward([elements.microstep.forward(4), elements.stepper.forward(1.8), elements.leadscrew.forward(8), elements.invert.forward(False)])
        self.zAxis = elements.elementChain.forward([elements.microstep.forward(4), elements.stepper.forward(1.8), elements.leadscrew.forward(8), elements.invert.forward(False)])

        self.stageKinematics = kinematics.hair_milling_delta()

When it was all set, we just found out 3 out of 4 Gestalt nodes were definitely broken and cannot fuse.

So, in the end, we got rid of these code and also Gestalt. The winter is coming again and again…

Result - Foot Massage Machine

We looked forward and used RAMPS 1.4 with Marlin from Reprap community to control our machine. And then I just figured out that I could not customise the kinematics to mill hair. After that, we decided to make a “foot massage machine”, which could not have to use tool platform!

In the future, it is able to improve by adding foot "fixture" in this machine and new feature of detecting the size of foot.

Here is our video