Example #1
0
    def __init__(self, steppers, pru_firmware):
        self.steppers = steppers
        self.pru = Pru(pru_firmware)  # Make the PRU
        self.paths = Queue.Queue(10)  # Make a queue of paths
        self.current_pos = {"X": 0.0, "Y": 0.0, "Z": 0.0, "E": 0.0, "H": 0.0}
        self.running = True  # Yes, we are running
        self.pru_data = []
        self.t = Thread(target=self._do_work)  # Make the thread
        self.t.daemon = True
        self.travel_length = {"X": 0.0, "Y": 0.0, "Z": 0.0}
        self.center_offset = {"X": 0.0, "Y": 0.0, "Z": 0.0}

        if __name__ != '__main__':
            self.t.start()
Example #2
0
 def __init__(self, steppers, current_pos):
     self.steppers    = steppers
     self.pru         = Pru()                                # Make the PRU
     self.paths       = Queue.Queue(30)                      # Make a queue of paths
     self.current_pos = current_pos                          # Current position in (x, y, z, e)
     self.running     = True                                 # Yes, we are running
     self.pru_data    = defaultdict(int)
     self.t           = Thread(target=self._do_work)         # Make the thread
     self.t.start()		                
Example #3
0
 def __init__(self, steppers, current_pos):
     self.steppers    = steppers
     self.pru         = Pru()                                # Make the PRU
     self.paths       = Queue.Queue(10)                      # Make a queue of paths
     self.current_pos = current_pos                          # Current position in (x, y, z, e)
     self.running     = True                                 # Yes, we are running
     self.pru_data    = []
     self.t           = Thread(target=self._do_work)         # Make the thread
     if __name__ != '__main__':
         self.t.start()		 
Example #4
0
    def __init__(self, steppers, pru_firmware):
        self.steppers    = steppers
        self.pru         = Pru(pru_firmware)                 # Make the PRU
        self.paths       = Queue.Queue(10)                      # Make a queue of paths
        self.current_pos = {"X":0.0, "Y":0.0, "Z":0.0, "E":0.0,"H":0.0}
        self.running     = True                                 # Yes, we are running
        self.pru_data    = []
        self.t           = Thread(target=self._do_work)         # Make the thread
        self.t.daemon    = True
        self.travel_length = {"X":0.0, "Y":0.0, "Z":0.0}
        self.center_offset = {"X":0.0, "Y":0.0, "Z":0.0}

        if __name__ != '__main__':
            self.t.start()		 
