3D Drucker Steuerung über Raspberry Pi mit GCode Interpreter

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
Antworten
elchico
User
Beiträge: 29
Registriert: Dienstag 10. März 2015, 00:06

Hallo zusammen,

anbei mein Code für eine 3D Drucker Software (ist noch nicht ganz fertig, aber auf einem guten Weg). Sie kann 4 Motoren quasi-gleichzeitig steuern (per Extrapolation).

Ich habe zur Zeit noch einen komische Rechenpower Anstieg: Wenn ich eine GCode Zeile habe, die zum Beispiel so aussieht: "G1 X67 Y67 E20.1 F7800" (X 67 mm, Y 67 mm und 20.1 mm, bei 7800 mm/sek), dann rechnet mein Pi das runter und steuert die 3 Motoren "gleichzeitig" (leider langsamer, als ich es erwarten würde, weiß noch nicht genau, warum. Vielleicht, weil es ja nur "mehr oder weniger" gleichzeitig ist...).
Wenn nun eine GCode Zeile folgendermaßen ausschaut: "G1 X92.150 Y93.154 F7800.000", dann hängt er sich auf, die Motoren drehen sich nicht und der Pi braucht ewig. WinSCP und Putty sagen beide, die Verbindung wäre verloren.

Ich habe das ganze auch schon laufen lassen, ohne dass die Motoren mit Strom versorgt wären, und der kleine Computer braucht trotzdem ewig. Der RAM Verbrauch schießt von 13 % auf 95 % + SWAP Auslagerung => Es ist ein software-seitiges Rechenproblem. Allerdings finde ich in meinem Code nirgends das verantwortliche Teilstück.

Vielleicht hat ja von euch einer eine Idee :)
VG

elchico



PS: Die Funktion IJPosition ist bisher nur "show" und hat noch keine richtige Funktion...


Code: Alles auswählen

#####################################################
#                                                                                                                #
#     G code interpreter and executer for 3D Printer using Raspberry Pi                   #
#                                                                                                                #
#                                                                                                                #
#                                                                                                                #
#####################################################


import RPi.GPIO as GPIO
import time
from numpy import pi, sin, cos, sqrt, arccos, arcsin, abs

file = 'test2.gcode'                                                                                                 #file name of the Gcode commands
GPIO.setwarnings(False)
GPIO.cleanup()

class Step_Motor(object):
    def __init__(self, pins, resolution, mm_round, mode=3, phase=0):
        """Initialise the motor object.

        pins -- a list of 4 integers referring to the GPIO pins that the IN1, IN2
        IN3 and IN4 pins of the ULN2003 board are wired to

        """
        self.P1 = pins[0]
        self.P2 = pins[1]
        self.P3 = pins[2]
        self.P4 = pins[3]
        self.resolution = resolution
        self_dis = mm_round
        self.mode = mode
        self.phase=phase
        self.deg_per_step = self.resolution                     # for half-step drive (mode 3)
        self.steps_per_rev = int(360 / self.deg_per_step)      # 4096
        self.step_angle = 0                                  # Assume the way it is pointing is zero degrees
        self.position = 0
        
        for p in pins:
            GPIO.setup(p, GPIO.OUT)
            GPIO.output(p, 0)

    def _set_rpm(self, rpm):
        """Set the turn speed in RPM."""
        self._rpm = self.resolution
        # T is the amount of time to stop between signals
        self._T = (60.0 / self._rpm) / self.steps_per_rev

    # This means you can set "rpm" as if it is an attribute and
    # behind the scenes it sets the _T attribute
    rpm = property(lambda self: self._rpm, _set_rpm)
    

    def move(self, direction, steps, delay=0.001):
        #phase_seq=[[1,1,0,0],[0,1,1,0],[0,0,1,1],[1,0,0,1]]                                                                        #full step sequence. maximum torque
        phase_seq=[[1,0,0,0],[1,1,0,0],[0,1,0,0],[0,1,1,0],[0,0,1,0],[0,0,1,1],[0,0,0,1],[1,0,0,1]]                                    #half-step sequence. double resolution. But the torque of the stepper motor is not constant 
        num_phase=len(phase_seq);
        
        for i in range(steps):
            next_phase=(self.phase+direction) % num_phase;
            
            GPIO.output(self.P1,phase_seq[next_phase][0]);
            GPIO.output(self.P2,phase_seq[next_phase][1]);
            GPIO.output(self.P3,phase_seq[next_phase][2]);
            GPIO.output(self.P4,phase_seq[next_phase][3]);
            
            self.phase=next_phase;
            time.sleep(delay);
            self.position = self.position + (direction*steps)
        
        

    def clear(self):
        GPIO.output(self.P1, 0)
        GPIO.output(self.P2, 0)
        GPIO.output(self.P3, 0)
        GPIO.output(self.P4, 0)
        
        

