Example #1
0
    def __init__(self, portnumber):
        """
        @param portnumber: serial port number. check your os-specific serial.py file for details 
        """
        #error tolerance for positioning axes (inches)
        self.movtol = 0.0001
        self.portnumber = portnumber
        self.baudrate = 19200
        #in seconds
        self.sertimeout = None

        self.virtualmachine = Machine()
        self.gui = None

        self.board = EventQueue(self._finished_milling_board_callback)
        self.one_offs = EventQueue()

        self.board.pause()
        self.one_offs.go()

        self.board.setName("Board Queue Thread")
        self.one_offs.setName("One Offs Thread")

        # jogging move parameters
        self.PEN_STATES = {'up': 'up', 'down': 'down', 'cut': 'cut'}
        self.pen_state = self.PEN_STATES['up']
        self.pen_up_z = 0.05
        self.pen_down_z = -0.005
        self.pen_cut_z = -0.010  # ??
        self.move_amount = 0.1
        self.traverse_speed = 8.0
        self.retract_speed = 8.0
        self.cutting_speed = 4.0
        self.plunge_speed = 4.0

        # start event queues
        self.board.start()
        self.one_offs.start()
Example #2
0
 def __init__(self, portnumber):
     """
     @param portnumber: serial port number. check your os-specific serial.py file for details 
     """
     #error tolerance for positioning axes (inches)
     self.movtol = 0.0001
     self.portnumber = portnumber
     self.baudrate = 19200
     #in seconds
     self.sertimeout = None
     
     self.virtualmachine = Machine()
     self.gui = None
     
     self.board = EventQueue(self._finished_milling_board_callback)
     self.one_offs = EventQueue()
     
     self.board.pause()
     self.one_offs.go()
     
     self.board.setName("Board Queue Thread")
     self.one_offs.setName("One Offs Thread")
     
     # jogging move parameters
     self.PEN_STATES = {'up':'up', 'down':'down', 'cut':'cut'}
     self.pen_state = self.PEN_STATES['up']
     self.pen_up_z = 0.05
     self.pen_down_z = -0.005
     self.pen_cut_z = -0.010 # ??
     self.move_amount = 0.1
     self.traverse_speed = 8.0
     self.retract_speed = 8.0
     self.cutting_speed = 4.0
     self.plunge_speed = 4.0
     
     # start event queues
     self.board.start()
     self.one_offs.start()
