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 __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()
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
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