def params():                                                                                                    #returns list_params = [MX, MY, MZ, MF, dx, dy, dz, df, print_speed, fan, heatbed]
    
    list_params=[]
    GPIO.setmode(GPIO.BCM)

    pins_xyzf=[(22,23,24,27), (12,16,20,21), (11,14,15,7), (25,26,4,17)]    #pin number for a1,a2,b1,b2.  a1 and a2 form coil A; b1 and b2 form coil B
    
    degx=5.625/64                                                            #degree per step
    degy=5.625/64
    degz=5.625/64
    degf=5.625/64
    
    round_x=67                                                                #mm/round = mm/360
    round_y=67
    round_z=67
    round_f=67
    
    dx=round_x/(360/degx)                                                    #mm/step
    dy=round_y/(360/degy)
    dz=round_z/(360/degz)
    df=round_f/(360/degf)
    
    rpm_x = 18                                                                #rounds/min
    rpm_y = 18
    rpm_z = 18
    rpm_f = 18
    
    
    #extruder_heat_on=7                                
    heatbed=8                                                                #heatbed
    speed_filament=5                                                        #filament release speed
    fan=18                                                                    #Fan

    
    #free pins: 2, 3, 5, 6, 9, 10

    
    MX=Step_Motor(pins_xyzf[0], degx, dx)                
    MY=Step_Motor(pins_xyzf[1], degy, dy)
    MZ=Step_Motor(pins_xyzf[2], degz, dz)
    MF=Step_Motor(pins_xyzf[3], degf, df)
    
    
    
    MX.rpm=[rpm_x]                                
    MY.rpm=[rpm_y]    
    MZ.rpm=[rpm_z]    
    MF.rpm=[rpm_f]
    
    MZ_Home_position = 0.5                                                    #Home Position of MZ in mm
    MZ.position=int(MZ_Home_position/dz)
        
    GPIO.setup(fan,GPIO.OUT)
    GPIO.setup(fan, False)    
    GPIO.setup(heatbed,GPIO.OUT)            
    GPIO.output(heatbed,False)

    print_speed=min(((360/degx)*rpm_x/60),    ((360/degy)*rpm_y/60),((360/degf)*rpm_f/60))         #max. step/sec
    
    list_params = [MX, MY, MZ, MF, dx, dy, dz, df, print_speed, fan, heatbed]
    return list_params
    