Example #3
0
class Controller(object):
    """
    this space intentionally left blank
    """
    def __init__(self, portnumber):
        """
        @param portnumber: serial port number. check your os-specific serial.py file for details 
        """
        #error tolerance for positioning axes (inches)
        self.movtol = 0.0001
        self.portnumber = portnumber
        self.baudrate = 19200
        #in seconds
        self.sertimeout = None

        self.virtualmachine = Machine()
        self.gui = None

        self.board = EventQueue(self._finished_milling_board_callback)
        self.one_offs = EventQueue()

        self.board.pause()
        self.one_offs.go()

        self.board.setName("Board Queue Thread")
        self.one_offs.setName("One Offs Thread")

        # jogging move parameters
        self.PEN_STATES = {'up': 'up', 'down': 'down', 'cut': 'cut'}
        self.pen_state = self.PEN_STATES['up']
        self.pen_up_z = 0.05
        self.pen_down_z = -0.005
        self.pen_cut_z = -0.010  # ??
        self.move_amount = 0.1
        self.traverse_speed = 8.0
        self.retract_speed = 8.0
        self.cutting_speed = 4.0
        self.plunge_speed = 4.0

        # start event queues
        self.board.start()
        self.one_offs.start()

    def _finished_milling_board_callback(self):
        self.board.pause()
        self.one_offs.go()

    def is_pen_up(self):
        return self.pen_state == self.PEN_STATES['up']

    def is_pen_down(self):
        return self.pen_state == self.PEN_STATES['down']

    def is_pen_cutting(self):
        return self.pen_state == self.PEN_STATES['cut']

    def set_gui(self, gui):
        """
        Set self's gui field. This should be part of the constructor, but because GUI's constructor
        also takes a Controller, we need a set_gui method to resolve the catch-22.
        
        If set_gui is never called, that's ok. There will simply be no gui.
        If set_gui is called twice, self.gui is overwritten
        
        @param gui: a GUI instance 
        """
        self.gui = gui

    def _movegen(self, moveto):
        """
        @param moveto: 
        @return: delta
        """
        #ensures that error always starts out larger than the tolerance
        error = self.movtol * 2 * numpy.ones(len(moveto))
        #copies machine position to hypothetical position
        position = self.virtualmachine.position
        delta = numpy.zeros(len(moveto))

        while max(error) > self.movtol:
            error = moveto - position
            travel = self.virtualmachine.move(error)
            position = position + travel
            delta = delta + error

        return delta

    def _GCD(self, a, b):
        while b != 0:
            (a, b) = (b, a % b)
        return a

    def _stepgen(self, traverse, rate):
        """
        @param traverse: 
        @param rate: in in/min
        
        @TODO Currently only works propertly with 1 and 2 axis movement. Simultaneous
        movement in 3 axes is buggy. "This implies that error mapping won't work yet."
        """
        # convert to inches per second
        rate = rate / 60.
        # this flag gets set if a no-move condition is triggered
        nomove = 0
        distancesquaredsum = 0
        clockspeeds = numpy.zeros(self.virtualmachine.numberofaxes)
        prescalars = numpy.zeros(self.virtualmachine.numberofaxes)
        stepsizes = numpy.zeros(self.virtualmachine.numberofaxes)
        softwarecountersize = numpy.zeros(self.virtualmachine.numberofaxes)
        hardwarecountersize = numpy.zeros(self.virtualmachine.numberofaxes)

        for i in range(self.virtualmachine.numberofaxes):
            distancesquaredsum = distancesquaredsum + (traverse[i] *
                                                       traverse[i])
            clockspeeds[i] = self.virtualmachine.motorcontrollers[i].clockspeed
            prescalars[i] = self.virtualmachine.motorcontrollers[i].prescalar
            stepsizes[i] = self.virtualmachine.motorcontrollers[i].stepsize
            softwarecountersize[i] = self.virtualmachine.motorcontrollers[
                i].softwarecountersize
            hardwarecountersize[i] = self.virtualmachine.motorcontrollers[
                i].hardwarecountersize

        distance = math.sqrt(distancesquaredsum)  #Euclidean distance of move
        movetime = distance / rate  #Duration of move in seconds
        if settings.LOG: print "MOVETIME", movetime

        scaledclock = clockspeeds / prescalars  #Motor controller clock speeds (ticks / second)

        steps = traverse / stepsizes  #number of steps needed
        steps = numpy.round(steps)  #convert steps into integers
        absteps = numpy.abs(steps)  #absolute step values

        movingaxes = numpy.nonzero(steps)[0]  #isolates only the moving axes
        movingsteps = numpy.take(steps, movingaxes)
        absmovingsteps = numpy.take(absteps, movingaxes)
        counter_durations = numpy.zeros(len(steps))

        directions = movingsteps / absmovingsteps  #-1 = reverse, 1 = forward

        if len(movingsteps) > 2:
            if settings.LOG:
                print "3+ AXIS SIMULTANEOUS MOVES NOT SUPPORTED BY THIS STEP GENERATOR"

        if len(movingsteps) != 0:
            nomove = 0

            if len(movingsteps) == 2:
                gcd = self._GCD(absmovingsteps[0], absmovingsteps[1])
                gcd_movingsteps = absmovingsteps / gcd

                # flip gcd_movingsteps
                moving_durations = gcd_movingsteps[::-1]
                overall_duration = absmovingsteps[0] * moving_durations[0]

            else:  # len == 1
                moving_durations = [1]
                overall_duration = absmovingsteps[0]

            maxsteps = max(absmovingsteps)
            stepinterval = overall_duration / absmovingsteps

            softwarecounterrange = (softwarecountersize / maxsteps).astype(int)
            hardwarecounterrange = numpy.ones(
                self.virtualmachine.numberofaxes) * min(hardwarecountersize)

            # movetime / number of sw counter ticks = time per click
            neededtimeperpulse = movetime / overall_duration

            prehwcounterpulsetime = prescalars / clockspeeds

            hardwarecounterstemp = numpy.ceil(neededtimeperpulse /
                                              prehwcounterpulsetime)
            hardwarecountersovfl = numpy.ceil(hardwarecounterstemp /
                                              hardwarecounterrange)

            softwarecounters = numpy.min(
                [softwarecounterrange, hardwarecountersovfl], axis=0)
            hardwarecounters = numpy.ceil(
                neededtimeperpulse /
                (prehwcounterpulsetime * softwarecounters))

            durations = numpy.zeros(self.virtualmachine.numberofaxes)
            numpy.put(durations, movingaxes, stepinterval)

            numpy.put(counter_durations, movingaxes, moving_durations)

            counter_durations = counter_durations * softwarecounters
            overall_duration = overall_duration * softwarecounters[
                0]  #this is a hack for now

            directions2 = numpy.zeros(self.virtualmachine.numberofaxes)
            numpy.put(directions2, movingaxes, directions)

            for i in range(self.virtualmachine.numberofaxes):
                self.virtualmachine.motorcontrollers[
                    i].hardwarecounter = hardwarecounters[i]
                self.virtualmachine.motorcontrollers[
                    i].softwarecounter = counter_durations[i]
                self.virtualmachine.motorcontrollers[
                    i].duration = overall_duration
                if directions2[i] == -1:
                    directions2[i] = 2
                self.virtualmachine.motorcontrollers[
                    i].direction = directions2[i]
        else:
            nomove = 1
            for i in range(self.virtualmachine.numberofaxes):
                self.virtualmachine.motorcontrollers[i].hardwarecounter = 0
                self.virtualmachine.motorcontrollers[i].softwarecounter = 0
                self.virtualmachine.motorcontrollers[i].duration = 0
                self.virtualmachine.motorcontrollers[i].direction = 0
            return nomove

    def _xmit(self):
        """
        Constructs and sends packet over serial port.
        
        Packet contains instructions for different speeds for each of the 
        three motors, as well as a single instruction duration.
        
        Blocking on receiving acknowledge. @TODO implement time_out with x retries
        before raising an exception.
        
        packet format:
            byte0 - Start Byte
            byte1 - hwcounter
            byte2 - AXIS 1 Rate 0
            byte3 - AXIS 1 Rate 1
            byte4 - AXIS 2 Rate 0
            byte5 - AXIS 2 Rate 1
            byte6 - AXIS 3 Rate 0
            byte7 - AXIS 3 Rate 1
            byte8 - move duration 0
            byte9 - move duration 1
            byte10 - move duration 2
            byte11 - move duration 3
            byte12 - sync / motor direction (00ZrZfYrYfXrXf) where r is reverse and f is forward
        
        @TODO add byte13 - checksum
        
        @return: outgoing packet
        @raise serial.SerialException: If cannot open serial port and in use-serial mode (USE_SERIAL is True)
        """
        packetlength = 13
        xmitteraxes = self.virtualmachine.numberofaxes
        outgoing = numpy.zeros(packetlength)
        outgoing[0] = 255
        outgoing[1] = self.virtualmachine.motorcontrollers[0].hardwarecounter
        for i in range(xmitteraxes):
            outgoing[i * 2 + 2] = int(
                self.virtualmachine.motorcontrollers[i].softwarecounter % 256)
            outgoing[i * 2 + 3] = int(
                self.virtualmachine.motorcontrollers[i].softwarecounter / 256)
            outgoing[12] = outgoing[12] + self.virtualmachine.motorcontrollers[
                i].direction * (4**i)

        duration = self.virtualmachine.motorcontrollers[0].duration

        outgoingindex = 11
        remainder = duration
        for i in range(4):
            outgoing[11 - i] = int(remainder / 256**(3 - i))
            remainder = remainder % 256**(3 - i)

        try:
            # open serial port
            serport = serial.Serial(self.portnumber,
                                    self.baudrate,
                                    timeout=self.sertimeout)

            # send command
            for i in range(len(outgoing)):
                serport.write(chr(outgoing[i]))
                a = serport.read()

            start = time.time()
            serport.read()
            if settings.LOG: print "XINT TIME", time.time() - start

        except serial.SerialException, details:
            if not settings.USE_SERIAL:
                pass
            else:
                print "\nEXCEPTION RAISED:", details
                sys.exit(0)

        return outgoing