Example #5
0
class Path_planner:
    ''' Init the planner '''
    def __init__(self, steppers, current_pos):
        self.steppers    = steppers
        self.pru         = Pru()                                # Make the PRU
        self.paths       = Queue.Queue(10)                      # Make a queue of paths
        self.current_pos = current_pos                          # Current position in (x, y, z, e)
        self.running     = True                                 # Yes, we are running
        self.pru_data    = []
        self.t           = Thread(target=self._do_work)         # Make the thread
        if __name__ != '__main__':
            self.t.start()		 

    ''' Set the acceleration used '''                           # Fix me, move this to path
    def set_acceleration(self, acceleration):
        self.acceleration = acceleration

    ''' Add a path segment to the path planner '''        
    def add_path(self, new):   
        if not new.is_G92():     
            if hasattr(self, 'prev'):
                self.prev.set_next(new)
                new.set_prev(self.prev)
            self.prev = new        
        self.paths.put(new)

    ''' Return the number of paths currently on queue '''
    def nr_of_paths(self):
        return self.paths.qsize()

    ''' Set position for an axis '''
    def set_pos(self, axis, val):
        self.current_pos[axis] = val
	
    def wait_until_done(self):
        '''Wait until planner is done'''
        self.paths.join()
        self.pru.wait_until_done()		 

    def _do_work(self):
        """ This loop pops a path, sends it to the PRU and waits for an event """
        while self.running:       
           self.do_work()
    
    def do_work(self):
        """ This is just a separate function so the test at the bottom will pass """		
        path = self.paths.get()                            # Get the last path added
        path.set_global_pos(self.current_pos.copy())       # Set the global position of the printer

        if path.is_G92():                                   # Only set the position of the axes
            for axis, pos in path.get_pos().iteritems():                       # Run through all the axes in the path    
                self.set_pos(axis, pos)           
            self.paths.task_done()            
            return                
       
        all_data = {}
        
        for axis in path.get_axes():                       # Run through all the axes in the path    
            stepper = self.steppers[axis]                  # Get a handle of  the stepper                    
            data = self._make_data(path, axis)            
            if len(data[0]) > 0:
                if len(self.pru_data) == 0:
                    self.pru_data = zip(*data)
                else:
                    self.pru_data = self._braid_data(self.pru_data, zip(*data))
                    #self._braid_data1(self.pru_data, zip(*data))

        while len(self.pru_data) > 0:  
            data = self.pru_data[0:0x20000/8]
            del self.pru_data[0:0x20000/8]
            if len(self.pru_data) > 0:
                logging.debug("Long path segment is cut. remaining: "+str(len(self.pru_data)))       
            while not self.pru.has_capacity_for(len(data)*8):          
                #logging.debug("Pru full")              
                time.sleep(1)               
            self.pru.add_data(zip(*data))
            self.pru.commit_data()                            # Commit data to ddr
        
        self.pru_data = []
        
        self.paths.task_done()
        path.unlink()                                         # Remove reference to enable garbage collection
        path = None

    def _braid_data(self, data1, data2):
        """ Braid/merge together the data from the two data sets"""
        return braid.braid_data_c(data1, data2)
    
    def _braid_data1(self, data1, data2):
        """ Braid/merge together the data from the two data sets"""
        line = 0
        (pin1, dly1) = data1[line]
        (pin2, dly2) = data2.pop(0)
        while True: 
            dly = min(dly1, dly2)
            dly1 -= dly    
            dly2 -= dly            
            try: 
                if dly1 == 0 and dly2 == 0:
                    data1[line] = (pin1+pin2, dly)
                    (pin1, dly1) = data1[line+1]
                    (pin2, dly2) = data2.pop(0)
                elif dly1 == 0:
                    data1[line] = (pin1+pin2, dly)
                    (pin1, dly1) = data1[line+1]
                elif dly2 == 0:    
                    data1.insert(line, (pin1+pin2, dly))
                    (pin2, dly2) = data2.pop(0)
                line += 1
            except IndexError, e:
                break

        if dly2 > 0:   
            data1[line] =  (data1[line][0], data1[line][1]+dly2)        
        elif dly1 > 0:
            data1[line] = (data1[line][0], data1[line][1]+dly1)  
            data1.pop(line+1)
        
        while len(data2) > 0:
            line += 1
            (pin2, dly2) = data2.pop(0)
            data1.append((pin2+pin1, dly2))
        while len(data1) > line+1:
            line += 1
            (pin1, dly1) = data1[line]
            data1[line] = (pin2+pin1, dly1)