def control(stepper1, step1, stepper2, step2, stepper3, step3, stepperf, stepf,df, velo):                         #calculates least common multiplier and directions and calls class-function 'step_motor.move'
    def LCM(a,b,c):                                #returns smallest common number
        d = b                #a&b
        lst = [a,b,c]
        if 0 in lst:
            lst[lst.index(0)]=1
        a = lst[0]
        b = lst[1]
        c = lst[2]
        while d%a != 0:
            d=d+b
        e = d
        while d%c != 0:        #and c
            e=e+d
        return e

    def sign(a):                                 #returns the sign of number a
        if a>0:
            return 1;
        elif a<0:
            return -1;
        else:
            return 0;
        
    def Motor_Step(stepper1, step1, stepper2, step2, stepper3, step3, stepperf, stepf, velo):
    #control stepper motor 1 and 2 simultaneously
    #stepper1 and stepper2 are objects of Bipolar_Stepper_Motor class
    #direction is reflected in the polarity of [step1] or [step2]

        dir1=sign(step1)                                                  #get direction from the polarity of argument [step]
        dir2=sign(step2)
        dir3=sign(step3)
        dirf=sign(stepf)

        step1=int(abs(round(step1)))                                    #absolute value of steps
        step2=int(abs(round(step2)))
        step3=int(abs(round(step3)))
        stepf=int(round(abs(stepf)))                                    #Feedrate in steps/sec
        
    #[total_micro_step] total number of micro steps
    #stepper motor 1 will move one step every [micro_step1] steps
    #stepper motor 2 will move one step every [micro_step2] steps
    #first motor 3 will move (MZ), then filament-motor will start and work continuously and last, the 2D-motors will move

        if step1==0 and step2==0 and stepf==0:
            total_micro_step=0
        elif step1==0 and stepf==0 and step2!=0:
            total_micro_step=step2;
            micro_step2=1;
            micro_step1=total_micro_step+100
            micro_stepf=total_micro_step+100
        elif step1==0 and step2==0 and stepf!=0:
            total_micro_step=stepf;
            micro_stepf=1;
            micro_step2=total_micro_step+100
            micro_step1=total_micro_step+100
        elif step1==0 and step2!=0 and stepf!=0:
            total_micro_step=LCM(step1,step2,stepf)
            micro_step2=total_micro_step/step2
            micro_stepf=total_micro_step/stepf
            micro_step1=total_micro_step+100
        elif step2==0 and stepf==0 and step1!=0:
            total_micro_step=step1;
            micro_step1=1;
            micro_step2=total_micro_step+100
            micro_stepf=total_micro_step+100
        elif step2==0 and step1!=0 and stepf!=0:
            total_micro_step=LCM(step1,step2,stepf)
            micro_step1=total_micro_step/step1
            micro_stepf=total_micro_step/stepf
            micro_step2=total_micro_step+100
        elif stepf==0 and step1!=0 and step2!=0:
            total_micro_step=LCM(step1,step2,stepf)
            micro_step1=total_micro_step/step1
            micro_step2=total_micro_step/step2
            micro_stepf=total_micro_step+100
        else:
            total_micro_step=LCM(step1,step2,stepf)
            micro_step1=total_micro_step/step1
            micro_step2=total_micro_step/step2
            micro_stepf=total_micro_step/stepf
        
        
        t = max(step1,step2,stepf)/velo+step3/velo                        #Calculation time per line
        print "time:", t
        if step3 != 0:
            for i in range (1,step3+1):
                stepper3.move(dir3,1)                                    #first MZ
        else:
            pass
        if total_micro_step==0 or velo==0:
            print "No Steps or no Velocity."
            return 0
        else:    
            dt=1/velo                                                    #time delay every micro_step        

        for i in range(1,total_micro_step+1):                           #i is the iterator for the micro_step. i cannot start from 0
            time_laps=0;
            micro_step=0
            if (i % micro_step1)==0:                                    #motor 1 need to turn one step
                stepper1.move(dir1,1,dt/3);
                time_laps+=dt/3
                micro_step=1
            if (i % micro_step2)==0:                                    #motor 2 need to turn one step
                stepper2.move(dir2,1,dt/3);
                time_laps+=dt/3
                micro_step=1
            if (i % micro_stepf)==0:                                    #motor 2 need to turn one step
                stepperf.move(dirf,1,dt/3);
                time_laps+=dt/3
                micro_step=1
            if micro_step==1:
                time.sleep(dt-time_laps)

            else:
                pass
    Motor_Step(stepper1, step1, stepper2, step2, stepper3, step3, stepperf, stepf, velo)
    return 0;

def XYZFEposition(lines,old_x,old_y,old_z,old_f,old_velo,quot):                                                 #calculates new position of MX & MY & MZ & Feedrate f_rate
    #given a movement command line, return the X Y Z position and Feedrate
    try:    #x_pos
        xchar_loc=lines.index('X');
        i=xchar_loc+1
        while i < len(lines) and lines[i] in ("0","1","2","3","4","5","6","7","8","9","-", "."):
                i += 1
        x_pos=float(lines[xchar_loc+1:i])
    except:
        x_pos=old_x
    try:    #y_pos
        ychar_loc=lines.index('Y');
        i=ychar_loc+1
        while i < len(lines) and lines[i] in ("0","1","2","3","4","5","6","7","8","9","-", "."):
                i += 1
        y_pos=float(lines[ychar_loc+1:i]);    
    except:
        y_pos=old_y
    try:     #z_pos
        zchar_loc=lines.index('Z');
        i=zchar_loc+1
        while i < len(lines) and lines[i] in ("0","1","2","3","4","5","6","7","8","9","-", "."):
                i += 1
        z_pos=float(lines[zchar_loc+1:i]);
    except:
        z_pos=old_z
    try:    #f_rate
        echar_loc=lines.index('E');
        i=echar_loc+1
        while i < len(lines) and lines[i] in ("0","1","2","3","4","5","6","7","8","9","-", "."):
                i += 1
        f_rate=float(lines[echar_loc+1:i]);
    except:
        f_rate=0
    try:     #Velocity
        vchar_loc=lines.index('F');
        i=vchar_loc+1
        while i < len(lines) and lines[i] in ("0","1","2","3","4","5","6","7","8","9","-", "."):
                i += 1
        velo=float(lines[vchar_loc+1:i]);
    except:
        velo=old_velo/quot
    print "-----------------------------------------------------------------"
    if f_rate == 0:                                                                                     
        print 'No printer, fast movement'
    else:                                                                        
        print 'Printer on'
    print "X-Pos:", old_x,"-->",x_pos,"mm \nY-Pos:", old_y,"-->",y_pos,"mm \nZ-Pos:", old_z,"-->",z_pos,"mm \nFeedrate:", f_rate, "mm"
    return x_pos,y_pos,z_pos,f_rate,velo