Example #4
0
class Controller(object):
    """
    this space intentionally left blank
    """
    
    def __init__(self, portnumber):
        """
        @param portnumber: serial port number. check your os-specific serial.py file for details 
        """
        #error tolerance for positioning axes (inches)
        self.movtol = 0.0001
        self.portnumber = portnumber
        self.baudrate = 19200
        #in seconds
        self.sertimeout = None
        
        self.virtualmachine = Machine()
        self.gui = None
        
        self.board = EventQueue(self._finished_milling_board_callback)
        self.one_offs = EventQueue()
        
        self.board.pause()
        self.one_offs.go()
        
        self.board.setName("Board Queue Thread")
        self.one_offs.setName("One Offs Thread")
        
        # jogging move parameters
        self.PEN_STATES = {'up':'up', 'down':'down', 'cut':'cut'}
        self.pen_state = self.PEN_STATES['up']
        self.pen_up_z = 0.05
        self.pen_down_z = -0.005
        self.pen_cut_z = -0.010 # ??
        self.move_amount = 0.1
        self.traverse_speed = 8.0
        self.retract_speed = 8.0
        self.cutting_speed = 4.0
        self.plunge_speed = 4.0
        
        # start event queues
        self.board.start()
        self.one_offs.start()
    
    def _finished_milling_board_callback(self):
        self.board.pause()
        self.one_offs.go()

    def is_pen_up(self): return self.pen_state == self.PEN_STATES['up']
    def is_pen_down(self): return self.pen_state == self.PEN_STATES['down']
    def is_pen_cutting(self): return self.pen_state == self.PEN_STATES['cut']
    
    def set_gui(self, gui):
        """
        Set self's gui field. This should be part of the constructor, but because GUI's constructor
        also takes a Controller, we need a set_gui method to resolve the catch-22.
        
        If set_gui is never called, that's ok. There will simply be no gui.
        If set_gui is called twice, self.gui is overwritten
        
        @param gui: a GUI instance 
        """
        self.gui = gui
    
    def _movegen(self, moveto):
        """
        @param moveto: 
        @return: delta
        """
        #ensures that error always starts out larger than the tolerance
        error = self.movtol*2*numpy.ones(len(moveto))
        #copies machine position to hypothetical position
        position = self.virtualmachine.position
        delta = numpy.zeros(len(moveto))
        
        while max(error) > self.movtol:
            error = moveto - position
            travel = self.virtualmachine.move(error)
            position=position + travel
            delta = delta + error
                
        return delta

    def _GCD(self, a, b):
        while b != 0:
            (a, b) = (b, a%b)
        return a

    def _stepgen(self, traverse, rate):
        """
        @param traverse: 
        @param rate: in in/min
        
        @TODO Currently only works propertly with 1 and 2 axis movement. Simultaneous
        movement in 3 axes is buggy. "This implies that error mapping won't work yet."
        """
        # convert to inches per second
        rate = rate / 60.
        # this flag gets set if a no-move condition is triggered
        nomove = 0
        distancesquaredsum = 0
        clockspeeds = numpy.zeros(self.virtualmachine.numberofaxes)
        prescalars = numpy.zeros(self.virtualmachine.numberofaxes)
        stepsizes = numpy.zeros(self.virtualmachine.numberofaxes)
        softwarecountersize = numpy.zeros(self.virtualmachine.numberofaxes)
        hardwarecountersize = numpy.zeros(self.virtualmachine.numberofaxes)
        
        for i in range(self.virtualmachine.numberofaxes):
            distancesquaredsum = distancesquaredsum + (traverse[i]*traverse[i])
            clockspeeds[i] = self.virtualmachine.motorcontrollers[i].clockspeed
            prescalars[i] = self.virtualmachine.motorcontrollers[i].prescalar
            stepsizes[i] = self.virtualmachine.motorcontrollers[i].stepsize
            softwarecountersize[i] = self.virtualmachine.motorcontrollers[i].softwarecountersize
            hardwarecountersize[i] = self.virtualmachine.motorcontrollers[i].hardwarecountersize

        distance = math.sqrt(distancesquaredsum)    #Euclidean distance of move
        movetime = distance / rate                  #Duration of move in seconds
        if settings.LOG: print "MOVETIME", movetime

        scaledclock = clockspeeds / prescalars      #Motor controller clock speeds (ticks / second)
        
        steps   = traverse / stepsizes              #number of steps needed
        steps   = numpy.round(steps)                #convert steps into integers
        absteps = numpy.abs(steps)                  #absolute step values
        
        movingaxes        = numpy.nonzero(steps)[0] #isolates only the moving axes
        movingsteps       = numpy.take(steps, movingaxes)
        absmovingsteps    = numpy.take(absteps, movingaxes)
        counter_durations = numpy.zeros(len(steps))

        directions = movingsteps/absmovingsteps     #-1 = reverse, 1 = forward

        if len(movingsteps)>2:
            if settings.LOG: print "3+ AXIS SIMULTANEOUS MOVES NOT SUPPORTED BY THIS STEP GENERATOR"

        if len(movingsteps) !=0:        
            nomove = 0
            
            if len(movingsteps) == 2:
                gcd = self._GCD(absmovingsteps[0], absmovingsteps[1])
                gcd_movingsteps = absmovingsteps / gcd
                
                # flip gcd_movingsteps
                moving_durations = gcd_movingsteps[::-1] 
                overall_duration = absmovingsteps[0]*moving_durations[0]
                
            else: # len == 1
                moving_durations = [1]
                overall_duration = absmovingsteps[0]
                
            maxsteps = max(absmovingsteps)
            stepinterval = overall_duration / absmovingsteps
    
            softwarecounterrange = (softwarecountersize / maxsteps).astype(int)
            hardwarecounterrange = numpy.ones(self.virtualmachine.numberofaxes)*min(hardwarecountersize)
    
            # movetime / number of sw counter ticks = time per click
            neededtimeperpulse = movetime / overall_duration
    
            prehwcounterpulsetime = prescalars / clockspeeds
    
            hardwarecounterstemp = numpy.ceil(neededtimeperpulse / prehwcounterpulsetime)
            hardwarecountersovfl = numpy.ceil(hardwarecounterstemp / hardwarecounterrange)
    
        
            softwarecounters = numpy.min([softwarecounterrange, hardwarecountersovfl], axis = 0)
            hardwarecounters = numpy.ceil(neededtimeperpulse/(prehwcounterpulsetime*softwarecounters))
    
            
            durations = numpy.zeros(self.virtualmachine.numberofaxes)
            numpy.put(durations, movingaxes, stepinterval)
            
            numpy.put(counter_durations, movingaxes, moving_durations)
    
            counter_durations = counter_durations * softwarecounters
            overall_duration = overall_duration * softwarecounters[0]   #this is a hack for now
        
            directions2 = numpy.zeros(self.virtualmachine.numberofaxes)
            numpy.put(directions2, movingaxes, directions)
    
            for i in range(self.virtualmachine.numberofaxes):
                self.virtualmachine.motorcontrollers[i].hardwarecounter = hardwarecounters[i]
                self.virtualmachine.motorcontrollers[i].softwarecounter = counter_durations[i]
                self.virtualmachine.motorcontrollers[i].duration = overall_duration
                if directions2[i] == -1:
                    directions2[i] = 2
                self.virtualmachine.motorcontrollers[i].direction = directions2[i]
        else:
            nomove = 1
            for i in range(self.virtualmachine.numberofaxes):
                    self.virtualmachine.motorcontrollers[i].hardwarecounter = 0
                    self.virtualmachine.motorcontrollers[i].softwarecounter = 0
                    self.virtualmachine.motorcontrollers[i].duration = 0
                    self.virtualmachine.motorcontrollers[i].direction = 0
            return nomove

    def _xmit(self):
        """
        Constructs and sends packet over serial port.
        
        Packet contains instructions for different speeds for each of the 
        three motors, as well as a single instruction duration.
        
        Blocking on receiving acknowledge. @TODO implement time_out with x retries
        before raising an exception.
        
        packet format:
            byte0 - Start Byte
            byte1 - hwcounter
            byte2 - AXIS 1 Rate 0
            byte3 - AXIS 1 Rate 1
            byte4 - AXIS 2 Rate 0
            byte5 - AXIS 2 Rate 1
            byte6 - AXIS 3 Rate 0
            byte7 - AXIS 3 Rate 1
            byte8 - move duration 0
            byte9 - move duration 1
            byte10 - move duration 2
            byte11 - move duration 3
            byte12 - sync / motor direction (00ZrZfYrYfXrXf) where r is reverse and f is forward
        
        @TODO add byte13 - checksum
        
        @return: outgoing packet
        @raise serial.SerialException: If cannot open serial port and in use-serial mode (USE_SERIAL is True)
        """
        packetlength = 13
        xmitteraxes = self.virtualmachine.numberofaxes
        outgoing = numpy.zeros(packetlength)
        outgoing[0] = 255
        outgoing[1] = self.virtualmachine.motorcontrollers[0].hardwarecounter
        for i in range(xmitteraxes):
            outgoing[i*2+2] = int(self.virtualmachine.motorcontrollers[i].softwarecounter % 256)
            outgoing[i*2+3] = int(self.virtualmachine.motorcontrollers[i].softwarecounter / 256)
            outgoing[12] = outgoing[12] + self.virtualmachine.motorcontrollers[i].direction*(4**i)
            
        duration = self.virtualmachine.motorcontrollers[0].duration

        outgoingindex = 11
        remainder = duration
        for i in range(4):
            outgoing[11-i] = int(remainder / 256**(3-i))
            remainder = remainder % 256**(3-i)
        

        try:
            # open serial port
            serport = serial.Serial(self.portnumber, self.baudrate, timeout=self.sertimeout)

            # send command
            for i in range(len(outgoing)):
                serport.write(chr(outgoing[i]))
                a = serport.read()
    
            start = time.time()
            serport.read()
            if settings.LOG: print "XINT TIME", time.time() - start
            
        except serial.SerialException, details:
            if not settings.USE_SERIAL:
                pass
            else:
                print "\nEXCEPTION RAISED:", details
                sys.exit(0)
            
        return outgoing