Example #6
0
class PathPlanner:
    ''' Init the planner '''
    def __init__(self, steppers, pru_firmware):
        self.steppers = steppers
        self.pru = Pru(pru_firmware)  # Make the PRU
        self.paths = Queue.Queue(10)  # Make a queue of paths
        self.current_pos = {"X": 0.0, "Y": 0.0, "Z": 0.0, "E": 0.0, "H": 0.0}
        self.running = True  # Yes, we are running
        self.pru_data = []
        self.t = Thread(target=self._do_work)  # Make the thread
        self.t.daemon = True
        self.travel_length = {"X": 0.0, "Y": 0.0, "Z": 0.0}
        self.center_offset = {"X": 0.0, "Y": 0.0, "Z": 0.0}

        if __name__ != '__main__':
            self.t.start()

    ''' Set travel length (printer size) for all axis '''

    def set_travel_length(self, travel):
        self.travel_length = travel

    ''' Set offset of printer center for all axis '''

    def set_center_offset(self, offset):
        self.center_offset = offset

    ''' Set the acceleration used '''  # Fix me, move this to path

    def set_acceleration(self, acceleration):
        self.acceleration = acceleration

    ''' Home the given axis using endstops (min) '''

    def home(self, axis):

        if axis == "E" or axis == "H":
            return

        logging.debug("homing " + axis)

        p = Path({axis: -self.travel_length[axis]}, 0.01, "RELATIVE", False,
                 False)
        p.set_homing_feedrate()

        #p.set_max_speed(p.get_max_speed()*0.2)
        self.add_path(p)
        path = Path({axis: -self.center_offset[axis]}, 0.01, "G92")
        self.add_path(path)
        p = Path({axis: 0}, 0.01, "ABSOLUTE")
        p.set_homing_feedrate()
        #p.set_max_speed(p.get_max_speed()*0.2)
        self.add_path(p)
        self.wait_until_done()
        logging.debug("homing done for " + axis)

    ''' Add a path segment to the path planner '''

    def add_path(self, new):
        if not new.is_G92():
            if hasattr(self, 'prev'):
                self.prev.set_next(new)
                new.set_prev(self.prev)
            self.prev = new
        self.paths.put(new)

    ''' Return the number of paths currently on queue '''

    def nr_of_paths(self):
        return self.paths.qsize()

    ''' Set position for an axis '''

    def _set_pos(self, axis, val):
        self.current_pos[axis] = val

    def wait_until_done(self):
        '''Wait until planner is done'''
        self.paths.join()
        self.pru.wait_until_done()

    def _do_work(self):
        """ This loop pops a path, sends it to the PRU and waits for an event """
        while self.running:
            self.do_work()

    def do_work(self):
        """ This is just a separate function so the test at the bottom will pass """
        path = self.paths.get()  # Get the last path added
        path.set_global_pos(
            self.current_pos.copy())  # Set the global position of the printer

        if path.is_G92():  # Only set the position of the axes
            for axis, pos in path.get_pos().iteritems(
            ):  # Run through all the axes in the path
                self._set_pos(axis, pos)
            self.paths.task_done()
            return

        for axis in path.get_axes():  # Run through all the axes in the path
            data = self._make_data(path, axis)
            if len(data[0]) > 0:
                if len(self.pru_data) == 0:
                    self.pru_data = zip(*data)
                else:
                    #self._braid_data1(self.pru_data, zip(*data))
                    self.pru_data = self._braid_data(self.pru_data, zip(*data))
        while len(self.pru_data) > 0:
            data = self.pru_data[0:0x10000 / 8]
            del self.pru_data[0:0x10000 / 8]
            if len(self.pru_data) > 0:
                logging.debug("Long path segment is cut. remaining: " +
                              str(len(self.pru_data)))
            while not self.pru.has_capacity_for(len(data) * 8):
                #logging.debug("Pru full")
                time.sleep(0.5)
            self.pru.add_data(zip(*data))
            self.pru.commit_data()  # Commit data to ddr

        self.pru_data = []

        self.paths.task_done()
        path.unlink()  # Remove reference to enable garbage collection

    def emergency_interrupt(self):
        self.pru.emergency_interrupt()
        while True:
            try:
                path = self.paths.get(block=False)
                if path != None:
                    self.paths.task_done()
                    path.unlink()
            except Queue.Empty:
                break

    def _braid_data(self, data1, data2):
        """ Braid/merge together the data from the two data sets"""
        return braid.braid_data_c(
            data1, data2)  # Use the Optimized C-function foir this.

    def _braid_data1(self, data1, data2):
        """ Braid/merge together the data from the two data sets"""
        line = 0
        (pin1, dir1, o1, dly1) = data1[line]
        (pin2, dir2, o2, dly2) = data2.pop(0)
        while True:
            dly = min(dly1, dly2)
            dly1 -= dly
            dly2 -= dly
            try:
                if dly1 == 0 and dly2 == 0:
                    data1[line] = (pin1 | pin2, dir1 | dir2, o1 | o2, dly)
                    (pin1, dir1, o1, dly1) = data1[line + 1]
                    (pin2, dir2, o2, dly2) = data2.pop(0)
                elif dly1 == 0:
                    data1[line] = (pin1, dir1, o1, dly)
                    (pin1, dir1, o1, dly1) = data1[line + 1]
                elif dly2 == 0:
                    data1.insert(line, (pin2, dir2, o2, dly))
                    (pin2, dir2, o2, dly2) = data2.pop(0)
                line += 1
            except IndexError:
                break

        if dly2 > 0:
            #data1[line] =  (data1[line][0],data1[line][1],data1[line][2], data1[line][3]+dly2)
            data1.append((pin2, dir2, o2, dly2))
            line += 1
        elif dly1 > 0:
            data1[line] = (data1[line][0], data1[line][1], data1[line][2],
                           data1[line][3] + dly1)
            #data1.pop(line+1)

        while len(data2) > 0:
            line += 1
            (pin2, dir2, o2, dly2) = data2.pop(0)
            data1.append((pin2, dir2, o2, dly2))
        #while len(data1) > line+1:
        #    line += 1
        #    (pin1, dir1,o1, dly1) = data1[line]
        #    data1[line] = (pin2|pin1,dir1 | dir2,o1 | o2, dly1)

    ''' Join the thread '''

    def exit(self):
        self.running = False
        self.pru.join()
        self.t.join()

    def force_exit(self):
        self.running = False
        self.pru.force_exit()

    ''' Make the data for the PRU or steppers '''

    def _make_data(self, path, axis):
        stepper = self.steppers[axis]
        steps_pr_meter = stepper.get_steps_pr_meter()
        vec = path.get_axis_length(axis)  # Total travel distance
        num_steps = int(abs(vec) * steps_pr_meter)  # Number of steps to tick
        if num_steps == 0:
            return ([], [])
        step_pin = stepper.get_step_pin()  # Get the step pin
        dir_pin = stepper.get_dir_pin()  # Get the direction pin
        #if stepper.get_direction() > 0:
        dir_pin = 0 if vec < 0 else dir_pin  # Disable the dir-pin if we are going backwards
        #else:
        #    dir_pin     = 0 if vec >= 0 else dir_pin
        step_pins = [step_pin] * num_steps  # Make the pin states
        dir_pins = [dir_pin] * num_steps
        option_pins = [1 if path.is_cancellable() else 0] * num_steps

        s = abs(path.get_axis_length(axis))  # Get the length of the vector
        ratio = path.get_axis_ratio(
            axis)  # Ratio is the length of this axis to the total length

        Vm = path.get_max_speed() * ratio  # The travelling speed in m/s
        a = self.acceleration * ratio  # Accelleration in m/s/s
        ds = 1.0 / steps_pr_meter  # Delta S, distance in meters travelled pr step.

        #logging.debug('Start speed '+str(path.get_start_speed()))
        #logging.debug('End speed '+str(path.get_end_speed()))

        if path.is_type_print_segment(
        ):  # If there is currently a segment being processed,
            u_start = ratio * path.get_start_speed(
            )  # The end speed, depends on the angle to the next
        else:
            u_start = 0
        if path.is_type_print_segment(
        ):  # If there are paths in queue, we might not have to slow down
            u_end = ratio * path.get_end_speed(
            )  # The start speed. Depends on the angle to the prev.
        else:
            u_end = 0

        tm_start = (
            Vm - u_start) / a  # Calculate the time for when max speed is met.
        tm_end = (Vm -
                  u_end) / a  # Calculate the time for when max speed is met.
        sm_start = min(
            u_start * tm_start + 0.5 * a * tm_start**2,
            s / 2.0)  # Calculate the distance traveled when max speed is met
        sm_end = min(
            u_end * tm_end + 0.5 * a * tm_end**2,
            s / 2.0)  # Calculate the distance traveled when max speed is met

        distances_start = np.arange(0, sm_start, ds)  # Table of distances
        distances_end = np.arange(0, sm_end, ds)  # Table of distances

        timestamps_start = (-u_start +
                            np.sqrt(2.0 * a * distances_start +
                                    u_start**2)) / a  # When ticks occur
        timestamps_end = (-u_end + np.sqrt(2.0 * a * distances_end +
                                           u_end**2)) / a  # When ticks occur

        delays_start = np.diff(
            timestamps_start
        ) / 2.0  # We are more interested in the delays pr second.
        delays_end = np.diff(
            timestamps_end
        ) / 2.0  # We are more interested in the delays pr second.
        #delays_start     = np.array([delays_start, delays_start]).transpose().flatten()
        #delays_end       = np.array([delays_end, delays_end]).transpose().flatten()

        i_steps = num_steps - len(delays_start) - len(
            delays_end)  # Find out how many delays are missing
        i_delays = [(ds / Vm) / 2.0] * i_steps  # Make the intermediate steps
        delays = np.concatenate(
            [delays_start, i_delays,
             np.flipud(delays_end)])  # Add the missing delays.
        td = num_steps / steps_pr_meter  # Calculate the actual travelled distance
        if vec < 0:  # If the vector is negative, negate it.
            td *= -1.0

        # If the axes are X or Y, we need to transform back in case of
        # H-belt or some other transform.
        if axis == "X" or axis == "Y":
            (td_x, td_y) = path.stepper_to_axis(td, axis)
            self.current_pos["X"] += td_x
            self.current_pos["Y"] += td_y
        else:
            self.current_pos[axis] += td  # Update the global position vector

        return (step_pins, dir_pins, option_pins, delays
                )  # return the pin states and the data