def IJposition(lines):                                                                                             #calculates new position for circular movement
    #given a G02 or G03 movement command line, return the I J position
    ichar_loc=lines.index('I');
    i=ichar_loc+1;
    while (47<ord(lines[i])<58)|(lines[i]=='.')|(lines[i]=='-'):
        i+=1;
    i_pos=float(lines[ichar_loc+1:i]);    
    
    jchar_loc=lines.index('J');
    i=jchar_loc+1;
    while (47<ord(lines[i])<58)|(lines[i]=='.')|(lines[i]=='-'):
        i+=1;
    j_pos=float(lines[jchar_loc+1:i]);    

    return i_pos,j_pos;

def moveto(MX,x_pos,dx,MY,y_pos,dy,MZ,z_pos,dz,MF,f_rate,df,velo):                                                #3D movement + f_rate + Velocity
#Move to (x_pos,y_pos, z_pos, f_rate) with Velocity (velo)
    stepx = int(x_pos/dx)-MX.position
    stepy = int(y_pos/dy)-MY.position
    stepz = int(z_pos/dz)-MZ.position
    stepf = int(f_rate/df)
    velo = (velo/60)/min(dx,dy,dz,df)
    print"Velocity:", velo,"steps/sec"
    if stepx==0 and stepy==0 and stepz==0 and stepf==0:
        return 0
    else:
        control(MX,stepx,MY,stepy,MZ,stepz,MF,stepf,df,velo);
    return 0;

def velocity(filename, print_speed,dx,dy,dz,df):                                                                #looking for velocities and compare with max_poss. velocity
    velocities = []
    for lines in open(filename, 'r'):
        if lines[0:2]=='G1' or lines[0:3]=='G01' or lines[0:3]=='G00' or lines[0:2]=='G0' or lines[0:3]=='G02' or lines[0:3]=='G03' or lines[0:2]=='G2' or lines[0:2]=='G3':
            if 'F' in lines:
                vchar_loc=lines.index('F');
                i=vchar_loc+1
                while i < len(lines) and lines[i] in ("0","1","2","3","4","5","6","7","8","9","-", "."):
                    i += 1
                v = int(round(float(lines[vchar_loc+1:i])))
                velocities.append(v)
            else:
                pass
        else:
            pass
    if len(velocities) == 0: 
        print "No velocity given!"
        return 0
    else:
        velocities.sort()
        maximum_mm = int(velocities.pop(len(velocities)-1))/60
        max_poss = print_speed*min(dx,dy,dz,df)
        if maximum_mm == max_poss:
            quot == 1
        elif maximum_mm > max_poss:
            quot = max_poss/maximum_mm
            quot = quot - 0.1*quot
        elif maximum_mm < max_poss:
            quot = max_poss/maximum_mm
            quot = quot - 0.1*quot
        return quot

def total_time(filename,MX,MY,MZ,MF,dx,dy,dz,df,old_x,old_y,old_z,old_f,quot):                                    #Calculation of total time required
    T_total = 0
    old_velo = 0
    for lines in open(filename,'r'):
        if lines[0:3]=='G1 ' or lines[0:3]=='G01' or lines[0:3]=='G00' or lines[0:3]=='G0 ' or lines[0:3]=='G02' or lines[0:3]=='G03' or lines[0:3]=='G2 ' or lines[0:3]=='G3 ':
            try:                                                             #Velocity
                vchar_loc=lines.index('F');
                i=vchar_loc+1
                while i < len(lines) and lines[i] in ("0","1","2","3","4","5","6","7","8","9","-", "."):
                        i += 1
                velo=float(lines[vchar_loc+1:i])
                old_velo=velo
            except:
                velo=old_velo
            try:                                                            #x_pos
                xchar_loc=lines.index('X');
                i=xchar_loc+1
                while i < len(lines) and lines[i] in ("0","1","2","3","4","5","6","7","8","9","-", "."):
                        i += 1
                x_pos=float(lines[xchar_loc+1:i])
            except:
                x_pos=old_x
            try:                                                            #y_pos
                ychar_loc=lines.index('Y');
                i=ychar_loc+1
                while i < len(lines) and lines[i] in ("0","1","2","3","4","5","6","7","8","9","-", "."):
                        i += 1
                y_pos=float(lines[ychar_loc+1:i]);    
            except:
                y_pos=old_y
            try:                                                             #z_pos
                zchar_loc=lines.index('Z');
                i=zchar_loc+1
                while i < len(lines) and lines[i] in ("0","1","2","3","4","5","6","7","8","9","-", "."):
                        i += 1
                z_pos=float(lines[zchar_loc+1:i]);
            except:
                z_pos=old_z
            try:                                                            #f_rate
                echar_loc=lines.index('E');
                i=echar_loc+1
                while i < len(lines) and lines[i] in ("0","1","2","3","4","5","6","7","8","9","-", "."):
                        i += 1
                f_rate=float(lines[echar_loc+1:i]);
            except:
                f_rate=0
        
            velo = velo*quot
            stepx = int(round(x_pos/dx))-MX.position;
            stepy = int(round(y_pos/dy))-MY.position;
            stepz = int(round(z_pos/dz))-MZ.position;
            stepf = int(round(f_rate/df))
            velo = (velo/60)/(min(dx,dy,dz,df))

            step1=int(abs(stepx))                                            #absolute value of steps
            step2=int(abs(stepy))
            step3=int(abs(stepz))
            f_rate=int(round(abs(stepf)/60))                                #Feedrate in steps/sec
            
            max_distance=max(step1,step2,f_rate)
            if step3==0:
                T_total=T_total+(max_distance/velo)
            else:
                T_total=T_total+(max_distance/velo)+step3/velo
            
        else:
            pass
    print "-----------------------------------------------------------------"
    print "Total time", (T_total/60), "min"
    
