Korekcija putanje glave 3D printera
Kada za dobivanje ispravnog prvog layera ne pomaže niti bed leveling, možda je spas u g-code post processingu...
Uvod
Konkretno, u mom slučaju radi se o 3D printeru tipa delta, premda vjerujem da sličnih problema ima i kod modela s klasičnim XYZ mostom, tj. kretnjom ispisne glave. Naime, kod delta printera ako dođe do nepravilnosti u geometriji, ona se manifestira kao oscilacija ispisne glave po Z osi. Geometrijska nepravilnost najčešće nije posljedica konstrukcije printera, već njegove montaže. Za razliku od XYZ mostova, gdje se nepravilnost uglavnom svodi na paralelnost između glave i podloge, kod delte ima više toga. Npr. međusobna udaljenost između tornjeva, paralelnost tornjeva, duljina poluga koje povezuju vertikalna kolica i ispisnu glavu itd. Kako se algoritam pretvorbe XYZ koordinate uglavnom svodi na trigonometriju, onda te nepravilnosti utječu na Z os. Točnije, za horizontalni pomak ispisne glave gdje bi Z os trebala biti konstantna, u naravi glava putuje gore/dolje, kao da je podloga valovita. Ako su ti pomaci mali, onda problem rješava bed autoleveling procedura. Međutim, ako su ti pomaci veliki, (u mom slučaju i do 0,8 mm između min i max vrijednosti, što je 4x više od debljine layera), onda bi trebalo ili korigirati geometriju printera (što je obično nemoguće) ili napisati program koji će proći kroz G-kod i izvršiti matematičku korekciju Z osi. Naravno, ja sam se odlučio za matematičku korekciju. Ako vam se događa da bed autoleveling daje različite visine na različitim XY koordinatama i to tako da ne ispada da je podloga nagnuta, odnosno vrijednosti ne rastu ili padaju u određenom smjeru, nego da je valovita jer su vrijednosti čas gore, čas dolje, vrlo vjerojatno ste kandidat da ovaj članak pročitate do kraja. Konačni rezultat takve nepravilnosti se svodi na to da se ne može cijela podloga koristiti za printanje jer postoje koordinate gdje je glava u zraku, a isto tako gdje grebe po podlozi. Kada je razlika 0,8mm a debljina layera 0,2mm, onda nema mehanizma koji bi sam od sebe riješio taj problem.
Detekcija problema
Da bi sam sebi dokazao da mi je pretpostavka ispravna, jer razlika između najmanje i najveće Z koordinate je 0,8mm, izveo sam jednostavan test. Koristim Repetier-Host kao program za upravljanje 3D printerom i u njemu sam ručno spustio glavu tik do podloge, ali na koordinati za koju mi je bed autoleveling rekao da je najmanja visina jer na toj koordinati je podloga najbliža glavi kada je u „home“ položaju. Nakon toga sam zadao G1 naredbu da glava ode do koordinate za koju mi je javio da je najviša, ali da se kreće vrlo sporo. Tu su bitne dvije pretpostavke. Prvo, daje Marlin firmware korektno napisan, a druga da je podloga ravna. Marlin je takav kakav je i samo mogu prihvatiti da je ispravan jer bi netko već primijetio da nešto ne štima. Podloga je staklena i sigurno nije idealno ravna ali isto tako nije ni deformirana da bi razlika bila 0,8mm. Pokrenuo sam G1 naredbu i pogledom pratio glavu kako putuje na ciljanu koordinatu. Vidio sam da se glava lagano udaljava od podloge, što ako uzmemo one dvije pretpostavke u obzir, jedino objašnjenje bi bila geometrijska nepravilnost. Jer ako Marlin firmware šalje naredbe koračnim motorima da glave ide ravno, odnosno uopće ne šalje naredbu za pomak po Z osi, i ako je podloga ravna, onda se glava ne bi smjela ni udaljavati od podloge, ni približavati, a test pokazuje suprotno.
Rješenje problema
Nakon što se model provuče kroz slicer dobivamo gomilu G-kod naredbi a one koje nas zanimaju se odnose na horizontalni pomak glave. Slicer u pravilu radi tako da ispisnu glavu podesi po visini i dok traje ispisivanje trenutnog layera Z os ne dira, dakle u G1 naredbi nema Z parametra, samo X i Y. Kako je prethodni test pokazao da pri horizontalnom pomaku glava putuje gore/dolje, a to predstavlja problem, G-kod treba modificirati tako da se glava pomakne po Z osi za onoliko kolika je greška. Kako greška nije na svim kombinacijama XY koordinata ista, trebalo je napraviti tablicu koja će opisati stvarno stanje. Prva testiranja sam napravio s koordinatama koje daje bed autoleveling, a kasnije sam još malo povećao broj točaka kako bi dobio gušću mrežu, a time i precizniju. Zbog toga više nisam mogao koristiti blagodati bed autolevelinga ali kako se radi o geometrijskoj nepravilnosti, to mjerenje je dovoljno napraviti jednom. Nakon što slicer napravi G-kod, treba ga snimiti kao običnu datoteku i provući je kroz Python skriptu iz komandne linije kao npr. python Z-calibration.py < slicer_gcode > modificirani_gcode. Sadržaj datoteke modificriani_gcode treba jednostavno otvoriti u nekom editoru i napraviti copy/paste u program za 3D printanje. Što je napravio Z-calibration.py? Zapravo ništa spektakularno. Sve Z koordinate je korigirao metodom bi-linearne interpolacije prema stvarnim visinama koje je dao bed autoleveling ili koje smo sami izmjerili. Na taj način se ispisna glava pozicionirala na stvarnu visinu od podloge za dotičnu XY koordinatu i na taj način zaobišla geometrijsku nepravilnost. Ali tu se javio još jedan problem, barem u mom slučaju. Ako imate neki veći objekt, npr. ploču 6x6 cm, G-kod izgleda tako da se zapisuju samo koordinate točaka u uglovima ploče, a sve između odrađuje sam firmware printera. To u praksi znači da je Z-calibration.py ispravno korigirao točke u uglovima, ali ne i točke koje su između. Kako je potez između početne i konačne točke relativno velik ispisna glava će i dalje ići gore/dolje jer se te točke ne interpoliraju. Kada bi model bio kružnog oblika, onda bi stvar štimala jer slicer krivulje pretvara u gomilu kratkih linija od kojih svaka ima svoju početnu i konačnu koordinatu koju bi Z-calibration.py korigirao. Kako dugačke linije slicer ne pretvara u puno manjih, to se neće dogoditi i greška će i dalje biti prisutna. Zato je tu još jedan Python program, G-splitline.py koji radi upravo to. U njemu se može definirati koja je najveća dopuštena duljina pojedinačne linije i sve koju se dulje od tog parametra, skratit će ih. Išao sam forsirati program da vidim kako radi i namjestio sam da ne smije biti dulje od 2,5 mm i stvar radi savršeno. G-kod bude nešto veći, ali koga briga. G-splitline.py osim što je skratio liniju iz originalnog G-koda, morao je korigirati i parametar ekstrudera jer je sada kraća linija koja se printa. Sada bi naredba iz komandne linije otprilike izgledala ovako: python G-splitline.py < slicer_gcode | python Z-calibration.py > modificirani_gcode. S ova dva Python programa su obuhvaćeni svi događaji koji mogu utjecati na visinu ispisne glave a da imaju veze s geometrijskom nepravilnošću.
Zaključak
Zbog geometrijske nepravilnosti pri konstantnoj Z osi ispisna glava putuje gore/dolje i to različito na različitim XY koordinatama. Ručno ili pomoću bed autolevelinga se izmjeri stvarna udaljenost glave od podloge i te vrijednosti se upišu u Z-calibration.py. Pošto se radi o geometrijskoj nepravilnosti, to mjerenje je u pravilu dovoljno napraviti jednom. Svaki put kada originalni G-kod koji je napravio slicer provučemo kroz Z-calibration.py korigirat će se Z os za svaku XY koordinatu prema vrijednostima koje smo dobili mjerenjem. Kako slicer sve ravne linije ne pretvara u više manjih linija, problem i dalje može biti prisutan ako su te linije dugačke. Iz tog razloga je napisan G-splitline.py koji će sve dugačke linije pretvoriti u više manjih, a sami određujemo koliko smije biti duga najdulja linija. Budući da se sada Z os korigira u svakoj G-kod naredbi, moguće je da program za printanje neće ispravno prikazivati ukupni broj layera i broj layera koji se trenutno printa, ali će ih ispravno printati. Još jednu stvar treba napomenuti. Obje Python skripte rade s pretpostavkom da su kod slicera podešene apsolutne koordinate. To znači da negdje na početku originalnog G-koda trebaju biti naredbe G90 i M82.
Podešavanje parametara za ispravan rad Python skripti
Krenut ću od G-splitline.py jer je jednostavniji. Ima samo jedan parametar, Lmax, vidi sliku.
Što je broj manji, to će G-splitline.py napraviti više linija. Ne treba pretjerivati, a meni se 2,5 mm pokazao kao dobar izbor.
Program Z-calibration.py je nešto kompliciraniji, ali ništa strašno. Evo prvo slike, a onda slijedi objašnjenje.
Uokvireni dio je koji se eventualno mijenja. Parametre ću objasniti po redu kako se pojavljuju.
Z_OFFSET=-0.05
Ovo je generalni offset po Z osi u mm. Vrijednost koja ovdje piše će se dodati na svaku izračunatu vrijednost Z koordinate. Ako je manja od 0, glava će se za toliko spustiti, a ako je veća, podignuti, na svim točkama jednako.
Z_PROBE_OFFSET_FROM_EXTRUDER=0
Ako ručnom metodom određujete na kojoj je visini podloga, onda ova vrijednost mora biti 0. Ako koristite bed autoleveling, onda morate upisti vrijednost koja se nalazi u Marlin firmware-u (Configuration.h datoteka). Tamo to otprilike izgleda ovako:
Varijabla je istog naziva i u Marlinu i u Z-calibration.py radi lakšeg snalaženja. U mojoj verziji Marlina nalazi se u 462 redu, što ne mora biti slučaj za sve, ali je tu negdje oko tog broja.
ABL=[] je definicija polja, to ne dirate.
Sad slijedi niz ABL.append([h1,h2,h3,...,h]). Umjesto h upisujete visinu koju je ispisao bed autoleveling, ili koju ste sami izmjerili. Ako idete uspoređivati te dvije vrijednosti dobivene bed autleveling i ručnom metodom, one će razlikovati upravo za parametar Z_PROBE_OFFSET_FROM_EXTRUDER iz Marlin configuration.h datoteke. Bed autoleveling uzima u obzir taj parametar, a ručna metoda ne. Zato je bitno da u Z-calibration.py upišete ili 0 ili vrijednost iz Marlina. Ako ste upisali 0, onda visine morate sami izmjeriti, a ako iz Marlina, samo ih prepišete iz programa kojim ste napravili bed autoleveling.
Međutim, ovo su samo visine. Još treba odrediti za koje kombinacije XY koordinata se odnose. To piše u slijedeća dva polja koja su najčešće ista, mada ne moraju biti.
X_os=[x1,x2,x3,...,x]
Y_os=[y1,y2,y3,...,y]
Kod ručnog mjerenja sami odredite kako gustu mrežu želite, a kod bed autolevelinga Marlin određuje sam. U oba slučaja ih samo prepišete. Bitno je da ABL.append, X_os i Y_os ima isti broj točaka, inače će program prijaviti grešku. U mom slučaju ih ima 11. Dakle, ABL.append se ponavlja 11 puta kao i broj točaka unutar njega i X_os i Y_os.
Primjećujete da je za neke rubne vrijednosti upisano -100. Kako delta ima kružnu podlogu za printanje, a mreža je kvadratnog oblika, onda neke kombinacije XY koordinata nisu dohvatljive, odnosno izlaze izvan područja za printanje. Takve koordinate označite s -100 jer se taj broj ne može pojaviti kao izmjerena visina, pa u programu predstavlja nedefiniranu vrijednost, odnosno područje izvan dosega glave za printanje.
I to je to. Ako se ne želite mučiti ostavite vrijednosti koje su u programu, jedino morate ručno izmjeriti visinu podloge za baš te koordinate. Ručno mjerenje možete obaviti na slijedeći način:
- prvo ustanovite koja je sigurna visina glave od podloge, a da nije previsoko (kod mene je 3 mm)
- naredbom G1 Z3 brzo dovedete glavu na taj položaj
- zatim naredbom npr. G1 X0 Y0 dovedete glavu na željenu koordinatu i onda je ručno spuštate po 0,1 mm
- ispod glave stavite papirić i nakon svakog spuštanja za 0,1 mm provjerite možete li ga pomaknuti
- onog trenutka kada papirić ide teško, ili uopće ne ide, tu vrijednost upišite kao visinu za taj par XY koordinata
- opet s G1 Z3 podignite glavu, odaberite novu kombinaciju XY koordinata i ponovite ručni postupak spuštanja glave
Na 11 točaka po X i Y koordinati ima puno mjerenja, točnije 105 jer su neke točke označene s -100, ali srećom to trebate napraviti samo jednom.
A sad programi.
Z-calibration.py
import fileinput, sys, math, time
########################## OVDJE SU VRIJEDNOSTI KOJE SE MIJENAJU RUCNO ##########################
Z_OFFSET=-0.05 #ovo je korisnicki offset (na svaku tocku se dodaje ova vrijednost)
Z_PROBE_OFFSET_FROM_EXTRUDER=0 # -7.4 #prepisi ovu vrijednost iz Marlin Configuratio.h datoteke ako mjeris
# pomocu ABL (G29), inace je 0 ako rucno mjeris
ABL=[] # 11 tocaka po X i Y, sveukupno 105 tocaka
#rucno izmjeri visine
ABL.append([-100, -100, 1.80, 1.80, 1.84, 1.80, 1.70, 1.60, 1.50, -100, -100]) # 65
ABL.append([-100, -100, 1.80, 1.80, 1.90, 1.80, 1.70, 1.60, 1.50, -100, -100]) # 60
ABL.append([1.94, 2.10, 2.00, 1.90, 2.00, 1.90, 1.80, 1.70, 1.60, 1.40, 1.30]) # 45
ABL.append([2.20, 2.20, 2.10, 2.00, 2.10, 2.00, 1.90, 1.80, 1.60, 1.40, 1.30]) # 30
ABL.append([2.30, 2.30, 2.20, 2.10, 2.14, 2.10, 2.00, 1.80, 1.60, 1.40, 1.30]) # 15
ABL.append([2.34, 2.34, 2.20, 2.20, 2.20, 2.10, 2.00, 1.80, 1.60, 1.34, 1.30]) # 0
ABL.append([2.40, 2.40, 2.20, 2.20, 2.14, 2.10, 2.00, 1.80, 1.60, 1.40, 1.30]) # -15
ABL.append([2.40, 2.40, 2.20, 2.20, 2.10, 2.10, 1.90, 1.60, 1.40, 1.20, 1.20]) # -30
ABL.append([2.10, 2.30, 2.20, 2.10, 2.00, 2.00, 1.90, 1.70, 1.50, 1.30, 1.30]) # -45
ABL.append([-100, -100, 2.14, 2.00, 1.90, 1.90, 1.70, 1.60, 1.40, -100, -100]) # -60
ABL.append([-100, -100, 2.04, 2.00, 1.90, 1.80, 1.70, 1.50, 1.40, -100, -100]) # -65
# -65, -60 -45 -30 -15 0 15 30 45 60 65
# ponovo izmjeri -45, -30, 2.40 je možda previše
X_os=[-65, -60, -45, -30, -15, 0, 15, 30, 45, 60, 65] #koordinate na osi X
Y_os=[-65, -60, -45, -30, -15, 0, 15, 30, 45, 60, 65] #koordinate na osi Y
########################## OVDJE SU VRIJEDNOSTI KOJE SE MIJENAJU RUCNO ##########################
Last_X=-100 #za Deltatron ne moze biti -100 pa je taj broj uzet kao nedefinirana vrijednost
Last_Y=-100 #za Deltatron ne moze biti -100 pa je taj broj uzet kao nedefinirana vrijednost
Last_Z=-1 #ne moze biti -1 pa je taj broj uzet kao nedefinirana vrijednost
#3D linearna interpolacija
def XYZlin_int(x,y):
#ispravni raspon koordinata je od -65 do 64.9
Z=0
#Z1=Z2=Z3=Z4=-100 #-100 je nedozvoljena vrijednost pa je uzeta kao nedefinirana
X_start=X_stop=Y_start=Y_stop=-1 #indeks tocke u polju je nedfiniran
for i in range(len(X_os)): #trazi indeks polja X koordinate
#print('i=', i, 'X',X_os[i])
if x<X_os[i]:
X_start=i-1
if X_start<0:
print('Greska u liniji', Linija_br, '! X=', x, '<',X_os[i],file=sys.stderr)
exit()
X_stop=i
#print('X start=', X_start, 'X stop=', X_stop)
break
for j in range(len(Y_os)): #trazi indeks polja X koordinate
#print('j=', j, 'Y',Y_os[j])
if y<Y_os[j]:
Y_start=j-1
if Y_start<0:
print('Greska u liniji', Linija_br, '! Y=', y, '<',Y_os[j],file=sys.stderr)
exit()
Y_stop=j
#print('Y start=', Y_start, 'Y stop=', Y_stop)
break
if X_start==-1 or X_stop==-1 or Y_start==-1 or Y_stop==-1:
print('Greska u liniji', Linija_br, '! Jedna od koordinata je izvan granica! X=', x, 'Y=', y ,file=sys.stderr)
exit()
Z1=ABL[-Y_start-1][X_start]
Z2=ABL[-Y_start-1][X_stop]
Z3=ABL[-Y_stop-1][X_start]
Z4=ABL[-Y_stop-1][X_stop]
## print('Z1=', Z1)
## print('Z2=', Z2)
## print('Z3=', Z3)
## print('Z4=', Z4)
if Z1==-100 or Z2==-100 or Z3==-100 or Z4==-100:
print('Greska u liniji', Linija_br, '! Za X=', x, 'Y=', y, 'ABL nema definiranu vrijednost!', file=sys.stderr)
print('Z3=',Z3,'\tZ4=',Z4, file=sys.stderr)
print('Z1=',Z1,'\tZ2=',Z2, file=sys.stderr)
exit(0)
Za=lin_int(x, X_os[X_start], Z1, X_os[X_stop],Z2)
Zb=lin_int(y, Y_os[Y_start], Z1, Y_os[Y_stop],Z3)
Zc=lin_int(x, X_os[X_start], Z3, X_os[X_stop],Z4)
Zd=lin_int(y, Y_os[Y_start], Z2, Y_os[Y_stop],Z4)
Zac=lin_int(x, X_os[X_start], Za, X_os[X_stop],Zc) # linearna interpolacija izmedju interpoliranih visina
Zbd=lin_int(y, Y_os[Y_start], Zb, Y_os[Y_stop],Zd)
Zabcd=(Zac+Zbd)/2 # aritmeticka sredina interpoliranih visina (ako ne radi uzmi manju vrijednost)
## print('Za={:.3f}'.format(Za))
## print('Zb={:.3f}'.format(Zb))
## print('Zc={:.3f}'.format(Zc))
## print('Zd={:.3f}'.format(Zd))
Z=(Za+Zb+Zc+Zd)/4 #prosjek od sve 4 tocke
Z_min=min(Za,Zb) #izracunaj najmanju vrijednost
Z_min=min(Z_min,Zc)
Z_min=min(Z_min,Zd)
Z_max=max(Za,Zb) #izracunaj najvecu vrijednost
Z_max=max(Z_max,Zc)
Z_max=max(Z_max,Zd)
## print(Z_min, Z_max, Z, Z_max-Z_min)
## print(Z-Z_min)
Z=Zabcd
# if (Z-Z_min)>=0.15: #ako je max - min >= 0.1
# Z=(Zabcd+Z_min)/2 # za Z uzimi sredinu između interpolirane i najmanje vrijednosti
# print('Z={:.3f}'.format(Z))
#provjeri je li x,y pada tocno u raster
for i in X_os:
if i==x:
for j in Y_os:
if j==y:
Z=Z1 #ako pada tocno u raster, to je onda tocka Z1
break
#print('XYZ ->', '{:.3f}'.format(x), '{:.3f}'.format(y), '{:.3f}'.format(Z))
return Z
#3D linearna interpolacija
#mijenja ili dodaje Z koordinatu u G1 naredbu
def Z_replace(G1,z):
nasao_X=False
nasao_Y=False
nasao_Z=False
new_G1=""
argumenti=G1.split(' ')
for i in range(len(argumenti)):
if argumenti[i].startswith('Z'):
nasao_Z=True
nasao_X=True
nasao_Y=True
argumenti[i]='Z'+'{:.3f}'.format(z)
new_G1+=" "+argumenti[i]
if not nasao_Z: #u G1 liniji nema Z, trazi Y
new_G1=""
for i in range(len(argumenti)):
if argumenti[i].startswith('Y'):
nasao_Y=True
nasao_X=True
new_G1+=" "+argumenti[i]+' Z'+'{:.3f}'.format(z)
continue
new_G1+=" "+argumenti[i]
if not nasao_Y: #u G1 liniji nema ni Z, ni Y, trazi X
new_G1=""
for i in range(len(argumenti)):
if argumenti[i].startswith('X'):
nasao_Y=True
nasao_X=True
new_G1+=" "+argumenti[i]+' Z'+'{:.3f}'.format(z)
continue
new_G1+=" "+argumenti[i]
return new_G1.strip()
# end Z_replace
# linearna intervpolacije
def lin_int(x0,x1,y1,x2,y2):
x=y1+(x0-x1)*(y2-y1)/(x2-x1)
return x
# linearna interpolacija
#racuna novu Z koordinatu
def New_Z(x_coord,y_coord,z_coord):
## if math.sqrt(x_coord**2+y_coord**2)>Print_radius: #ovo provjeri nakon sto omogucis printanje po cijelom radijusu
## print('Greska u liniji', Linija_br, 'Tocka izvan print radijusa!',file=sys.stderr)
## exit()
Final_Z=XYZlin_int(x_coord,y_coord) #odredi Z obzirom na ABL
Final_Z+=z_coord #dodaj Z iz G koda
Final_Z+=Z_PROBE_OFFSET_FROM_EXTRUDER #dodaj offset iz Marlin Configuration.h
Final_Z+=Z_OFFSET #dodaj vlastiti offset na Z
return Final_Z
#end New_Z
#parsira G code
def Parser(gcode):
global Last_X, Last_Y, Last_Z
new_gcode=""
#trazi G-code G0 ili G1 s X ili Y ili Z parametrom
if gcode.startswith("G0") or gcode.startswith("G1"):
# ovu sam liniju naknadno prebacio ovdje pa provjeri kako radi (ako bi X, Y ili Z bili u komentaru, radilo bi krivo
gcode=gcode.split(';')[0] #iz G1 linije makni komentar ako ga ima
if " X" in gcode or " Y" in gcode or " Z" in gcode: #u G1 naredbi se nalazi X ili Y ili Z
#gcode=gcode.split(';')[0] #iz G1 linije makni komentar ako ga ima
# new_gcode="\t[" #samo radi testiranja
for gcode_arg in gcode.split(' '): #napravi listu od argumenata
new_gcode+=' '
if gcode_arg.startswith('X'): #ako je argument X
# new_gcode+='('+gcode_arg+')' #samo radi testiranja
new_gcode+=gcode_arg #prava verzija
Last_X=float(gcode_arg.strip('X'))
#print(Last_X)
elif gcode_arg.startswith('Y'): #ako je argument Y
# new_gcode+='('+gcode_arg+')' #samo radi testiranja
new_gcode+=gcode_arg #prava verzija
Last_Y=float(gcode_arg.strip('Y'))
#print(Last_Y)
elif gcode_arg.startswith('Z'): #ako je argument Z
#ovdje ide korekcija po Z osi
# new_gcode+='('+gcode_arg+')' #samo radi testiranja
Last_Z=float(gcode_arg.strip('Z'))
Last_Z_korigiran=New_Z(Last_X,Last_Y,Last_Z) #korigiraj Z
#print('1:',Last_Z_korigiran)
new_gcode+="Z"+'{:.3f}'.format(Last_Z_korigiran)
else: #ostali argumenti
new_gcode+=gcode_arg
#end for gcode_arg
# new_gcode+="]" #samo radi testiranja
#print('2:',Last_Z)
Last_Z_korigiran=New_Z(Last_X,Last_Y,Last_Z)
#print('3:',Last_Z_korigiran)
new_gcode=Z_replace(new_gcode,Last_Z_korigiran)
#new_gcode+=" -> "+'{:.3f}'.format(Last_Z) #samo radi testiranja
else:
new_gcode=gcode #ako u G1 nema X ili Y ili Z ostavi originalnu naredbu
else:
new_gcode=gcode #ako nije G1 ostavi originalni G-code
return new_gcode
#end Parser
# ------------------- ovdje pocinje glavni program ------------------------
Print_radius=(abs(X_os[0])+abs(X_os[-1]))/2
Linija_br=0
print("; G-code modificiran pomocu Z-calibration.py")
print("; Vrijeme: "+time.strftime("%d.%m.%Y %H:%M",time.localtime())+"\n")
print('Z-calibration: parsiram ...', file=sys.stderr)
for lajna in fileinput.input():
Linija_br+=1
lajna=lajna.strip() #makni whitespace
if lajna.startswith(";") or lajna=="": #ovo je komentar ili prazan red
print(lajna)
else: #ovdje se obradjuje G-code
print(Parser(lajna))
print('Z-calibration: br. linija:', Linija_br, file=sys.stderr)
print('Z-calibration: gotovo!', file=sys.stderr)
# end for lajna
# ------------------- ovdje zavrsava glavni program ------------------------
G-splitline.py
import fileinput, sys, math, time
Lmax=2.5 # max duljina jednog segmenta u mm
X1=Y1=X2=Y2=-100 # T1 i T2 ne moze biti -100 pa se taj broj smatra nedefiniranom vrijednosti
E=0 # originalna kolicina filamenta
E1=E2=0 # kolicina filamenta kod pocetne i konacne tocke jedne linije
Ei=0 # kolicina filamenta po segmentu
Li=0 # duljina jednog segmenta
Lx=Ly=0 # odsjecak na osi X i Y
alfa=0 # kut linije koja se crta
#parsira G code
def Parser(gcode):
global X1, Y1, X2, Y2, E, E1, E2
I=1 # broj segmenata
X=Y=-100
L=0
original_gcode=gcode #sacuvaj originalni red
new_gcode=""
#trazi G-code G0 ili G1 s X, Y, E parametrom
if gcode.startswith("G0") or gcode.startswith("G1"):
gcode=gcode.split(';')[0] #iz G1 linije makni komentar ako ga ima
if " E" in gcode and not " X" in gcode and not " Y" in gcode:
for gcode_arg in gcode.split(' '): #napravi listu od argumenata
if gcode_arg.startswith('E'): #ako je argument E
E=float(gcode_arg.strip('E'))
E1=E
E2=0
if " X" in gcode and " Y" in gcode and not " E" in gcode: # 1. tocka (G1 mora imati X,Y ali bez E)
for gcode_arg in gcode.split(' '): #napravi listu od argumenata
if gcode_arg.startswith('X'): #ako je argument X
X1=float(gcode_arg.strip('X'))
X=X1
X2=-100 # ponisti X druge tocke
elif gcode_arg.startswith('Y'): #ako je argument Y
Y1=float(gcode_arg.strip('Y'))
Y=Y1
Y2=-100 # ponisti Y druge tocke
# new_gcode='; 1: '+original_gcode # ovo je samo radi testiranja
new_gcode=original_gcode
print(new_gcode)
elif " X" in gcode and " Y" in gcode and " E" in gcode: # od 2. do zadnje tocke (G1 mora imati X,Y,E)
for gcode_arg in gcode.split(' '): #napravi listu od argumenata
if gcode_arg.startswith('X'): #ako je argument X
if X1==-100:
X1=float(gcode_arg.strip('X'))
X=X1
elif X2==-100:
X2=float(gcode_arg.strip('X'))
X=X2
elif gcode_arg.startswith('Y'): #ako je argument Y
if Y1==-100:
Y1=float(gcode_arg.strip('Y'))
Y=Y1
elif Y2==-100:
Y2=float(gcode_arg.strip('Y'))
Y=Y2
elif gcode_arg.startswith('E'): #ako je argument E
E=float(gcode_arg.strip('E'))
if Y2==-100:
E1=E # radi se o pocetnoj vrijednosti filamenta
else:
E2=E # radi se o konacnoj vrijednosti filamenta
# print(E1,E2)
#end for
# izracunaj duljinu pravca
# print(X1,Y1,X2,Y2) # ovo je samo radi testiranja
if X1>-100 and Y1>-100 and X2>-100 and Y2>-100: # ako su obje tocke definirane
L=math.sqrt((X1-X2)**2+(Y1-Y2)**2)
if X1==X2:
alfa=math.pi/2
else:
alfa=math.atan((Y1-Y2)/(X1-X2))
if L%Lmax>0: # gdje je ostatak >0, racuna broj segmenata
I=int(L/Lmax)+1
else:
I=int(L/Lmax)
Ei=(E2-E1)/I # kolicina filamenta po segmentu
Li=L/I # duljina jednog segmenta
Lx=abs(Li*math.cos(alfa)) # odsjecak na osi X
Ly=abs(Li*math.sin(alfa)) # odsjecak na osi Y
Xs=X1 # postavi koordinate na 1. tocku
Ys=Y1
# ispisi koordinate segmenata (od 2. do predzadnjeg)
for segment in range(2,I+1): # ako je pocetna koordinata manja od konacne, odsjecak zbroji, inace oduzmi
if X2>X1:
Xs+=Lx
elif X2<X1:
Xs-=Lx
else: Xs=X1
if Y2>Y1:
Ys+=Ly
elif Y2<Y1:
Ys-=Ly
else:
Ys=Y1
Ei=E1+(segment-1)*(E2-E1)/I # kolicina filamenta po segmentu
print("G1 X"+'{:.3f}'.format(Xs)+" Y"+'{:.3f}'.format(Ys)+" E"+'{:.5f}'.format(Ei)) # G-code dodanog segmenta
Ei=E1+(E2-E1) # kolicina filamenta za zadnji segment
print("G1 X"+'{:.3f}'.format(X2)+" Y"+'{:.3f}'.format(Y2)+" E"+'{:.5f}'.format(Ei)) # zadnji segment
# pripremi se za slijedecu tocku
X1=X2 # druga tocka postaje prva
Y1=Y2
E1=E2
X2=-100 # ponisti drugu tocku
Y2=-100
E2=0
if original_gcode.startswith("G0"):
new_gcode="; 2: G0 "+"X"+'{:.3f}'.format(X)+" Y"+'{:.3f}'.format(Y)+" E"+'{:.5f}'.format(E)+"\tL="+'{:.6f}'.format(L)+" I="+'{:d}'.format(I)
else:
new_gcode="; 2: G1 "+"X"+'{:.3f}'.format(X)+" Y"+'{:.3f}'.format(Y)+" E"+'{:.5f}'.format(E)+"\tL="+'{:.6f}'.format(L)+" I="+'{:d}'.format(I)
# print(new_gcode) # ovo je samo radi testiranja
else:
# print('; 3:'+original_gcode) #ostavi originalni gcode, mada ima G1, ovo je samo radi testiranja
print(original_gcode) #ostavi originalni gcode, mada ima G1
else:
# print('; 4:'+original_gcode) #ostavi originalni gcode, nije G1, ovo je samo radi testiranja
print(original_gcode) #ostavi originalni gcode, nije G1
#end Parser
# ------------------- ovdje pocinje glavni program ------------------------
Linija_br=0
print("; G-code modificiran pomoću G-splitline.py")
print("; Vrijeme: "+time.strftime("%d.%m.%Y %H:%M",time.localtime())+"\n")
print('G-splitline: parsiram ...', file=sys.stderr)
for lajna in fileinput.input():
Linija_br+=1
lajna=lajna.strip() #makni whitespace
if lajna.startswith(";") or lajna=="": #ovo je komentar ili prazan red
print(lajna)
else: #ovdje se obradjuje G-code
Parser(lajna)
print('G-splitline: br. linija:', Linija_br, file=sys.stderr)
print('G-splitline: gotovo!', file=sys.stderr)
# end for lajna
# ------------------- ovdje zavrsava glavni program ------------------------