Example #7
0
class PathPlanner:
    ''' Init the planner '''
    def __init__(self, steppers, pru_firmware):
        self.steppers    = steppers
        self.pru         = Pru(pru_firmware)                 # Make the PRU
        self.paths       = Queue.Queue(10)                      # Make a queue of paths
        self.current_pos = {"X":0.0, "Y":0.0, "Z":0.0, "E":0.0,"H":0.0}
        self.running     = True                                 # Yes, we are running
        self.pru_data    = []
        self.t           = Thread(target=self._do_work)         # Make the thread
        self.t.daemon    = True
        self.travel_length = {"X":0.0, "Y":0.0, "Z":0.0}
        self.center_offset = {"X":0.0, "Y":0.0, "Z":0.0}

        if __name__ != '__main__':
            self.t.start()		 


    ''' Set travel length (printer size) for all axis '''  
    def set_travel_length(self, travel):
        self.travel_length = travel

    ''' Set offset of printer center for all axis '''  
    def set_center_offset(self, offset):
        self.center_offset = offset

    ''' Set the acceleration used '''                           # Fix me, move this to path
    def set_acceleration(self, acceleration):
        self.acceleration = acceleration

    ''' Home the given axis using endstops (min) '''
    def home(self,axis):

        if axis=="E" or axis=="H":
            return

        logging.debug("homing "+axis)
        
        p = Path({axis:-self.travel_length[axis]}, 0.01, "RELATIVE", False, False)
        p.set_homing_feedrate()

        #p.set_max_speed(p.get_max_speed()*0.2)
        self.add_path(p)
        path = Path({axis:-self.center_offset[axis]}, 0.01, "G92")
        self.add_path(path)  
        p = Path({axis:0}, 0.01, "ABSOLUTE")
        p.set_homing_feedrate()
        #p.set_max_speed(p.get_max_speed()*0.2)
        self.add_path(p)
        self.wait_until_done()
        logging.debug("homing done for "+axis)

    ''' Add a path segment to the path planner '''        
    def add_path(self, new):   
        if not new.is_G92():     
            if hasattr(self, 'prev'):
                self.prev.set_next(new)
                new.set_prev(self.prev)
            self.prev = new        
        self.paths.put(new)

    ''' Return the number of paths currently on queue '''
    def nr_of_paths(self):
        return self.paths.qsize()

    ''' Set position for an axis '''
    def _set_pos(self, axis, val):
        self.current_pos[axis] = val

    def wait_until_done(self):
        '''Wait until planner is done'''
        self.paths.join()
        self.pru.wait_until_done()		 

    def _do_work(self):
        """ This loop pops a path, sends it to the PRU and waits for an event """
        while self.running:       
           self.do_work()
    
    def do_work(self):
        """ This is just a separate function so the test at the bottom will pass """		
        path = self.paths.get()                            # Get the last path added
        path.set_global_pos(self.current_pos.copy())       # Set the global position of the printer

        if path.is_G92():                                   # Only set the position of the axes
            for axis, pos in path.get_pos().iteritems():                       # Run through all the axes in the path    
                self._set_pos(axis, pos)           
            self.paths.task_done()            
            return                
        
        for axis in path.get_axes():                       # Run through all the axes in the path                   
            data = self._make_data(path, axis)        
            if len(data[0]) > 0:
                if len(self.pru_data) == 0:
                    self.pru_data = zip(*data)
                else:
                    #self._braid_data1(self.pru_data, zip(*data))
                    self.pru_data = self._braid_data(self.pru_data, zip(*data))
        while len(self.pru_data) > 0:  
            data = self.pru_data[0:0x10000/8]
            del self.pru_data[0:0x10000/8]
            if len(self.pru_data) > 0:
                logging.debug("Long path segment is cut. remaining: "+str(len(self.pru_data)))       
            while not self.pru.has_capacity_for(len(data)*8):          
                #logging.debug("Pru full")              
                time.sleep(0.5)               
            self.pru.add_data(zip(*data))
            self.pru.commit_data()                            # Commit data to ddr
        
        self.pru_data = []
        
        self.paths.task_done()
        path.unlink()                                         # Remove reference to enable garbage collection

    def emergency_interrupt(self):
        self.pru.emergency_interrupt()
        while True:
            try:
                path = self.paths.get(block=False)
                if path != None:
                    self.paths.task_done()
                    path.unlink()
            except Queue.Empty:
                break

    def _braid_data(self, data1, data2):
        """ Braid/merge together the data from the two data sets"""
        return braid.braid_data_c(data1, data2)               # Use the Optimized C-function foir this. 
    
    def _braid_data1(self, data1, data2):
        """ Braid/merge together the data from the two data sets"""
        line = 0
        (pin1,dir1,o1, dly1) = data1[line]
        (pin2,dir2,o2, dly2) = data2.pop(0)
        while True: 
            dly = min(dly1, dly2)
            dly1 -= dly    
            dly2 -= dly            
            try: 
                if dly1==0 and dly2==0:
                    data1[line] = (pin1|pin2, dir1 | dir2,o1 | o2, dly)
                    (pin1,dir1,o1, dly1) = data1[line+1]
                    (pin2,dir2,o2, dly2) = data2.pop(0)
                elif dly1==0:
                    data1[line] = (pin1, dir1 ,o1 , dly)
                    (pin1,dir1,o1, dly1) = data1[line+1]
                elif dly2==0:    
                    data1.insert(line, (pin2, dir2, o2, dly))
                    (pin2,dir2,o2, dly2) = data2.pop(0)
                line += 1
            except IndexError:
                break

        if dly2 > 0:   
            #data1[line] =  (data1[line][0],data1[line][1],data1[line][2], data1[line][3]+dly2) 
            data1.append((pin2, dir2,o2, dly2)) 
            line += 1      
        elif dly1 > 0:
            data1[line] = (data1[line][0], data1[line][1],data1[line][2], data1[line][3]+dly1)  
            #data1.pop(line+1)
        
        while len(data2) > 0:
            line += 1
            (pin2,dir2,o2, dly2) = data2.pop(0)
            data1.append((pin2, dir2,o2, dly2))
        #while len(data1) > line+1:
        #    line += 1
        #    (pin1, dir1,o1, dly1) = data1[line]
        #    data1[line] = (pin2|pin1,dir1 | dir2,o1 | o2, dly1)

    ''' Join the thread '''
    def exit(self):
        self.running = False
        self.pru.join()
        self.t.join()


    def force_exit(self):
        self.running = False
        self.pru.force_exit()

    ''' Make the data for the PRU or steppers '''
    def _make_data(self, path, axis):  
        stepper         = self.steppers[axis]
        steps_pr_meter  = stepper.get_steps_pr_meter()
        vec             = path.get_axis_length(axis)                        # Total travel distance
        num_steps       = int(abs(vec) * steps_pr_meter)                    # Number of steps to tick
        if num_steps == 0:
            return ([], [])
        step_pin    = stepper.get_step_pin()                            # Get the step pin
        dir_pin     = stepper.get_dir_pin()                             # Get the direction pin
        #if stepper.get_direction() > 0:
        dir_pin     = 0 if vec < 0 else dir_pin                         # Disable the dir-pin if we are going backwards  
        #else:
        #    dir_pin     = 0 if vec >= 0 else dir_pin
        step_pins       = [step_pin]*num_steps           # Make the pin states
        dir_pins        = [dir_pin]*num_steps 
        option_pins     = [1 if path.is_cancellable() else 0]*num_steps 

        s           = abs(path.get_axis_length(axis))                   # Get the length of the vector
        ratio       = path.get_axis_ratio(axis)                         # Ratio is the length of this axis to the total length

        Vm       = path.get_max_speed()*ratio				            # The travelling speed in m/s
        a        = self.acceleration*ratio    		                    # Accelleration in m/s/s
        ds       = 1.0/steps_pr_meter                                   # Delta S, distance in meters travelled pr step.         
        
        #logging.debug('Start speed '+str(path.get_start_speed()))
        #logging.debug('End speed '+str(path.get_end_speed()))

        if path.is_type_print_segment():                                # If there is currently a segment being processed, 
            u_start  = ratio*path.get_start_speed()                 	    # The end speed, depends on the angle to the next
        else:
            u_start = 0
        if path.is_type_print_segment():     # If there are paths in queue, we might not have to slow down
            u_end    = ratio*path.get_end_speed()                 	    # The start speed. Depends on the angle to the prev.
        else:
            u_end = 0

        tm_start = (Vm-u_start)/a					                    # Calculate the time for when max speed is met. 
        tm_end   = (Vm-u_end)/a					                        # Calculate the time for when max speed is met. 
        sm_start = min(u_start*tm_start + 0.5*a*tm_start**2, s/2.0)     # Calculate the distance traveled when max speed is met
        sm_end   = min(u_end*tm_end + 0.5*a*tm_end**2, s/2.0)           # Calculate the distance traveled when max speed is met

        distances_start  = np.arange(0, sm_start, ds)		            # Table of distances                     
        distances_end    = np.arange(0, sm_end, ds)		                # Table of distances     

        timestamps_start = (-u_start+np.sqrt(2.0*a*distances_start+u_start**2))/a    # When ticks occur
        timestamps_end   = (-u_end  +np.sqrt(2.0*a*distances_end+u_end**2))/a        # When ticks occur

        delays_start     = np.diff(timestamps_start)/2.0			    # We are more interested in the delays pr second. 
        delays_end       = np.diff(timestamps_end)/2.0			        # We are more interested in the delays pr second.         
        #delays_start     = np.array([delays_start, delays_start]).transpose().flatten()
        #delays_end       = np.array([delays_end, delays_end]).transpose().flatten()

        i_steps     = num_steps-len(delays_start)-len(delays_end)     # Find out how many delays are missing
        i_delays    = [(ds/Vm)/2.0]*i_steps  		                    # Make the intermediate steps
        delays      = np.concatenate([delays_start, i_delays, np.flipud(delays_end)])# Add the missing delays. 
        td          = num_steps/steps_pr_meter                          # Calculate the actual travelled distance        
        if vec < 0:                                                     # If the vector is negative, negate it.      
            td     *= -1.0

        # If the axes are X or Y, we need to transform back in case of 
        # H-belt or some other transform. 
        if axis == "X" or axis == "Y":
            (td_x, td_y) = path.stepper_to_axis(td, axis)
            self.current_pos["X"] += td_x 
            self.current_pos["Y"] += td_y 
        else:                        
            self.current_pos[axis] += td                                    # Update the global position vector
        
        return (step_pins,dir_pins,option_pins, delays)                                           # return the pin states and the data