def gcode_interpreter(filename, parameters):                                                                    #reading and executing G code
    MX = parameters[0]
    MY = parameters[1]
    MZ = parameters[2]
    MF = parameters[3]
    dx = parameters[4]
    dy = parameters[5]
    dz = parameters[6]
    df = parameters[7]
    print_speed = parameters[8]
    fan = parameters[9]
    heatbed = parameters[10]
    
    old_x=MX.position
    old_y=MY.position
    old_z=float(round(MZ.position*dz,1))
    old_f=0
    old_velo=0
    
    quot = velocity(filename, print_speed,dx,dy,dz,df)
    total_time(filename,MX,MY,MZ,MF,dx,dy,dz,df,old_x,old_y,old_z,old_f,quot)
    
    for lines in open(filename,'r'):            #start Gcode-program
        if lines[0]=='G':                        #movement
            if lines[0:3]=='G90':                                                                    #start gcode interpreting
                print "-----------------------------------------------------------------"
                print "-----------------------------------------------------------------"
                print "-----------------------------------------------------------------"
                print 'start, absolute coordinates';
            elif lines[0:3]=='G0 ' or lines[0:3]=='G00':                                            #rapid linear move
                [x_pos,y_pos,z_pos,f_rate,velo]=XYZFEposition(lines,old_x,old_y,old_z,old_f,old_velo,quot)
                velo = velo*quot
                moveto(MX,x_pos,dx,MY,y_pos,dy,MZ,z_pos,dz,MF,f_rate,df,velo)        
                old_x=x_pos
                old_y=y_pos
                old_z=z_pos
                old_velo=velo
            elif lines[0:3]=='G1 ' or lines[0:3]=='G01':                                            #rapid linear move
                [x_pos,y_pos,z_pos,f_rate,velo]=XYZFEposition(lines,old_x,old_y,old_z,old_f,old_velo,quot)
                velo = velo*quot
                moveto(MX,x_pos,dx,MY,y_pos,dy,MZ,z_pos,dz,MF,f_rate,df,velo)                
                old_x=x_pos
                old_y=y_pos
                old_z=z_pos
                old_f=f_rate
                old_velo=velo
            elif lines[0:3]=='G02' or lines[0:3]=='G03' or lines[0:3]=='G2 ' or lines[0:3]=='G3 ':    #circular interpolation
                pass
            elif lines[0:3]=='G4 ' or lines[0:3]=='G04':                                            #wait
                try:
                    index_P = lines.index(P)
                    sleep_time=0
                    for i in range (len(lines), index_P+1):
                        expo = 0
                        sleep_time=(sleep_time+lines[i]^expo)/1000
                        expo+=1
                    time.sleep(sleep_time)
                except:
                    index_S = lines.index(S)
                    sleep_time=0
                    for i in range (len(lines), index_S+1):
                        expo = 0
                        sleep_time=sleep_time+lines[i]^expo
                        expo+=1
                    time.sleep(sleep_time)            
            elif lines[0:3]=='G21':                                                                    #working in mm
                print 'Working in mm';
            elif lines[0:3]=='G20':                                                                    #cannot work in inch
                print 'Wrong Unit of length'
                break
            elif lines[0:3]=='G28':                                                                    #Move home
                [x_pos,y_pos,z_pos,f_rate,velo]=((0),(0),(0.5),0,(print_speed*60*min(dx,dy,dz,df)))
                print "-----------------------------------------------------------------"
                print "Move Home"                
                moveto(MX,x_pos,dx,MY,y_pos,dy,MZ,z_pos,dz,MF,f_rate,df,velo)
        elif lines[0]=='M':                        #options
            if lines[0:3]=='M00' or lines[0:2]=='M0' or lines[0:3]=='M01' or lines[0:2]=='M1':        #printer off
                MX.clear()
                MY.clear()
                MZ.clear()
                MF.clear()                    
            elif lines[0:3]=='M02' or lines[0:2]=='M2':                                                #Program ends
                print 'Program finished';
            elif lines[0:4]=='M104' or lines[0:4]=='M109':                                            #Extruder Temp
                try:
                    index_S = lines.index(S)
                    extruder_temp=0
                    for i in range (len(lines), index_S+1):
                        expo = 0
                        extruder_temp=extruder_temp+lines[i]^expo
                        expo+=1
                    #setting extruder temp
                except:
                    print 'Error: Extruder Temp not accessible'
                    break
            elif lines[0:4]=='M106':                                                                #Fan On
                index_S = lines.index(S)
                fan_speed=0
                for i in range (len(lines), index_S+1):
                    expo = 0
                    fan_speed=fan_speed+lines[i]^expo
                    expo+=1
                GPIO.output(fan, True)
            elif lines[0:4]=='M107':                                                                #Fan Off
                GPIO.output(fan, False)
            elif lines[0:4]=='M140' or lines[0:4]=='M190':                                            #Heatbed on
                index_S = lines.index(S)
                heatbed_temp=0
                for i in range (len(lines), index_S+1):
                    expo = 0
                    heatbed_temp=heatbed_temp+lines[i]^expo
                    expo+=1
                if heatbed_temp == 0:
                    GPIO.output(heatbed, False)
                else:
                    GPIO.output(heatbed, True)  #wait until it is hot            
        else:                                    #Comments, Tools (not supported) in GCode = ignore
            pass


    GPIO.output(fan, False)
    GPIO.output(heatbed, False)
    time.sleep(2)
    GPIO.cleanup()
    


def main():
    try:
        list_params = params()                            #parameters, motors, heatbed, speed, steps (microstep or full step)
        gcode_interpreter(file, list_params)
    except KeyboardInterrupt:
        pass


if __name__=='__main__':
    main()
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

@elchico: warum rollst Du die pins-Liste in StepMotor nach P1..4 aus? Unten in __init__ zeigst Du doch schön, wie bequem es ist, per for-Schleife die pins abzuarbeiten? Außerhalb von __init__ (_set_rpm) sollten keine neuen Attribute mehr erzeugt werden. In params solltest Du auch die Parameter für einen Motor in eine Struktur packen und alle Motoren dann in eine Liste. Du schreibst ja praktisch alles 4-fach. Und so zieht sich das durch alle Funktionen durch. Idealerweise sollte es an höchstens einer Stelle im Programm nötig sein, zu wissen, wie viele Motoren es gibt.

Funktionen innerhalb von Funktionen zu definieren ist unschön, vor allem wenn sie so lang sind. Zum parsen der Zeilen schreib Dir eine Funktion, die Dir ein Wörterbuch (Buchstabe->Wert) zurückliefert.

Wenn Du das erledigt hast und das Programm nur noch ~200 Zeilen hat, ließe sich vielleicht der Fehler auch einfacher finden.
elchico
User
Beiträge: 29
Registriert: Dienstag 10. März 2015, 00:06

@Sirius3:

danke :) ich bin gerade am ausbessern der ganzen 4-fach Schreibweisen.

Ich habe aber, denke ich, das Problem gefunden. Das Problem ist die "control" Funktion.

In dieser arbeite ich zwar auch vieles mehrfach ab, aber das Hauptproblem dafür, dass der Computer in die Knie geht, ist folgendes: Ich suche die kleinste Zahl x, die durch alle drei vorgegebenen Längen (im unteren Code: a, b, c) teilbar ist. Dann iteriere ich von 1 bis x und immer, wenn x durch eine der drei Längen teilbar ist, macht dieser Motor einen Schritt. Außerdem soll nur dann eine bestimmte Zeit gewartet werden, falls überhaupt mindestens ein Schritt gemacht wird. Andernfalls soll einfach weiter iteriert werden.

Das funkioniert ganz gut, wenn die Längen irgendwie gut miteinander korrelieren (alle 67 oder 67 und 33.5 etc). Falls aber zum Beispiel 67 und 66 auftaucht, muss der Computer 4422 (kleinste gemeinsame Zahl) finden und von 1 iterieren, weswegen er hier leistungstechnisch in die Knie geht (bei größeren Zahlen oder 3 Zahlen noch viel eher...).

Zusammenfassung: Das Problem ist zum einen das Suchen von "x" (wobei das sehr sehr lange dauern...), und auch das iterieren bis x. Gibt es hierfür eine schnellere Lösung?


VG
elchico