Example #8
0
class Path_planner:
    ''' Init the planner '''
    def __init__(self, steppers, current_pos):
        self.steppers    = steppers
        self.pru         = Pru()                                # Make the PRU
        self.paths       = Queue.Queue(30)                      # Make a queue of paths
        self.current_pos = current_pos                          # Current position in (x, y, z, e)
        self.running     = True                                 # Yes, we are running
        self.pru_data    = defaultdict(int)
        self.t           = Thread(target=self._do_work)         # Make the thread
        self.t.start()		                

    ''' Set the acceleration used '''
    def set_acceleration(self, acceleration):
        self.acceleration = acceleration

    ''' Add a path segment to the path planner '''        
    def add_path(self, new):        
        self.paths.put(new)
        if hasattr(self, 'prev'):
            self.prev.set_next(new)
            new.set_prev(self.prev)
        self.prev = new        
        
    ''' Return the number of paths currently on queue '''
    def nr_of_paths(self):
        return self.paths.qsize()

    ''' Set position for an axis '''
    def set_pos(self, axis, val):
        self.current_pos[axis] = val
	
    def wait_until_done(self):
        '''Wait until planner is done'''
        self.paths.join()
        self.pru.wait_until_done()		 

    ''' This loop pops a path, sends it to the PRU 
    and waits for an event '''
    def _do_work(self):
        events_waiting = 0
        while self.running:       
            try: 
                path = self.paths.get(timeout = 1)                            # Get the last path added
                path.set_global_pos(self.current_pos.copy())       # Set the global position of the printer
                axes_added = 0
                all_data = {}
                slowest =  0
                for axis in path.get_axes():                       # Run through all the axes in the path    
                    stepper = self.steppers[axis]                  # Get a handle of  the stepper                    
                    data = self._make_data(path, axis)
                    if len(data[0]) > 0:
                        all_data[axis] = data                      # Generate the timing and pin data                         
                        slowest = max(slowest, sum(data[1]))                                   
                
                for axis in all_data:                         
                    packet = all_data[axis]                           
                    delays = np.array(packet[1])
                    diff = (slowest-sum(delays))/len(delays)
                    for j, delay in enumerate(delays):
                        delays[j] = max(delay+diff, 1.0/10000.0)    # min 0.2ms                     
                    data = (packet[0], delays)  
                
                if "Z" in all_data:     # HACK! The Z-axis cannot be combined with the other data. Somehow it goes backwards...
                    packet = all_data["Z"]      
                    while not self.pru.has_capacity_for(len(packet[0])*8):# Wait until the PRU has capacity for this chunk of data
                        time.sleep(1)                   
                    if self.pru.add_data(packet) > 0:                        
                        self.pru.commit_data() 
                    del all_data["Z"]
                    
                for axis in all_data:   # Commit the other axes    
                    packet = all_data[axis]
                    z = zip(np.cumsum(packet[1]), packet[0])
                    for item in z:
                        self.pru_data[item[0]] += item[1]

                if len(self.pru_data) > 0:
                    z = zip(*sorted(self.pru_data.items()))
                    self.pru_data = (list(z[1]), list(np.diff([0]+list(z[0]))))

                    while not self.pru.has_capacity_for(len(self.pru_data[0])*8):
                        time.sleep(0.1)                   
                    self.pru.add_data(self.pru_data)
                    self.pru.commit_data()                            # Commit data to ddr

                self.pru_data = defaultdict(int)                    
                self.paths.task_done()
               
            except Queue.Empty:
                pass

    def merge_data(self):
        self.pru_data        

    def _add_or_append(self, old, ts, pin):
        if ts == old[-1][0]:
            return (x, old[-1][1]+y) 
        return (x, y)


    ''' Join the thread '''
    def exit(self):
        self.running = False
        self.pru.join()
        logging.debug("pru joined")
        self.t.join()
        logging.debug("path planner joined")


    ''' Make the data for the PRU or steppers '''
    def _make_data(self, path, axis):     
        stepper         = self.steppers[axis]
        steps_pr_meter  = stepper.get_steps_pr_meter()
        vec             = path.get_axis_length(axis)                        # Total travel distance
        num_steps       = int(abs(vec) * steps_pr_meter)                    # Number of steps to tick
        if num_steps == 0:
            return ([], [])
        step_pin    = stepper.get_step_pin()                            # Get the step pin
        dir_pin     = stepper.get_dir_pin()                             # Get the direction pin
        dir_pin     = 0 if vec < 0 else dir_pin                         # Disable the dir-pin if we are going backwards               
        pins        = [step_pin | dir_pin, dir_pin]*num_steps           # Make the pin states

        s           = abs(path.get_axis_length(axis))                   # Get the length of the vector
        ratio       = path.get_axis_ratio(axis)                         # Ratio is the length of this axis to the total length

        Vm       = path.get_max_speed()*ratio				            # The travelling speed in m/s
        a        = self.acceleration*ratio    		                    # Accelleration in m/s/s
        ds       = 1.0/steps_pr_meter                                   # Delta S, distance in meters travelled pr step.         
        if self.pru.is_processing():                                    # If there is currently a segment being processed, 
            u_start  = ratio*path.get_start_speed()                 	    # The end speed, depends on the angle to the next
        else:
            u_start = 0
        if self.paths.qsize() > 0:                                      # If there are paths in queue, we do not have to slow down
            u_end    = ratio*path.get_end_speed()                 	    # The start speed. Depends on the angle to the prev.
        else:
            u_end = 0

        #print "Max speed for "+axis+" is "+str(Vm)
        #print "Start speed for "+axis+" is "+str(u_start)
        #print "End speed for "+axis+" is "+str(u_end)
        tm_start = (Vm-u_start)/a					                    # Calculate the time for when max speed is met. 
        tm_end   = (Vm-u_end)/a					                        # Calculate the time for when max speed is met. 
        sm_start = min(u_start*tm_start + 0.5*a*tm_start**2, s/2.0)     # Calculate the distance traveled when max speed is met
        sm_end   = min(u_end*tm_end + 0.5*a*tm_end**2, s/2.0)           # Calculate the distance traveled when max speed is met

        distances_start  = list(np.arange(0, sm_start, ds))		        # Table of distances                       
        distances_end    = list(np.arange(0, sm_end, ds))		        # Table of distances                       
        timestamps_start = [(-u_start+np.sqrt(2.0*a*ss+u_start**2))/a for ss in distances_start]# When ticks occur
        timestamps_end   = [(-u_end  +np.sqrt(2.0*a*ss+u_end**2))/a for ss in distances_end]# When ticks occur
        delays_start     = np.diff(timestamps_start)/2.0			         # We are more interested in the delays pr second. 
        delays_end       = np.diff(timestamps_end)/2.0			         # We are more interested in the delays pr second.         
        delays_start     = list(np.array([delays_start, delays_start]).transpose().flatten())         
        delays_end       = list(np.array([delays_end, delays_end]).transpose().flatten()) 

        i_steps     = 2*num_steps-len(delays_start)-len(delays_end)       # Find out how many delays are missing
        i_delays    = [(ds/Vm)/2.0]*i_steps  		                    # Make the intermediate steps
        delays      = delays_start+i_delays+delays_end[::-1]                  # Add the missing delays. These are max_speed
        td          = num_steps/steps_pr_meter                          # Calculate the actual travelled distance        
        if vec < 0:                                                     # If the vector is negative, negate it.      
            td     *= -1.0

        path.set_travelled_distance(axis, td)                           # Set the travelled distance back in the path 
        self.current_pos[axis] += td                                    # Update the global position vector

        #with open(axis+"_delays", "w+") as f:
        #    f.write(", ".join(map(str, delays)))

        return (pins, delays)                                           # return the pin states and the data