PS: ich habe einen Code geschrieben, der genau das gleiche macht wie der in meinem Skript unten, nur etwas übersichtlicher ist.


Code: Alles auswählen

import time
    
a = 67
b = 66
c = 68

def LCM(a,b,c):                                #gibt kleinste Zahl zurück, die durch a, b und c teilbar ist
    d = b                #a&b
    lst = [a,b,c]
    if 0 in lst:
        lst[lst.index(0)]=1
    a = lst[0]
    b = lst[1]
    c = lst[2]
    while d%a != 0:
        d=d+b
    e = d
    while d%c != 0:        #and c
        e=e+d
    return e

common_number = LCM(a,b,c)
a_step = common_number/a
b_step = common_number/b
c_step = common_number/c

for i in range (1, common_number+1):             #prüfen, ob kleinste Zahl durch a und/oder b und/oder c teilbar ist
    m = 0                                        #falls kleinste Zahl durch mindestens eine der drei Zahlen (a,b,c) teilbar ist => warte 1 sek
    if (i % a_step) == 0:
        print a
        m = 1
    if (i % b_step) == 0:
        print b
        m = 1
    if (i % c_step) == 0:
        print c
        m = 1
    if m == 1:
        print "timesleep"
        time.sleep(1)
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

Wenn das ganze mal funktioniert, dann könntest Du ja ein Bild posten von einem ausgedruckten Gegenstand.

Ein paar Sachen gehen kürzer (der Code ist ja lang genug):
Zeile 26 bis 29:

Code: Alles auswählen

self.P1, self.P2, self.P3, self.P4 = pins
Zeile 149 bis 151:

Code: Alles auswählen

a, b, c = lst
Mit Numpy könnte der Code in def Motor_Step wesentlich gekürzt werden. Mit Numpy könntest Du aus step1,step2,step3 ein array step machen und die ganzen Operationen wie Zeile 172-174, 177-179 würden zu 1-Zeilern werden. Eventuell könnte man damit auch den etwas unübersichtlichen Teil 187-223 klarer schreiben. Auch die Zeilen 88-91, 93-96, 98-101, 103-106, 118-121, 125-128 könnte man mit Numpy-Arrays jeweils zu 1-Zeilern kürzen.
a fool with a tool is still a fool, www.magben.de, YouTube
Benutzeravatar
bwbg
User
Beiträge: 407
Registriert: Mittwoch 23. Januar 2008, 13:35

Es kann dauern, wenn Du versuchst, das kleinste gemeinsame Vielfache mittels brute-force heraus zu finden. Wesentlich schneller wäre ein besserer Algorithmus. Hier böte es sich an, das ganze über die Primfaktorzerlegung zu realisieren.

Nebenbei: Das kleineste gemeinsame Vielfach von 67 und 66 ist 67*66, da 67 eine Primzahl ist.

Ich habe ein bisschen "rumgeschmiert":

Code: Alles auswählen

#!/usr/bin/env python3
import collections
from functools import reduce
from operator import mul
import time

def dividable(x, ys):
    """Tests if ``x`` is dividable by any of the numbers of the sequence 
    ``ys``."""
    return any(x % e == 0 for e in ys)
    
def create_prime_numbers(upper_bound):
    """Returns a list of prime-numbers up to the ``upper_bound``."""
    primes = [2]
    for x in range(3, upper_bound, 2):
        if not dividable(x, primes):
            primes.append(x)
    return primes

def prime_factors(x, primes): # {Prime: Exponent}
    """Returns a dictionary of prime-factors to their exponents."""
    result = collections.defaultdict(int)
    for p in primes:
        while x % p == 0:
            x /= p
            result[p] += 1
    return result

def least_common_multiple(xs, primes):
    """Returns the least common multiple of the numbers of the sequence ``xs``.
    """
    result = collections.defaultdict(int)
    for pfx in (prime_factors(e, primes) for e in xs):
        for p, exp in pfx.items():
            if exp > result[p]:
                result[p] = exp
    return reduce(mul, (p ** exp for p, exp in result.items()))


def main():
    primes = create_prime_numbers(1000) # Just to be sure ...
    begin = time.time()
    for _ in range(10000):
        least_common_multiple([67, 66, 68], primes)
    end = time.time()
    print(end - begin)
    
if __name__ == '__main__':
    main()
"Du bist der Messias! Und ich muss es wissen, denn ich bin schon einigen gefolgt!"
BlackJack

@elchico: Anstelle sich `lcm()` selber zu schreiben könnte man auch eine fertige, effiziente Implementierung verwenden, wie beispielsweise die aus dem `gmpy`-Modul.

Was passiert bei Deiner Funktion eigentlich wenn mehr als eine der Variablen den Wert 0 hat?

Bei Python 2 sollte man `range()` nur verwenden wenn man tatsächlich eine Liste mit den ganzen Zahlen benötigt. Nicht dass *das* am Ende der Grund für Deine Speicherprobleme ist.
Benutzeravatar
bwbg
User
Beiträge: 407
Registriert: Mittwoch 23. Januar 2008, 13:35

LCM hat schon eine unterirdische Laufzeit. Ich habe diese nicht abschließend getestet -> CTRL+C ;)
Wenn man nur den einzelnen Wert benötigt, sollte man in Python2 statt range xrange verwenden.
"Du bist der Messias! Und ich muss es wissen, denn ich bin schon einigen gefolgt!"
elchico
User
Beiträge: 29
Registriert: Dienstag 10. März 2015, 00:06

danke an alle :) bin grad fleißig am ausbessern und verbessern.

Habe meine LCM nochmals umgeschrieben (bissi googeln hilft, hier eine gute Funktion zu finden ;) ) und diese läuft jetzt auch in annehmbaren Zeiten.

Mein Problem hat sich nun gewandelt:
wenn ich das gemeinsame Vielfache gefunden habe, und von 1 ab iteriere und jedes mal prüfe, ob es teilbar ist => geht bei kleinen Zahlen gut, bei Zahlen über 1000 oder in Richtung 8000 oder mehr fast nicht mehr.

Habe mir nun zuerst überlegt, da ich ja nicht während dem Druck bei jeder Zeile ewig warten kann, bis Python durch iteriert, dies im Vorfeld zu übernehmen, dann eine Liste anzufertigen und pro "Zahl ist durch a,b,c teilbar" entweder a,b oder c in die Liste einzufügen. Dann arbeite ich die Liste einfach von vorne bis hinten durch und lass pro Eintrag den entsprechenden Schritt machen.
Die Idee ist glaub ich, nicht ganz schlecht, hilft aber beim eigentlichen Problem nicht ;) Denn immernoch muss python von 1 bis kgv durch iterieren ...

Es geht also nun um den Part von Zeile 31 bis Ende.
Weiß jemand eine "schnelle" Lösung?
Des einzige, was mir einfallen würde, wäre, sich immer nur einen Bruchteil von der ganzen Geschichte anzuschauen (also erst die ersten 50 Schritte, dann die nächsten 50 usw.). Damit würde sich die Lauf- und Rechenzeit enorm verkürzen nehme ich an(?). Allerdings wäre es, finde ich, eine etwas unschöne Lösung....

VG
elchico



PS: Habe jetzt auch das Problem mit "wenn zwei Zahlen = 0 sind" mit eingebaut :)

Code: Alles auswählen

a = 13                                      #bei großen Zahlen (z.B.: 4096) dauert es ewig, bei kleinen (bis zu 60 oder so) geht es sehr schnell ...
b = 17
c = 18

def LCM(a,b,c):                                #returns smallest common multiple
    numbers = [a,b,c]						   #is a, b or c = 0?
    for i in numbers:
        if i == 0:
            d = numbers.index(i)
            numbers.pop(d)
            numbers.insert(d, 1)
    
    a = numbers[0]
    b = numbers[1]
    c = numbers[2]
    d = a
    e = b
    f = c
    while e != 0:    #return d
        d, e = e, d%e#
    while d != 0:    #return d
        f, d = d, f%d
        
    return (a*b)/f
lcm = LCM (a,b,c)
print lcm

lst = []
for i in range (1, lcm+1):
    try:
        if i%a == 0:
            lst.append("a")
    except:
        pass
    try:
        if i%b == 0:
            lst.append("b")
    except:
        pass
    try:
        if i%c == 0:
            lst.append("c")
    except:
        pass
print lst

PPS: Ich weiß, dass ich wieder vieles mehrfach geschrieben habe... Habe in meinem Orginalcode inzwischen auch andere Bezeichnungen etc., aber in diesem Skript hier nur auf die Schnelle etwas vergleichbares geschaffen.

PPPS: @MagBen: Joooo mach ich, aber des wird noch eeeewig dauern, falls es jemals überhaupt soweit kommt. Ich habe zwar "schon" drei von vier Motoren hier, aber der ganze Rest fehlt ;)
BlackJack

@elchico: Dieses `index()`, `pop()`, `insert()` in der Schleife ist *ganz furchtbar*. Erstelle einfach eine neue Liste mit entweder 1 oder dem Wert falls er nicht 0 ist. Das geht in einer Zeile mit einer „list comprehension“. Wenn man die Ergebniswerte dort dann nicht mit einzelnen Indexzugriffen heraus holt, sondern gleich mehreren Namen zuweist wie MagBen das bereits gezeigt hat, dann kann man auch einen Generatorausdruck schreiben.
Antworten